From d2a265a72e78fcb0c4a71dbfc2dc556c8110226c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9D=87=E6=BA=90?= Date: Thu, 30 Mar 2023 21:09:21 +0800 Subject: [PATCH 01/32] feat: atomic --- .../caucho/hessian/io/SerializerFactory.java | 19 +++ .../hessian/io/atomic/AtomicHandle.java | 78 +++++++++++ .../hessian/io/atomic/AtomicSerializer.java | 68 ++++++++++ .../caucho/hessian/test/java8/AtomicTest.java | 126 +++++++++++++++++ .../hessian/test/java8/AtomicWrapper.java | 127 ++++++++++++++++++ 5 files changed, 418 insertions(+) create mode 100644 src/main/java/com/caucho/hessian/io/atomic/AtomicHandle.java create mode 100644 src/main/java/com/caucho/hessian/io/atomic/AtomicSerializer.java create mode 100644 src/test/java/com/caucho/hessian/test/java8/AtomicTest.java create mode 100644 src/test/java/com/caucho/hessian/test/java8/AtomicWrapper.java diff --git a/src/main/java/com/caucho/hessian/io/SerializerFactory.java b/src/main/java/com/caucho/hessian/io/SerializerFactory.java index ddff624..b1b32a3 100644 --- a/src/main/java/com/caucho/hessian/io/SerializerFactory.java +++ b/src/main/java/com/caucho/hessian/io/SerializerFactory.java @@ -51,6 +51,7 @@ import com.alipay.hessian.ClassNameResolver; import com.alipay.hessian.ClassNameResolverBuilder; import com.caucho.burlap.io.BurlapRemoteObject; +import com.caucho.hessian.io.atomic.AtomicSerializer; import com.caucho.hessian.io.java8.DurationHandle; import com.caucho.hessian.io.java8.InstantHandle; import com.caucho.hessian.io.java8.Java8TimeSerializer; @@ -72,6 +73,11 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; import java.util.logging.Level; import java.util.logging.Logger; @@ -429,6 +435,7 @@ public Deserializer getObjectDeserializer(String type, Class cl) if (cl == null || cl == reader.getType() + || HessianHandle.class.isAssignableFrom(reader.getType()) || cl.isAssignableFrom(reader.getType())) { return reader; } @@ -662,6 +669,18 @@ protected static void addBasic(Class cl, String typeName, int type) log.warning(String.valueOf(t.getCause())); } + try { + AtomicSerializer atomicSerializer = new AtomicSerializer(); + _staticSerializerMap.put(AtomicInteger.class, atomicSerializer); + _staticSerializerMap.put(AtomicLong.class, atomicSerializer); + _staticSerializerMap.put(AtomicBoolean.class, atomicSerializer); + _staticSerializerMap.put(AtomicLongArray.class, atomicSerializer); + _staticSerializerMap.put(AtomicIntegerArray.class, atomicSerializer); + + } catch (Throwable t) { + log.warning(String.valueOf(t.getCause())); + } + } /** diff --git a/src/main/java/com/caucho/hessian/io/atomic/AtomicHandle.java b/src/main/java/com/caucho/hessian/io/atomic/AtomicHandle.java new file mode 100644 index 0000000..0daea98 --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/atomic/AtomicHandle.java @@ -0,0 +1,78 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.atomic; + +import com.caucho.hessian.io.HessianHandle; + +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; + +/** + * + * @author junyuan + * @version AtomicHandle.java, v 0.1 2023年03月30日 19:57 junyuan Exp $ + */ +public class AtomicHandle implements HessianHandle, Serializable { + private static final long serialVersionUID = 5253528013752548947L; + + private Class type; + private Object value; + + public AtomicHandle(Class type, Object value) { + this.type = type; + this.value = value; + } + + protected Object readResolve() { + if (AtomicInteger.class.equals(type)) { + return new AtomicInteger((Integer) value); + } + else if (AtomicLong.class.equals(type)) { + return new AtomicLong((Long) value); + } + else if (AtomicBoolean.class.equals(type)) { + return new AtomicBoolean((Boolean) value); + } + else if (AtomicReference.class.equals(type)) { + return new AtomicReference(value); + } + else if (AtomicIntegerArray.class.equals(type)) { + int[] tmp = (int[]) value; + int len = tmp.length; + AtomicIntegerArray array = new AtomicIntegerArray(len); + for (int i = 0; i < len; i++) { + array.set(i, tmp[i]); + } + return array; + } + else if (AtomicLongArray.class.equals(type)) { + long[] tmp = (long[]) value; + int len = tmp.length; + AtomicLongArray array = new AtomicLongArray(len); + for (int i = 0; i < len; i++) { + array.set(i, tmp[i]); + } + return array; + } + else if (AtomicReferenceArray.class.equals(type)) { + Object[] tmp = (Object[]) value; + int len = tmp.length; + AtomicReferenceArray array = new AtomicReferenceArray(len); + for (int i = 0; i < len; i++) { + array.set(i, tmp[i]); + } + return array; + } + + throw new UnsupportedOperationException(String.valueOf(this)); + } + +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/atomic/AtomicSerializer.java b/src/main/java/com/caucho/hessian/io/atomic/AtomicSerializer.java new file mode 100644 index 0000000..69d64ab --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/atomic/AtomicSerializer.java @@ -0,0 +1,68 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.atomic; + +import com.caucho.hessian.io.AbstractHessianOutput; +import com.caucho.hessian.io.AbstractSerializer; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; +import java.util.concurrent.atomic.AtomicReference; + +/** + * + * @author junyuan + * @version AtomicSerializer.java, v 0.1 2023年03月30日 17:29 junyuan Exp $ + */ +public class AtomicSerializer extends AbstractSerializer { + @Override + public void writeObject(Object obj, AbstractHessianOutput out) throws IOException { + if (obj == null) { + out.writeNull(); + return; + } + + Object value; + if (obj instanceof AtomicBoolean) { + value = ((AtomicBoolean) obj).get(); + } + else if (obj instanceof AtomicInteger) { + value = ((AtomicInteger) obj).get(); + } + else if (obj instanceof AtomicLong) { + value = ((AtomicLong) obj).get(); + } + else if (obj instanceof AtomicReference) { + value = ((AtomicReference) obj).get(); + } + else if (obj instanceof AtomicIntegerArray) { + AtomicIntegerArray tmp = (AtomicIntegerArray) obj; + int len = tmp.length(); + int[] ints = new int[len]; + for (int i = 0; i < len; i++) { + ints[i] = tmp.get(i); + } + value = ints; + } + else if (obj instanceof AtomicLongArray) { + AtomicLongArray tmp = (AtomicLongArray) obj; + int len = tmp.length(); + long[] ints = new long[len]; + for (int i = 0; i < len; i++) { + ints[i] = tmp.get(i); + } + value = ints; + } else { + throw new UnsupportedOperationException(String.valueOf(this)); + } + + out.writeObject(new AtomicHandle(obj.getClass(), value)); + } + +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/java8/AtomicTest.java b/src/test/java/com/caucho/hessian/test/java8/AtomicTest.java new file mode 100644 index 0000000..66f8aca --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/java8/AtomicTest.java @@ -0,0 +1,126 @@ +/* + * 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 com.caucho.hessian.test.java8; + +import com.caucho.hessian.io.Hessian2Input; +import com.caucho.hessian.io.Hessian2Output; +import com.caucho.hessian.io.SerializerFactory; +import junit.framework.TestCase; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; + +/** + * + * @author junyuan + * @version AtomicTest.java, v 0.1 2023年03月29日 18:16 junyuan Exp $ + */ +public class AtomicTest { + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + + os = new ByteArrayOutputStream(); + } + + @Test + public void test_atomicWrapper() throws IOException { + os.reset(); + + // prepare + AtomicWrapper atomicWrapper = new AtomicWrapper(); + atomicWrapper.setaInteger(new AtomicInteger(17)); + atomicWrapper.setaBoolean(new AtomicBoolean(true)); + atomicWrapper.setaLong(new AtomicLong(2147483649L)); + + atomicWrapper.setaIntegerArray(new AtomicIntegerArray(2)); + atomicWrapper.getaIntegerArray().set(0, 0); + atomicWrapper.getaIntegerArray().set(1, 1); + + atomicWrapper.setaLongArray(new AtomicLongArray(2)); + atomicWrapper.getaLongArray().set(0, 0L); + atomicWrapper.getaLongArray().set(1, 1L); + + // work + Object actual = doEncodeNDecode(atomicWrapper); + + // check + Assert.assertTrue(actual instanceof AtomicWrapper); + Assert.assertEquals(atomicWrapper.getaInteger().get(), ((AtomicWrapper) actual).getaInteger().get()); + Assert.assertEquals(atomicWrapper.getaBoolean().get(), ((AtomicWrapper) actual).getaBoolean().get()); + Assert.assertEquals(atomicWrapper.getaLong().get(), ((AtomicWrapper) actual).getaLong().get()); + + Assert + .assertEquals(atomicWrapper.getaIntegerArray().get(0), ((AtomicWrapper) actual).getaIntegerArray().get(0)); + Assert + .assertEquals(atomicWrapper.getaIntegerArray().get(1), ((AtomicWrapper) actual).getaIntegerArray().get(1)); + + Assert.assertEquals(atomicWrapper.getaLongArray().get(0), ((AtomicWrapper) actual).getaLongArray().get(0)); + Assert.assertEquals(atomicWrapper.getaLongArray().get(1), ((AtomicWrapper) actual).getaLongArray().get(1)); + } + + @Test + public void test_atomic_unwrappedInteger() throws IOException { + os.reset(); + AtomicInteger i = new AtomicInteger(1); + Object actual = doEncodeNDecode(i); + + Assert.assertTrue(actual instanceof AtomicInteger); + TestCase.assertEquals(i.get(), ((AtomicInteger) actual).get()); + } + + @Test + public void test_atomic_unwrappedIntegerArray() throws IOException { + os.reset(); + AtomicIntegerArray i = new AtomicIntegerArray(2); + i.set(0, 0); + i.set(1, 1); + + Object actual = doEncodeNDecode(i); + + Assert.assertTrue(actual instanceof AtomicIntegerArray); + Assert.assertEquals(i.get(0), ((AtomicIntegerArray) actual).get(0)); + Assert.assertEquals(i.get(1), ((AtomicIntegerArray) actual).get(1)); + } + + protected Object doEncodeNDecode(Object origin) throws IOException { + os.reset(); + Hessian2Output output = new Hessian2Output(os); + + output.setSerializerFactory(factory); + output.writeObject(origin); + output.flush(); + + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + Hessian2Input input = new Hessian2Input(is); + input.setSerializerFactory(factory); + Object actual = input.readObject(); + return actual; + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/java8/AtomicWrapper.java b/src/test/java/com/caucho/hessian/test/java8/AtomicWrapper.java new file mode 100644 index 0000000..da08a79 --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/java8/AtomicWrapper.java @@ -0,0 +1,127 @@ +/* + * 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 com.caucho.hessian.test.java8; + +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; + +/** + * + * @author junyuan + * @version AtomicWrapper.java, v 0.1 2023年03月29日 18:15 junyuan Exp $ + */ +public class AtomicWrapper implements Serializable { + private AtomicInteger aInteger; + private AtomicBoolean aBoolean; + private AtomicLong aLong; + private AtomicIntegerArray aIntegerArray; + private AtomicLongArray aLongArray; + + /** + * Getter method for property ai. + * + * @return property value of ai + */ + public AtomicInteger getaInteger() { + return aInteger; + } + + /** + * Setter method for property ai. + * + * @param aInteger value to be assigned to property ai + */ + public void setaInteger(AtomicInteger aInteger) { + this.aInteger = aInteger; + } + + /** + * Getter method for property ab. + * + * @return property value of ab + */ + public AtomicBoolean getaBoolean() { + return aBoolean; + } + + /** + * Setter method for property ab. + * + * @param aBoolean value to be assigned to property ab + */ + public void setaBoolean(AtomicBoolean aBoolean) { + this.aBoolean = aBoolean; + } + + /** + * Getter method for property al. + * + * @return property value of al + */ + public AtomicLong getaLong() { + return aLong; + } + + /** + * Setter method for property al. + * + * @param aLong value to be assigned to property al + */ + public void setaLong(AtomicLong aLong) { + this.aLong = aLong; + } + + /** + * Getter method for property aia. + * + * @return property value of aia + */ + public AtomicIntegerArray getaIntegerArray() { + return aIntegerArray; + } + + /** + * Setter method for property aia. + * + * @param aIntegerArray value to be assigned to property aia + */ + public void setaIntegerArray(AtomicIntegerArray aIntegerArray) { + this.aIntegerArray = aIntegerArray; + } + + /** + * Getter method for property ala. + * + * @return property value of ala + */ + public AtomicLongArray getaLongArray() { + return aLongArray; + } + + /** + * Setter method for property ala. + * + * @param aLongArray value to be assigned to property ala + */ + public void setaLongArray(AtomicLongArray aLongArray) { + this.aLongArray = aLongArray; + } +} \ No newline at end of file From d0054ba3453206478b9ae4a89eae986032bafe83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9D=87=E6=BA=90?= Date: Thu, 30 Mar 2023 21:32:40 +0800 Subject: [PATCH 02/32] feat: ignore failure when set accessible --- .../caucho/hessian/io/JavaDeserializer.java | 11 ++-- .../com/caucho/hessian/io/JavaSerializer.java | 9 ++-- .../caucho/hessian/util/ReflectionUtil.java | 51 +++++++++++++++++++ 3 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/caucho/hessian/util/ReflectionUtil.java diff --git a/src/main/java/com/caucho/hessian/io/JavaDeserializer.java b/src/main/java/com/caucho/hessian/io/JavaDeserializer.java index 42718b3..eb4bc86 100644 --- a/src/main/java/com/caucho/hessian/io/JavaDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/JavaDeserializer.java @@ -48,6 +48,7 @@ package com.caucho.hessian.io; +import com.caucho.hessian.util.ReflectionUtil; import sun.misc.Unsafe; import java.io.IOException; @@ -99,7 +100,7 @@ public JavaDeserializer(Class cl) _readResolve = getReadResolve(cl); if (_readResolve != null) { - _readResolve.setAccessible(true); + ReflectionUtil.setAccessible(_readResolve); } Constructor[] constructors = cl.getDeclaredConstructors(); @@ -138,7 +139,7 @@ else if (param[j].isPrimitive()) } if (_constructor != null) { - _constructor.setAccessible(true); + ReflectionUtil.setAccessible(_constructor); Class[] params = _constructor.getParameterTypes(); _constructorArgs = new Object[params.length]; for (int i = 0; i < params.length; i++) { @@ -325,11 +326,7 @@ else if (fieldMap.get(field.getName()) != null) continue; // XXX: could parameterize the handler to only deal with public - try { - field.setAccessible(true); - } catch (Throwable e) { - e.printStackTrace(); - } + ReflectionUtil.setAccessible(field); Class type = field.getType(); FieldDeserializer deser; diff --git a/src/main/java/com/caucho/hessian/io/JavaSerializer.java b/src/main/java/com/caucho/hessian/io/JavaSerializer.java index 327a859..51035b3 100644 --- a/src/main/java/com/caucho/hessian/io/JavaSerializer.java +++ b/src/main/java/com/caucho/hessian/io/JavaSerializer.java @@ -48,6 +48,8 @@ package com.caucho.hessian.io; +import com.caucho.hessian.util.ReflectionUtil; + import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -70,8 +72,9 @@ public class JavaSerializer extends AbstractSerializer public JavaSerializer(Class cl) { _writeReplace = getWriteReplace(cl); - if (_writeReplace != null) - _writeReplace.setAccessible(true); + if (_writeReplace != null) { + ReflectionUtil.setAccessible(_writeReplace); + } ArrayList primitiveFields = new ArrayList(); ArrayList compoundFields = new ArrayList(); @@ -86,7 +89,7 @@ public JavaSerializer(Class cl) continue; // XXX: could parameterize the handler to only deal with public - field.setAccessible(true); + ReflectionUtil.setAccessible(field); if (field.getType().isPrimitive() || field.getType().getName().startsWith("java.lang.") && diff --git a/src/main/java/com/caucho/hessian/util/ReflectionUtil.java b/src/main/java/com/caucho/hessian/util/ReflectionUtil.java new file mode 100644 index 0000000..6fef8e6 --- /dev/null +++ b/src/main/java/com/caucho/hessian/util/ReflectionUtil.java @@ -0,0 +1,51 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.util; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author junyuan + * @version ReflectionUtil.java, v 0.1 2023年03月30日 21:23 junyuan Exp $ + */ +public class ReflectionUtil { + private static final Logger log = Logger.getLogger(ReflectionUtil.class.getName()); + + public static boolean setAccessible(Method m) { + try { + m.setAccessible(true); + } catch (Throwable t) { + log.log(Level.WARNING, t.toString(), t); + return false; + } + return true; + } + + public static boolean setAccessible(Constructor c) { + try { + c.setAccessible(true); + } catch (Throwable t) { + log.log(Level.WARNING, t.toString(), t); + return false; + } + return true; + } + + public static boolean setAccessible(Field f) { + try { + f.setAccessible(true); + } catch (Throwable t) { + log.log(Level.WARNING, t.toString(), t); + return false; + } + return true; + } + +} \ No newline at end of file From dc26a3852ab09f7005af31c11014e712c8f5f672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9D=87=E6=BA=90?= Date: Tue, 4 Apr 2023 12:26:17 +0800 Subject: [PATCH 03/32] feat: replace reflection with specific get set for atomic-s --- .../caucho/hessian/io/SerializerFactory.java | 13 + .../hessian/io/atomic/AtomicDeserializer.java | 83 ++++++ .../hessian/io/atomic/AtomicSerializer.java | 92 ++++-- .../AtomicDeserializeTest.java} | 77 ++++- .../hessian/test/atomic/AtomicWrapper.java | 167 +++++++++++ .../test/atomic/SerializeCompatibleTest.java | 262 ++++++++++++++++++ .../hessian/test/java8/AtomicWrapper.java | 127 --------- 7 files changed, 673 insertions(+), 148 deletions(-) create mode 100644 src/main/java/com/caucho/hessian/io/atomic/AtomicDeserializer.java rename src/test/java/com/caucho/hessian/test/{java8/AtomicTest.java => atomic/AtomicDeserializeTest.java} (61%) create mode 100644 src/test/java/com/caucho/hessian/test/atomic/AtomicWrapper.java create mode 100644 src/test/java/com/caucho/hessian/test/atomic/SerializeCompatibleTest.java delete mode 100644 src/test/java/com/caucho/hessian/test/java8/AtomicWrapper.java diff --git a/src/main/java/com/caucho/hessian/io/SerializerFactory.java b/src/main/java/com/caucho/hessian/io/SerializerFactory.java index b1b32a3..64178e3 100644 --- a/src/main/java/com/caucho/hessian/io/SerializerFactory.java +++ b/src/main/java/com/caucho/hessian/io/SerializerFactory.java @@ -51,6 +51,7 @@ import com.alipay.hessian.ClassNameResolver; import com.alipay.hessian.ClassNameResolverBuilder; import com.caucho.burlap.io.BurlapRemoteObject; +import com.caucho.hessian.io.atomic.AtomicDeserializer; import com.caucho.hessian.io.atomic.AtomicSerializer; import com.caucho.hessian.io.java8.DurationHandle; import com.caucho.hessian.io.java8.InstantHandle; @@ -78,6 +79,8 @@ import java.util.concurrent.atomic.AtomicIntegerArray; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLongArray; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.logging.Level; import java.util.logging.Logger; @@ -674,8 +677,18 @@ protected static void addBasic(Class cl, String typeName, int type) _staticSerializerMap.put(AtomicInteger.class, atomicSerializer); _staticSerializerMap.put(AtomicLong.class, atomicSerializer); _staticSerializerMap.put(AtomicBoolean.class, atomicSerializer); + _staticSerializerMap.put(AtomicReference.class, atomicSerializer); _staticSerializerMap.put(AtomicLongArray.class, atomicSerializer); _staticSerializerMap.put(AtomicIntegerArray.class, atomicSerializer); + _staticSerializerMap.put(AtomicReferenceArray.class, atomicSerializer); + + _staticDeserializerMap.put(AtomicInteger.class, new AtomicDeserializer(AtomicInteger.class)); + _staticDeserializerMap.put(AtomicLong.class, new AtomicDeserializer(AtomicLong.class)); + _staticDeserializerMap.put(AtomicBoolean.class, new AtomicDeserializer(AtomicBoolean.class)); + _staticDeserializerMap.put(AtomicReference.class, new AtomicDeserializer(AtomicReference.class)); + _staticDeserializerMap.put(AtomicLongArray.class, new AtomicDeserializer(AtomicLongArray.class)); + _staticDeserializerMap.put(AtomicIntegerArray.class, new AtomicDeserializer(AtomicIntegerArray.class)); + _staticDeserializerMap.put(AtomicReferenceArray.class, new AtomicDeserializer(AtomicReferenceArray.class)); } catch (Throwable t) { log.warning(String.valueOf(t.getCause())); diff --git a/src/main/java/com/caucho/hessian/io/atomic/AtomicDeserializer.java b/src/main/java/com/caucho/hessian/io/atomic/AtomicDeserializer.java new file mode 100644 index 0000000..072b255 --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/atomic/AtomicDeserializer.java @@ -0,0 +1,83 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.atomic; + +import com.caucho.hessian.io.AbstractDeserializer; +import com.caucho.hessian.io.AbstractHessianInput; +import com.caucho.hessian.io.ArrayDeserializer; +import com.caucho.hessian.io.BasicDeserializer; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; + +/** + * + * @author junyuan + * @version AtomicDeserializer.java, v 0.1 2023年03月31日 17:34 junyuan Exp $ + */ +public class AtomicDeserializer extends AbstractDeserializer { + + private Class _type; + + private BasicDeserializer intArrayDsr = new BasicDeserializer(BasicDeserializer.INTEGER_ARRAY); + private BasicDeserializer longArrayDsr = new BasicDeserializer(BasicDeserializer.LONG_ARRAY); + private BasicDeserializer objectArrayDsr = new BasicDeserializer(BasicDeserializer.OBJECT_ARRAY); + + public AtomicDeserializer(Class cl) { + this._type = cl; + } + + @Override + public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IOException { + + if (AtomicInteger.class.equals(_type)) { + return new AtomicInteger(in.readInt()); + } + else if (AtomicBoolean.class.equals(_type)) { + return new AtomicBoolean(in.readInt() == 1); + } + else if (AtomicLong.class.equals(_type)) { + return new AtomicLong(in.readLong()); + } + else if (AtomicReference.class.equals(_type)) { + return new AtomicReference(in.readObject()); + } + else if (AtomicIntegerArray.class.equals(_type)) { + int[] res = (int[]) intArrayDsr.readObject(in); + int len = res.length; + AtomicIntegerArray array = new AtomicIntegerArray(len); + for (int i = 0; i < len; i++) { + array.set(i, res[i]); + } + return array; + } + else if (AtomicLongArray.class.equals(_type)) { + long[] res = (long[]) longArrayDsr.readObject(in); + int len = res.length; + AtomicLongArray array = new AtomicLongArray(len); + for (int i = 0; i < len; i++) { + array.set(i, res[i]); + } + return array; + } + else if (AtomicReferenceArray.class.equals(_type)) { + Object[] res = (Object[]) in.readObject((new Object[0]).getClass()); + int len = res.length; + AtomicReferenceArray array = new AtomicReferenceArray(len); + for (int i = 0; i < len; i++) { + array.set(i, res[i]); + } + return array; + } + + throw new UnsupportedOperationException(String.valueOf(this)); + } +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/atomic/AtomicSerializer.java b/src/main/java/com/caucho/hessian/io/atomic/AtomicSerializer.java index 69d64ab..5ac4a4e 100644 --- a/src/main/java/com/caucho/hessian/io/atomic/AtomicSerializer.java +++ b/src/main/java/com/caucho/hessian/io/atomic/AtomicSerializer.java @@ -14,6 +14,7 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLongArray; import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; /** * @@ -30,39 +31,94 @@ public void writeObject(Object obj, AbstractHessianOutput out) throws IOExceptio Object value; if (obj instanceof AtomicBoolean) { - value = ((AtomicBoolean) obj).get(); + doWrite(obj, "value", out); } else if (obj instanceof AtomicInteger) { - value = ((AtomicInteger) obj).get(); + doWrite(obj, "value", out); } else if (obj instanceof AtomicLong) { - value = ((AtomicLong) obj).get(); + doWrite(obj, "value", out); } else if (obj instanceof AtomicReference) { - value = ((AtomicReference) obj).get(); + doWrite(obj, "value", out); } else if (obj instanceof AtomicIntegerArray) { - AtomicIntegerArray tmp = (AtomicIntegerArray) obj; - int len = tmp.length(); - int[] ints = new int[len]; + doWrite(obj, "array", out); + } + else if (obj instanceof AtomicLongArray) { + doWrite(obj, "array", out); + } + else if (obj instanceof AtomicReferenceArray) { + doWrite(obj, "array", out); + } + else { + throw new UnsupportedOperationException(String.valueOf(this)); + } + } + + protected void doWrite(Object obj, String fieldName, AbstractHessianOutput out) throws IOException { + if (out.addRef(obj)) { + return; + } + Class cl = obj.getClass(); + int ref = out.writeObjectBegin(cl.getName()); // atomicinteger.class + if (ref < -1) { + // writeObject10(obj, out); + out.writeString(fieldName/*field name*/); + // field do serialize + writeFieldValue(obj, out); + } + else { + if (ref == -1) { + out.writeClassFieldLength(1); + out.writeString(fieldName/*field name*/); + out.writeObjectBegin(cl.getName()); + } + // field foreach do serialize + writeFieldValue(obj, out); + } + } + + private void writeFieldValue(Object obj, AbstractHessianOutput out) throws IOException { + if (obj instanceof AtomicInteger) { + out.writeInt(((AtomicInteger) obj).get()); + } + else if (obj instanceof AtomicLong) { + out.writeLong(((AtomicLong) obj).get()); + } + else if (obj instanceof AtomicBoolean) { + out.writeInt(((AtomicBoolean) obj).get() ? 1 : 0); + } + else if (obj instanceof AtomicReference) { + out.writeObject(((AtomicReference) obj).get()); + } + else if (obj instanceof AtomicIntegerArray) { + AtomicIntegerArray array = (AtomicIntegerArray) obj; + int len = array.length(); + int[] tmp = new int[len]; for (int i = 0; i < len; i++) { - ints[i] = tmp.get(i); + tmp[i] = array.get(i); } - value = ints; + out.writeObject(tmp); } else if (obj instanceof AtomicLongArray) { - AtomicLongArray tmp = (AtomicLongArray) obj; - int len = tmp.length(); - long[] ints = new long[len]; + AtomicLongArray array = (AtomicLongArray) obj; + int len = array.length(); + long[] tmp = new long[len]; for (int i = 0; i < len; i++) { - ints[i] = tmp.get(i); + tmp[i] = array.get(i); } - value = ints; - } else { - throw new UnsupportedOperationException(String.valueOf(this)); + out.writeObject(tmp); + } + else if (obj instanceof AtomicReferenceArray) { + AtomicReferenceArray array = (AtomicReferenceArray) obj; + int len = array.length(); + Object[] tmp = new Object[len]; + for (int i = 0; i < len; i++) { + tmp[i] = array.get(i); + } + out.writeObject(tmp); } - - out.writeObject(new AtomicHandle(obj.getClass(), value)); } } \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/java8/AtomicTest.java b/src/test/java/com/caucho/hessian/test/atomic/AtomicDeserializeTest.java similarity index 61% rename from src/test/java/com/caucho/hessian/test/java8/AtomicTest.java rename to src/test/java/com/caucho/hessian/test/atomic/AtomicDeserializeTest.java index 66f8aca..cd6f54d 100644 --- a/src/test/java/com/caucho/hessian/test/java8/AtomicTest.java +++ b/src/test/java/com/caucho/hessian/test/atomic/AtomicDeserializeTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.caucho.hessian.test.java8; +package com.caucho.hessian.test.atomic; import com.caucho.hessian.io.Hessian2Input; import com.caucho.hessian.io.Hessian2Output; @@ -32,13 +32,15 @@ import java.util.concurrent.atomic.AtomicIntegerArray; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLongArray; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; /** * * @author junyuan - * @version AtomicTest.java, v 0.1 2023年03月29日 18:16 junyuan Exp $ + * @version AtomicDeserializeTest.java, v 0.1 2023年03月29日 18:16 junyuan Exp $ */ -public class AtomicTest { +public class AtomicDeserializeTest { private static SerializerFactory factory; private static ByteArrayOutputStream os; @@ -58,6 +60,7 @@ public void test_atomicWrapper() throws IOException { atomicWrapper.setaInteger(new AtomicInteger(17)); atomicWrapper.setaBoolean(new AtomicBoolean(true)); atomicWrapper.setaLong(new AtomicLong(2147483649L)); + atomicWrapper.setaReference(new AtomicReference(new Integer(1))); atomicWrapper.setaIntegerArray(new AtomicIntegerArray(2)); atomicWrapper.getaIntegerArray().set(0, 0); @@ -67,6 +70,10 @@ public void test_atomicWrapper() throws IOException { atomicWrapper.getaLongArray().set(0, 0L); atomicWrapper.getaLongArray().set(1, 1L); + atomicWrapper.setaReferenceArray(new AtomicReferenceArray(2)); + atomicWrapper.getaReferenceArray().set(0, new Integer(1)); + atomicWrapper.getaReferenceArray().set(1, new Integer(2)); + // work Object actual = doEncodeNDecode(atomicWrapper); @@ -75,6 +82,7 @@ public void test_atomicWrapper() throws IOException { Assert.assertEquals(atomicWrapper.getaInteger().get(), ((AtomicWrapper) actual).getaInteger().get()); Assert.assertEquals(atomicWrapper.getaBoolean().get(), ((AtomicWrapper) actual).getaBoolean().get()); Assert.assertEquals(atomicWrapper.getaLong().get(), ((AtomicWrapper) actual).getaLong().get()); + Assert.assertEquals(atomicWrapper.getaReference().get(), ((AtomicWrapper) actual).getaReference().get()); Assert .assertEquals(atomicWrapper.getaIntegerArray().get(0), ((AtomicWrapper) actual).getaIntegerArray().get(0)); @@ -83,6 +91,11 @@ public void test_atomicWrapper() throws IOException { Assert.assertEquals(atomicWrapper.getaLongArray().get(0), ((AtomicWrapper) actual).getaLongArray().get(0)); Assert.assertEquals(atomicWrapper.getaLongArray().get(1), ((AtomicWrapper) actual).getaLongArray().get(1)); + + Assert.assertEquals(atomicWrapper.getaReferenceArray().get(0), ((AtomicWrapper) actual).getaReferenceArray() + .get(0)); + Assert.assertEquals(atomicWrapper.getaReferenceArray().get(1), ((AtomicWrapper) actual).getaReferenceArray() + .get(1)); } @Test @@ -95,6 +108,36 @@ public void test_atomic_unwrappedInteger() throws IOException { TestCase.assertEquals(i.get(), ((AtomicInteger) actual).get()); } + @Test + public void test_atomic_unwrappedLong() throws IOException { + os.reset(); + AtomicLong i = new AtomicLong(1L); + Object actual = doEncodeNDecode(i); + + Assert.assertTrue(actual instanceof AtomicLong); + TestCase.assertEquals(i.get(), ((AtomicLong) actual).get()); + } + + @Test + public void test_atomic_unwrappedBoolean() throws IOException { + os.reset(); + AtomicBoolean i = new AtomicBoolean(true); + Object actual = doEncodeNDecode(i); + + Assert.assertTrue(actual instanceof AtomicBoolean); + TestCase.assertEquals(i.get(), ((AtomicBoolean) actual).get()); + } + + @Test + public void test_atomic_unwrappedReference() throws IOException { + os.reset(); + AtomicReference i = new AtomicReference(new Integer(1)); + Object actual = doEncodeNDecode(i); + + Assert.assertTrue(actual instanceof AtomicReference); + TestCase.assertEquals(i.get(), ((AtomicReference) actual).get()); + } + @Test public void test_atomic_unwrappedIntegerArray() throws IOException { os.reset(); @@ -109,6 +152,34 @@ public void test_atomic_unwrappedIntegerArray() throws IOException { Assert.assertEquals(i.get(1), ((AtomicIntegerArray) actual).get(1)); } + @Test + public void test_atomic_unwrappedLongArray() throws IOException { + os.reset(); + AtomicLongArray i = new AtomicLongArray(2); + i.set(0, 0L); + i.set(1, 1L); + + Object actual = doEncodeNDecode(i); + + Assert.assertTrue(actual instanceof AtomicLongArray); + Assert.assertEquals(i.get(0), ((AtomicLongArray) actual).get(0)); + Assert.assertEquals(i.get(1), ((AtomicLongArray) actual).get(1)); + } + + @Test + public void test_atomic_unwrappedReferenceArray() throws IOException { + os.reset(); + AtomicReferenceArray i = new AtomicReferenceArray(2); + i.set(0, new Integer(0)); + i.set(1, new Integer(1)); + + Object actual = doEncodeNDecode(i); + + Assert.assertTrue(actual instanceof AtomicReferenceArray); + Assert.assertEquals(i.get(0), ((AtomicReferenceArray) actual).get(0)); + Assert.assertEquals(i.get(1), ((AtomicReferenceArray) actual).get(1)); + } + protected Object doEncodeNDecode(Object origin) throws IOException { os.reset(); Hessian2Output output = new Hessian2Output(os); diff --git a/src/test/java/com/caucho/hessian/test/atomic/AtomicWrapper.java b/src/test/java/com/caucho/hessian/test/atomic/AtomicWrapper.java new file mode 100644 index 0000000..bbc08c5 --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/atomic/AtomicWrapper.java @@ -0,0 +1,167 @@ +/* + * 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 com.caucho.hessian.test.atomic; + +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; + +/** + * + * @author junyuan + * @version AtomicWrapper.java, v 0.1 2023年03月29日 18:15 junyuan Exp $ + */ +public class AtomicWrapper implements Serializable { + private AtomicInteger aInteger; + private AtomicBoolean aBoolean; + private AtomicLong aLong; + private AtomicReference aReference; + private AtomicIntegerArray aIntegerArray; + private AtomicLongArray aLongArray; + private AtomicReferenceArray aReferenceArray; + + /** + * Getter method for property aInteger. + * + * @return property value of aInteger + */ + public AtomicInteger getaInteger() { + return aInteger; + } + + /** + * Setter method for property aInteger. + * + * @param aInteger value to be assigned to property aInteger + */ + public void setaInteger(AtomicInteger aInteger) { + this.aInteger = aInteger; + } + + /** + * Getter method for property aBoolean. + * + * @return property value of aBoolean + */ + public AtomicBoolean getaBoolean() { + return aBoolean; + } + + /** + * Setter method for property aBoolean. + * + * @param aBoolean value to be assigned to property aBoolean + */ + public void setaBoolean(AtomicBoolean aBoolean) { + this.aBoolean = aBoolean; + } + + /** + * Getter method for property aLong. + * + * @return property value of aLong + */ + public AtomicLong getaLong() { + return aLong; + } + + /** + * Setter method for property aLong. + * + * @param aLong value to be assigned to property aLong + */ + public void setaLong(AtomicLong aLong) { + this.aLong = aLong; + } + + /** + * Getter method for property aReference. + * + * @return property value of aReference + */ + public AtomicReference getaReference() { + return aReference; + } + + /** + * Setter method for property aReference. + * + * @param aReference value to be assigned to property aReference + */ + public void setaReference(AtomicReference aReference) { + this.aReference = aReference; + } + + /** + * Getter method for property aIntegerArray. + * + * @return property value of aIntegerArray + */ + public AtomicIntegerArray getaIntegerArray() { + return aIntegerArray; + } + + /** + * Setter method for property aIntegerArray. + * + * @param aIntegerArray value to be assigned to property aIntegerArray + */ + public void setaIntegerArray(AtomicIntegerArray aIntegerArray) { + this.aIntegerArray = aIntegerArray; + } + + /** + * Getter method for property aLongArray. + * + * @return property value of aLongArray + */ + public AtomicLongArray getaLongArray() { + return aLongArray; + } + + /** + * Setter method for property aLongArray. + * + * @param aLongArray value to be assigned to property aLongArray + */ + public void setaLongArray(AtomicLongArray aLongArray) { + this.aLongArray = aLongArray; + } + + /** + * Getter method for property aReferenceArray. + * + * @return property value of aReferenceArray + */ + public AtomicReferenceArray getaReferenceArray() { + return aReferenceArray; + } + + /** + * Setter method for property aReferenceArray. + * + * @param aReferenceArray value to be assigned to property aReferenceArray + */ + public void setaReferenceArray(AtomicReferenceArray aReferenceArray) { + this.aReferenceArray = aReferenceArray; + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/atomic/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/test/atomic/SerializeCompatibleTest.java new file mode 100644 index 0000000..00c09ad --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/atomic/SerializeCompatibleTest.java @@ -0,0 +1,262 @@ +/* + * 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 com.caucho.hessian.test.atomic; + +import com.caucho.hessian.io.Hessian2Output; +import com.caucho.hessian.io.SerializerFactory; +import com.caucho.hessian.io.atomic.AtomicSerializer; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; + +/** + * for version 3.5.0 + * assure that the result bytes of Atomic classes remains, whatever as object or field, same as + * previous version + * + * @author junyuan + * @version SerializeCompatibleTest.java, v 0.1 2023年03月31日 16:40 junyuan Exp $ + */ +public class SerializeCompatibleTest { + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + + os = new ByteArrayOutputStream(); + } + + @AfterClass + public static void tearDown() { + try { + Field field = SerializerFactory.class.getDeclaredField("_staticSerializerMap"); + field.setAccessible(true); + ConcurrentMap map = (ConcurrentMap) field.get(SerializerFactory.class); + AtomicSerializer atomicSerializer = new AtomicSerializer(); + map.put(AtomicInteger.class, atomicSerializer); + map.put(AtomicLong.class, atomicSerializer); + map.put(AtomicBoolean.class, atomicSerializer); + map.put(AtomicReference.class, atomicSerializer); + map.put(AtomicLongArray.class, atomicSerializer); + map.put(AtomicIntegerArray.class, atomicSerializer); + map.put(AtomicReferenceArray.class, atomicSerializer); + } catch (Throwable t) { + } + } + + /** + * result byte should be in same with former version so as to behaving compatible + * @throws IOException + */ + @Test + public void test_serialize() throws IOException, NoSuchFieldException, IllegalAccessException { + byte[] wrappedCaseAtomic = serializeAtomicWrapper(); + byte[] unwrappedIntegerAtomic = serializeAtomicInteger(); + byte[] unwrappedBooleanAtomic = serializeAtomicBoolean(); + byte[] unwrappedLongAtomic = serializeAtomicLong(); + byte[] unwrappedReferenceAtomic = serializeAtomicReference(); + byte[] unwrappedIntegerArrayAtomic = serializeAtomicIntegerArray(); + byte[] unwrappedLongArrayAtomic = serializeAtomicLongArray(); + byte[] unwrappedReferenceArrayAtomic = serializeAtomicReferenceArray(); + + // use origin java serialize then + Field field = SerializerFactory.class.getDeclaredField("_staticSerializerMap"); + field.setAccessible(true); + ConcurrentMap map = (ConcurrentMap) field.get(SerializerFactory.class); + map.remove(AtomicInteger.class); + map.remove(AtomicBoolean.class); + map.remove(AtomicLong.class); + map.remove(AtomicReference.class); + map.remove(AtomicIntegerArray.class); + map.remove(AtomicLongArray.class); + map.remove(AtomicReferenceArray.class); + + byte[] wrappedCaseJava = serializeAtomicWrapper(); + byte[] unwrappedIntegerJava = serializeAtomicInteger(); + byte[] unwrappedBooleanJava = serializeAtomicBoolean(); + byte[] unwrappedLongJava = serializeAtomicLong(); + byte[] unwrappedReferenceJava = serializeAtomicReference(); + byte[] unwrappedIntegerArrayJava = serializeAtomicIntegerArray(); + byte[] unwrappedLongArrayJava = serializeAtomicLongArray(); + byte[] unwrappedReferenceArrayJava = serializeAtomicReferenceArray(); + + Assert.assertTrue(bytesEquals(wrappedCaseAtomic, wrappedCaseJava)); + + Assert.assertTrue(bytesEquals(unwrappedIntegerAtomic, unwrappedIntegerJava)); + Assert.assertTrue(bytesEquals(unwrappedBooleanAtomic, unwrappedBooleanJava)); + Assert.assertTrue(bytesEquals(unwrappedLongAtomic, unwrappedLongJava)); + Assert.assertTrue(bytesEquals(unwrappedReferenceAtomic, unwrappedReferenceJava)); + Assert.assertTrue(bytesEquals(unwrappedIntegerArrayAtomic, unwrappedIntegerArrayJava)); + Assert.assertTrue(bytesEquals(unwrappedLongArrayAtomic, unwrappedLongArrayJava)); + Assert.assertTrue(bytesEquals(unwrappedReferenceArrayAtomic, unwrappedReferenceArrayJava)); + } + + protected boolean bytesEquals(byte[] src, byte[] target) { + if (src == null && target == null) { + return true; + } + + if (src == null || target == null) { + return false; + } + + if (src.length != target.length) { + return false; + } + + for (int i = 0; i < src.length; i++) { + if (src[i] != target[i]) { + return false; + } + } + return true; + } + + private byte[] serializeAtomicWrapper() throws IOException { + // wrapped + AtomicWrapper atomicWrapper = new AtomicWrapper(); + atomicWrapper.setaInteger(new AtomicInteger(17)); + atomicWrapper.setaBoolean(new AtomicBoolean(true)); + atomicWrapper.setaLong(new AtomicLong(2147483649L)); + atomicWrapper.setaReference(new AtomicReference()); + atomicWrapper.getaReference().set(new Integer(1)); + + atomicWrapper.setaIntegerArray(new AtomicIntegerArray(2)); + atomicWrapper.getaIntegerArray().set(0, 0); + atomicWrapper.getaIntegerArray().set(1, 1); + + atomicWrapper.setaLongArray(new AtomicLongArray(2)); + atomicWrapper.getaLongArray().set(0, 0L); + atomicWrapper.getaLongArray().set(1, 1L); + + atomicWrapper.setaReferenceArray(new AtomicReferenceArray(2)); + atomicWrapper.getaReferenceArray().set(0, new Integer(1)); + atomicWrapper.getaReferenceArray().set(1, new Integer(2)); + + os.reset(); + Hessian2Output wrapCaseOutput = new Hessian2Output(os); + + wrapCaseOutput.setSerializerFactory(factory); + wrapCaseOutput.writeObject(atomicWrapper); + wrapCaseOutput.flush(); + byte[] wrappedBytes = os.toByteArray(); + return wrappedBytes; + } + + private byte[] serializeAtomicInteger() throws IOException { + AtomicInteger atomic = new AtomicInteger(1); + + os.reset(); + Hessian2Output output = new Hessian2Output(os); + output.setSerializerFactory(factory); + output.writeObject(atomic); + output.flush(); + byte[] unwrappedBytes = os.toByteArray(); + return unwrappedBytes; + } + + private byte[] serializeAtomicBoolean() throws IOException { + AtomicBoolean atomic = new AtomicBoolean(true); + + os.reset(); + Hessian2Output output = new Hessian2Output(os); + output.setSerializerFactory(factory); + output.writeObject(atomic); + output.flush(); + byte[] unwrappedBytes = os.toByteArray(); + return unwrappedBytes; + } + + private byte[] serializeAtomicLong() throws IOException { + AtomicLong atomic = new AtomicLong(1L); + + os.reset(); + Hessian2Output output = new Hessian2Output(os); + output.setSerializerFactory(factory); + output.writeObject(atomic); + output.flush(); + byte[] unwrappedBytes = os.toByteArray(); + return unwrappedBytes; + } + + private byte[] serializeAtomicReference() throws IOException { + AtomicReference atomic = new AtomicReference(new Integer(1)); + + os.reset(); + Hessian2Output output = new Hessian2Output(os); + output.setSerializerFactory(factory); + output.writeObject(atomic); + output.flush(); + byte[] unwrappedBytes = os.toByteArray(); + return unwrappedBytes; + } + + private byte[] serializeAtomicIntegerArray() throws IOException { + AtomicIntegerArray array = new AtomicIntegerArray(2); + array.set(0, 0); + array.set(1, 1); + os.reset(); + Hessian2Output output = new Hessian2Output(os); + output.setSerializerFactory(factory); + output.writeObject(array); + output.flush(); + byte[] unwrappedBytes = os.toByteArray(); + return unwrappedBytes; + } + + private byte[] serializeAtomicLongArray() throws IOException { + AtomicLongArray array = new AtomicLongArray(2); + array.set(0, 0L); + array.set(1, 1L); + os.reset(); + Hessian2Output output = new Hessian2Output(os); + output.setSerializerFactory(factory); + output.writeObject(array); + output.flush(); + byte[] unwrappedBytes = os.toByteArray(); + return unwrappedBytes; + } + + private byte[] serializeAtomicReferenceArray() throws IOException { + AtomicReferenceArray array = new AtomicReferenceArray(2); + array.set(0, new Integer(0)); + array.set(1, new Integer(1)); + os.reset(); + Hessian2Output output = new Hessian2Output(os); + output.setSerializerFactory(factory); + output.writeObject(array); + output.flush(); + byte[] unwrappedBytes = os.toByteArray(); + return unwrappedBytes; + } + +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/java8/AtomicWrapper.java b/src/test/java/com/caucho/hessian/test/java8/AtomicWrapper.java deleted file mode 100644 index da08a79..0000000 --- a/src/test/java/com/caucho/hessian/test/java8/AtomicWrapper.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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 com.caucho.hessian.test.java8; - -import java.io.Serializable; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicIntegerArray; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicLongArray; - -/** - * - * @author junyuan - * @version AtomicWrapper.java, v 0.1 2023年03月29日 18:15 junyuan Exp $ - */ -public class AtomicWrapper implements Serializable { - private AtomicInteger aInteger; - private AtomicBoolean aBoolean; - private AtomicLong aLong; - private AtomicIntegerArray aIntegerArray; - private AtomicLongArray aLongArray; - - /** - * Getter method for property ai. - * - * @return property value of ai - */ - public AtomicInteger getaInteger() { - return aInteger; - } - - /** - * Setter method for property ai. - * - * @param aInteger value to be assigned to property ai - */ - public void setaInteger(AtomicInteger aInteger) { - this.aInteger = aInteger; - } - - /** - * Getter method for property ab. - * - * @return property value of ab - */ - public AtomicBoolean getaBoolean() { - return aBoolean; - } - - /** - * Setter method for property ab. - * - * @param aBoolean value to be assigned to property ab - */ - public void setaBoolean(AtomicBoolean aBoolean) { - this.aBoolean = aBoolean; - } - - /** - * Getter method for property al. - * - * @return property value of al - */ - public AtomicLong getaLong() { - return aLong; - } - - /** - * Setter method for property al. - * - * @param aLong value to be assigned to property al - */ - public void setaLong(AtomicLong aLong) { - this.aLong = aLong; - } - - /** - * Getter method for property aia. - * - * @return property value of aia - */ - public AtomicIntegerArray getaIntegerArray() { - return aIntegerArray; - } - - /** - * Setter method for property aia. - * - * @param aIntegerArray value to be assigned to property aia - */ - public void setaIntegerArray(AtomicIntegerArray aIntegerArray) { - this.aIntegerArray = aIntegerArray; - } - - /** - * Getter method for property ala. - * - * @return property value of ala - */ - public AtomicLongArray getaLongArray() { - return aLongArray; - } - - /** - * Setter method for property ala. - * - * @param aLongArray value to be assigned to property ala - */ - public void setaLongArray(AtomicLongArray aLongArray) { - this.aLongArray = aLongArray; - } -} \ No newline at end of file From 0d2b9f8c5e8f6664d8cc5253694379b92754a7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9D=87=E6=BA=90?= Date: Fri, 7 Apr 2023 15:31:46 +0800 Subject: [PATCH 04/32] fix: remove handle --- .../hessian/io/atomic/AtomicHandle.java | 78 ------------------- .../test/stacktrace/ExceptionClassTest.java | 65 ++++++++++++++++ .../test/stacktrace/ExceptionWrapper.java | 36 +++++++++ 3 files changed, 101 insertions(+), 78 deletions(-) delete mode 100644 src/main/java/com/caucho/hessian/io/atomic/AtomicHandle.java create mode 100644 src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java create mode 100644 src/test/java/com/caucho/hessian/test/stacktrace/ExceptionWrapper.java diff --git a/src/main/java/com/caucho/hessian/io/atomic/AtomicHandle.java b/src/main/java/com/caucho/hessian/io/atomic/AtomicHandle.java deleted file mode 100644 index 0daea98..0000000 --- a/src/main/java/com/caucho/hessian/io/atomic/AtomicHandle.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Ant Group - * Copyright (c) 2004-2023 All Rights Reserved. - */ -package com.caucho.hessian.io.atomic; - -import com.caucho.hessian.io.HessianHandle; - -import java.io.Serializable; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicIntegerArray; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicLongArray; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.atomic.AtomicReferenceArray; - -/** - * - * @author junyuan - * @version AtomicHandle.java, v 0.1 2023年03月30日 19:57 junyuan Exp $ - */ -public class AtomicHandle implements HessianHandle, Serializable { - private static final long serialVersionUID = 5253528013752548947L; - - private Class type; - private Object value; - - public AtomicHandle(Class type, Object value) { - this.type = type; - this.value = value; - } - - protected Object readResolve() { - if (AtomicInteger.class.equals(type)) { - return new AtomicInteger((Integer) value); - } - else if (AtomicLong.class.equals(type)) { - return new AtomicLong((Long) value); - } - else if (AtomicBoolean.class.equals(type)) { - return new AtomicBoolean((Boolean) value); - } - else if (AtomicReference.class.equals(type)) { - return new AtomicReference(value); - } - else if (AtomicIntegerArray.class.equals(type)) { - int[] tmp = (int[]) value; - int len = tmp.length; - AtomicIntegerArray array = new AtomicIntegerArray(len); - for (int i = 0; i < len; i++) { - array.set(i, tmp[i]); - } - return array; - } - else if (AtomicLongArray.class.equals(type)) { - long[] tmp = (long[]) value; - int len = tmp.length; - AtomicLongArray array = new AtomicLongArray(len); - for (int i = 0; i < len; i++) { - array.set(i, tmp[i]); - } - return array; - } - else if (AtomicReferenceArray.class.equals(type)) { - Object[] tmp = (Object[]) value; - int len = tmp.length; - AtomicReferenceArray array = new AtomicReferenceArray(len); - for (int i = 0; i < len; i++) { - array.set(i, tmp[i]); - } - return array; - } - - throw new UnsupportedOperationException(String.valueOf(this)); - } - -} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java new file mode 100644 index 0000000..83770b1 --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java @@ -0,0 +1,65 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.test.stacktrace; + +import com.caucho.hessian.io.Hessian2Input; +import com.caucho.hessian.io.Hessian2Output; +import com.caucho.hessian.io.SerializerFactory; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * + * @author junyuan + * @version ExceptionClassTest.java, v 0.1 2023年04月04日 15:38 junyuan Exp $ + */ +public class ExceptionClassTest { + + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + os = new ByteArrayOutputStream(); + } + + @Test + public void test_stacktrace() throws IOException { + Throwable t = null; + try { + int x = 1 / 0; + } catch (Exception e) { + t = e; + } + + ExceptionWrapper w = new ExceptionWrapper(); + w.setT(t); + + Object result = doEncodeNDecode(w); + + + } + + protected Object doEncodeNDecode(Object origin) throws IOException { + os.reset(); + Hessian2Output output = new Hessian2Output(os); + + output.setSerializerFactory(factory); + output.writeObject(origin); + output.flush(); + + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + Hessian2Input input = new Hessian2Input(is); + input.setSerializerFactory(factory); + Object actual = input.readObject(); + return actual; + } + +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionWrapper.java b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionWrapper.java new file mode 100644 index 0000000..d6ce3e2 --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionWrapper.java @@ -0,0 +1,36 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.test.stacktrace; + +import java.io.Serializable; + +/** + * + * @author junyuan + * @version ExceptionWrapper.java, v 0.1 2023年04月04日 15:40 junyuan Exp $ + */ +public class ExceptionWrapper implements Serializable { + private static final long serialVersionUID = 4065571790594438646L; + + Throwable t; + + /** + * Getter method for property t. + * + * @return property value of t + */ + public Throwable getT() { + return t; + } + + /** + * Setter method for property t. + * + * @param t value to be assigned to property t + */ + public void setT(Throwable t) { + this.t = t; + } +} \ No newline at end of file From f82011570867df43c13c6f40b46d2f3fd6a98f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9D=87=E6=BA=90?= Date: Mon, 10 Apr 2023 10:39:41 +0800 Subject: [PATCH 05/32] fix: log level --- .../java/com/caucho/hessian/util/ReflectionUtil.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/caucho/hessian/util/ReflectionUtil.java b/src/main/java/com/caucho/hessian/util/ReflectionUtil.java index 6fef8e6..304458a 100644 --- a/src/main/java/com/caucho/hessian/util/ReflectionUtil.java +++ b/src/main/java/com/caucho/hessian/util/ReflectionUtil.java @@ -22,7 +22,8 @@ public static boolean setAccessible(Method m) { try { m.setAccessible(true); } catch (Throwable t) { - log.log(Level.WARNING, t.toString(), t); + log.log(Level.INFO, + "failed when setting accessible on method [" + m.toString() + "], error message: " + t.getMessage(), t); return false; } return true; @@ -32,7 +33,8 @@ public static boolean setAccessible(Constructor c) { try { c.setAccessible(true); } catch (Throwable t) { - log.log(Level.WARNING, t.toString(), t); + log.log(Level.INFO, "failed when setting accessible on constructor [" + c.toString() + + "], error message: " + t.getMessage(), t); return false; } return true; @@ -42,7 +44,8 @@ public static boolean setAccessible(Field f) { try { f.setAccessible(true); } catch (Throwable t) { - log.log(Level.WARNING, t.toString(), t); + log.log(Level.INFO, + "failed when setting accessible on field [" + f.toString() + "], error message: " + t.getMessage(), t); return false; } return true; From d5bc41329b959dbce41720d751d42580c3177525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9D=87=E6=BA=90?= Date: Mon, 10 Apr 2023 16:22:10 +0800 Subject: [PATCH 06/32] feat: stack trace serialize without reflect --- .../caucho/hessian/io/SerializerFactory.java | 2 +- .../io/StackTraceElementSerializer.java | 160 ++++++++++++++++++ .../test/throwable/ExceptionWrapper.java | 49 ++++++ .../throwable/SerializeCompatibleTest.java | 151 +++++++++++++++++ .../test/throwable/ThrowableClassTest.java | 76 +++++++++ 5 files changed, 437 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/caucho/hessian/io/StackTraceElementSerializer.java create mode 100644 src/test/java/com/caucho/hessian/test/throwable/ExceptionWrapper.java create mode 100644 src/test/java/com/caucho/hessian/test/throwable/SerializeCompatibleTest.java create mode 100644 src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java diff --git a/src/main/java/com/caucho/hessian/io/SerializerFactory.java b/src/main/java/com/caucho/hessian/io/SerializerFactory.java index ddff624..4567c94 100644 --- a/src/main/java/com/caucho/hessian/io/SerializerFactory.java +++ b/src/main/java/com/caucho/hessian/io/SerializerFactory.java @@ -619,7 +619,7 @@ protected static void addBasic(Class cl, String typeName, int type) try { Class stackTrace = Class.forName("java.lang.StackTraceElement"); - + _staticSerializerMap.put(stackTrace, new StackTraceElementSerializer()); _staticDeserializerMap.put(stackTrace, new StackTraceElementDeserializer()); } catch (Throwable e) { } diff --git a/src/main/java/com/caucho/hessian/io/StackTraceElementSerializer.java b/src/main/java/com/caucho/hessian/io/StackTraceElementSerializer.java new file mode 100644 index 0000000..a7ab9dd --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/StackTraceElementSerializer.java @@ -0,0 +1,160 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author junyuan + * @version StackTraceElementSerializer.java, v 0.1 2023年04月10日 11:12 junyuan Exp $ + */ +public class StackTraceElementSerializer extends AbstractSerializer { + protected static final Logger log = Logger + .getLogger(StackTraceElementSerializer.class + .getName()); + + private Class _clazz = StackTraceElement.class; + private Field[] _fields; + { + Field[] originFields = _clazz.getDeclaredFields(); + ArrayList tmp = new ArrayList(); + for (int i = 0; i < originFields.length; i++) { + Field field = originFields[i]; + + if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { + continue; + } + if ("declaringClassObject".equals(field.getName()) || "format".equals(field.getName())) { + continue; + } + tmp.add(field); + } + _fields = new Field[tmp.size()]; + tmp.toArray(_fields); + } + + private final static String GET_PREFIX = "get"; + + private Map _readMethods = new HashMap(); + { + for (Field field : _fields) { + String fieldName = field.getName(); + + String methodName = GET_PREFIX + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); + if ("declaringClass".equals(fieldName)) { + methodName = GET_PREFIX + "ClassName"; + } + + try { + Method m = _clazz.getMethod(methodName); + _readMethods.put(fieldName, m); + } catch (NoSuchMethodException e) { + log.log(Level.WARNING, "getter not found: " + methodName, e); + } catch (Exception e) { + log.log(Level.WARNING, e.toString(), e); + } + } + } + + @Override + public void writeObject(Object obj, AbstractHessianOutput out) throws IOException { + if (obj == null) { + out.writeNull(); + return; + } + + if (!(obj instanceof StackTraceElement)) { + throw new UnsupportedOperationException(String.valueOf(this)); + } + + if (out.addRef(obj)) { + return; + } + Class cl = obj.getClass(); + int ref = out.writeObjectBegin(cl.getName()); + + if (ref < -1) { + writeObject10(obj, out); + } + else { + if (ref == -1) { + writeDefinition20(out); + out.writeObjectBegin(cl.getName()); + } + + writeInstance(obj, out); + } + } + + private void writeObject10(Object obj, AbstractHessianOutput out) + throws IOException + { + for (int i = 0; i < _fields.length; i++) { + Field field = _fields[i]; + + out.writeString(field.getName()); + + serializeField(out, obj, field); + } + + out.writeMapEnd(); + } + + private void writeDefinition20(AbstractHessianOutput out) + throws IOException + { + out.writeClassFieldLength(_fields.length); + + for (int i = 0; i < _fields.length; i++) { + Field field = _fields[i]; + + out.writeString(field.getName()); + } + } + + public void writeInstance(Object obj, AbstractHessianOutput out) + throws IOException + { + for (int i = 0; i < _fields.length; i++) { + Field field = _fields[i]; + + serializeField(out, obj, field); + } + } + + private void serializeField(AbstractHessianOutput out, Object obj, Field field) + throws IOException { + // only String and int field is required to be serialized + if (String.class.equals(field.getType())) { + String value = null; + try { + value = (String) _readMethods.get(field.getName()).invoke(obj); + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + out.writeString(value); + } else if (int.class.equals(field.getType())) { + Integer value = 0; + try { + value = (Integer) _readMethods.get(field.getName()).invoke(obj); + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + out.writeInt(value); + } else { + out.writeNull(); + } + + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/throwable/ExceptionWrapper.java b/src/test/java/com/caucho/hessian/test/throwable/ExceptionWrapper.java new file mode 100644 index 0000000..6f74c5b --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/throwable/ExceptionWrapper.java @@ -0,0 +1,49 @@ +/* + * 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 com.caucho.hessian.test.throwable; + +import java.io.Serializable; + +/** + * + * @author junyuan + * @version ExceptionWrapper.java, v 0.1 2023年04月10日 14:41 junyuan Exp $ + */ +public class ExceptionWrapper implements Serializable { + private static final long serialVersionUID = 4065571790594438646L; + + Throwable t; + + /** + * Getter method for property t. + * + * @return property value of t + */ + public Throwable getT() { + return t; + } + + /** + * Setter method for property t. + * + * @param t value to be assigned to property t + */ + public void setT(Throwable t) { + this.t = t; + } + +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/throwable/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/test/throwable/SerializeCompatibleTest.java new file mode 100644 index 0000000..908266e --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/throwable/SerializeCompatibleTest.java @@ -0,0 +1,151 @@ +/* + * 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 com.caucho.hessian.test.throwable; + +import com.caucho.hessian.io.Hessian2Output; +import com.caucho.hessian.io.SerializerFactory; +import com.caucho.hessian.io.StackTraceElementSerializer; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.concurrent.ConcurrentMap; + +/** + * + * @author junyuan + * @version SerializeCompatibleTest.java, v 0.1 2023年04月10日 14:52 junyuan Exp $ + */ +public class SerializeCompatibleTest { + + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + Throwable t = null; + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + + os = new ByteArrayOutputStream(); + } + + @AfterClass + public static void tearDown() { + try { + Field field = SerializerFactory.class.getDeclaredField("_staticSerializerMap"); + field.setAccessible(true); + ConcurrentMap map = (ConcurrentMap) field.get(SerializerFactory.class); + StackTraceElementSerializer serializer = new StackTraceElementSerializer(); + map.put(StackTraceElement.class, serializer); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + { + try { + double x = 1 / 0; + } catch (Exception e) { + t = e; + } + } + + /** + * result byte should be in same with former version so as to behaving compatible + * @throws IOException + */ + @Test + public void test_serialize() throws IOException, NoSuchFieldException, IllegalAccessException { + byte[] wrappedCaseStackTrace = serializeExceptionWrapper(); + byte[] unwrappedStackTrace = serializeStackTraceElement(); + + // use origin java serialize then + Field field = SerializerFactory.class.getDeclaredField("_staticSerializerMap"); + field.setAccessible(true); + ConcurrentMap map = (ConcurrentMap) field.get(SerializerFactory.class); + map.remove(StackTraceElement.class); + + byte[] wrappedStackTraceCaseJava = serializeExceptionWrapper(); + byte[] unwrappedStackTraceJava = serializeStackTraceElement(); + + Assert.assertTrue(bytesEquals(wrappedCaseStackTrace, wrappedStackTraceCaseJava)); + + Assert.assertTrue(bytesEquals(unwrappedStackTrace, unwrappedStackTraceJava)); + } + + protected boolean bytesEquals(byte[] src, byte[] target) { + if (src == null && target == null) { + return true; + } + + if (src == null || target == null) { + return false; + } + + if (src.length != target.length) { + return false; + } + + for (int i = 0; i < src.length; i++) { + if (src[i] != target[i]) { + return false; + } + } + return true; + } + + private byte[] serializeExceptionWrapper() throws IOException { + // wrapped + ExceptionWrapper exceptionWrapper = new ExceptionWrapper(); + + exceptionWrapper.setT(t); + + os.reset(); + Hessian2Output wrapCaseOutput = new Hessian2Output(os); + + wrapCaseOutput.setSerializerFactory(factory); + wrapCaseOutput.writeObject(exceptionWrapper); + wrapCaseOutput.flush(); + byte[] wrappedBytes = os.toByteArray(); + return wrappedBytes; + } + + private byte[] serializeStackTraceElement() throws IOException { + Throwable t = null; + try { + double x = 1 / 0; + } catch (Exception e) { + t = e; + } + StackTraceElement e = t.getStackTrace()[0]; + ; + + os.reset(); + Hessian2Output output = new Hessian2Output(os); + output.setSerializerFactory(factory); + output.writeObject(e); + output.flush(); + byte[] unwrappedBytes = os.toByteArray(); + return unwrappedBytes; + } + +} diff --git a/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java b/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java new file mode 100644 index 0000000..9f80315 --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java @@ -0,0 +1,76 @@ +/* + * 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 com.caucho.hessian.test.throwable; + +import com.caucho.hessian.io.Hessian2Input; +import com.caucho.hessian.io.Hessian2Output; +import com.caucho.hessian.io.SerializerFactory; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * + * @author junyuan + * @version ThrowableClassTest.java, v 0.1 2023年04月10日 14:42 junyuan Exp $ + */ +public class ThrowableClassTest { + + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + os = new ByteArrayOutputStream(); + } + + @Test + public void test_stacktrace() throws IOException { + Throwable t = null; + try { + int x = 1 / 0; + } catch (Exception e) { + t = e; + } + + ExceptionWrapper w = new ExceptionWrapper(); + w.setT(t); + + Object result = doEncodeNDecode(w); + } + + protected Object doEncodeNDecode(Object origin) throws IOException { + os.reset(); + Hessian2Output output = new Hessian2Output(os); + + output.setSerializerFactory(factory); + output.writeObject(origin); + output.flush(); + + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + Hessian2Input input = new Hessian2Input(is); + input.setSerializerFactory(factory); + Object actual = input.readObject(); + return actual; + } + +} From 979a52bf7d2e5a75fd834182f1ce19987a16c1a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9D=87=E6=BA=90?= Date: Mon, 10 Apr 2023 17:52:15 +0800 Subject: [PATCH 07/32] add test case for stack trace --- .../io/StackTraceElementDeserializer.java | 89 ++++++++++++++++++- .../test/throwable/ThrowableClassTest.java | 8 ++ 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/caucho/hessian/io/StackTraceElementDeserializer.java b/src/main/java/com/caucho/hessian/io/StackTraceElementDeserializer.java index a60dfe8..de0abb8 100644 --- a/src/main/java/com/caucho/hessian/io/StackTraceElementDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/StackTraceElementDeserializer.java @@ -48,17 +48,98 @@ package com.caucho.hessian.io; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + /** * Deserializing a JDK 1.4 StackTraceElement * @author pangu */ -public class StackTraceElementDeserializer extends JavaDeserializer { +public class StackTraceElementDeserializer extends AbstractDeserializer { + protected static final Logger log = Logger.getLogger(StackTraceElementSerializer.class.getName()); + + private Map _fields = new HashMap(); + + private Constructor _constructor; + + @Override + public Class getType() { + return StackTraceElement.class; + } + public StackTraceElementDeserializer() { - super(StackTraceElement.class); + Class clazz = StackTraceElement.class; + Field[] originFields = clazz.getDeclaredFields(); + for (int i = 0; i < originFields.length; i++) { + Field field = originFields[i]; + + if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { + continue; + } + if ("declaringClassObject".equals(field.getName()) || "format".equals(field.getName())) { + continue; + } + _fields.put(field.getName(), field); + } + + try { + if (_fields.size() == 7) { + // available since java 9 + _constructor = clazz.getDeclaredConstructor(String.class, String.class, String.class, String.class, + String.class, String.class, int.class); + } else { + // default, only read class, method, file and line + _constructor = clazz.getDeclaredConstructor(String.class, String.class, String.class, int.class); + } + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + } @Override - protected Object instantiate() throws Exception { - return new StackTraceElement("", "", "", 0); + public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IOException { + try { + int ref = in.addRef(_constructor.newInstance("", "", "", 0)); + Map fieldValueMap = new HashMap(); + + for (int i = 0; i < fieldNames.length; i++) { + String name = fieldNames[i]; + Field field = _fields.get(name); + + if (String.class.equals(field.getType())) { + fieldValueMap.put(name, in.readString()); + } else if (int.class.equals(field.getType())) { + fieldValueMap.put(name, in.readInt()); + } + } + + StackTraceElement obj; + if (fieldNames.length == 7) { + obj = _constructor.newInstance( + fieldValueMap.get("classLoaderName"), fieldValueMap.get("moduleName"), + fieldValueMap.get("moduleVersion"), fieldValueMap.get("declaringClass"), + fieldValueMap.get("methodName"), fieldValueMap.get("fileName"), + fieldValueMap.get("lineNumber")); + } else { + obj = _constructor.newInstance( + fieldValueMap.get("declaringClass"), fieldValueMap.get("methodName"), + fieldValueMap.get("fileName"), fieldValueMap.get("lineNumber")); + } + + in.setRef(ref, obj); + + return obj; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(StackTraceElement.class.getName() + ":" + e, e); + } } } diff --git a/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java b/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java index 9f80315..bde04f5 100644 --- a/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java +++ b/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java @@ -56,6 +56,14 @@ public void test_stacktrace() throws IOException { w.setT(t); Object result = doEncodeNDecode(w); + + Assert.assertTrue(result instanceof ExceptionWrapper); + + StackTraceElement[] origin = w.getT().getStackTrace(); + StackTraceElement[] target = ((ExceptionWrapper) result).getT().getStackTrace(); + + Assert.assertEquals(origin.length, target.length); + Assert.assertArrayEquals(origin, target); } protected Object doEncodeNDecode(Object origin) throws IOException { From 0ad2bbecdda5b322fa42645384f7f9e56a6c3409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9D=87=E6=BA=90?= Date: Wed, 12 Apr 2023 16:40:01 +0800 Subject: [PATCH 08/32] fix: log --- pom.xml | 2 +- .../caucho/hessian/util/ReflectionUtil.java | 47 +++++++++++++++---- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 4c7aa3d..493cd0c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.alipay.sofa hessian - 3.4.0 + 3.5.0-jdk17-SNAPSHOT jar ${project.groupId}:${project.artifactId} diff --git a/src/main/java/com/caucho/hessian/util/ReflectionUtil.java b/src/main/java/com/caucho/hessian/util/ReflectionUtil.java index 304458a..43aeda0 100644 --- a/src/main/java/com/caucho/hessian/util/ReflectionUtil.java +++ b/src/main/java/com/caucho/hessian/util/ReflectionUtil.java @@ -4,11 +4,11 @@ */ package com.caucho.hessian.util; +import org.slf4j.Logger; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.logging.Level; -import java.util.logging.Logger; /** * @@ -16,14 +16,35 @@ * @version ReflectionUtil.java, v 0.1 2023年03月30日 21:23 junyuan Exp $ */ public class ReflectionUtil { - private static final Logger log = Logger.getLogger(ReflectionUtil.class.getName()); + private static org.slf4j.Logger LOGGER = judgeLogger(); + + //do not change this + public static final String HESSIAN_SERIALIZE_LOG_NAME = "HessianSerializeLog"; + public static final String CONFIG_LOG_SPACE_NAME = "com.alipay.sofa.hessian"; + + private static Logger judgeLogger() { + + try { + ReflectionUtil.class.getClassLoader().loadClass("com.alipay.sofa.common.log.LoggerSpaceManager"); + } catch (Throwable e) { + //do nothing + return null; + } + + return com.alipay.sofa.common.log.LoggerSpaceManager.getLoggerBySpace(HESSIAN_SERIALIZE_LOG_NAME, + CONFIG_LOG_SPACE_NAME); + } public static boolean setAccessible(Method m) { try { m.setAccessible(true); } catch (Throwable t) { - log.log(Level.INFO, - "failed when setting accessible on method [" + m.toString() + "], error message: " + t.getMessage(), t); + if (LOGGER.isDebugEnabled()) { + LOGGER + .debug( + "failed when setting accessible on method [" + m.toString() + "], error message: " + + t.getMessage(), t); + } return false; } return true; @@ -33,8 +54,12 @@ public static boolean setAccessible(Constructor c) { try { c.setAccessible(true); } catch (Throwable t) { - log.log(Level.INFO, "failed when setting accessible on constructor [" + c.toString() + - "], error message: " + t.getMessage(), t); + if (LOGGER.isDebugEnabled()) { + LOGGER + .debug( + "failed when setting accessible on method [" + c.toString() + "], error message: " + + t.getMessage(), t); + } return false; } return true; @@ -44,8 +69,12 @@ public static boolean setAccessible(Field f) { try { f.setAccessible(true); } catch (Throwable t) { - log.log(Level.INFO, - "failed when setting accessible on field [" + f.toString() + "], error message: " + t.getMessage(), t); + if (LOGGER.isDebugEnabled()) { + LOGGER + .debug( + "failed when setting accessible on method [" + f.toString() + "], error message: " + + t.getMessage(), t); + } return false; } return true; From 2902e4cba7115993c9a57a9b3784b8a02428f4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9D=87=E6=BA=90?= Date: Tue, 11 Apr 2023 10:09:43 +0800 Subject: [PATCH 09/32] feat: throw v1 --- .../hessian/io/NonReflectionSerializer.java | 80 ++++++++++ .../caucho/hessian/io/SerializerFactory.java | 7 +- .../io/StackTraceElementSerializer.java | 85 ++-------- .../hessian/io/ThrowableSerializer.java | 71 -------- .../io/throwable/ThrowableDeserializer.java | 115 +++++++++++++ .../hessian/io/throwable/ThrowableHelper.java | 54 +++++++ .../io/throwable/ThrowableSerializer.java | 119 ++++++++++++++ ...nstantNotPresentExceptionDeserializer.java | 20 +++ ...ConstantNotPresentExceptionSerializer.java | 37 +++++ .../test/stacktrace/ExceptionClassTest.java | 76 +++++++++ .../test/stacktrace/ExceptionWrapper.java | 48 ++++++ .../stacktrace/SerializeCompatibleTest.java | 151 ++++++++++++++++++ .../test/throwable/MeaninglessEnum.java | 16 ++ .../test/throwable/SelfDefinedException.java | 35 ++++ .../throwable/SerializeCompatibleTest.java | 67 +++----- .../SerializeFactoryWithoutThrowable.java | 122 ++++++++++++++ .../test/throwable/ThrowableClassTest.java | 77 ++++++++- 17 files changed, 984 insertions(+), 196 deletions(-) create mode 100644 src/main/java/com/caucho/hessian/io/NonReflectionSerializer.java delete mode 100644 src/main/java/com/caucho/hessian/io/ThrowableSerializer.java create mode 100644 src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java create mode 100644 src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java create mode 100644 src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java create mode 100644 src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java create mode 100644 src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java create mode 100644 src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java create mode 100644 src/test/java/com/caucho/hessian/test/stacktrace/ExceptionWrapper.java create mode 100644 src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java create mode 100644 src/test/java/com/caucho/hessian/test/throwable/MeaninglessEnum.java create mode 100644 src/test/java/com/caucho/hessian/test/throwable/SelfDefinedException.java create mode 100644 src/test/java/com/caucho/hessian/test/throwable/SerializeFactoryWithoutThrowable.java diff --git a/src/main/java/com/caucho/hessian/io/NonReflectionSerializer.java b/src/main/java/com/caucho/hessian/io/NonReflectionSerializer.java new file mode 100644 index 0000000..1efe58b --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/NonReflectionSerializer.java @@ -0,0 +1,80 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io; + +import java.io.IOException; +import java.lang.reflect.Field; + +/** + * + * @author junyuan + * @version NonReflectionSerializer.java, v 0.1 2023年04月10日 19:34 junyuan Exp $ + */ +public abstract class NonReflectionSerializer extends AbstractSerializer { + + protected Field[] _fields; + + public void writeObject(Object obj, AbstractHessianOutput out) throws IOException { + if (obj == null) { + out.writeNull(); + return; + } + + if (out.addRef(obj)) { + return; + } + Class cl = obj.getClass(); + int ref = out.writeObjectBegin(cl.getName()); + + if (ref < -1) { + writeObject10(obj, out); + } + else { + if (ref == -1) { + writeDefinition20(out); + out.writeObjectBegin(cl.getName()); + } + + writeInstance(obj, out); + } + } + + private void writeObject10(Object obj, AbstractHessianOutput out) + throws IOException + { + for (int i = 0; i < _fields.length; i++) { + Field field = _fields[i]; + + out.writeString(field.getName()); + + serializeField(out, obj, field); + } + + out.writeMapEnd(); + } + + private void writeDefinition20(AbstractHessianOutput out) + throws IOException + { + out.writeClassFieldLength(_fields.length); + + for (int i = 0; i < _fields.length; i++) { + Field field = _fields[i]; + + out.writeString(field.getName()); + } + } + + public void writeInstance(Object obj, AbstractHessianOutput out) + throws IOException + { + for (int i = 0; i < _fields.length; i++) { + Field field = _fields[i]; + serializeField(out, obj, field); + } + } + + protected abstract void serializeField(AbstractHessianOutput out, Object obj, Field field) throws IOException; +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/SerializerFactory.java b/src/main/java/com/caucho/hessian/io/SerializerFactory.java index 4567c94..125e22f 100644 --- a/src/main/java/com/caucho/hessian/io/SerializerFactory.java +++ b/src/main/java/com/caucho/hessian/io/SerializerFactory.java @@ -66,6 +66,8 @@ import com.caucho.hessian.io.java8.ZoneIdSerializer; import com.caucho.hessian.io.java8.ZoneOffsetHandle; import com.caucho.hessian.io.java8.ZonedDateTimeHandle; +import com.caucho.hessian.io.throwable.ThrowableHelper; +import com.caucho.hessian.io.throwable.ThrowableSerializer; import java.io.*; import java.math.BigDecimal; @@ -236,7 +238,7 @@ else if (cl.isArray()) serializer = new ArraySerializer(); else if (Throwable.class.isAssignableFrom(cl)) - serializer = new ThrowableSerializer(cl); + serializer = ThrowableHelper.getSerializer(cl); else if (InputStream.class.isAssignableFrom(cl)) serializer = new InputStreamSerializer(); @@ -343,6 +345,9 @@ else if (Enumeration.class.isAssignableFrom(cl)) else if (Enum.class.isAssignableFrom(cl)) deserializer = new EnumDeserializer(cl); + else if (Throwable.class.isAssignableFrom(cl)) + deserializer = ThrowableHelper.getDeserializer(cl); + else deserializer = getDefaultDeserializer(cl); diff --git a/src/main/java/com/caucho/hessian/io/StackTraceElementSerializer.java b/src/main/java/com/caucho/hessian/io/StackTraceElementSerializer.java index a7ab9dd..3ef807d 100644 --- a/src/main/java/com/caucho/hessian/io/StackTraceElementSerializer.java +++ b/src/main/java/com/caucho/hessian/io/StackTraceElementSerializer.java @@ -19,14 +19,18 @@ * @author junyuan * @version StackTraceElementSerializer.java, v 0.1 2023年04月10日 11:12 junyuan Exp $ */ -public class StackTraceElementSerializer extends AbstractSerializer { +public class StackTraceElementSerializer extends NonReflectionSerializer { protected static final Logger log = Logger .getLogger(StackTraceElementSerializer.class .getName()); - private Class _clazz = StackTraceElement.class; - private Field[] _fields; - { + private final Class _clazz = StackTraceElement.class; + + private final static String GET_PREFIX = "get"; + + private Map _readMethods = new HashMap(); + + public StackTraceElementSerializer() { Field[] originFields = _clazz.getDeclaredFields(); ArrayList tmp = new ArrayList(); for (int i = 0; i < originFields.length; i++) { @@ -42,12 +46,8 @@ public class StackTraceElementSerializer extends AbstractSerializer { } _fields = new Field[tmp.size()]; tmp.toArray(_fields); - } - - private final static String GET_PREFIX = "get"; - private Map _readMethods = new HashMap(); - { + // get getter for (Field field : _fields) { String fieldName = field.getName(); @@ -68,72 +68,7 @@ public class StackTraceElementSerializer extends AbstractSerializer { } @Override - public void writeObject(Object obj, AbstractHessianOutput out) throws IOException { - if (obj == null) { - out.writeNull(); - return; - } - - if (!(obj instanceof StackTraceElement)) { - throw new UnsupportedOperationException(String.valueOf(this)); - } - - if (out.addRef(obj)) { - return; - } - Class cl = obj.getClass(); - int ref = out.writeObjectBegin(cl.getName()); - - if (ref < -1) { - writeObject10(obj, out); - } - else { - if (ref == -1) { - writeDefinition20(out); - out.writeObjectBegin(cl.getName()); - } - - writeInstance(obj, out); - } - } - - private void writeObject10(Object obj, AbstractHessianOutput out) - throws IOException - { - for (int i = 0; i < _fields.length; i++) { - Field field = _fields[i]; - - out.writeString(field.getName()); - - serializeField(out, obj, field); - } - - out.writeMapEnd(); - } - - private void writeDefinition20(AbstractHessianOutput out) - throws IOException - { - out.writeClassFieldLength(_fields.length); - - for (int i = 0; i < _fields.length; i++) { - Field field = _fields[i]; - - out.writeString(field.getName()); - } - } - - public void writeInstance(Object obj, AbstractHessianOutput out) - throws IOException - { - for (int i = 0; i < _fields.length; i++) { - Field field = _fields[i]; - - serializeField(out, obj, field); - } - } - - private void serializeField(AbstractHessianOutput out, Object obj, Field field) + protected void serializeField(AbstractHessianOutput out, Object obj, Field field) throws IOException { // only String and int field is required to be serialized if (String.class.equals(field.getType())) { diff --git a/src/main/java/com/caucho/hessian/io/ThrowableSerializer.java b/src/main/java/com/caucho/hessian/io/ThrowableSerializer.java deleted file mode 100644 index 628dd81..0000000 --- a/src/main/java/com/caucho/hessian/io/ThrowableSerializer.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. - * - * The Apache Software License, Version 1.1 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowlegement: - * "This product includes software developed by the - * Caucho Technology (http://www.caucho.com/)." - * Alternately, this acknowlegement may appear in the software itself, - * if and wherever such third-party acknowlegements normally appear. - * - * 4. The names "Burlap", "Resin", and "Caucho" must not be used to - * endorse or promote products derived from this software without prior - * written permission. For written permission, please contact - * info@caucho.com. - * - * 5. Products derived from this software may not be called "Resin" - * nor may "Resin" appear in their names without prior written - * permission of Caucho Technology. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT - * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @author Scott Ferguson - */ - -package com.caucho.hessian.io; - -import java.io.IOException; - -/** - * Serializing an object for known object types. - */ -public class ThrowableSerializer extends JavaSerializer { - public ThrowableSerializer(Class cl) - { - super(cl); - } - - public void writeObject(Object obj, AbstractHessianOutput out) - throws IOException - { - Throwable e = (Throwable) obj; - - e.getStackTrace(); - - super.writeObject(obj, out); - } -} diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java new file mode 100644 index 0000000..29910b1 --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java @@ -0,0 +1,115 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.throwable; + +import com.caucho.hessian.io.AbstractDeserializer; +import com.caucho.hessian.io.AbstractHessianInput; +import com.caucho.hessian.io.IOExceptionWrapper; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + * @author junyuan + * @version ThrowableDeserializer.java, v 0.1 2023年04月10日 20:37 junyuan Exp $ + */ +public class ThrowableDeserializer extends AbstractDeserializer { + + private Class _type; + protected Map _fields = new HashMap(); + protected Method addSuppressed = null; + + protected Constructor _constructor = null; + + public ThrowableDeserializer(Class cl) { + _type = cl; + Class clazz = Throwable.class; + + Field[] originFields = clazz.getDeclaredFields(); + for (int i = 0; i < originFields.length; i++) { + Field field = originFields[i]; + + if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { + continue; + } + + _fields.put(field.getName(), field); + } + + try { + _constructor = _type.getDeclaredConstructor(String.class); + } catch (NoSuchMethodException e) { + + } + + try { + addSuppressed = clazz.getDeclaredMethod("addSuppressed", Throwable.class); + } catch (NoSuchMethodException e) { + + } + } + + @Override + public Class getType() { + return _type; + } + + @Override + public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IOException { + try { + int ref = in.addRef(new Throwable()); + Map fieldValueMap = new HashMap(); + + for (int i = 0; i < fieldNames.length; i++) { + String name = fieldNames[i]; + Field field = _fields.get(name); + + if (String.class.equals(field.getType())) { + fieldValueMap.put(name, in.readString()); + } else { + fieldValueMap.put(name, in.readObject()); + } + } + Throwable obj = null; + if (_constructor != null) { + try { + obj = (Throwable) _constructor.newInstance(fieldValueMap.get("detailMessage")); + Throwable cause = (Throwable) fieldValueMap.get("cause"); + if (cause != null) { + obj.initCause(cause); + } + } catch (Exception e) { + e.printStackTrace(); + obj = new Throwable((String) fieldValueMap.get("detailMessage"), + (Throwable) fieldValueMap.get("cause")); + } + } else { + obj = new Throwable((String) fieldValueMap.get("detailMessage"), (Throwable) fieldValueMap.get("cause")); + } + + obj.setStackTrace((StackTraceElement[]) fieldValueMap.get("stackTrace")); + + List suppress = (List) fieldValueMap.get("suppressedExceptions"); + for (Throwable t : suppress) { + addSuppressed.invoke(obj, t); + } + + in.setRef(ref, obj); + + return obj; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(Throwable.class.getName() + ":" + e, e); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java new file mode 100644 index 0000000..0db75a0 --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java @@ -0,0 +1,54 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.throwable; + +import com.caucho.hessian.io.AbstractDeserializer; +import com.caucho.hessian.io.AbstractSerializer; +import com.caucho.hessian.io.JavaDeserializer; +import com.caucho.hessian.io.JavaSerializer; +import com.caucho.hessian.io.throwable.adapter.EnumConstantNotPresentExceptionSerializer; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author junyuan + * @version ThrowableHelper.java, v 0.1 2023年04月27日 16:56 junyuan Exp $ + */ +public class ThrowableHelper { + + private static final boolean isLessThanJdk17 = isLessThanJdk17(); + + private static boolean isLessThanJdk17() { + String javaVersion = System.getProperty("java.specification.version"); + return Double.parseDouble(javaVersion) < 17; + } + + public static AbstractDeserializer getDeserializer(Class cl) { + if (isLessThanJdk17) { + return new JavaDeserializer(cl); + } + return new ThrowableDeserializer(cl); + } + + public static AbstractSerializer getSerializer(Class cl) { + if (isLessThanJdk17) { + return new JavaSerializer(cl); + } + + if (throwableSerializerMap.containsKey(cl.getName())) { + return throwableSerializerMap.get(cl.getName()); + } + + return new ThrowableSerializer(); + } + + private static final Map throwableSerializerMap = new HashMap(); + static { + throwableSerializerMap.put(EnumConstantNotPresentException.class.getName(), + new EnumConstantNotPresentExceptionSerializer()); + } +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java new file mode 100644 index 0000000..6ca655e --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java @@ -0,0 +1,119 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.throwable; + +import com.caucho.hessian.io.AbstractHessianOutput; +import com.caucho.hessian.io.NonReflectionSerializer; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * + * @author junyuan + * @version ThrowableSerializer.java, v 0.1 2023年04月10日 19:30 junyuan Exp $ + */ +public class ThrowableSerializer extends NonReflectionSerializer { + + private final Class _clazz = Throwable.class; + protected Method getSuppressed = null; + + public ThrowableSerializer() { + Field[] fields = _clazz.getDeclaredFields(); + List tmp = new ArrayList(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + + if (Modifier.isTransient(field.getModifiers()) || + Modifier.isStatic(field.getModifiers())) { + continue; + } + + tmp.add(field); + } + + _fields = new Field[tmp.size()]; + tmp.toArray(_fields); + + try { + getSuppressed = _clazz.getDeclaredMethod("getSuppressed"); + } catch (NoSuchMethodException e) { + + } + } + + @Override + protected void serializeField(AbstractHessianOutput out, Object obj, Field field) + throws IOException { + if (!(obj instanceof Throwable)) { + throw new UnsupportedOperationException(String.valueOf(this)); + } + + Throwable current = (Throwable) obj; + + if ("detailMessage".equals(field.getName())) { + out.writeString(current.getMessage()); + } + else if ("cause".equals(field.getName())) { + out.writeObject(current.getCause()); + } + else if ("stackTrace".equals(field.getName())) { + out.writeObject(current.getStackTrace()); + } + else if ("suppressedExceptions".equals(field.getName())) { + if (getSuppressed == null) { + throw new UnsupportedOperationException(String.valueOf(this)); + } + Throwable[] throwableArray; + try { + throwableArray = (Throwable[]) getSuppressed.invoke(obj); + } catch (Exception e) { + throw new UnsupportedOperationException(e); + } + + List throwableList; + if (throwableArray.length == 0) { + throwableList = Collections.emptyList(); + } else { + throwableList = Arrays.asList(throwableArray); + } + + out.writeObject(throwableList); + } + else { + defaultSerializeField(out, obj, field); + } + + } + + /** + * 针对自定义的 field, 尝试以反射方式获取 + * @param out + * @param obj + * @param field + * @throws IOException + */ + protected void defaultSerializeField(AbstractHessianOutput out, Object obj, Field field) + throws IOException { + Object fieldValue = null; + try { + fieldValue = field.get(obj); + } catch (IllegalAccessException e) { + try { + field.setAccessible(true); + fieldValue = field.get(obj); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + out.writeObject(fieldValue); + } +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java new file mode 100644 index 0000000..c184091 --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java @@ -0,0 +1,20 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.throwable.adapter; + +import com.caucho.hessian.io.throwable.ThrowableDeserializer; + +/** + * + * @author junyuan + * @version EnumConstantNotPresentExceptionDeserializer.java, v 0.1 2023年04月27日 17:55 junyuan Exp $ + */ +public class EnumConstantNotPresentExceptionDeserializer extends ThrowableDeserializer { + public EnumConstantNotPresentExceptionDeserializer(Class cl) { + super(cl); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java new file mode 100644 index 0000000..18c415a --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java @@ -0,0 +1,37 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.throwable.adapter; + +import com.caucho.hessian.io.AbstractHessianOutput; +import com.caucho.hessian.io.throwable.ThrowableSerializer; + +import java.io.IOException; +import java.lang.reflect.Field; + +/** + * + * @author junyuan + * @version EnumConstantNotPresentExceptionSerializer.java, v 0.1 2023年04月27日 17:56 junyuan Exp $ + */ +public class EnumConstantNotPresentExceptionSerializer extends ThrowableSerializer { + + @Override + protected void defaultSerializeField(AbstractHessianOutput out, Object obj, Field field) + throws IOException { + if (!(obj instanceof EnumConstantNotPresentException)) { + throw new UnsupportedOperationException(String.valueOf(this)); + } + + EnumConstantNotPresentException cast = (EnumConstantNotPresentException) obj; + + if (field.getName().equals("enumType")) { + out.writeObject(cast.enumType()); + } else if (field.getName().equals("constantName")) { + out.writeString(cast.constantName()); + } else { + super.defaultSerializeField(out, obj, field); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java new file mode 100644 index 0000000..ecbb936 --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java @@ -0,0 +1,76 @@ +/* + * 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 com.caucho.hessian.test.stacktrace; + +import com.caucho.hessian.io.Hessian2Input; +import com.caucho.hessian.io.Hessian2Output; +import com.caucho.hessian.io.SerializerFactory; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * + * @author junyuan + * @version ExceptionClassTest.java, v 0.1 2023年04月04日 15:38 junyuan Exp $ + */ +public class ExceptionClassTest { + + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + os = new ByteArrayOutputStream(); + } + + @Test + public void test_stacktrace() throws IOException { + Throwable t = null; + try { + int x = 1 / 0; + } catch (Exception e) { + t = e; + } + + ExceptionWrapper w = new ExceptionWrapper(); + w.setT(t); + + Object result = doEncodeNDecode(w); + + } + + protected Object doEncodeNDecode(Object origin) throws IOException { + os.reset(); + Hessian2Output output = new Hessian2Output(os); + + output.setSerializerFactory(factory); + output.writeObject(origin); + output.flush(); + + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + Hessian2Input input = new Hessian2Input(is); + input.setSerializerFactory(factory); + Object actual = input.readObject(); + return actual; + } + +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionWrapper.java b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionWrapper.java new file mode 100644 index 0000000..d7a39a9 --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionWrapper.java @@ -0,0 +1,48 @@ +/* + * 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 com.caucho.hessian.test.stacktrace; + +import java.io.Serializable; + +/** + * + * @author junyuan + * @version ExceptionWrapper.java, v 0.1 2023年04月04日 15:40 junyuan Exp $ + */ +public class ExceptionWrapper implements Serializable { + private static final long serialVersionUID = 4065571790594438646L; + + Throwable t; + + /** + * Getter method for property t. + * + * @return property value of t + */ + public Throwable getT() { + return t; + } + + /** + * Setter method for property t. + * + * @param t value to be assigned to property t + */ + public void setT(Throwable t) { + this.t = t; + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java new file mode 100644 index 0000000..5b55347 --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java @@ -0,0 +1,151 @@ +/* + * 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 com.caucho.hessian.test.stacktrace; + +import com.caucho.hessian.io.Hessian2Output; +import com.caucho.hessian.io.SerializerFactory; +import com.caucho.hessian.io.StackTraceElementSerializer; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.concurrent.ConcurrentMap; + +/** + * + * @author junyuan + * @version SerializeCompatibleTest.java, v 0.1 2023年04月10日 14:52 junyuan Exp $ + */ +public class SerializeCompatibleTest { + + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + Throwable t = null; + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + + os = new ByteArrayOutputStream(); + } + + @AfterClass + public static void tearDown() { + try { + Field field = SerializerFactory.class.getDeclaredField("_staticSerializerMap"); + field.setAccessible(true); + ConcurrentMap map = (ConcurrentMap) field.get(SerializerFactory.class); + StackTraceElementSerializer serializer = new StackTraceElementSerializer(); + map.put(StackTraceElement.class, serializer); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + { + try { + double x = 1 / 0; + } catch (Exception e) { + t = e; + } + } + + /** + * result byte should be in same with former version so as to behaving compatible + * @throws IOException + */ + @Test + public void test_serialize() throws IOException, NoSuchFieldException, IllegalAccessException { + byte[] wrappedCaseStackTrace = serializeExceptionWrapper(); + byte[] unwrappedStackTrace = serializeStackTraceElement(); + + // use origin java serialize then + Field field = SerializerFactory.class.getDeclaredField("_staticSerializerMap"); + field.setAccessible(true); + ConcurrentMap map = (ConcurrentMap) field.get(SerializerFactory.class); + map.remove(StackTraceElement.class); + + byte[] wrappedStackTraceCaseJava = serializeExceptionWrapper(); + byte[] unwrappedStackTraceJava = serializeStackTraceElement(); + + Assert.assertTrue(bytesEquals(wrappedCaseStackTrace, wrappedStackTraceCaseJava)); + + Assert.assertTrue(bytesEquals(unwrappedStackTrace, unwrappedStackTraceJava)); + } + + protected boolean bytesEquals(byte[] src, byte[] target) { + if (src == null && target == null) { + return true; + } + + if (src == null || target == null) { + return false; + } + + if (src.length != target.length) { + return false; + } + + for (int i = 0; i < src.length; i++) { + if (src[i] != target[i]) { + return false; + } + } + return true; + } + + private byte[] serializeExceptionWrapper() throws IOException { + // wrapped + com.caucho.hessian.test.throwable.ExceptionWrapper exceptionWrapper = new com.caucho.hessian.test.throwable.ExceptionWrapper(); + + exceptionWrapper.setT(t); + + os.reset(); + Hessian2Output wrapCaseOutput = new Hessian2Output(os); + + wrapCaseOutput.setSerializerFactory(factory); + wrapCaseOutput.writeObject(exceptionWrapper); + wrapCaseOutput.flush(); + byte[] wrappedBytes = os.toByteArray(); + return wrappedBytes; + } + + private byte[] serializeStackTraceElement() throws IOException { + Throwable t = null; + try { + double x = 1 / 0; + } catch (Exception e) { + t = e; + } + StackTraceElement e = t.getStackTrace()[0]; + ; + + os.reset(); + Hessian2Output output = new Hessian2Output(os); + output.setSerializerFactory(factory); + output.writeObject(e); + output.flush(); + byte[] unwrappedBytes = os.toByteArray(); + return unwrappedBytes; + } + +} diff --git a/src/test/java/com/caucho/hessian/test/throwable/MeaninglessEnum.java b/src/test/java/com/caucho/hessian/test/throwable/MeaninglessEnum.java new file mode 100644 index 0000000..af9d0ac --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/throwable/MeaninglessEnum.java @@ -0,0 +1,16 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.test.throwable; + +/** + * + * @author junyuan + * @version MeaninglessEnum.java, v 0.1 2023年04月27日 19:21 junyuan Exp $ + */ +public enum MeaninglessEnum { + S1, + S2, + S3 +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/throwable/SelfDefinedException.java b/src/test/java/com/caucho/hessian/test/throwable/SelfDefinedException.java new file mode 100644 index 0000000..5b7f2d6 --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/throwable/SelfDefinedException.java @@ -0,0 +1,35 @@ +/* + * 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 com.caucho.hessian.test.throwable; + +/** + * + * @author junyuan + * @version SelfDefinedException.java, v 0.1 2023年04月11日 10:12 junyuan Exp $ + */ +public class SelfDefinedException extends RuntimeException { + + private String bizCode; + + private String bizMessage; + + public SelfDefinedException(String bizCode, String bizMessage) { + this.bizCode = bizCode; + this.bizMessage = bizMessage; + } + +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/throwable/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/test/throwable/SerializeCompatibleTest.java index 908266e..2eefb3e 100644 --- a/src/test/java/com/caucho/hessian/test/throwable/SerializeCompatibleTest.java +++ b/src/test/java/com/caucho/hessian/test/throwable/SerializeCompatibleTest.java @@ -36,14 +36,19 @@ */ public class SerializeCompatibleTest { + private static SerializerFactory originFactory; private static SerializerFactory factory; private static ByteArrayOutputStream os; Throwable t = null; + { + t = new EnumConstantNotPresentException(MeaninglessEnum.class, "CIG"); + } @BeforeClass public static void setUp() { factory = new SerializerFactory(); + originFactory = new SerializeFactoryWithoutThrowable(); os = new ByteArrayOutputStream(); } @@ -61,35 +66,22 @@ public static void tearDown() { } } - { - try { - double x = 1 / 0; - } catch (Exception e) { - t = e; - } - } - /** * result byte should be in same with former version so as to behaving compatible * @throws IOException */ @Test - public void test_serialize() throws IOException, NoSuchFieldException, IllegalAccessException { - byte[] wrappedCaseStackTrace = serializeExceptionWrapper(); - byte[] unwrappedStackTrace = serializeStackTraceElement(); - - // use origin java serialize then - Field field = SerializerFactory.class.getDeclaredField("_staticSerializerMap"); - field.setAccessible(true); - ConcurrentMap map = (ConcurrentMap) field.get(SerializerFactory.class); - map.remove(StackTraceElement.class); - - byte[] wrappedStackTraceCaseJava = serializeExceptionWrapper(); - byte[] unwrappedStackTraceJava = serializeStackTraceElement(); - - Assert.assertTrue(bytesEquals(wrappedCaseStackTrace, wrappedStackTraceCaseJava)); + public void test_EnumConstantNotPresentExceptionSerialize() throws IOException { + byte[] caseOrigin = serializeEnumConstantNotPresentException(originFactory); + byte[] caseNew = serializeEnumConstantNotPresentException(factory); + Assert.assertTrue(bytesEquals(caseOrigin, caseNew)); + } - Assert.assertTrue(bytesEquals(unwrappedStackTrace, unwrappedStackTraceJava)); + @Test + public void test_EnumConstantNotPresentExceptionWrapperSerialize() throws IOException { + byte[] wrapperCaseOrigin = serializeEnumConstantNotPresentExceptionWrapper(originFactory); + byte[] wrapperCaseNew = serializeEnumConstantNotPresentExceptionWrapper(factory); + Assert.assertTrue(bytesEquals(wrapperCaseOrigin, wrapperCaseNew)); } protected boolean bytesEquals(byte[] src, byte[] target) { @@ -113,39 +105,28 @@ protected boolean bytesEquals(byte[] src, byte[] target) { return true; } - private byte[] serializeExceptionWrapper() throws IOException { - // wrapped + private byte[] serializeEnumConstantNotPresentExceptionWrapper(SerializerFactory sf) throws IOException { ExceptionWrapper exceptionWrapper = new ExceptionWrapper(); exceptionWrapper.setT(t); os.reset(); Hessian2Output wrapCaseOutput = new Hessian2Output(os); - - wrapCaseOutput.setSerializerFactory(factory); + wrapCaseOutput.setSerializerFactory(sf); wrapCaseOutput.writeObject(exceptionWrapper); wrapCaseOutput.flush(); byte[] wrappedBytes = os.toByteArray(); return wrappedBytes; } - private byte[] serializeStackTraceElement() throws IOException { - Throwable t = null; - try { - double x = 1 / 0; - } catch (Exception e) { - t = e; - } - StackTraceElement e = t.getStackTrace()[0]; - ; - + private byte[] serializeEnumConstantNotPresentException(SerializerFactory sf) throws IOException { os.reset(); - Hessian2Output output = new Hessian2Output(os); - output.setSerializerFactory(factory); - output.writeObject(e); - output.flush(); - byte[] unwrappedBytes = os.toByteArray(); - return unwrappedBytes; + Hessian2Output wrapCaseOutput = new Hessian2Output(os); + wrapCaseOutput.setSerializerFactory(sf); + wrapCaseOutput.writeObject(t); + wrapCaseOutput.flush(); + byte[] wrappedBytes = os.toByteArray(); + return wrappedBytes; } } diff --git a/src/test/java/com/caucho/hessian/test/throwable/SerializeFactoryWithoutThrowable.java b/src/test/java/com/caucho/hessian/test/throwable/SerializeFactoryWithoutThrowable.java new file mode 100644 index 0000000..bf97f1c --- /dev/null +++ b/src/test/java/com/caucho/hessian/test/throwable/SerializeFactoryWithoutThrowable.java @@ -0,0 +1,122 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.test.throwable; + +import com.caucho.burlap.io.BurlapRemoteObject; +import com.caucho.hessian.io.AbstractSerializerFactory; +import com.caucho.hessian.io.ArraySerializer; +import com.caucho.hessian.io.CalendarSerializer; +import com.caucho.hessian.io.CollectionSerializer; +import com.caucho.hessian.io.EnumSerializer; +import com.caucho.hessian.io.EnumerationSerializer; +import com.caucho.hessian.io.HessianProtocolException; +import com.caucho.hessian.io.HessianRemoteObject; +import com.caucho.hessian.io.InputStreamSerializer; +import com.caucho.hessian.io.IteratorSerializer; +import com.caucho.hessian.io.LocaleSerializer; +import com.caucho.hessian.io.MapSerializer; +import com.caucho.hessian.io.RemoteSerializer; +import com.caucho.hessian.io.Serializer; +import com.caucho.hessian.io.SerializerFactory; + +import java.io.InputStream; +import java.util.Calendar; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + +/** + * + * @author junyuan + * @version SerializeFactoryWithoutThrowable.java, v 0.1 2023年04月27日 19:14 junyuan Exp $ + */ +public class SerializeFactoryWithoutThrowable extends SerializerFactory { + + @Override + public Serializer getSerializer(Class cl) throws HessianProtocolException { + Serializer serializer; + + serializer = (Serializer) _staticSerializerMap.get(cl); + if (serializer != null) + return serializer; + + //must before "else if (JavaSerializer.getWriteReplace(cl) != null)" or will be WriteReplace +// if (isZoneId(cl)) { +// serializer = ZoneIdSerializer.getInstance(); +// return serializer; +// } + + serializer = (Serializer) _cachedSerializerMap.get(cl); + if (serializer != null) + return serializer; + + if (classNameResolver != null) { + try { + classNameResolver.resolve(cl.getName()); + } catch (Exception e) { + throw new HessianProtocolException(e); + } + } + + for (int i = 0; serializer == null && _factories != null && i < _factories.size(); i++) { + AbstractSerializerFactory factory; + + factory = (AbstractSerializerFactory) _factories.get(i); + + serializer = factory.getSerializer(cl); + } + + if (serializer != null) { + } + + else if (HessianRemoteObject.class.isAssignableFrom(cl)) + serializer = new RemoteSerializer(); + + else if (BurlapRemoteObject.class.isAssignableFrom(cl)) + serializer = new RemoteSerializer(); + + else if (Map.class.isAssignableFrom(cl)) + serializer = new MapSerializer(); + + else if (Collection.class.isAssignableFrom(cl)) { + if (_collectionSerializer == null) { + _collectionSerializer = new CollectionSerializer(); + } + + serializer = _collectionSerializer; + } + + else if (cl.isArray()) + serializer = new ArraySerializer(); + + else if (InputStream.class.isAssignableFrom(cl)) + serializer = new InputStreamSerializer(); + + else if (Iterator.class.isAssignableFrom(cl)) + serializer = IteratorSerializer.create(); + + else if (Enumeration.class.isAssignableFrom(cl)) + serializer = EnumerationSerializer.create(); + + else if (Calendar.class.isAssignableFrom(cl)) + serializer = CalendarSerializer.create(); + + else if (Locale.class.isAssignableFrom(cl)) + serializer = LocaleSerializer.create(); + + else if (Enum.class.isAssignableFrom(cl)) + serializer = new EnumSerializer(cl); + + if (serializer == null) + serializer = getDefaultSerializer(cl); + + _cachedSerializerMap.put(cl, serializer); + + return serializer; + } + +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java b/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java index bde04f5..16d5d76 100644 --- a/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java +++ b/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java @@ -26,6 +26,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.lang.reflect.Method; /** * @@ -36,6 +37,16 @@ public class ThrowableClassTest { private static SerializerFactory factory; private static ByteArrayOutputStream os; + private Method addSuppressed = null; + private Method getSuppressed = null; + { + try { + addSuppressed = Throwable.class.getMethod("addSuppressed", Throwable.class); + getSuppressed = Throwable.class.getMethod("getSuppressed"); + } catch (Exception e) { + + } + } @BeforeClass public static void setUp() { @@ -44,26 +55,51 @@ public static void setUp() { } @Test - public void test_stacktrace() throws IOException { + public void test_Throwable() throws IOException { Throwable t = null; try { int x = 1 / 0; } catch (Exception e) { - t = e; + t = new Throwable(e); } ExceptionWrapper w = new ExceptionWrapper(); w.setT(t); + if (addSuppressed != null) { + addSuppress(w); + addSuppress(w); + } Object result = doEncodeNDecode(w); Assert.assertTrue(result instanceof ExceptionWrapper); + Throwable origin = w.getT(); + Throwable target = ((ExceptionWrapper) result).getT(); + + // stack trace + Assert.assertEquals(origin.getStackTrace().length, target.getStackTrace().length); + Assert.assertArrayEquals(origin.getStackTrace(), target.getStackTrace()); - StackTraceElement[] origin = w.getT().getStackTrace(); - StackTraceElement[] target = ((ExceptionWrapper) result).getT().getStackTrace(); + // detail message + Assert.assertEquals(origin.getMessage(), target.getMessage()); - Assert.assertEquals(origin.length, target.length); - Assert.assertArrayEquals(origin, target); + // cause, now only assert on cause.detailMessage + Assert.assertEquals(origin.getCause().getMessage(), target.getCause().getMessage()); + + // suppress + Throwable[] originSuppressed = getSuppress(origin); + Throwable[] targetSuppressed = getSuppress(target); + if (originSuppressed == null && targetSuppressed == null) { + return; + } + + Assert.assertTrue("one suppress is null while another is not", + !(originSuppressed == null || targetSuppressed == null)); + + Assert.assertTrue(originSuppressed.length == targetSuppressed.length); + for (int i = 0; i < originSuppressed.length; i++) { + Assert.assertEquals(originSuppressed[i].getMessage(), targetSuppressed[i].getMessage()); + } } protected Object doEncodeNDecode(Object origin) throws IOException { @@ -81,4 +117,33 @@ protected Object doEncodeNDecode(Object origin) throws IOException { return actual; } + private void mockCause(ExceptionWrapper w) { + } + + private void addSuppress(ExceptionWrapper w) { + Throwable suppressT1 = null; + try { + String x = null; + x.equals(""); + } catch (NullPointerException e) { + suppressT1 = e; + } + + try { + addSuppressed.invoke(w.getT(), suppressT1); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private Throwable[] getSuppress(Throwable t) { + Throwable[] ts = null; + try { + ts = (Throwable[]) getSuppressed.invoke(t); + } catch (Exception e) { + e.printStackTrace(); + } + return ts; + } + } From 887d0bc71600ef27db06b1bc5312d928d2e8387a Mon Sep 17 00:00:00 2001 From: lo1nt Date: Fri, 5 May 2023 17:10:18 +0800 Subject: [PATCH 10/32] feat: throwable --- ...a => AbstractFieldSpecificSerializer.java} | 33 +- .../caucho/hessian/io/SerializerFactory.java | 3 +- .../AbstractFieldSpecificDeserializer.java | 43 +++ .../throwable/ReflectThrowableSerializer.java | 28 ++ .../StackTraceElementDeserializer.java | 71 ++-- .../StackTraceElementSerializer.java | 48 ++- .../io/throwable/ThrowableDeserializer.java | 317 +++++++++++++++--- .../hessian/io/throwable/ThrowableHelper.java | 12 +- .../io/throwable/ThrowableSerializer.java | 39 +-- ...nstantNotPresentExceptionDeserializer.java | 10 +- ...ConstantNotPresentExceptionSerializer.java | 4 + .../throwable/ExceptionWrapper.java | 2 +- .../io/throwable/JDK17SerializeFactory.java | 59 ++++ .../throwable/MeaninglessEnum.java | 2 +- .../throwable/SelfDefinedException.java | 2 +- .../SerializeCompatibleSelfTest.java} | 97 ++++-- .../io/throwable/SerializeCompatibleTest.java | 292 ++++++++++++++++ .../SerializeFactoryWithoutThrowable.java | 80 +++++ .../throwable/ThrowableClassTest.java | 2 +- .../ThrowableJdk17SerializeTest.java | 172 ++++++++++ .../stacktrace/SerializeCompatibleTest.java | 6 +- .../SerializeFactoryWithoutThrowable.java | 122 ------- 22 files changed, 1156 insertions(+), 288 deletions(-) rename src/main/java/com/caucho/hessian/io/{NonReflectionSerializer.java => AbstractFieldSpecificSerializer.java} (62%) create mode 100644 src/main/java/com/caucho/hessian/io/throwable/AbstractFieldSpecificDeserializer.java create mode 100644 src/main/java/com/caucho/hessian/io/throwable/ReflectThrowableSerializer.java rename src/main/java/com/caucho/hessian/io/{ => throwable}/StackTraceElementDeserializer.java (69%) rename src/main/java/com/caucho/hessian/io/{ => throwable}/StackTraceElementSerializer.java (71%) rename src/test/java/com/caucho/hessian/{test => io}/throwable/ExceptionWrapper.java (97%) create mode 100644 src/test/java/com/caucho/hessian/io/throwable/JDK17SerializeFactory.java rename src/test/java/com/caucho/hessian/{test => io}/throwable/MeaninglessEnum.java (84%) rename src/test/java/com/caucho/hessian/{test => io}/throwable/SelfDefinedException.java (96%) rename src/test/java/com/caucho/hessian/{test/throwable/SerializeCompatibleTest.java => io/throwable/SerializeCompatibleSelfTest.java} (54%) create mode 100644 src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleTest.java create mode 100644 src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java rename src/test/java/com/caucho/hessian/{test => io}/throwable/ThrowableClassTest.java (99%) create mode 100644 src/test/java/com/caucho/hessian/io/throwable/ThrowableJdk17SerializeTest.java delete mode 100644 src/test/java/com/caucho/hessian/test/throwable/SerializeFactoryWithoutThrowable.java diff --git a/src/main/java/com/caucho/hessian/io/NonReflectionSerializer.java b/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificSerializer.java similarity index 62% rename from src/main/java/com/caucho/hessian/io/NonReflectionSerializer.java rename to src/main/java/com/caucho/hessian/io/AbstractFieldSpecificSerializer.java index 1efe58b..96d888e 100644 --- a/src/main/java/com/caucho/hessian/io/NonReflectionSerializer.java +++ b/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificSerializer.java @@ -6,16 +6,23 @@ import java.io.IOException; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; /** * * @author junyuan - * @version NonReflectionSerializer.java, v 0.1 2023年04月10日 19:34 junyuan Exp $ + * @version AbstractFieldSpecificSerializer.java, v 0.1 2023年04月10日 19:34 junyuan Exp $ */ -public abstract class NonReflectionSerializer extends AbstractSerializer { +public abstract class AbstractFieldSpecificSerializer extends AbstractSerializer { protected Field[] _fields; + public AbstractFieldSpecificSerializer(Class clazz) { + this._fields = getFieldsForSerialize(clazz); + } + public void writeObject(Object obj, AbstractHessianOutput out) throws IOException { if (obj == null) { out.writeNull(); @@ -77,4 +84,26 @@ public void writeInstance(Object obj, AbstractHessianOutput out) } protected abstract void serializeField(AbstractHessianOutput out, Object obj, Field field) throws IOException; + + /** + * get all fields + * include super class + * exclude transient or static + * @param cl + * @return + */ + protected Field[] getFieldsForSerialize(Class cl) { + List fields = new ArrayList(); + for (; cl != null; cl = cl.getSuperclass()) { + Field[] originFields = cl.getDeclaredFields(); + for (int i = 0; i < originFields.length; i++) { + Field field = originFields[i]; + if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { + continue; + } + fields.add(field); + } + } + return fields.toArray(new Field[0]); + } } \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/SerializerFactory.java b/src/main/java/com/caucho/hessian/io/SerializerFactory.java index 125e22f..c684bba 100644 --- a/src/main/java/com/caucho/hessian/io/SerializerFactory.java +++ b/src/main/java/com/caucho/hessian/io/SerializerFactory.java @@ -66,8 +66,9 @@ import com.caucho.hessian.io.java8.ZoneIdSerializer; import com.caucho.hessian.io.java8.ZoneOffsetHandle; import com.caucho.hessian.io.java8.ZonedDateTimeHandle; +import com.caucho.hessian.io.throwable.StackTraceElementDeserializer; +import com.caucho.hessian.io.throwable.StackTraceElementSerializer; import com.caucho.hessian.io.throwable.ThrowableHelper; -import com.caucho.hessian.io.throwable.ThrowableSerializer; import java.io.*; import java.math.BigDecimal; diff --git a/src/main/java/com/caucho/hessian/io/throwable/AbstractFieldSpecificDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/AbstractFieldSpecificDeserializer.java new file mode 100644 index 0000000..018f2be --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/throwable/AbstractFieldSpecificDeserializer.java @@ -0,0 +1,43 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.throwable; + +import com.caucho.hessian.io.AbstractDeserializer; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author junyuan + * @version AbstractFieldSpecificDeserializer.java, v 0.1 2023年05月06日 14:21 junyuan Exp $ + */ +public abstract class AbstractFieldSpecificDeserializer extends AbstractDeserializer { + + protected Map _fields; + + public AbstractFieldSpecificDeserializer(Class cl) { + _fields = getFieldMapForSerialize(cl); + } + + protected Map getFieldMapForSerialize(Class cl) { + Map fields = new HashMap(); + for (; cl != null; cl = cl.getSuperclass()) { + Field[] originFields = cl.getDeclaredFields(); + for (int i = 0; i < originFields.length; i++) { + Field field = originFields[i]; + if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { + continue; + } else if (fields.containsKey(field.getName())) { + continue; + } + fields.put(field.getName(), field); + } + } + return fields; + } +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/throwable/ReflectThrowableSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/ReflectThrowableSerializer.java new file mode 100644 index 0000000..57ad5e3 --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/throwable/ReflectThrowableSerializer.java @@ -0,0 +1,28 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.throwable; + +import com.caucho.hessian.io.AbstractHessianOutput; +import com.caucho.hessian.io.JavaSerializer; + +import java.io.IOException; + +/** + * use under jdk 17 + * @author junyuan + * @version ReflectThrowableSerializer.java, v 0.1 2023年05月06日 10:46 junyuan Exp $ + */ +public class ReflectThrowableSerializer extends JavaSerializer { + public ReflectThrowableSerializer(Class cl) { + super(cl); + } + + @Override + public void writeObject(Object obj, AbstractHessianOutput out) throws IOException { + // 如果需要反射操作获取 stack trace, 这里需要先 get 一下 + ((Throwable) obj).getStackTrace(); + super.writeObject(obj, out); + } +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/StackTraceElementDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java similarity index 69% rename from src/main/java/com/caucho/hessian/io/StackTraceElementDeserializer.java rename to src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java index de0abb8..abe5117 100644 --- a/src/main/java/com/caucho/hessian/io/StackTraceElementDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java @@ -46,7 +46,10 @@ * @author Scott Ferguson */ -package com.caucho.hessian.io; +package com.caucho.hessian.io.throwable; + +import com.caucho.hessian.io.AbstractHessianInput; +import com.caucho.hessian.io.IOExceptionWrapper; import java.io.IOException; import java.lang.reflect.Constructor; @@ -61,12 +64,12 @@ * Deserializing a JDK 1.4 StackTraceElement * @author pangu */ -public class StackTraceElementDeserializer extends AbstractDeserializer { +public class StackTraceElementDeserializer extends AbstractFieldSpecificDeserializer { protected static final Logger log = Logger.getLogger(StackTraceElementSerializer.class.getName()); - private Map _fields = new HashMap(); + private Constructor _defaultConstructor = null; - private Constructor _constructor; + private Constructor _constructorJdk9 = null; @Override public Class getType() { @@ -74,29 +77,16 @@ public Class getType() { } public StackTraceElementDeserializer() { - Class clazz = StackTraceElement.class; - Field[] originFields = clazz.getDeclaredFields(); - for (int i = 0; i < originFields.length; i++) { - Field field = originFields[i]; - - if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { - continue; - } - if ("declaringClassObject".equals(field.getName()) || "format".equals(field.getName())) { - continue; - } - _fields.put(field.getName(), field); - } + super(StackTraceElement.class); try { - if (_fields.size() == 7) { + if (_fields.size() > 4) { // available since java 9 - _constructor = clazz.getDeclaredConstructor(String.class, String.class, String.class, String.class, + _constructorJdk9 = StackTraceElement.class.getDeclaredConstructor(String.class, String.class, String.class, String.class, String.class, String.class, int.class); - } else { - // default, only read class, method, file and line - _constructor = clazz.getDeclaredConstructor(String.class, String.class, String.class, int.class); } + // default, only read class, method, file and line + _defaultConstructor = StackTraceElement.class.getDeclaredConstructor(String.class, String.class, String.class, int.class); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } @@ -106,7 +96,14 @@ public StackTraceElementDeserializer() { @Override public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IOException { try { - int ref = in.addRef(_constructor.newInstance("", "", "", 0)); + Object tmp; + if (_constructorJdk9 != null) { + tmp = _constructorJdk9.newInstance("", "", "", "", "", "", 0); + } else { + tmp = _defaultConstructor.newInstance("", "", "", 0); + } + + int ref = in.addRef(tmp); Map fieldValueMap = new HashMap(); for (int i = 0; i < fieldNames.length; i++) { @@ -121,16 +118,18 @@ public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IO } StackTraceElement obj; - if (fieldNames.length == 7) { - obj = _constructor.newInstance( + if (_constructorJdk9 != null) { + obj = _constructorJdk9.newInstance( fieldValueMap.get("classLoaderName"), fieldValueMap.get("moduleName"), fieldValueMap.get("moduleVersion"), fieldValueMap.get("declaringClass"), fieldValueMap.get("methodName"), fieldValueMap.get("fileName"), fieldValueMap.get("lineNumber")); - } else { - obj = _constructor.newInstance( + } else if (_defaultConstructor != null) { + obj = _defaultConstructor.newInstance( fieldValueMap.get("declaringClass"), fieldValueMap.get("methodName"), fieldValueMap.get("fileName"), fieldValueMap.get("lineNumber")); + } else { + throw new UnsupportedOperationException("no constructor for " + getType().getName() + " found"); } in.setRef(ref, obj); @@ -142,4 +141,22 @@ public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IO throw new IOExceptionWrapper(StackTraceElement.class.getName() + ":" + e, e); } } + + @Override + protected Map getFieldMapForSerialize(Class cl) { + Map fields = new HashMap(); + for (; cl != null; cl = cl.getSuperclass()) { + Field[] originFields = cl.getDeclaredFields(); + for (int i = 0; i < originFields.length; i++) { + Field field = originFields[i]; + if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { + continue; + } else if (fields.containsKey(field.getName()) || "format".equals(field.getName())) { + continue; + } + fields.put(field.getName(), field); + } + } + return fields; + } } diff --git a/src/main/java/com/caucho/hessian/io/StackTraceElementSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java similarity index 71% rename from src/main/java/com/caucho/hessian/io/StackTraceElementSerializer.java rename to src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java index 3ef807d..0542ea3 100644 --- a/src/main/java/com/caucho/hessian/io/StackTraceElementSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java @@ -2,7 +2,10 @@ * Ant Group * Copyright (c) 2004-2023 All Rights Reserved. */ -package com.caucho.hessian.io; +package com.caucho.hessian.io.throwable; + +import com.caucho.hessian.io.AbstractFieldSpecificSerializer; +import com.caucho.hessian.io.AbstractHessianOutput; import java.io.IOException; import java.lang.reflect.Field; @@ -10,6 +13,7 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -19,7 +23,7 @@ * @author junyuan * @version StackTraceElementSerializer.java, v 0.1 2023年04月10日 11:12 junyuan Exp $ */ -public class StackTraceElementSerializer extends NonReflectionSerializer { +public class StackTraceElementSerializer extends AbstractFieldSpecificSerializer { protected static final Logger log = Logger .getLogger(StackTraceElementSerializer.class .getName()); @@ -31,21 +35,7 @@ public class StackTraceElementSerializer extends NonReflectionSerializer { private Map _readMethods = new HashMap(); public StackTraceElementSerializer() { - Field[] originFields = _clazz.getDeclaredFields(); - ArrayList tmp = new ArrayList(); - for (int i = 0; i < originFields.length; i++) { - Field field = originFields[i]; - - if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { - continue; - } - if ("declaringClassObject".equals(field.getName()) || "format".equals(field.getName())) { - continue; - } - tmp.add(field); - } - _fields = new Field[tmp.size()]; - tmp.toArray(_fields); + super(StackTraceElement.class); // get getter for (Field field : _fields) { @@ -70,6 +60,11 @@ public StackTraceElementSerializer() { @Override protected void serializeField(AbstractHessianOutput out, Object obj, Field field) throws IOException { + if (!_readMethods.containsKey(field.getName())) { + out.writeNull(); + return; + } + // only String and int field is required to be serialized if (String.class.equals(field.getType())) { String value = null; @@ -90,6 +85,25 @@ protected void serializeField(AbstractHessianOutput out, Object obj, Field field } else { out.writeNull(); } + } + + @Override + protected Field[] getFieldsForSerialize(Class cl) { + List fields = new ArrayList(); + for (; cl != null; cl = cl.getSuperclass()) { + Field[] originFields = cl.getDeclaredFields(); + for (int i = 0; i < originFields.length; i++) { + Field field = originFields[i]; + if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { + continue; + } + if ("format".equals(field.getName())) { + continue; + } + fields.add(field); + } + } + return fields.toArray(new Field[0]); } } \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java index 29910b1..e2da9d3 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java @@ -4,15 +4,14 @@ */ package com.caucho.hessian.io.throwable; -import com.caucho.hessian.io.AbstractDeserializer; import com.caucho.hessian.io.AbstractHessianInput; +import com.caucho.hessian.io.HessianFieldException; import com.caucho.hessian.io.IOExceptionWrapper; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -22,37 +21,20 @@ * @author junyuan * @version ThrowableDeserializer.java, v 0.1 2023年04月10日 20:37 junyuan Exp $ */ -public class ThrowableDeserializer extends AbstractDeserializer { +public class ThrowableDeserializer extends AbstractFieldSpecificDeserializer { - private Class _type; - protected Map _fields = new HashMap(); - protected Method addSuppressed = null; + private final Class _type; + protected Method addSuppressed = null; - protected Constructor _constructor = null; + private final Throwable selfRef = new Throwable(); public ThrowableDeserializer(Class cl) { + super(cl); _type = cl; - Class clazz = Throwable.class; - - Field[] originFields = clazz.getDeclaredFields(); - for (int i = 0; i < originFields.length; i++) { - Field field = originFields[i]; - - if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { - continue; - } - - _fields.put(field.getName(), field); - } - - try { - _constructor = _type.getDeclaredConstructor(String.class); - } catch (NoSuchMethodException e) { - - } try { - addSuppressed = clazz.getDeclaredMethod("addSuppressed", Throwable.class); + // since 1.7 + addSuppressed = Throwable.class.getDeclaredMethod("addSuppressed", Throwable.class); } catch (NoSuchMethodException e) { } @@ -66,50 +48,275 @@ public Class getType() { @Override public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IOException { try { - int ref = in.addRef(new Throwable()); - Map fieldValueMap = new HashMap(); + int ref = in.addRef(selfRef); + Map fieldValueMap = readField(in, fieldNames); + Throwable obj = instantiate(_type, fieldValueMap); + fillFields(_type, obj, fieldValueMap); + in.setRef(ref, obj); + return obj; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(Throwable.class.getName() + ":" + e, e); + } + } - for (int i = 0; i < fieldNames.length; i++) { - String name = fieldNames[i]; - Field field = _fields.get(name); + protected Map readField(AbstractHessianInput in, String[] fieldNames) + throws IOException { + Map fieldValueMap = new HashMap(); + for (int i = 0; i < fieldNames.length; i++) { + String name = fieldNames[i]; + Field field = _fields.get(name); + if (field == null) { + continue; + } - if (String.class.equals(field.getType())) { - fieldValueMap.put(name, in.readString()); - } else { - fieldValueMap.put(name, in.readObject()); + if (String.class.equals(field.getType())) { + fieldValueMap.put(name, in.readString()); + } else { + fieldValueMap.put(name, in.readObject()); + } + } + return fieldValueMap; + } + + protected Throwable instantiate(Class clazz, Map fieldValueMap) + throws Exception { + Throwable ex = null; + try { + ex = doInstantiate(clazz, fieldValueMap); + } catch (Exception instantiateException) { + // todo: unsafe + } finally { + if (ex == null) { + // 兜底返回 Throwable + ex = new Throwable((String) fieldValueMap.get("detailMessage"), (Throwable) fieldValueMap.get("cause")); + } + } + return ex; + } + + protected void fillFields(Class clazz, Throwable obj, Map valueMap) + throws IOException { + for (String key: valueMap.keySet()) { + Object value = valueMap.get(key); + if (value == null) continue; + + if (key.equals("cause")) { + // 如果 cause 还未被写入, init + if (value.equals(selfRef)) { + // 如果 cause 是自己, 跳过不写 + continue; + } + + if (obj.getCause() == null) { + try { + obj.initCause((Throwable) value); + } catch (Exception e) { + logDeserializeError(_fields.get(key), value, e); + } } } - Throwable obj = null; - if (_constructor != null) { + else if (key.equals("suppressedExceptions")) { + // since 1.7 try { - obj = (Throwable) _constructor.newInstance(fieldValueMap.get("detailMessage")); - Throwable cause = (Throwable) fieldValueMap.get("cause"); - if (cause != null) { - obj.initCause(cause); + if (!(value instanceof List)) { + continue; + } + List listValue = (List) value; + if (listValue.size() == 0) { + continue; + } + if (addSuppressed != null) { + for (Object item: listValue) { + addSuppressed.invoke(obj, item); + } } } catch (Exception e) { e.printStackTrace(); - obj = new Throwable((String) fieldValueMap.get("detailMessage"), - (Throwable) fieldValueMap.get("cause")); } - } else { - obj = new Throwable((String) fieldValueMap.get("detailMessage"), (Throwable) fieldValueMap.get("cause")); } + else if (key.equals("stackTrace")) { + obj.setStackTrace((StackTraceElement[]) value); + } + // 其他所有 field + else { + fillOtherFields(clazz, obj, key, value); + } + } + } - obj.setStackTrace((StackTraceElement[]) fieldValueMap.get("stackTrace")); + protected void fillOtherFields(Class clazz, Throwable obj, String key, Object value) + throws IOException { + Field field = _fields.get(key); + if (field == null) { + return; + } - List suppress = (List) fieldValueMap.get("suppressedExceptions"); - for (Throwable t : suppress) { - addSuppressed.invoke(obj, t); + try { + field.setAccessible(true); + field.set(obj, value); + } catch (Exception e) { + logDeserializeError(field, value, e); + } + } + + /** + * 实例化, 这里只会返回 targetClass 的实例对象或者 null + * 根据优先级分别使用构造函数 + * ExceptionClass(String message, Throwable cause) + * ExceptionClass(String message) + * ExceptionClass() + * ExceptionClass(args...) 无法确认参数, 使用默认值进行构造, 优先级最低 + * + * @param clazz + * @param fieldValueMap + * @return + * @throws Exception + */ + private Throwable doInstantiate(Class clazz, Map fieldValueMap) + throws Exception { + Constructor causeConstructor = null; + Constructor messageConstructor = null; + Constructor defaultConstructor = null; + Constructor constructorByCost = null; + + long bestCost = Long.MAX_VALUE; + // 只会返回public的构造方法 + for (Constructor c: clazz.getDeclaredConstructors()) { + Class[] pTypes = c.getParameterTypes(); + + if (pTypes.length == 0) { + defaultConstructor = c; + continue; } - in.setRef(ref, obj); + if (pTypes.length == 1 && pTypes[0].equals(String.class)) { + // Exception(String detailMessage) + messageConstructor = c; + continue; + } - return obj; - } catch (IOException e) { - throw e; - } catch (Exception e) { - throw new IOExceptionWrapper(Throwable.class.getName() + ":" + e, e); + if (pTypes.length == 2 && pTypes[0].equals(String.class) && pTypes[1].equals(Throwable.class)) { + // Exception(String detailMessage, Throwable cause) + causeConstructor = c; + continue; + } + + // 对于不是以上三种的构造方法, 根据JavaDeserializer的cost计算方式获取constructor + if (calculateCost(pTypes) < bestCost) { + constructorByCost = c; + } + } + + // 根据优先级调用 + String detailMessage = (String) fieldValueMap.get("detailMessage"); + Throwable cause = (Throwable) fieldValueMap.get("cause"); + if (causeConstructor != null) { + return (Throwable) causeConstructor.newInstance(detailMessage, cause); + } + if (messageConstructor != null) { + return (Throwable) messageConstructor.newInstance(detailMessage); + } + if (defaultConstructor != null) { + return (Throwable) defaultConstructor.newInstance(); } + if (constructorByCost != null) { + Object[] args = getConstructorArgs(constructorByCost); + return (Throwable) constructorByCost.newInstance(args); + } + + return null; + } + + /** + * get default arg value + * @param c + * @return + */ + protected Object[] getConstructorArgs(Constructor c) { + Class[] pTypes = c.getParameterTypes(); + Object[] constructorArgs = new Object[pTypes.length]; + for (int i = 0; i < pTypes.length; i++) { + constructorArgs[i] = getParamArg(pTypes[i]); + } + return constructorArgs; + } + + /** + * ref to {@link com.caucho.hessian.io.JavaDeserializer#JavaDeserializer(java.lang.Class)} + * @param pTypes + * @return + */ + protected long calculateCost(Class[] pTypes) { + long cost = 0; + + for (int j = 0; j < pTypes.length; j++) { + cost = 4 * cost; + + if (Object.class.equals(pTypes[j])) + cost += 1; + else if (String.class.equals(pTypes[j])) + cost += 2; + else if (int.class.equals(pTypes[j])) + cost += 3; + else if (long.class.equals(pTypes[j])) + cost += 4; + else if (pTypes[j].isPrimitive()) + cost += 5; + else + cost += 6; + } + + if (cost < 0 || cost > (1 << 48)) + cost = 1 << 48; + + cost += pTypes.length << 48; + return cost; + } + + /** + * ref to {@link com.caucho.hessian.io.JavaDeserializer#JavaDeserializer(java.lang.Class)} + * @param cl + * @return + */ + protected Object getParamArg(Class cl) { + if (!cl.isPrimitive()) + return null; + else if (boolean.class.equals(cl)) + return Boolean.FALSE; + else if (byte.class.equals(cl)) + return new Byte((byte) 0); + else if (short.class.equals(cl)) + return new Short((short) 0); + else if (char.class.equals(cl)) + return new Character((char) 0); + else if (int.class.equals(cl)) + return Integer.valueOf(0); + else if (long.class.equals(cl)) + return Long.valueOf(0); + else if (float.class.equals(cl)) + return Float.valueOf(0); + else if (double.class.equals(cl)) + return Double.valueOf(0); + else + throw new UnsupportedOperationException(); + } + + private void logDeserializeError(Field field, Object value, Throwable e) throws IOException { + String fieldName = (field.getDeclaringClass().getName() + + "." + field.getName()); + + if (e instanceof HessianFieldException) + throw (HessianFieldException) e; + else if (e instanceof IOException) + throw new HessianFieldException(fieldName + ": " + e.getMessage(), e); + + if (value != null) + throw new HessianFieldException(fieldName + ": " + value.getClass().getName() + " (" + value + ")" + + " cannot be assigned to " + field.getType().getName()); + else + throw new HessianFieldException(fieldName + ": " + field.getType().getName() + + " cannot be assigned from null", e); } } \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java index 0db75a0..0445ba5 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java @@ -8,6 +8,7 @@ import com.caucho.hessian.io.AbstractSerializer; import com.caucho.hessian.io.JavaDeserializer; import com.caucho.hessian.io.JavaSerializer; +import com.caucho.hessian.io.throwable.adapter.EnumConstantNotPresentExceptionDeserializer; import com.caucho.hessian.io.throwable.adapter.EnumConstantNotPresentExceptionSerializer; import java.util.HashMap; @@ -31,19 +32,23 @@ public static AbstractDeserializer getDeserializer(Class cl) { if (isLessThanJdk17) { return new JavaDeserializer(cl); } + if (EnumConstantNotPresentException.class.isAssignableFrom(cl)) { + return new EnumConstantNotPresentExceptionDeserializer(cl); + } + return new ThrowableDeserializer(cl); } public static AbstractSerializer getSerializer(Class cl) { if (isLessThanJdk17) { - return new JavaSerializer(cl); + return new ReflectThrowableSerializer(cl); } if (throwableSerializerMap.containsKey(cl.getName())) { return throwableSerializerMap.get(cl.getName()); } - return new ThrowableSerializer(); + return new ThrowableSerializer(cl); } private static final Map throwableSerializerMap = new HashMap(); @@ -51,4 +56,5 @@ public static AbstractSerializer getSerializer(Class cl) { throwableSerializerMap.put(EnumConstantNotPresentException.class.getName(), new EnumConstantNotPresentExceptionSerializer()); } -} \ No newline at end of file + +} diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java index 6ca655e..4d8d20b 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java @@ -5,12 +5,11 @@ package com.caucho.hessian.io.throwable; import com.caucho.hessian.io.AbstractHessianOutput; -import com.caucho.hessian.io.NonReflectionSerializer; +import com.caucho.hessian.io.AbstractFieldSpecificSerializer; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -21,35 +20,28 @@ * @author junyuan * @version ThrowableSerializer.java, v 0.1 2023年04月10日 19:30 junyuan Exp $ */ -public class ThrowableSerializer extends NonReflectionSerializer { +public class ThrowableSerializer extends AbstractFieldSpecificSerializer { - private final Class _clazz = Throwable.class; protected Method getSuppressed = null; - public ThrowableSerializer() { - Field[] fields = _clazz.getDeclaredFields(); - List tmp = new ArrayList(); - for (int i = 0; i < fields.length; i++) { - Field field = fields[i]; - - if (Modifier.isTransient(field.getModifiers()) || - Modifier.isStatic(field.getModifiers())) { - continue; - } - - tmp.add(field); - } - - _fields = new Field[tmp.size()]; - tmp.toArray(_fields); + public ThrowableSerializer(Class clazz) { + super(clazz); try { - getSuppressed = _clazz.getDeclaredMethod("getSuppressed"); + getSuppressed = clazz.getMethod("getSuppressed"); } catch (NoSuchMethodException e) { } } + @Override + public void writeObject(Object obj, AbstractHessianOutput out) throws IOException { + // 如果需要反射操作获取 stack trace, 这里需要先 get 一下 + ((Throwable) obj).getStackTrace(); + + super.writeObject(obj, out); + } + @Override protected void serializeField(AbstractHessianOutput out, Object obj, Field field) throws IOException { @@ -81,9 +73,10 @@ else if ("suppressedExceptions".equals(field.getName())) { List throwableList; if (throwableArray.length == 0) { - throwableList = Collections.emptyList(); + // 旧版通过反射会获取到这个类型 + throwableList = Collections.unmodifiableList(new ArrayList()); } else { - throwableList = Arrays.asList(throwableArray); + throwableList = new ArrayList(Arrays.asList(throwableArray)); } out.writeObject(throwableList); diff --git a/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java index c184091..77f7ba7 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java @@ -6,6 +6,8 @@ import com.caucho.hessian.io.throwable.ThrowableDeserializer; +import java.util.Map; + /** * * @author junyuan @@ -16,5 +18,11 @@ public EnumConstantNotPresentExceptionDeserializer(Class cl) { super(cl); } - + @Override + protected Throwable instantiate(Class clazz, Map fieldValueMap) + throws Exception { + Class enumType = (Class) fieldValueMap.remove("enumType"); + String constantName = (String) fieldValueMap.remove("constantName"); + return new EnumConstantNotPresentException(enumType, constantName); + } } \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java index 18c415a..eae4984 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java @@ -17,6 +17,10 @@ */ public class EnumConstantNotPresentExceptionSerializer extends ThrowableSerializer { + public EnumConstantNotPresentExceptionSerializer() { + super(EnumConstantNotPresentException.class); + } + @Override protected void defaultSerializeField(AbstractHessianOutput out, Object obj, Field field) throws IOException { diff --git a/src/test/java/com/caucho/hessian/test/throwable/ExceptionWrapper.java b/src/test/java/com/caucho/hessian/io/throwable/ExceptionWrapper.java similarity index 97% rename from src/test/java/com/caucho/hessian/test/throwable/ExceptionWrapper.java rename to src/test/java/com/caucho/hessian/io/throwable/ExceptionWrapper.java index 6f74c5b..2425862 100644 --- a/src/test/java/com/caucho/hessian/test/throwable/ExceptionWrapper.java +++ b/src/test/java/com/caucho/hessian/io/throwable/ExceptionWrapper.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.caucho.hessian.test.throwable; +package com.caucho.hessian.io.throwable; import java.io.Serializable; diff --git a/src/test/java/com/caucho/hessian/io/throwable/JDK17SerializeFactory.java b/src/test/java/com/caucho/hessian/io/throwable/JDK17SerializeFactory.java new file mode 100644 index 0000000..496c0ad --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/throwable/JDK17SerializeFactory.java @@ -0,0 +1,59 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.throwable; + +import com.caucho.hessian.io.Deserializer; +import com.caucho.hessian.io.HessianProtocolException; +import com.caucho.hessian.io.Serializer; +import com.caucho.hessian.io.SerializerFactory; +import com.caucho.hessian.io.throwable.adapter.EnumConstantNotPresentExceptionDeserializer; +import com.caucho.hessian.io.throwable.adapter.EnumConstantNotPresentExceptionSerializer; + +/** + * 可以在 java8 环境下运行专门给 jdk17 使用的序列化器 + * 以便在 java8 下进行兼容测试 + * @author junyuan + * @version JDK17SerializeFactory.java, v 0.1 2023年05月06日 11:13 junyuan Exp $ + */ +public class JDK17SerializeFactory extends SerializerFactory { + @Override + public Serializer getSerializer(Class cl) throws HessianProtocolException { + Serializer serializer = super.getSerializer(cl); + + if (Throwable.class.isAssignableFrom(cl)) { + if (EnumConstantNotPresentException.class.equals(cl)) { + serializer = new EnumConstantNotPresentExceptionSerializer(); + } else { + serializer = new ThrowableSerializer(cl); + } + } + + if (StackTraceElement.class.isAssignableFrom(cl)) { + serializer = new StackTraceElementSerializer(); + } + _cachedSerializerMap.put(cl, serializer); + + return serializer; + } + + @Override + public Deserializer getDeserializer(Class cl) throws HessianProtocolException { + Deserializer deserializer = super.getDeserializer(cl); + + if (Throwable.class.isAssignableFrom(cl)) { + if (EnumConstantNotPresentException.class.equals(cl)) { + deserializer = new EnumConstantNotPresentExceptionDeserializer(cl); + } else { + deserializer = new ThrowableDeserializer(cl); + } + } + + if (StackTraceElement.class.isAssignableFrom(cl)) + deserializer = new StackTraceElementDeserializer(); + + _cachedDeserializerMap.put(cl, deserializer); + return deserializer; + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/throwable/MeaninglessEnum.java b/src/test/java/com/caucho/hessian/io/throwable/MeaninglessEnum.java similarity index 84% rename from src/test/java/com/caucho/hessian/test/throwable/MeaninglessEnum.java rename to src/test/java/com/caucho/hessian/io/throwable/MeaninglessEnum.java index af9d0ac..1bec5f7 100644 --- a/src/test/java/com/caucho/hessian/test/throwable/MeaninglessEnum.java +++ b/src/test/java/com/caucho/hessian/io/throwable/MeaninglessEnum.java @@ -2,7 +2,7 @@ * Ant Group * Copyright (c) 2004-2023 All Rights Reserved. */ -package com.caucho.hessian.test.throwable; +package com.caucho.hessian.io.throwable; /** * diff --git a/src/test/java/com/caucho/hessian/test/throwable/SelfDefinedException.java b/src/test/java/com/caucho/hessian/io/throwable/SelfDefinedException.java similarity index 96% rename from src/test/java/com/caucho/hessian/test/throwable/SelfDefinedException.java rename to src/test/java/com/caucho/hessian/io/throwable/SelfDefinedException.java index 5b7f2d6..3becc40 100644 --- a/src/test/java/com/caucho/hessian/test/throwable/SelfDefinedException.java +++ b/src/test/java/com/caucho/hessian/io/throwable/SelfDefinedException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.caucho.hessian.test.throwable; +package com.caucho.hessian.io.throwable; /** * diff --git a/src/test/java/com/caucho/hessian/test/throwable/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleSelfTest.java similarity index 54% rename from src/test/java/com/caucho/hessian/test/throwable/SerializeCompatibleTest.java rename to src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleSelfTest.java index 2eefb3e..a878929 100644 --- a/src/test/java/com/caucho/hessian/test/throwable/SerializeCompatibleTest.java +++ b/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleSelfTest.java @@ -14,74 +14,104 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.caucho.hessian.test.throwable; +package com.caucho.hessian.io.throwable; import com.caucho.hessian.io.Hessian2Output; import com.caucho.hessian.io.SerializerFactory; -import com.caucho.hessian.io.StackTraceElementSerializer; -import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; -import org.junit.Test; +//import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.lang.reflect.Field; -import java.util.concurrent.ConcurrentMap; +import java.lang.reflect.Method; /** + * jdk17 下的throwable 序列化必定有损, 这个类仅用于自测, 实际运行必然不通过 * * @author junyuan - * @version SerializeCompatibleTest.java, v 0.1 2023年04月10日 14:52 junyuan Exp $ + * @version SerializeCompatibleSelfTest.java, v 0.1 2023年04月10日 14:52 junyuan Exp $ */ -public class SerializeCompatibleTest { +public class SerializeCompatibleSelfTest { + private static SerializerFactory useJdk17Factory; private static SerializerFactory originFactory; private static SerializerFactory factory; private static ByteArrayOutputStream os; + private Method addSuppressed = null; + private Method getSuppressed = null; + { + try { + addSuppressed = Throwable.class.getMethod("addSuppressed", Throwable.class); + getSuppressed = Throwable.class.getMethod("getSuppressed"); + } catch (Exception e) { + + } + } + Throwable t = null; { - t = new EnumConstantNotPresentException(MeaninglessEnum.class, "CIG"); + Throwable x = null; + try { + t.getStackTrace(); + } catch (NullPointerException e) { + t = e; + } + +// t = new EnumConstantNotPresentException(MeaninglessEnum.class, "CIG"); + +// try { +// addSuppressed.invoke(t, x); +// } catch (Exception e) { +// e.printStackTrace(); +// } + } + private static final boolean isLessThanJdk17 = isLessThanJdk17(); + + private static boolean isLessThanJdk17() { + String javaVersion = System.getProperty("java.specification.version"); + return Double.parseDouble(javaVersion) < 17; } @BeforeClass public static void setUp() { factory = new SerializerFactory(); originFactory = new SerializeFactoryWithoutThrowable(); + // 可以在jdk8环境下使用到专门为jdk17准备的 serializer + useJdk17Factory = new JDK17SerializeFactory(); os = new ByteArrayOutputStream(); } - @AfterClass - public static void tearDown() { - try { - Field field = SerializerFactory.class.getDeclaredField("_staticSerializerMap"); - field.setAccessible(true); - ConcurrentMap map = (ConcurrentMap) field.get(SerializerFactory.class); - StackTraceElementSerializer serializer = new StackTraceElementSerializer(); - map.put(StackTraceElement.class, serializer); - } catch (Throwable t) { - t.printStackTrace(); - } - } - /** * result byte should be in same with former version so as to behaving compatible * @throws IOException */ - @Test +// @Test public void test_EnumConstantNotPresentExceptionSerialize() throws IOException { - byte[] caseOrigin = serializeEnumConstantNotPresentException(originFactory); - byte[] caseNew = serializeEnumConstantNotPresentException(factory); - Assert.assertTrue(bytesEquals(caseOrigin, caseNew)); + if (isLessThanJdk17) { + // jdk 17 开始不需要执行这个, 反射受限, 执行会失败 + byte[] caseOrigin = serializeEnumConstantNotPresentException(originFactory); + byte[] caseNew = serializeEnumConstantNotPresentException(factory); + byte[] caseJdk17 = serializeEnumConstantNotPresentException(useJdk17Factory); + Assert.assertTrue(bytesEquals(caseOrigin, caseNew)); + Assert.assertTrue(bytesEquals(caseJdk17, caseNew)); + } + } - @Test +// @Test public void test_EnumConstantNotPresentExceptionWrapperSerialize() throws IOException { - byte[] wrapperCaseOrigin = serializeEnumConstantNotPresentExceptionWrapper(originFactory); - byte[] wrapperCaseNew = serializeEnumConstantNotPresentExceptionWrapper(factory); - Assert.assertTrue(bytesEquals(wrapperCaseOrigin, wrapperCaseNew)); + if (isLessThanJdk17) { + byte[] wrapperCaseOrigin = serializeEnumConstantNotPresentExceptionWrapper(originFactory); + byte[] wrapperCaseNew = serializeEnumConstantNotPresentExceptionWrapper(factory); + byte[] wrapperCaseJdk17 = serializeEnumConstantNotPresentExceptionWrapper(useJdk17Factory); + Assert.assertTrue(bytesEquals(wrapperCaseOrigin, wrapperCaseNew)); + bytePrint(wrapperCaseJdk17); + bytePrint(wrapperCaseOrigin); + Assert.assertTrue(bytesEquals(wrapperCaseJdk17, wrapperCaseNew)); + } } protected boolean bytesEquals(byte[] src, byte[] target) { @@ -105,6 +135,13 @@ protected boolean bytesEquals(byte[] src, byte[] target) { return true; } + private void bytePrint(byte[] bytes) { + for (int i = 0; i < bytes.length; i++) { + System.out.print(bytes[i] + " "); + } + System.out.println(); + } + private byte[] serializeEnumConstantNotPresentExceptionWrapper(SerializerFactory sf) throws IOException { ExceptionWrapper exceptionWrapper = new ExceptionWrapper(); diff --git a/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleTest.java new file mode 100644 index 0000000..f0b2216 --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleTest.java @@ -0,0 +1,292 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.throwable; + +import com.caucho.hessian.io.Hessian2Input; +import com.caucho.hessian.io.Hessian2Output; +import com.caucho.hessian.io.SerializerFactory; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * jdk17的兼容测试 + * 由于throwable类必定有损 + * 这里的测试方法为分别使用不同的方式进行序列化和反序列化, 排除允许有损的字段后, 检查其余字段 + * + * @author junyuan + * @version SerializeCompatibleTest.java, v 0.1 2023年05月06日 15:52 junyuan Exp $ + */ +public class SerializeCompatibleTest { + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + originFactory = new SerializeFactoryWithoutThrowable(); + // 可以在jdk8环境下使用到专门为jdk17准备的 serializer + useJdk17Factory = new JDK17SerializeFactory(); + + os = new ByteArrayOutputStream(); + } + + /** + * Wrapper + * use jdk8 to serialize and jdk17 to deserialize + * 'cause' is bound to lost here as getCause may return null + */ + @Test + public void test_case_1() throws IOException { + if (isLessThanJdk17) { + test_EnumConstantNotPresentExceptionWrapper(originFactory, useJdk17Factory); + } + } + + /** + * Exception class directly + * use jdk17 to serialize and jdk8 to deserialize + */ + @Test + public void test_case_2() throws IOException { + if (isLessThanJdk17) { + test_EnumConstantNotPresentException(originFactory, useJdk17Factory); + } + } + + /** + * Wrapper + * use jdk17 to serialize and jdk8 to deserialize + */ + @Test + public void test_case_3() throws IOException { + if (isLessThanJdk17) { + test_EnumConstantNotPresentExceptionWrapper(useJdk17Factory, originFactory); + } + } + + /** + * Exception class directly + * use jdk17 to serialize and jdk8 to deserialize + */ + @Test + public void test_case_4() throws IOException { + if (isLessThanJdk17) { + test_EnumConstantNotPresentException(useJdk17Factory, originFactory); + } + } + + /** + * Wrapper + * use jdk17 to serialize and deserialize + */ + @Test + public void test_case_5() throws IOException { + test_EnumConstantNotPresentExceptionWrapper(factory, factory); + } + + /** + * Exception class directly + * use jdk17 to serialize and deserialize + */ + @Test + public void test_case_6() throws IOException { + test_EnumConstantNotPresentException(factory, factory); + } + + + + + private static SerializerFactory useJdk17Factory; + private static SerializerFactory originFactory; + /** + * 和originFactory的差别有一个 stack trance element deserializer不同 + * factory直接使用了新版的, originFactory创建了一个老版本的deserializer + * factory 可以在jdk8或者17环境下运行, 自适应 + */ + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + private Method addSuppressed = null; + private Method getSuppressed = null; + { + try { + addSuppressed = Throwable.class.getMethod("addSuppressed", Throwable.class); + getSuppressed = Throwable.class.getMethod("getSuppressed"); + } catch (Exception e) { + + } + } + + Throwable t = null; + { + Throwable x = null; + try { + t.getStackTrace(); + } catch (NullPointerException e) { + x = e; + } + + t = new EnumConstantNotPresentException(MeaninglessEnum.class, "CIG"); + + try { + addSuppressed.invoke(t, x); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static final boolean isLessThanJdk17 = isLessThanJdk17(); + private static boolean isLessThanJdk17() { + String javaVersion = System.getProperty("java.specification.version"); + return Double.parseDouble(javaVersion) < 17; + } + + protected Object doEncodeNDecode(Object origin, SerializerFactory serializerFactory, SerializerFactory deserializerFactory) throws IOException { + os.reset(); + Hessian2Output output = new Hessian2Output(os); + + output.setSerializerFactory(serializerFactory); + output.writeObject(origin); + output.flush(); + + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + Hessian2Input input = new Hessian2Input(is); + input.setSerializerFactory(deserializerFactory); + Object actual = input.readObject(); + return actual; + } + + /** + * EnumConstantNotPresentException 作为成员变量 + * @throws IOException + */ + private void test_EnumConstantNotPresentExceptionWrapper(SerializerFactory serialize, SerializerFactory deserialize) throws IOException { + if (isLessThanJdk17) { + // jdk 17 开始不需要执行这个, 反射受限, 执行会失败 + + ExceptionWrapper exceptionWrapper = new ExceptionWrapper(); + exceptionWrapper.setT(t); + if (addSuppressed != null) { + addSuppress(exceptionWrapper.getT()); + addSuppress(exceptionWrapper.getT()); + } + + Object result = doEncodeNDecode(exceptionWrapper, serialize, deserialize); + Assert.assertTrue(result instanceof ExceptionWrapper); + + Throwable origin = exceptionWrapper.getT(); + Throwable target = ((ExceptionWrapper) result).getT(); + + // stack trace + Assert.assertEquals(origin.getStackTrace().length, target.getStackTrace().length); + Assert.assertArrayEquals(origin.getStackTrace(), target.getStackTrace()); + + // detail message + Assert.assertEquals(origin.getMessage(), target.getMessage()); + + // cause, now only assert on cause.detailMessage + if (origin.getCause() == null) { + // getCause为空可能是 cause == this 的情况, 此时只需要保证target也为null即可 + // 实际该字段可能在序列化过程中有损 + Assert.assertNull(target.getCause()); + } + + // suppress + Throwable[] originSuppressed = getSuppress(origin); + Throwable[] targetSuppressed = getSuppress(target); + if (originSuppressed == null && targetSuppressed == null) { + return; + } + + Assert.assertTrue("one suppress is null while another is not", + !(originSuppressed == null || targetSuppressed == null)); + + Assert.assertTrue(originSuppressed.length == targetSuppressed.length); + for (int i = 0; i < originSuppressed.length; i++) { + Assert.assertEquals(originSuppressed[i].getMessage(), targetSuppressed[i].getMessage()); + } + } + } + + /** + * EnumConstantNotPresentException 直接进行序列化 + * @throws IOException + */ + private void test_EnumConstantNotPresentException(SerializerFactory serialize, SerializerFactory deserialize) throws IOException { + if (isLessThanJdk17) { + // jdk 17 开始不需要执行这个, 反射受限, 执行会失败 + + + if (addSuppressed != null) { + addSuppress(t); + addSuppress(t); + } + + Object result = doEncodeNDecode(t, serialize, deserialize); + Assert.assertTrue(result instanceof Throwable); + + Throwable origin = t; + Throwable target = (Throwable) result; + + // stack trace + Assert.assertEquals(origin.getStackTrace().length, target.getStackTrace().length); + Assert.assertArrayEquals(origin.getStackTrace(), target.getStackTrace()); + + // detail message + Assert.assertEquals(origin.getMessage(), target.getMessage()); + + // cause, now only assert on cause.detailMessage + if (origin.getCause() == null) { + // getCause为空可能是 cause == this 的情况, 此时只需要保证target也为null即可 + // 实际该字段可能在序列化过程中有损 + Assert.assertNull(target.getCause()); + } + + // suppress + Throwable[] originSuppressed = getSuppress(origin); + Throwable[] targetSuppressed = getSuppress(target); + if (originSuppressed == null && targetSuppressed == null) { + return; + } + + Assert.assertTrue("one suppress is null while another is not", + !(originSuppressed == null || targetSuppressed == null)); + + Assert.assertTrue(originSuppressed.length == targetSuppressed.length); + for (int i = 0; i < originSuppressed.length; i++) { + Assert.assertEquals(originSuppressed[i].getMessage(), targetSuppressed[i].getMessage()); + } + } + } + + private void addSuppress(Throwable t) { + Throwable suppressT1 = null; + try { + String x = null; + x.equals(""); + } catch (NullPointerException e) { + suppressT1 = e; + } + + try { + addSuppressed.invoke(t, suppressT1); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private Throwable[] getSuppress(Throwable t) { + Throwable[] ts = null; + try { + ts = (Throwable[]) getSuppressed.invoke(t); + } catch (Exception e) { + e.printStackTrace(); + } + return ts; + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java b/src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java new file mode 100644 index 0000000..a8128f5 --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java @@ -0,0 +1,80 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.throwable; + +import com.caucho.hessian.io.AbstractHessianOutput; +import com.caucho.hessian.io.Deserializer; +import com.caucho.hessian.io.HessianProtocolException; +import com.caucho.hessian.io.JavaDeserializer; +import com.caucho.hessian.io.JavaSerializer; +import com.caucho.hessian.io.Serializer; +import com.caucho.hessian.io.SerializerFactory; + +import java.io.IOException; + +/** + * + * @author junyuan + * @version SerializeFactoryWithoutThrowable.java, v 0.1 2023年04月27日 19:14 junyuan Exp $ + */ +public class SerializeFactoryWithoutThrowable extends SerializerFactory { + + @Override + public Serializer getSerializer(Class cl) throws HessianProtocolException { + Serializer serializer = super.getSerializer(cl); + + if (Throwable.class.isAssignableFrom(cl)) + serializer = new OriginThrowableSerializer(cl); + + if (StackTraceElement.class.isAssignableFrom(cl)) + serializer = new JavaSerializer(cl); + + _cachedSerializerMap.put(cl, serializer); + + return serializer; + } + + @Override + public Deserializer getDeserializer(Class cl) throws HessianProtocolException { + Deserializer deserializer = super.getDeserializer(cl); + + if (Throwable.class.isAssignableFrom(cl)) + deserializer = new JavaDeserializer(cl); + + if (StackTraceElement.class.isAssignableFrom(cl)) + deserializer = new OriginStackTraceElementDeserializer(); + + _cachedDeserializerMap.put(cl, deserializer); + + return deserializer; + } + + private static class OriginThrowableSerializer extends JavaSerializer { + + public OriginThrowableSerializer(Class cl) { + super(cl); + } + + @Override + public void writeObject(Object obj, AbstractHessianOutput out) throws IOException { + Throwable t = (Throwable) obj; + t.getStackTrace(); + + super.writeObject(obj, out); + } + } + + public class OriginStackTraceElementDeserializer extends JavaDeserializer { + public OriginStackTraceElementDeserializer() { + super(StackTraceElement.class); + } + + @Override + protected Object instantiate() throws Exception { + return new StackTraceElement("", "", "", 0); + } + } + +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java b/src/test/java/com/caucho/hessian/io/throwable/ThrowableClassTest.java similarity index 99% rename from src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java rename to src/test/java/com/caucho/hessian/io/throwable/ThrowableClassTest.java index 16d5d76..835e714 100644 --- a/src/test/java/com/caucho/hessian/test/throwable/ThrowableClassTest.java +++ b/src/test/java/com/caucho/hessian/io/throwable/ThrowableClassTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.caucho.hessian.test.throwable; +package com.caucho.hessian.io.throwable; import com.caucho.hessian.io.Hessian2Input; import com.caucho.hessian.io.Hessian2Output; diff --git a/src/test/java/com/caucho/hessian/io/throwable/ThrowableJdk17SerializeTest.java b/src/test/java/com/caucho/hessian/io/throwable/ThrowableJdk17SerializeTest.java new file mode 100644 index 0000000..961280d --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/throwable/ThrowableJdk17SerializeTest.java @@ -0,0 +1,172 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.throwable; + +import com.caucho.hessian.io.Hessian2Input; +import com.caucho.hessian.io.Hessian2Output; +import com.caucho.hessian.io.SerializerFactory; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * can run under JDK 6 - 17 + * under jdk 8, reflection are used to do the serialize work + * under jdk17, getter and reflection take effects simultaneously + * + * @author junyuan + * @version ThrowableJdk17SerializeTest.java, v 0.1 2023年05月06日 17:53 junyuan Exp $ + */ +public class ThrowableJdk17SerializeTest { + + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + private Method addSuppressed = null; + private Method getSuppressed = null; + { + try { + addSuppressed = Throwable.class.getMethod("addSuppressed", Throwable.class); + getSuppressed = Throwable.class.getMethod("getSuppressed"); + } catch (Exception e) { + + } + } + + Throwable t = null; + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + os = new ByteArrayOutputStream(); + } + + @Test + public void test_EnumConstantNotPresentException() throws IOException { + EnumConstantNotPresentException e = new EnumConstantNotPresentException(MeaninglessEnum.class, "CIG"); + + try { + addSuppressed.invoke(e, new NullPointerException()); + } catch (Exception e1) { + + } + + Object result = doEncodeNDecode(e); + Assert.assertTrue(result instanceof EnumConstantNotPresentException); + + Throwable origin = e; + Throwable target = (Throwable) result; + + serializeCheck(origin, target); + } + + @Test + public void test_NPE() throws IOException { + Throwable t = null; + try { + t.getMessage(); + } catch (NullPointerException e) { + t = e; + } + + Object result = doEncodeNDecode(t); + Assert.assertTrue(result instanceof NullPointerException); + + Throwable origin = t; + Throwable target = (Throwable) result; + + serializeCheck(origin, target); + } + + @Test + public void test_SelfDefined() throws IOException { + SelfDefinedException e = new SelfDefinedException("0023", "error msg"); + + Object result = doEncodeNDecode(e); + Assert.assertTrue(result instanceof SelfDefinedException); + + Throwable origin = t; + Throwable target = (Throwable) result; + } + + + + protected Object doEncodeNDecode(Object origin) throws IOException { + os.reset(); + Hessian2Output output = new Hessian2Output(os); + + output.setSerializerFactory(factory); + output.writeObject(origin); + output.flush(); + + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + Hessian2Input input = new Hessian2Input(is); + input.setSerializerFactory(factory); + Object actual = input.readObject(); + return actual; + } + + protected void serializeCheck(Throwable origin, Throwable target) { + // stack trace + Assert.assertEquals(origin.getStackTrace().length, target.getStackTrace().length); + Assert.assertArrayEquals(origin.getStackTrace(), target.getStackTrace()); + + // detail message + Assert.assertEquals(origin.getMessage(), target.getMessage()); + + // cause, now only assert on cause.detailMessage + if (origin.getCause() == null) { + // getCause为空可能是 cause == this 的情况, 此时只需要保证target也为null即可 + // 实际该字段可能在序列化过程中有损 + Assert.assertNull(target.getCause()); + } + + // suppress + try { + Throwable[] originSuppressed = (Throwable[]) getSuppressed.invoke(origin); + Throwable[] targetSuppressed = (Throwable[]) getSuppressed.invoke(target); + if (originSuppressed == null && targetSuppressed == null) { + return; + } + Assert.assertFalse("one suppress is null while another is not", + originSuppressed == null || targetSuppressed == null); + + Assert.assertEquals(originSuppressed.length, targetSuppressed.length); + for (int i = 0; i < originSuppressed.length; i++) { + Assert.assertEquals(originSuppressed[i].getMessage(), targetSuppressed[i].getMessage()); + } + } catch (Exception e2) { + + } + } + + private static final boolean isLessThanJdk17 = isLessThanJdk17(); + private static boolean isLessThanJdk17() { + String javaVersion = System.getProperty("java.specification.version"); + return Double.parseDouble(javaVersion) < 17; + } + + { + Throwable x = null; + try { + t.getStackTrace(); + } catch (NullPointerException e) { + x = e; + } + + t = new EnumConstantNotPresentException(MeaninglessEnum.class, "CIG"); + + try { + addSuppressed.invoke(t, x); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java index 5b55347..1be0d91 100644 --- a/src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java +++ b/src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java @@ -18,7 +18,7 @@ import com.caucho.hessian.io.Hessian2Output; import com.caucho.hessian.io.SerializerFactory; -import com.caucho.hessian.io.StackTraceElementSerializer; +import com.caucho.hessian.io.throwable.StackTraceElementSerializer; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; @@ -32,7 +32,7 @@ /** * * @author junyuan - * @version SerializeCompatibleTest.java, v 0.1 2023年04月10日 14:52 junyuan Exp $ + * @version SerializeCompatibleSelfTest.java, v 0.1 2023年04月10日 14:52 junyuan Exp $ */ public class SerializeCompatibleTest { @@ -115,7 +115,7 @@ protected boolean bytesEquals(byte[] src, byte[] target) { private byte[] serializeExceptionWrapper() throws IOException { // wrapped - com.caucho.hessian.test.throwable.ExceptionWrapper exceptionWrapper = new com.caucho.hessian.test.throwable.ExceptionWrapper(); + com.caucho.hessian.io.throwable.ExceptionWrapper exceptionWrapper = new com.caucho.hessian.io.throwable.ExceptionWrapper(); exceptionWrapper.setT(t); diff --git a/src/test/java/com/caucho/hessian/test/throwable/SerializeFactoryWithoutThrowable.java b/src/test/java/com/caucho/hessian/test/throwable/SerializeFactoryWithoutThrowable.java deleted file mode 100644 index bf97f1c..0000000 --- a/src/test/java/com/caucho/hessian/test/throwable/SerializeFactoryWithoutThrowable.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Ant Group - * Copyright (c) 2004-2023 All Rights Reserved. - */ -package com.caucho.hessian.test.throwable; - -import com.caucho.burlap.io.BurlapRemoteObject; -import com.caucho.hessian.io.AbstractSerializerFactory; -import com.caucho.hessian.io.ArraySerializer; -import com.caucho.hessian.io.CalendarSerializer; -import com.caucho.hessian.io.CollectionSerializer; -import com.caucho.hessian.io.EnumSerializer; -import com.caucho.hessian.io.EnumerationSerializer; -import com.caucho.hessian.io.HessianProtocolException; -import com.caucho.hessian.io.HessianRemoteObject; -import com.caucho.hessian.io.InputStreamSerializer; -import com.caucho.hessian.io.IteratorSerializer; -import com.caucho.hessian.io.LocaleSerializer; -import com.caucho.hessian.io.MapSerializer; -import com.caucho.hessian.io.RemoteSerializer; -import com.caucho.hessian.io.Serializer; -import com.caucho.hessian.io.SerializerFactory; - -import java.io.InputStream; -import java.util.Calendar; -import java.util.Collection; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Locale; -import java.util.Map; - -/** - * - * @author junyuan - * @version SerializeFactoryWithoutThrowable.java, v 0.1 2023年04月27日 19:14 junyuan Exp $ - */ -public class SerializeFactoryWithoutThrowable extends SerializerFactory { - - @Override - public Serializer getSerializer(Class cl) throws HessianProtocolException { - Serializer serializer; - - serializer = (Serializer) _staticSerializerMap.get(cl); - if (serializer != null) - return serializer; - - //must before "else if (JavaSerializer.getWriteReplace(cl) != null)" or will be WriteReplace -// if (isZoneId(cl)) { -// serializer = ZoneIdSerializer.getInstance(); -// return serializer; -// } - - serializer = (Serializer) _cachedSerializerMap.get(cl); - if (serializer != null) - return serializer; - - if (classNameResolver != null) { - try { - classNameResolver.resolve(cl.getName()); - } catch (Exception e) { - throw new HessianProtocolException(e); - } - } - - for (int i = 0; serializer == null && _factories != null && i < _factories.size(); i++) { - AbstractSerializerFactory factory; - - factory = (AbstractSerializerFactory) _factories.get(i); - - serializer = factory.getSerializer(cl); - } - - if (serializer != null) { - } - - else if (HessianRemoteObject.class.isAssignableFrom(cl)) - serializer = new RemoteSerializer(); - - else if (BurlapRemoteObject.class.isAssignableFrom(cl)) - serializer = new RemoteSerializer(); - - else if (Map.class.isAssignableFrom(cl)) - serializer = new MapSerializer(); - - else if (Collection.class.isAssignableFrom(cl)) { - if (_collectionSerializer == null) { - _collectionSerializer = new CollectionSerializer(); - } - - serializer = _collectionSerializer; - } - - else if (cl.isArray()) - serializer = new ArraySerializer(); - - else if (InputStream.class.isAssignableFrom(cl)) - serializer = new InputStreamSerializer(); - - else if (Iterator.class.isAssignableFrom(cl)) - serializer = IteratorSerializer.create(); - - else if (Enumeration.class.isAssignableFrom(cl)) - serializer = EnumerationSerializer.create(); - - else if (Calendar.class.isAssignableFrom(cl)) - serializer = CalendarSerializer.create(); - - else if (Locale.class.isAssignableFrom(cl)) - serializer = LocaleSerializer.create(); - - else if (Enum.class.isAssignableFrom(cl)) - serializer = new EnumSerializer(cl); - - if (serializer == null) - serializer = getDefaultSerializer(cl); - - _cachedSerializerMap.put(cl, serializer); - - return serializer; - } - -} \ No newline at end of file From 3dcb68ba89257214bd50387940fc24572bd910a4 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Sat, 6 May 2023 20:02:59 +0800 Subject: [PATCH 11/32] format --- .../AbstractFieldSpecificDeserializer.java | 4 +- .../io/AbstractFieldSpecificSerializer.java | 2 +- .../throwable/ReflectThrowableSerializer.java | 2 +- .../StackTraceElementDeserializer.java | 12 +++-- .../StackTraceElementSerializer.java | 2 +- .../io/throwable/ThrowableDeserializer.java | 32 +++++++------- .../hessian/io/throwable/ThrowableHelper.java | 2 +- .../io/throwable/ThrowableSerializer.java | 6 +-- ...nstantNotPresentExceptionDeserializer.java | 4 +- ...ConstantNotPresentExceptionSerializer.java | 4 +- .../io/throwable/ExceptionWrapper.java | 2 +- .../io/throwable/JDK17SerializeFactory.java | 18 ++++++-- .../hessian/io/throwable/MeaninglessEnum.java | 18 ++++++-- .../io/throwable/SelfDefinedException.java | 2 +- .../SerializeCompatibleSelfTest.java | 25 +++++------ .../io/throwable/SerializeCompatibleTest.java | 44 ++++++++++++------- .../SerializeFactoryWithoutThrowable.java | 18 ++++++-- .../ThrowableJdk17SerializeTest.java | 31 ++++++++----- 18 files changed, 146 insertions(+), 82 deletions(-) rename src/main/java/com/caucho/hessian/io/{throwable => }/AbstractFieldSpecificDeserializer.java (96%) diff --git a/src/main/java/com/caucho/hessian/io/throwable/AbstractFieldSpecificDeserializer.java b/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificDeserializer.java similarity index 96% rename from src/main/java/com/caucho/hessian/io/throwable/AbstractFieldSpecificDeserializer.java rename to src/main/java/com/caucho/hessian/io/AbstractFieldSpecificDeserializer.java index 018f2be..778c370 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/AbstractFieldSpecificDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificDeserializer.java @@ -2,7 +2,7 @@ * Ant Group * Copyright (c) 2004-2023 All Rights Reserved. */ -package com.caucho.hessian.io.throwable; +package com.caucho.hessian.io; import com.caucho.hessian.io.AbstractDeserializer; @@ -40,4 +40,4 @@ protected Map getFieldMapForSerialize(Class cl) { } return fields; } -} \ No newline at end of file +} diff --git a/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificSerializer.java b/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificSerializer.java index 96d888e..e3987bf 100644 --- a/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificSerializer.java +++ b/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificSerializer.java @@ -106,4 +106,4 @@ protected Field[] getFieldsForSerialize(Class cl) { } return fields.toArray(new Field[0]); } -} \ No newline at end of file +} diff --git a/src/main/java/com/caucho/hessian/io/throwable/ReflectThrowableSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/ReflectThrowableSerializer.java index 57ad5e3..048ffbb 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/ReflectThrowableSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/ReflectThrowableSerializer.java @@ -25,4 +25,4 @@ public void writeObject(Object obj, AbstractHessianOutput out) throws IOExceptio ((Throwable) obj).getStackTrace(); super.writeObject(obj, out); } -} \ No newline at end of file +} diff --git a/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java index abe5117..0b9093b 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java @@ -48,6 +48,7 @@ package com.caucho.hessian.io.throwable; +import com.caucho.hessian.io.AbstractFieldSpecificDeserializer; import com.caucho.hessian.io.AbstractHessianInput; import com.caucho.hessian.io.IOExceptionWrapper; @@ -65,11 +66,12 @@ * @author pangu */ public class StackTraceElementDeserializer extends AbstractFieldSpecificDeserializer { - protected static final Logger log = Logger.getLogger(StackTraceElementSerializer.class.getName()); + protected static final Logger log = Logger.getLogger(StackTraceElementSerializer.class + .getName()); private Constructor _defaultConstructor = null; - private Constructor _constructorJdk9 = null; + private Constructor _constructorJdk9 = null; @Override public Class getType() { @@ -82,11 +84,13 @@ public StackTraceElementDeserializer() { try { if (_fields.size() > 4) { // available since java 9 - _constructorJdk9 = StackTraceElement.class.getDeclaredConstructor(String.class, String.class, String.class, String.class, + _constructorJdk9 = StackTraceElement.class.getDeclaredConstructor(String.class, String.class, + String.class, String.class, String.class, String.class, int.class); } // default, only read class, method, file and line - _defaultConstructor = StackTraceElement.class.getDeclaredConstructor(String.class, String.class, String.class, int.class); + _defaultConstructor = StackTraceElement.class.getDeclaredConstructor(String.class, String.class, + String.class, int.class); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } diff --git a/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java index 0542ea3..e317fbc 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java @@ -106,4 +106,4 @@ protected Field[] getFieldsForSerialize(Class cl) { } return fields.toArray(new Field[0]); } -} \ No newline at end of file +} diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java index e2da9d3..717f1b4 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java @@ -4,6 +4,7 @@ */ package com.caucho.hessian.io.throwable; +import com.caucho.hessian.io.AbstractFieldSpecificDeserializer; import com.caucho.hessian.io.AbstractHessianInput; import com.caucho.hessian.io.HessianFieldException; import com.caucho.hessian.io.IOExceptionWrapper; @@ -23,10 +24,10 @@ */ public class ThrowableDeserializer extends AbstractFieldSpecificDeserializer { - private final Class _type; - protected Method addSuppressed = null; + private final Class _type; + protected Method addSuppressed = null; - private final Throwable selfRef = new Throwable(); + private final Throwable selfRef = new Throwable(); public ThrowableDeserializer(Class cl) { super(cl); @@ -62,7 +63,7 @@ public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IO } protected Map readField(AbstractHessianInput in, String[] fieldNames) - throws IOException { + throws IOException { Map fieldValueMap = new HashMap(); for (int i = 0; i < fieldNames.length; i++) { String name = fieldNames[i]; @@ -81,7 +82,7 @@ protected Map readField(AbstractHessianInput in, String[] fieldN } protected Throwable instantiate(Class clazz, Map fieldValueMap) - throws Exception { + throws Exception { Throwable ex = null; try { ex = doInstantiate(clazz, fieldValueMap); @@ -97,10 +98,11 @@ protected Throwable instantiate(Class clazz, Map fieldValueMa } protected void fillFields(Class clazz, Throwable obj, Map valueMap) - throws IOException { - for (String key: valueMap.keySet()) { + throws IOException { + for (String key : valueMap.keySet()) { Object value = valueMap.get(key); - if (value == null) continue; + if (value == null) + continue; if (key.equals("cause")) { // 如果 cause 还未被写入, init @@ -128,7 +130,7 @@ else if (key.equals("suppressedExceptions")) { continue; } if (addSuppressed != null) { - for (Object item: listValue) { + for (Object item : listValue) { addSuppressed.invoke(obj, item); } } @@ -147,7 +149,7 @@ else if (key.equals("stackTrace")) { } protected void fillOtherFields(Class clazz, Throwable obj, String key, Object value) - throws IOException { + throws IOException { Field field = _fields.get(key); if (field == null) { return; @@ -175,7 +177,7 @@ protected void fillOtherFields(Class clazz, Throwable obj, String key, Object * @throws Exception */ private Throwable doInstantiate(Class clazz, Map fieldValueMap) - throws Exception { + throws Exception { Constructor causeConstructor = null; Constructor messageConstructor = null; Constructor defaultConstructor = null; @@ -183,7 +185,7 @@ private Throwable doInstantiate(Class clazz, Map fieldValueMa long bestCost = Long.MAX_VALUE; // 只会返回public的构造方法 - for (Constructor c: clazz.getDeclaredConstructors()) { + for (Constructor c : clazz.getDeclaredConstructors()) { Class[] pTypes = c.getParameterTypes(); if (pTypes.length == 0) { @@ -305,7 +307,7 @@ else if (double.class.equals(cl)) private void logDeserializeError(Field field, Object value, Throwable e) throws IOException { String fieldName = (field.getDeclaringClass().getName() - + "." + field.getName()); + + "." + field.getName()); if (e instanceof HessianFieldException) throw (HessianFieldException) e; @@ -314,9 +316,9 @@ else if (e instanceof IOException) if (value != null) throw new HessianFieldException(fieldName + ": " + value.getClass().getName() + " (" + value + ")" - + " cannot be assigned to " + field.getType().getName()); + + " cannot be assigned to " + field.getType().getName()); else throw new HessianFieldException(fieldName + ": " + field.getType().getName() + - " cannot be assigned from null", e); + " cannot be assigned from null", e); } } \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java index 0445ba5..8f682fc 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java @@ -54,7 +54,7 @@ public static AbstractSerializer getSerializer(Class cl) { private static final Map throwableSerializerMap = new HashMap(); static { throwableSerializerMap.put(EnumConstantNotPresentException.class.getName(), - new EnumConstantNotPresentExceptionSerializer()); + new EnumConstantNotPresentExceptionSerializer()); } } diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java index 4d8d20b..87f4e94 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java @@ -22,7 +22,7 @@ */ public class ThrowableSerializer extends AbstractFieldSpecificSerializer { - protected Method getSuppressed = null; + protected Method getSuppressed = null; public ThrowableSerializer(Class clazz) { super(clazz); @@ -95,7 +95,7 @@ else if ("suppressedExceptions".equals(field.getName())) { * @throws IOException */ protected void defaultSerializeField(AbstractHessianOutput out, Object obj, Field field) - throws IOException { + throws IOException { Object fieldValue = null; try { fieldValue = field.get(obj); @@ -109,4 +109,4 @@ protected void defaultSerializeField(AbstractHessianOutput out, Object obj, Fiel } out.writeObject(fieldValue); } -} \ No newline at end of file +} diff --git a/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java index 77f7ba7..618611d 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionDeserializer.java @@ -20,9 +20,9 @@ public EnumConstantNotPresentExceptionDeserializer(Class cl) { @Override protected Throwable instantiate(Class clazz, Map fieldValueMap) - throws Exception { + throws Exception { Class enumType = (Class) fieldValueMap.remove("enumType"); String constantName = (String) fieldValueMap.remove("constantName"); return new EnumConstantNotPresentException(enumType, constantName); } -} \ No newline at end of file +} diff --git a/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java index eae4984..cb1e505 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/adapter/EnumConstantNotPresentExceptionSerializer.java @@ -23,7 +23,7 @@ public EnumConstantNotPresentExceptionSerializer() { @Override protected void defaultSerializeField(AbstractHessianOutput out, Object obj, Field field) - throws IOException { + throws IOException { if (!(obj instanceof EnumConstantNotPresentException)) { throw new UnsupportedOperationException(String.valueOf(this)); } @@ -38,4 +38,4 @@ protected void defaultSerializeField(AbstractHessianOutput out, Object obj, Fiel super.defaultSerializeField(out, obj, field); } } -} \ No newline at end of file +} diff --git a/src/test/java/com/caucho/hessian/io/throwable/ExceptionWrapper.java b/src/test/java/com/caucho/hessian/io/throwable/ExceptionWrapper.java index 2425862..e4c7d79 100644 --- a/src/test/java/com/caucho/hessian/io/throwable/ExceptionWrapper.java +++ b/src/test/java/com/caucho/hessian/io/throwable/ExceptionWrapper.java @@ -46,4 +46,4 @@ public void setT(Throwable t) { this.t = t; } -} \ No newline at end of file +} diff --git a/src/test/java/com/caucho/hessian/io/throwable/JDK17SerializeFactory.java b/src/test/java/com/caucho/hessian/io/throwable/JDK17SerializeFactory.java index 496c0ad..c75f4d2 100644 --- a/src/test/java/com/caucho/hessian/io/throwable/JDK17SerializeFactory.java +++ b/src/test/java/com/caucho/hessian/io/throwable/JDK17SerializeFactory.java @@ -1,6 +1,18 @@ /* - * Ant Group - * Copyright (c) 2004-2023 All Rights Reserved. + * 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 com.caucho.hessian.io.throwable; @@ -56,4 +68,4 @@ public Deserializer getDeserializer(Class cl) throws HessianProtocolException { _cachedDeserializerMap.put(cl, deserializer); return deserializer; } -} \ No newline at end of file +} diff --git a/src/test/java/com/caucho/hessian/io/throwable/MeaninglessEnum.java b/src/test/java/com/caucho/hessian/io/throwable/MeaninglessEnum.java index 1bec5f7..6431772 100644 --- a/src/test/java/com/caucho/hessian/io/throwable/MeaninglessEnum.java +++ b/src/test/java/com/caucho/hessian/io/throwable/MeaninglessEnum.java @@ -1,6 +1,18 @@ /* - * Ant Group - * Copyright (c) 2004-2023 All Rights Reserved. + * 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 com.caucho.hessian.io.throwable; @@ -13,4 +25,4 @@ public enum MeaninglessEnum { S1, S2, S3 -} \ No newline at end of file +} diff --git a/src/test/java/com/caucho/hessian/io/throwable/SelfDefinedException.java b/src/test/java/com/caucho/hessian/io/throwable/SelfDefinedException.java index 3becc40..3b06bd5 100644 --- a/src/test/java/com/caucho/hessian/io/throwable/SelfDefinedException.java +++ b/src/test/java/com/caucho/hessian/io/throwable/SelfDefinedException.java @@ -32,4 +32,4 @@ public SelfDefinedException(String bizCode, String bizMessage) { this.bizMessage = bizMessage; } -} \ No newline at end of file +} diff --git a/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleSelfTest.java b/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleSelfTest.java index a878929..8d71e3c 100644 --- a/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleSelfTest.java +++ b/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleSelfTest.java @@ -20,7 +20,6 @@ import com.caucho.hessian.io.SerializerFactory; import org.junit.Assert; import org.junit.BeforeClass; -//import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -39,8 +38,8 @@ public class SerializeCompatibleSelfTest { private static SerializerFactory factory; private static ByteArrayOutputStream os; - private Method addSuppressed = null; - private Method getSuppressed = null; + private Method addSuppressed = null; + private Method getSuppressed = null; { try { addSuppressed = Throwable.class.getMethod("addSuppressed", Throwable.class); @@ -50,7 +49,7 @@ public class SerializeCompatibleSelfTest { } } - Throwable t = null; + Throwable t = null; { Throwable x = null; try { @@ -59,15 +58,15 @@ public class SerializeCompatibleSelfTest { t = e; } -// t = new EnumConstantNotPresentException(MeaninglessEnum.class, "CIG"); + t = new EnumConstantNotPresentException(MeaninglessEnum.class, "CIG"); -// try { -// addSuppressed.invoke(t, x); -// } catch (Exception e) { -// e.printStackTrace(); -// } + try { + addSuppressed.invoke(t, x); + } catch (Exception e) { + e.printStackTrace(); + } } - private static final boolean isLessThanJdk17 = isLessThanJdk17(); + private static final boolean isLessThanJdk17 = isLessThanJdk17(); private static boolean isLessThanJdk17() { String javaVersion = System.getProperty("java.specification.version"); @@ -88,7 +87,7 @@ public static void setUp() { * result byte should be in same with former version so as to behaving compatible * @throws IOException */ -// @Test + // @Test public void test_EnumConstantNotPresentExceptionSerialize() throws IOException { if (isLessThanJdk17) { // jdk 17 开始不需要执行这个, 反射受限, 执行会失败 @@ -101,7 +100,7 @@ public void test_EnumConstantNotPresentExceptionSerialize() throws IOException { } -// @Test + // @Test public void test_EnumConstantNotPresentExceptionWrapperSerialize() throws IOException { if (isLessThanJdk17) { byte[] wrapperCaseOrigin = serializeEnumConstantNotPresentExceptionWrapper(originFactory); diff --git a/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleTest.java index f0b2216..ffb8ac2 100644 --- a/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleTest.java +++ b/src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleTest.java @@ -1,6 +1,18 @@ /* - * Ant Group - * Copyright (c) 2004-2023 All Rights Reserved. + * 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 com.caucho.hessian.io.throwable; @@ -98,9 +110,6 @@ public void test_case_6() throws IOException { test_EnumConstantNotPresentException(factory, factory); } - - - private static SerializerFactory useJdk17Factory; private static SerializerFactory originFactory; /** @@ -111,8 +120,8 @@ public void test_case_6() throws IOException { private static SerializerFactory factory; private static ByteArrayOutputStream os; - private Method addSuppressed = null; - private Method getSuppressed = null; + private Method addSuppressed = null; + private Method getSuppressed = null; { try { addSuppressed = Throwable.class.getMethod("addSuppressed", Throwable.class); @@ -122,7 +131,7 @@ public void test_case_6() throws IOException { } } - Throwable t = null; + Throwable t = null; { Throwable x = null; try { @@ -140,13 +149,15 @@ public void test_case_6() throws IOException { } } - private static final boolean isLessThanJdk17 = isLessThanJdk17(); + private static final boolean isLessThanJdk17 = isLessThanJdk17(); + private static boolean isLessThanJdk17() { String javaVersion = System.getProperty("java.specification.version"); return Double.parseDouble(javaVersion) < 17; } - protected Object doEncodeNDecode(Object origin, SerializerFactory serializerFactory, SerializerFactory deserializerFactory) throws IOException { + protected Object doEncodeNDecode(Object origin, SerializerFactory serializerFactory, + SerializerFactory deserializerFactory) throws IOException { os.reset(); Hessian2Output output = new Hessian2Output(os); @@ -165,7 +176,8 @@ protected Object doEncodeNDecode(Object origin, SerializerFactory serializerFact * EnumConstantNotPresentException 作为成员变量 * @throws IOException */ - private void test_EnumConstantNotPresentExceptionWrapper(SerializerFactory serialize, SerializerFactory deserialize) throws IOException { + private void test_EnumConstantNotPresentExceptionWrapper(SerializerFactory serialize, SerializerFactory deserialize) + throws IOException { if (isLessThanJdk17) { // jdk 17 开始不需要执行这个, 反射受限, 执行会失败 @@ -204,7 +216,7 @@ private void test_EnumConstantNotPresentExceptionWrapper(SerializerFactory seria } Assert.assertTrue("one suppress is null while another is not", - !(originSuppressed == null || targetSuppressed == null)); + !(originSuppressed == null || targetSuppressed == null)); Assert.assertTrue(originSuppressed.length == targetSuppressed.length); for (int i = 0; i < originSuppressed.length; i++) { @@ -217,11 +229,11 @@ private void test_EnumConstantNotPresentExceptionWrapper(SerializerFactory seria * EnumConstantNotPresentException 直接进行序列化 * @throws IOException */ - private void test_EnumConstantNotPresentException(SerializerFactory serialize, SerializerFactory deserialize) throws IOException { + private void test_EnumConstantNotPresentException(SerializerFactory serialize, SerializerFactory deserialize) + throws IOException { if (isLessThanJdk17) { // jdk 17 开始不需要执行这个, 反射受限, 执行会失败 - if (addSuppressed != null) { addSuppress(t); addSuppress(t); @@ -255,7 +267,7 @@ private void test_EnumConstantNotPresentException(SerializerFactory serialize, S } Assert.assertTrue("one suppress is null while another is not", - !(originSuppressed == null || targetSuppressed == null)); + !(originSuppressed == null || targetSuppressed == null)); Assert.assertTrue(originSuppressed.length == targetSuppressed.length); for (int i = 0; i < originSuppressed.length; i++) { @@ -289,4 +301,4 @@ private Throwable[] getSuppress(Throwable t) { } return ts; } -} \ No newline at end of file +} diff --git a/src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java b/src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java index a8128f5..c334a59 100644 --- a/src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java +++ b/src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java @@ -1,6 +1,18 @@ /* - * Ant Group - * Copyright (c) 2004-2023 All Rights Reserved. + * 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 com.caucho.hessian.io.throwable; @@ -77,4 +89,4 @@ protected Object instantiate() throws Exception { } } -} \ No newline at end of file +} diff --git a/src/test/java/com/caucho/hessian/io/throwable/ThrowableJdk17SerializeTest.java b/src/test/java/com/caucho/hessian/io/throwable/ThrowableJdk17SerializeTest.java index 961280d..0011c56 100644 --- a/src/test/java/com/caucho/hessian/io/throwable/ThrowableJdk17SerializeTest.java +++ b/src/test/java/com/caucho/hessian/io/throwable/ThrowableJdk17SerializeTest.java @@ -1,6 +1,18 @@ /* - * Ant Group - * Copyright (c) 2004-2023 All Rights Reserved. + * 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 com.caucho.hessian.io.throwable; @@ -26,11 +38,11 @@ */ public class ThrowableJdk17SerializeTest { - private static SerializerFactory factory; + private static SerializerFactory factory; private static ByteArrayOutputStream os; - private Method addSuppressed = null; - private Method getSuppressed = null; + private Method addSuppressed = null; + private Method getSuppressed = null; { try { addSuppressed = Throwable.class.getMethod("addSuppressed", Throwable.class); @@ -40,7 +52,7 @@ public class ThrowableJdk17SerializeTest { } } - Throwable t = null; + Throwable t = null; @BeforeClass public static void setUp() { @@ -96,8 +108,6 @@ public void test_SelfDefined() throws IOException { Throwable target = (Throwable) result; } - - protected Object doEncodeNDecode(Object origin) throws IOException { os.reset(); Hessian2Output output = new Hessian2Output(os); @@ -136,7 +146,7 @@ protected void serializeCheck(Throwable origin, Throwable target) { return; } Assert.assertFalse("one suppress is null while another is not", - originSuppressed == null || targetSuppressed == null); + originSuppressed == null || targetSuppressed == null); Assert.assertEquals(originSuppressed.length, targetSuppressed.length); for (int i = 0; i < originSuppressed.length; i++) { @@ -148,6 +158,7 @@ protected void serializeCheck(Throwable origin, Throwable target) { } private static final boolean isLessThanJdk17 = isLessThanJdk17(); + private static boolean isLessThanJdk17() { String javaVersion = System.getProperty("java.specification.version"); return Double.parseDouble(javaVersion) < 17; @@ -169,4 +180,4 @@ private static boolean isLessThanJdk17() { e.printStackTrace(); } } -} \ No newline at end of file +} From f2b04c10668bf8a52f0ee0b9972ee51ba060ae49 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Mon, 8 May 2023 16:52:07 +0800 Subject: [PATCH 12/32] format --- .../com/caucho/hessian/test/stacktrace/ExceptionClassTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java index 7bccbfe..ecbb936 100644 --- a/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java +++ b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java @@ -56,7 +56,6 @@ public void test_stacktrace() throws IOException { Object result = doEncodeNDecode(w); - } protected Object doEncodeNDecode(Object origin) throws IOException { From a78fbbbfff770d6465ea285d11aacd6466c72b17 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Tue, 23 May 2023 10:20:42 +0800 Subject: [PATCH 13/32] chore: cr fix --- .../StackTraceElementSerializer.java | 1 + .../caucho/hessian/util/ReflectionUtil.java | 38 +++++++++++++------ .../SerializeFactoryWithoutThrowable.java | 2 +- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java index e317fbc..6dab107 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java @@ -83,6 +83,7 @@ protected void serializeField(AbstractHessianOutput out, Object obj, Field field } out.writeInt(value); } else { + log.warning("unsupported field " + field.getName() + "(" + field.getType() + "), will write null"); out.writeNull(); } } diff --git a/src/main/java/com/caucho/hessian/util/ReflectionUtil.java b/src/main/java/com/caucho/hessian/util/ReflectionUtil.java index 43aeda0..3515e87 100644 --- a/src/main/java/com/caucho/hessian/util/ReflectionUtil.java +++ b/src/main/java/com/caucho/hessian/util/ReflectionUtil.java @@ -36,48 +36,62 @@ private static Logger judgeLogger() { } public static boolean setAccessible(Method m) { + m.setAccessible(true); + return true; + } + + public static boolean setAccessible(Constructor c) { + c.setAccessible(true); + return true; + } + + public static boolean setAccessible(Field f) { + f.setAccessible(true); + return true; + } + + public static boolean trySetAccessible(Method m) { try { m.setAccessible(true); } catch (Throwable t) { if (LOGGER.isDebugEnabled()) { LOGGER - .debug( - "failed when setting accessible on method [" + m.toString() + "], error message: " + - t.getMessage(), t); + .debug( + "failed when setting accessible on method [" + m.toString() + "], error message: " + + t.getMessage(), t); } return false; } return true; } - public static boolean setAccessible(Constructor c) { + public static boolean trySetAccessible(Constructor c) { try { c.setAccessible(true); } catch (Throwable t) { if (LOGGER.isDebugEnabled()) { LOGGER - .debug( - "failed when setting accessible on method [" + c.toString() + "], error message: " + - t.getMessage(), t); + .debug( + "failed when setting accessible on method [" + c.toString() + "], error message: " + + t.getMessage(), t); } return false; } return true; } - public static boolean setAccessible(Field f) { + public static boolean trySetAccessible(Field f) { try { f.setAccessible(true); } catch (Throwable t) { if (LOGGER.isDebugEnabled()) { LOGGER - .debug( - "failed when setting accessible on method [" + f.toString() + "], error message: " + - t.getMessage(), t); + .debug( + "failed when setting accessible on method [" + f.toString() + "], error message: " + + t.getMessage(), t); } return false; } return true; } - } \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java b/src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java index c334a59..e4525fe 100644 --- a/src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java +++ b/src/test/java/com/caucho/hessian/io/throwable/SerializeFactoryWithoutThrowable.java @@ -78,7 +78,7 @@ public void writeObject(Object obj, AbstractHessianOutput out) throws IOExceptio } } - public class OriginStackTraceElementDeserializer extends JavaDeserializer { + private class OriginStackTraceElementDeserializer extends JavaDeserializer { public OriginStackTraceElementDeserializer() { super(StackTraceElement.class); } From 85313336fe55770b5c7fb6cb853d2e60350d70df Mon Sep 17 00:00:00 2001 From: lo1nt Date: Thu, 8 Jun 2023 11:47:20 +0800 Subject: [PATCH 14/32] chore: format --- .../caucho/hessian/util/ReflectionUtil.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/caucho/hessian/util/ReflectionUtil.java b/src/main/java/com/caucho/hessian/util/ReflectionUtil.java index 3515e87..00bf050 100644 --- a/src/main/java/com/caucho/hessian/util/ReflectionUtil.java +++ b/src/main/java/com/caucho/hessian/util/ReflectionUtil.java @@ -56,9 +56,9 @@ public static boolean trySetAccessible(Method m) { } catch (Throwable t) { if (LOGGER.isDebugEnabled()) { LOGGER - .debug( - "failed when setting accessible on method [" + m.toString() + "], error message: " + - t.getMessage(), t); + .debug( + "failed when setting accessible on method [" + m.toString() + "], error message: " + + t.getMessage(), t); } return false; } @@ -71,9 +71,9 @@ public static boolean trySetAccessible(Constructor c) { } catch (Throwable t) { if (LOGGER.isDebugEnabled()) { LOGGER - .debug( - "failed when setting accessible on method [" + c.toString() + "], error message: " + - t.getMessage(), t); + .debug( + "failed when setting accessible on method [" + c.toString() + "], error message: " + + t.getMessage(), t); } return false; } @@ -86,9 +86,9 @@ public static boolean trySetAccessible(Field f) { } catch (Throwable t) { if (LOGGER.isDebugEnabled()) { LOGGER - .debug( - "failed when setting accessible on method [" + f.toString() + "], error message: " + - t.getMessage(), t); + .debug( + "failed when setting accessible on method [" + f.toString() + "], error message: " + + t.getMessage(), t); } return false; } From 84ac46264110ad62a83ebb23254727a6779b457b Mon Sep 17 00:00:00 2001 From: lo1nt Date: Tue, 13 Jun 2023 11:30:04 +0800 Subject: [PATCH 15/32] add ci fo jdk17 --- .github/workflows/build-jdk17.yml | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/build-jdk17.yml diff --git a/.github/workflows/build-jdk17.yml b/.github/workflows/build-jdk17.yml new file mode 100644 index 0000000..ca0e7a5 --- /dev/null +++ b/.github/workflows/build-jdk17.yml @@ -0,0 +1,37 @@ +name: build + +on: + push: + branches: [ 3.x ] + pull_request: + branches: [ 3.x ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + # test against latest update of each major Java version, as well as specific updates of LTS versions: + java: [6, 7, 8, 17] + + steps: + - uses: actions/checkout@v2 + - name: Check format + run: ls -la && sh ./check_format.sh + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.java }} + - name: Build with Maven 3.2.5 + run: wget https://archive.apache.org/dist/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.zip -P ~/temp + && unzip -q ~/temp/apache-maven-3.2.5-bin.zip -d ~/temp + && export M2_HOME=~/temp/apache-maven-3.2.5 + && export PATH=$M2_HOME/bin:$PATH + && cp ./tools/ci/.settings.xml $HOME/.m2/settings.xml + && mvn -version + && mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -B -U -e + && sh ./check_format.sh + && mvn test cobertura:cobertura + - name: Codecov + uses: codecov/codecov-action@v1 \ No newline at end of file From dd4acab9507adf8aa3bc4c91baef5dfacedee838 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Tue, 13 Jun 2023 14:09:54 +0800 Subject: [PATCH 16/32] update jdk --- .github/workflows/build-jdk17.yml | 2 +- pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-jdk17.yml b/.github/workflows/build-jdk17.yml index ca0e7a5..68ce9e3 100644 --- a/.github/workflows/build-jdk17.yml +++ b/.github/workflows/build-jdk17.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: # test against latest update of each major Java version, as well as specific updates of LTS versions: - java: [6, 7, 8, 17] + java: [17] steps: - uses: actions/checkout@v2 diff --git a/pom.xml b/pom.xml index 9315be0..160fa92 100644 --- a/pom.xml +++ b/pom.xml @@ -161,8 +161,8 @@ maven-compiler-plugin 2.5.1 - 1.6 - 1.6 + 1.8 + 1.8 ${project.build.sourceEncoding} From 3811a8a6254307ea710d3a0f2222bb3783afd112 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Tue, 13 Jun 2023 14:13:50 +0800 Subject: [PATCH 17/32] remove version specific --- .github/workflows/build-jdk17.yml | 37 ------------------- pom.xml | 4 +- .../io/throwable/ThrowableDeserializer.java | 3 ++ .../io/throwable/ThrowableSerializer.java | 14 +++---- .../test/atomic/SerializeCompatibleTest.java | 14 +++++++ .../stacktrace/SerializeCompatibleTest.java | 10 +++++ 6 files changed, 36 insertions(+), 46 deletions(-) delete mode 100644 .github/workflows/build-jdk17.yml diff --git a/.github/workflows/build-jdk17.yml b/.github/workflows/build-jdk17.yml deleted file mode 100644 index 68ce9e3..0000000 --- a/.github/workflows/build-jdk17.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: build - -on: - push: - branches: [ 3.x ] - pull_request: - branches: [ 3.x ] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - matrix: - # test against latest update of each major Java version, as well as specific updates of LTS versions: - java: [17] - - steps: - - uses: actions/checkout@v2 - - name: Check format - run: ls -la && sh ./check_format.sh - - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@v1 - with: - java-version: ${{ matrix.java }} - - name: Build with Maven 3.2.5 - run: wget https://archive.apache.org/dist/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.zip -P ~/temp - && unzip -q ~/temp/apache-maven-3.2.5-bin.zip -d ~/temp - && export M2_HOME=~/temp/apache-maven-3.2.5 - && export PATH=$M2_HOME/bin:$PATH - && cp ./tools/ci/.settings.xml $HOME/.m2/settings.xml - && mvn -version - && mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -B -U -e - && sh ./check_format.sh - && mvn test cobertura:cobertura - - name: Codecov - uses: codecov/codecov-action@v1 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 160fa92..9315be0 100644 --- a/pom.xml +++ b/pom.xml @@ -161,8 +161,8 @@ maven-compiler-plugin 2.5.1 - 1.8 - 1.8 + 1.6 + 1.6 ${project.build.sourceEncoding} diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java index 717f1b4..cb2e553 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java @@ -141,6 +141,9 @@ else if (key.equals("suppressedExceptions")) { else if (key.equals("stackTrace")) { obj.setStackTrace((StackTraceElement[]) value); } + else if (key.equals("detailMessage")) { + // 只能通过构造方法写入 + } // 其他所有 field else { fillOtherFields(clazz, obj, key, value); diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java index 87f4e94..bcad138 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java @@ -14,6 +14,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; /** * @@ -22,7 +24,9 @@ */ public class ThrowableSerializer extends AbstractFieldSpecificSerializer { - protected Method getSuppressed = null; + protected static final Logger log = Logger.getLogger(ThrowableSerializer.class.getName()); + + protected Method getSuppressed = null; public ThrowableSerializer(Class clazz) { super(clazz); @@ -98,14 +102,10 @@ protected void defaultSerializeField(AbstractHessianOutput out, Object obj, Fiel throws IOException { Object fieldValue = null; try { + field.setAccessible(true); fieldValue = field.get(obj); } catch (IllegalAccessException e) { - try { - field.setAccessible(true); - fieldValue = field.get(obj); - } catch (Exception exception) { - exception.printStackTrace(); - } + log.log(Level.FINE, e.toString()); } out.writeObject(fieldValue); } diff --git a/src/test/java/com/caucho/hessian/test/atomic/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/test/atomic/SerializeCompatibleTest.java index 00c09ad..192c2e6 100644 --- a/src/test/java/com/caucho/hessian/test/atomic/SerializeCompatibleTest.java +++ b/src/test/java/com/caucho/hessian/test/atomic/SerializeCompatibleTest.java @@ -19,6 +19,7 @@ import com.caucho.hessian.io.Hessian2Output; import com.caucho.hessian.io.SerializerFactory; import com.caucho.hessian.io.atomic.AtomicSerializer; +import com.caucho.hessian.io.throwable.JDK17SerializeFactory; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; @@ -46,8 +47,17 @@ */ public class SerializeCompatibleTest { private static SerializerFactory factory; + private static SerializerFactory useJdk17Factory; + private static ByteArrayOutputStream os; + private static final boolean isLessThanJdk17 = isLessThanJdk17(); + + private static boolean isLessThanJdk17() { + String javaVersion = System.getProperty("java.specification.version"); + return Double.parseDouble(javaVersion) < 17; + } + @BeforeClass public static void setUp() { factory = new SerializerFactory(); @@ -79,6 +89,10 @@ public static void tearDown() { */ @Test public void test_serialize() throws IOException, NoSuchFieldException, IllegalAccessException { + if (!isLessThanJdk17) { + return; + } + byte[] wrappedCaseAtomic = serializeAtomicWrapper(); byte[] unwrappedIntegerAtomic = serializeAtomicInteger(); byte[] unwrappedBooleanAtomic = serializeAtomicBoolean(); diff --git a/src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java index 1be0d91..d15dbb1 100644 --- a/src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java +++ b/src/test/java/com/caucho/hessian/test/stacktrace/SerializeCompatibleTest.java @@ -36,6 +36,13 @@ */ public class SerializeCompatibleTest { + private static final boolean isLessThanJdk17 = isLessThanJdk17(); + + private static boolean isLessThanJdk17() { + String javaVersion = System.getProperty("java.specification.version"); + return Double.parseDouble(javaVersion) < 17; + } + private static SerializerFactory factory; private static ByteArrayOutputStream os; @@ -75,6 +82,9 @@ public static void tearDown() { */ @Test public void test_serialize() throws IOException, NoSuchFieldException, IllegalAccessException { + if (!isLessThanJdk17) { + return; + } byte[] wrappedCaseStackTrace = serializeExceptionWrapper(); byte[] unwrappedStackTrace = serializeStackTraceElement(); From 5199a3dfb6ccd7f6800a6aa065b4eec1eb4e3643 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Mon, 19 Jun 2023 10:45:36 +0800 Subject: [PATCH 18/32] version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b60ef6e..e629cd5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.alipay.sofa hessian - 3.5.0-jdk17-SNAPSHOT + 3.5.0-beta1-SNAPSHOT jar ${project.groupId}:${project.artifactId} From 8c7e066f485dd9e25f732a629ba51d71f287d591 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Mon, 19 Jun 2023 11:09:18 +0800 Subject: [PATCH 19/32] chore: add ci on sofaboot-4.0-support --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 196e84e..fa228a6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,9 @@ name: build on: push: - branches: [ master, 3.x ] + branches: [ master, 3.x,sofaboot-4.0-support ] pull_request: - branches: [ master, 3.x ] + branches: [ master, 3.x, sofaboot-4.0-support ] jobs: build: From e36303c91891b40580e183572f61c4338ee0669c Mon Sep 17 00:00:00 2001 From: lo1nt Date: Tue, 20 Jun 2023 10:23:18 +0800 Subject: [PATCH 20/32] 3.5.0.beta.1 release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e629cd5..997f4d9 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.alipay.sofa hessian - 3.5.0-beta1-SNAPSHOT + 3.5.0-beta1 jar ${project.groupId}:${project.artifactId} From 37767c774c30b563ff19e21de5d1a0afca89d6c9 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Tue, 8 Aug 2023 10:18:14 +0800 Subject: [PATCH 21/32] remove aci on sofaboot-4.0-support --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fa228a6..196e84e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,9 @@ name: build on: push: - branches: [ master, 3.x,sofaboot-4.0-support ] + branches: [ master, 3.x ] pull_request: - branches: [ master, 3.x, sofaboot-4.0-support ] + branches: [ master, 3.x ] jobs: build: From 8460cff6404ca7320983f725d9fbedfb1c91d9d2 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Wed, 9 Aug 2023 14:38:19 +0800 Subject: [PATCH 22/32] feat: add support on java util currency --- ... => AbstractFieldAdaptorDeserializer.java} | 7 +- ...va => AbstractFieldAdaptorSerializer.java} | 6 +- .../caucho/hessian/io/SerializerFactory.java | 11 + .../java17/base/JavaCurrencyDeserializer.java | 25 +++ .../java17/base/JavaCurrencySerializer.java | 34 +++ .../StackTraceElementDeserializer.java | 4 +- .../StackTraceElementSerializer.java | 4 +- .../io/throwable/ThrowableDeserializer.java | 4 +- .../io/throwable/ThrowableSerializer.java | 4 +- .../io/java17/base/CurrencyWrapper.java | 37 ++++ .../java17/base/SerializeCompatibleTest.java | 207 ++++++++++++++++++ .../base/SerializeFactoryWithoutCurrency.java | 38 ++++ 12 files changed, 366 insertions(+), 15 deletions(-) rename src/main/java/com/caucho/hessian/io/{AbstractFieldSpecificDeserializer.java => AbstractFieldAdaptorDeserializer.java} (78%) rename src/main/java/com/caucho/hessian/io/{AbstractFieldSpecificSerializer.java => AbstractFieldAdaptorSerializer.java} (91%) create mode 100644 src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java create mode 100644 src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencySerializer.java create mode 100644 src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java create mode 100644 src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java create mode 100644 src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithoutCurrency.java diff --git a/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificDeserializer.java b/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorDeserializer.java similarity index 78% rename from src/main/java/com/caucho/hessian/io/AbstractFieldSpecificDeserializer.java rename to src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorDeserializer.java index 778c370..f36aa28 100644 --- a/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorDeserializer.java @@ -4,7 +4,6 @@ */ package com.caucho.hessian.io; -import com.caucho.hessian.io.AbstractDeserializer; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -14,13 +13,13 @@ /** * * @author junyuan - * @version AbstractFieldSpecificDeserializer.java, v 0.1 2023年05月06日 14:21 junyuan Exp $ + * @version AbstractFieldAdaptorDeserializer.java, v 0.1 2023年05月06日 14:21 junyuan Exp $ */ -public abstract class AbstractFieldSpecificDeserializer extends AbstractDeserializer { +public abstract class AbstractFieldAdaptorDeserializer extends AbstractDeserializer { protected Map _fields; - public AbstractFieldSpecificDeserializer(Class cl) { + public AbstractFieldAdaptorDeserializer(Class cl) { _fields = getFieldMapForSerialize(cl); } diff --git a/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificSerializer.java b/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorSerializer.java similarity index 91% rename from src/main/java/com/caucho/hessian/io/AbstractFieldSpecificSerializer.java rename to src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorSerializer.java index e3987bf..0ea63f8 100644 --- a/src/main/java/com/caucho/hessian/io/AbstractFieldSpecificSerializer.java +++ b/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorSerializer.java @@ -13,13 +13,13 @@ /** * * @author junyuan - * @version AbstractFieldSpecificSerializer.java, v 0.1 2023年04月10日 19:34 junyuan Exp $ + * @version AbstractFieldAdaptorSerializer.java, v 0.1 2023年04月10日 19:34 junyuan Exp $ */ -public abstract class AbstractFieldSpecificSerializer extends AbstractSerializer { +public abstract class AbstractFieldAdaptorSerializer extends AbstractSerializer { protected Field[] _fields; - public AbstractFieldSpecificSerializer(Class clazz) { + public AbstractFieldAdaptorSerializer(Class clazz) { this._fields = getFieldsForSerialize(clazz); } diff --git a/src/main/java/com/caucho/hessian/io/SerializerFactory.java b/src/main/java/com/caucho/hessian/io/SerializerFactory.java index e952044..d64a08c 100644 --- a/src/main/java/com/caucho/hessian/io/SerializerFactory.java +++ b/src/main/java/com/caucho/hessian/io/SerializerFactory.java @@ -53,6 +53,8 @@ import com.caucho.burlap.io.BurlapRemoteObject; import com.caucho.hessian.io.atomic.AtomicDeserializer; import com.caucho.hessian.io.atomic.AtomicSerializer; +import com.caucho.hessian.io.java17.base.JavaCurrencyDeserializer; +import com.caucho.hessian.io.java17.base.JavaCurrencySerializer; import com.caucho.hessian.io.java8.DurationHandle; import com.caucho.hessian.io.java8.InstantHandle; import com.caucho.hessian.io.java8.Java8TimeSerializer; @@ -700,6 +702,15 @@ protected static void addBasic(Class cl, String typeName, int type) log.warning(String.valueOf(t.getCause())); } + try { + JavaCurrencySerializer currencySerializer = new JavaCurrencySerializer(Currency.class); + JavaCurrencyDeserializer currencyDeserializer = new JavaCurrencyDeserializer(); + _staticSerializerMap.put(Currency.class, currencySerializer); + _staticDeserializerMap.put(Currency.class, currencyDeserializer); + } catch (Throwable t) { + log.warning(String.valueOf(t.getCause())); + } + } /** diff --git a/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java new file mode 100644 index 0000000..3b4b7f2 --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java @@ -0,0 +1,25 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.java17.base; + +import com.caucho.hessian.io.AbstractDeserializer; +import com.caucho.hessian.io.AbstractHessianInput; + +import java.io.IOException; +import java.util.Currency; + +/** + * + * @author junyuan + * @version JavaCurrencyDeserializer.java, v 0.1 2023年08月09日 10:53 junyuan Exp $ + */ +public class JavaCurrencyDeserializer extends AbstractDeserializer { + + @Override + public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IOException { + String currencyCode = in.readString(); + return Currency.getInstance(currencyCode); + } +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencySerializer.java b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencySerializer.java new file mode 100644 index 0000000..8084de6 --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencySerializer.java @@ -0,0 +1,34 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.java17.base; + +import com.caucho.hessian.io.AbstractFieldAdaptorSerializer; +import com.caucho.hessian.io.AbstractHessianOutput; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Currency; + +/** + * + * @author junyuan + * @version JavaCurrencySerializer.java, v 0.1 2023年08月09日 10:32 junyuan Exp $ + */ +public class JavaCurrencySerializer extends AbstractFieldAdaptorSerializer { + + public JavaCurrencySerializer(Class clazz) { + super(clazz); + } + + @Override + protected void serializeField(AbstractHessianOutput out, Object obj, Field field) + throws IOException { + Currency currency = (Currency) obj; + if ("currencyCode".equals(field.getName())) { + String currencyCode = currency.getCurrencyCode(); + out.writeString(currencyCode); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java index 0b9093b..f2dcdf5 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java @@ -48,7 +48,7 @@ package com.caucho.hessian.io.throwable; -import com.caucho.hessian.io.AbstractFieldSpecificDeserializer; +import com.caucho.hessian.io.AbstractFieldAdaptorDeserializer; import com.caucho.hessian.io.AbstractHessianInput; import com.caucho.hessian.io.IOExceptionWrapper; @@ -65,7 +65,7 @@ * Deserializing a JDK 1.4 StackTraceElement * @author pangu */ -public class StackTraceElementDeserializer extends AbstractFieldSpecificDeserializer { +public class StackTraceElementDeserializer extends AbstractFieldAdaptorDeserializer { protected static final Logger log = Logger.getLogger(StackTraceElementSerializer.class .getName()); diff --git a/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java index 6dab107..cdf429d 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java @@ -4,7 +4,7 @@ */ package com.caucho.hessian.io.throwable; -import com.caucho.hessian.io.AbstractFieldSpecificSerializer; +import com.caucho.hessian.io.AbstractFieldAdaptorSerializer; import com.caucho.hessian.io.AbstractHessianOutput; import java.io.IOException; @@ -23,7 +23,7 @@ * @author junyuan * @version StackTraceElementSerializer.java, v 0.1 2023年04月10日 11:12 junyuan Exp $ */ -public class StackTraceElementSerializer extends AbstractFieldSpecificSerializer { +public class StackTraceElementSerializer extends AbstractFieldAdaptorSerializer { protected static final Logger log = Logger .getLogger(StackTraceElementSerializer.class .getName()); diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java index cb2e553..840c43e 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java @@ -4,7 +4,7 @@ */ package com.caucho.hessian.io.throwable; -import com.caucho.hessian.io.AbstractFieldSpecificDeserializer; +import com.caucho.hessian.io.AbstractFieldAdaptorDeserializer; import com.caucho.hessian.io.AbstractHessianInput; import com.caucho.hessian.io.HessianFieldException; import com.caucho.hessian.io.IOExceptionWrapper; @@ -22,7 +22,7 @@ * @author junyuan * @version ThrowableDeserializer.java, v 0.1 2023年04月10日 20:37 junyuan Exp $ */ -public class ThrowableDeserializer extends AbstractFieldSpecificDeserializer { +public class ThrowableDeserializer extends AbstractFieldAdaptorDeserializer { private final Class _type; protected Method addSuppressed = null; diff --git a/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java b/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java index bcad138..b66d968 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java @@ -5,7 +5,7 @@ package com.caucho.hessian.io.throwable; import com.caucho.hessian.io.AbstractHessianOutput; -import com.caucho.hessian.io.AbstractFieldSpecificSerializer; +import com.caucho.hessian.io.AbstractFieldAdaptorSerializer; import java.io.IOException; import java.lang.reflect.Field; @@ -22,7 +22,7 @@ * @author junyuan * @version ThrowableSerializer.java, v 0.1 2023年04月10日 19:30 junyuan Exp $ */ -public class ThrowableSerializer extends AbstractFieldSpecificSerializer { +public class ThrowableSerializer extends AbstractFieldAdaptorSerializer { protected static final Logger log = Logger.getLogger(ThrowableSerializer.class.getName()); diff --git a/src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java b/src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java new file mode 100644 index 0000000..c8f8780 --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java @@ -0,0 +1,37 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.java17.base; + +import java.io.Serializable; +import java.util.Currency; + +/** + * + * @author junyuan + * @version CurrencyWrapper.java, v 0.1 2023年08月09日 11:51 junyuan Exp $ + */ +public class CurrencyWrapper implements Serializable { + private static final long serialVersionUID = 6738644291381453889L; + + private Currency currency; + + /** + * Getter method for property currency. + * + * @return property value of currency + */ + public Currency getCurrency() { + return currency; + } + + /** + * Setter method for property currency. + * + * @param currency value to be assigned to property currency + */ + public void setCurrency(Currency currency) { + this.currency = currency; + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java new file mode 100644 index 0000000..efb1fd5 --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java @@ -0,0 +1,207 @@ +/* + * 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 com.caucho.hessian.io.java17.base; + +import com.caucho.hessian.io.Hessian2Input; +import com.caucho.hessian.io.Hessian2Output; +import com.caucho.hessian.io.SerializerFactory; +import com.caucho.hessian.io.throwable.ExceptionWrapper; +import com.caucho.hessian.io.throwable.JDK17SerializeFactory; +import com.caucho.hessian.io.throwable.MeaninglessEnum; +import com.caucho.hessian.io.throwable.SerializeFactoryWithoutThrowable; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Currency; +import java.util.Locale; + +/** + * 在 jdk8 下模拟运行 jdk17 下用的序列化器 + * + * @author junyuan + * @version SerializeCompatibleTest.java, v 0.1 2023年08月09日 15:52 junyuan Exp $ + */ +public class SerializeCompatibleTest { + // 把 currency 的去掉了 + private static SerializerFactory originFactory; + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + private static final boolean isLessThanJdk17 = isLessThanJdk17(); + + private static boolean isLessThanJdk17() { + String javaVersion = System.getProperty("java.specification.version"); + return Double.parseDouble(javaVersion) < 17; + } + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + originFactory = new SerializeFactoryWithoutCurrency(); + + os = new ByteArrayOutputStream(); + } + + /** + * Wrapper + * use currencySerializer to encode and java serializer to decode + * 'cause' is bound to lost here as getCause may return null + */ + @Test + public void test_case_1() throws IOException { + if (isLessThanJdk17) { + test_JavaCurrencyWrapper(factory, originFactory); + } + } + + /** + * wrapper + * use java serializer to encode and currencySerializer to decode + * @throws IOException + */ + @Test + public void test_case_2() throws IOException { + if (isLessThanJdk17) { + test_JavaCurrencyWrapper(originFactory, factory); + } + } + + /** + * + * @throws IOException + */ + @Test + public void test_case_3() throws IOException { + if (isLessThanJdk17) { + test_JavaCurrencyDirectly(factory, originFactory); + } + } + + @Test + public void test_case_4() throws IOException { + if (isLessThanJdk17) { + test_JavaCurrencyDirectly(originFactory, factory); + } + } + + /** + * 确保 CurrencySerializer 和 JavaSerialize 序列化的产物完全一致 + * @throws IOException + */ + @Test + public void test_bytes_equals() throws IOException { + if (isLessThanJdk17()) { + test_Serialize(originFactory, factory); + } + } + + @Test + public void test_decode_encode_jdk17() throws IOException { + if (!isLessThanJdk17()) { + test_JavaCurrencyWrapper(factory, factory); + } + + } + + + protected Object doEncodeNDecode(Object origin, SerializerFactory serializerFactory, + SerializerFactory deserializerFactory) throws IOException { + os.reset(); + Hessian2Output output = new Hessian2Output(os); + + output.setSerializerFactory(serializerFactory); + output.writeObject(origin); + output.flush(); + + ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); + Hessian2Input input = new Hessian2Input(is); + input.setSerializerFactory(deserializerFactory); + Object actual = input.readObject(); + return actual; + } + + private void test_JavaCurrencyWrapper(SerializerFactory serialize, SerializerFactory deserialize) + throws IOException { + if (isLessThanJdk17()) { + CurrencyWrapper cw = new CurrencyWrapper(); + cw.setCurrency(Currency.getInstance(Locale.getDefault())); + + Object result = doEncodeNDecode(cw, serialize, deserialize); + Assert.assertTrue(result instanceof CurrencyWrapper); + Currency newC = ((CurrencyWrapper) result).getCurrency(); + Assert.assertEquals(cw.getCurrency(), newC); + } + } + + private void test_JavaCurrencyDirectly(SerializerFactory serialize, SerializerFactory deserialize) + throws IOException { + if (isLessThanJdk17()) { + Currency origin = Currency.getInstance(Locale.getDefault()); + + Object result = doEncodeNDecode(origin, serialize, deserialize); + Assert.assertTrue(result instanceof Currency); + Assert.assertEquals(origin, result); + } + } + + private void test_Serialize(SerializerFactory originFactory, SerializerFactory newFactory) + throws IOException { + CurrencyWrapper cw = new CurrencyWrapper(); + cw.setCurrency(Currency.getInstance(Locale.getDefault())); + + byte[] resultOrigin = doSerialize(cw, originFactory); + byte[] resultNew = doSerialize(cw, factory); + Assert.assertTrue(bytesEquals(resultOrigin, resultNew)); + } + + private byte[] doSerialize(Object o, SerializerFactory factory) throws IOException { + os.reset(); + Hessian2Output output = new Hessian2Output(os); + + output.setSerializerFactory(originFactory); + output.writeObject(o); + output.flush(); + + return os.toByteArray(); + } + + protected boolean bytesEquals(byte[] src, byte[] target) { + if (src == null && target == null) { + return true; + } + + if (src == null || target == null) { + return false; + } + + if (src.length != target.length) { + return false; + } + + for (int i = 0; i < src.length; i++) { + if (src[i] != target[i]) { + return false; + } + } + return true; + } +} diff --git a/src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithoutCurrency.java b/src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithoutCurrency.java new file mode 100644 index 0000000..7e73562 --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithoutCurrency.java @@ -0,0 +1,38 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io.java17.base; + +import com.caucho.hessian.io.Deserializer; +import com.caucho.hessian.io.HessianProtocolException; +import com.caucho.hessian.io.JavaDeserializer; +import com.caucho.hessian.io.JavaSerializer; +import com.caucho.hessian.io.Serializer; +import com.caucho.hessian.io.SerializerFactory; + +import java.util.Currency; + +/** + * + * @author junyuan + * @version SerializeFactoryWithoutCurrency.java, v 0.1 2023年08月09日 11:48 junyuan Exp $ + */ +public class SerializeFactoryWithoutCurrency extends SerializerFactory { + + @Override + public Serializer getSerializer(Class cl) throws HessianProtocolException { + if (Currency.class.equals(cl)) { + return new JavaSerializer(cl); + } + return super.getSerializer(cl); + } + + @Override + public Deserializer getDeserializer(Class cl) throws HessianProtocolException { + if (Currency.class.equals(cl)) { + return new JavaDeserializer(cl); + } + return super.getDeserializer(cl); + } +} \ No newline at end of file From fa7ce54208de70f428fcf2849c906c231dff30a9 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Wed, 9 Aug 2023 14:42:10 +0800 Subject: [PATCH 23/32] format --- .../io/AbstractFieldAdaptorDeserializer.java | 1 - .../io/java17/base/JavaCurrencySerializer.java | 2 +- .../io/java17/base/CurrencyWrapper.java | 18 +++++++++++++++--- .../java17/base/SerializeCompatibleTest.java | 9 ++++----- .../base/SerializeFactoryWithoutCurrency.java | 16 ++++++++++++++-- 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorDeserializer.java b/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorDeserializer.java index f36aa28..22e4b04 100644 --- a/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorDeserializer.java @@ -4,7 +4,6 @@ */ package com.caucho.hessian.io; - import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashMap; diff --git a/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencySerializer.java b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencySerializer.java index 8084de6..37fded7 100644 --- a/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencySerializer.java +++ b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencySerializer.java @@ -24,7 +24,7 @@ public JavaCurrencySerializer(Class clazz) { @Override protected void serializeField(AbstractHessianOutput out, Object obj, Field field) - throws IOException { + throws IOException { Currency currency = (Currency) obj; if ("currencyCode".equals(field.getName())) { String currencyCode = currency.getCurrencyCode(); diff --git a/src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java b/src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java index c8f8780..b1d51f5 100644 --- a/src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java +++ b/src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java @@ -1,6 +1,18 @@ /* - * Ant Group - * Copyright (c) 2004-2023 All Rights Reserved. + * 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 com.caucho.hessian.io.java17.base; @@ -15,7 +27,7 @@ public class CurrencyWrapper implements Serializable { private static final long serialVersionUID = 6738644291381453889L; - private Currency currency; + private Currency currency; /** * Getter method for property currency. diff --git a/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java index efb1fd5..c2b4393 100644 --- a/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java +++ b/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java @@ -46,7 +46,7 @@ public class SerializeCompatibleTest { private static SerializerFactory factory; private static ByteArrayOutputStream os; - private static final boolean isLessThanJdk17 = isLessThanJdk17(); + private static final boolean isLessThanJdk17 = isLessThanJdk17(); private static boolean isLessThanJdk17() { String javaVersion = System.getProperty("java.specification.version"); @@ -122,7 +122,6 @@ public void test_decode_encode_jdk17() throws IOException { } - protected Object doEncodeNDecode(Object origin, SerializerFactory serializerFactory, SerializerFactory deserializerFactory) throws IOException { os.reset(); @@ -140,7 +139,7 @@ protected Object doEncodeNDecode(Object origin, SerializerFactory serializerFact } private void test_JavaCurrencyWrapper(SerializerFactory serialize, SerializerFactory deserialize) - throws IOException { + throws IOException { if (isLessThanJdk17()) { CurrencyWrapper cw = new CurrencyWrapper(); cw.setCurrency(Currency.getInstance(Locale.getDefault())); @@ -153,7 +152,7 @@ private void test_JavaCurrencyWrapper(SerializerFactory serialize, SerializerFac } private void test_JavaCurrencyDirectly(SerializerFactory serialize, SerializerFactory deserialize) - throws IOException { + throws IOException { if (isLessThanJdk17()) { Currency origin = Currency.getInstance(Locale.getDefault()); @@ -164,7 +163,7 @@ private void test_JavaCurrencyDirectly(SerializerFactory serialize, SerializerFa } private void test_Serialize(SerializerFactory originFactory, SerializerFactory newFactory) - throws IOException { + throws IOException { CurrencyWrapper cw = new CurrencyWrapper(); cw.setCurrency(Currency.getInstance(Locale.getDefault())); diff --git a/src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithoutCurrency.java b/src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithoutCurrency.java index 7e73562..e8ffdbd 100644 --- a/src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithoutCurrency.java +++ b/src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithoutCurrency.java @@ -1,6 +1,18 @@ /* - * Ant Group - * Copyright (c) 2004-2023 All Rights Reserved. + * 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 com.caucho.hessian.io.java17.base; From 845b01a35d9b214910e4f55cc0ba23cecadeb255 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Wed, 9 Aug 2023 14:43:22 +0800 Subject: [PATCH 24/32] init 3.5 snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8aca797..6883920 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.alipay.sofa hessian - 3.4.0 + 3.5.0-SNAPSHOT jar ${project.groupId}:${project.artifactId} From ff04ffabec2feb3d96cf639f20f41be1e1325a56 Mon Sep 17 00:00:00 2001 From: evenliu Date: Wed, 9 Aug 2023 14:49:43 +0800 Subject: [PATCH 25/32] optimized code (#94) Co-authored-by: liujianjun.ljj --- src/main/java/com/caucho/hessian/io/Hessian2Input.java | 2 +- src/main/java/com/caucho/hessian/io/JavaDeserializer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/caucho/hessian/io/Hessian2Input.java b/src/main/java/com/caucho/hessian/io/Hessian2Input.java index 3c4f45c..4f81721 100644 --- a/src/main/java/com/caucho/hessian/io/Hessian2Input.java +++ b/src/main/java/com/caucho/hessian/io/Hessian2Input.java @@ -3391,7 +3391,7 @@ protected IOException expect(String expect, int ch) if (obj != null) { return error("expected " + expect + " at 0x" + Integer.toHexString(ch & 0xff) - + " " + obj.getClass().getName() + " (" + obj + ")"); + + " " + obj.getClass().getName()); } else return error("expected " + expect diff --git a/src/main/java/com/caucho/hessian/io/JavaDeserializer.java b/src/main/java/com/caucho/hessian/io/JavaDeserializer.java index 42718b3..179bf9d 100644 --- a/src/main/java/com/caucho/hessian/io/JavaDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/JavaDeserializer.java @@ -620,7 +620,7 @@ else if (e instanceof IOException) throw new HessianFieldException(fieldName + ": " + e.getMessage(), e); if (value != null) - throw new HessianFieldException(fieldName + ": " + value.getClass().getName() + " (" + value + ")" + throw new HessianFieldException(fieldName + ": " + value.getClass().getName() + " cannot be assigned to " + field.getType().getName()); else throw new HessianFieldException(fieldName + ": " + field.getType().getName() + From 48b9936e657b3c4788f3b7f4849479c0558bcfcd Mon Sep 17 00:00:00 2001 From: lo1nt Date: Thu, 10 Aug 2023 16:37:25 +0800 Subject: [PATCH 26/32] format --- .../hessian/io/java17/base/SerializeCompatibleTest.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java index c2b4393..50be6c4 100644 --- a/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java +++ b/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java @@ -19,10 +19,6 @@ import com.caucho.hessian.io.Hessian2Input; import com.caucho.hessian.io.Hessian2Output; import com.caucho.hessian.io.SerializerFactory; -import com.caucho.hessian.io.throwable.ExceptionWrapper; -import com.caucho.hessian.io.throwable.JDK17SerializeFactory; -import com.caucho.hessian.io.throwable.MeaninglessEnum; -import com.caucho.hessian.io.throwable.SerializeFactoryWithoutThrowable; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -30,7 +26,6 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.lang.reflect.Method; import java.util.Currency; import java.util.Locale; From dee4e8b0a5ebc97fc2dce9417fc3cb9be7fb444a Mon Sep 17 00:00:00 2001 From: lo1nt Date: Fri, 11 Aug 2023 19:29:34 +0800 Subject: [PATCH 27/32] fix: ref problem on currency --- pom.xml | 2 +- .../java17/base/JavaCurrencyDeserializer.java | 10 ++++++- .../io/java17/base/CurrencyWrapper.java | 19 ++++++++++++ .../java17/base/SerializeCompatibleTest.java | 30 +++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6883920..40678b0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.alipay.sofa hessian - 3.5.0-SNAPSHOT + 3.5.0-SNAPSHOT-bug jar ${project.groupId}:${project.artifactId} diff --git a/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java index 3b4b7f2..37e04d2 100644 --- a/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java @@ -20,6 +20,14 @@ public class JavaCurrencyDeserializer extends AbstractDeserializer { @Override public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IOException { String currencyCode = in.readString(); - return Currency.getInstance(currencyCode); + Currency currency = null; + try { + // 如果该数据有问题, 保证至少塞入一个 null 作为 ref + currency = Currency.getInstance(currencyCode); + } finally { + in.addRef(currency); + + } + return currency; } } \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java b/src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java index b1d51f5..24c5099 100644 --- a/src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java +++ b/src/test/java/com/caucho/hessian/io/java17/base/CurrencyWrapper.java @@ -27,8 +27,18 @@ public class CurrencyWrapper implements Serializable { private static final long serialVersionUID = 6738644291381453889L; + private int cent; + private Currency currency; + public CurrencyWrapper() { + } + + public CurrencyWrapper(Currency currency) { + this.cent = currency.getCurrencyCode().hashCode(); + this.currency = currency; + } + /** * Getter method for property currency. * @@ -46,4 +56,13 @@ public Currency getCurrency() { public void setCurrency(Currency currency) { this.currency = currency; } + + /** + * Getter method for property cent. + * + * @return property value of cent + */ + public int getCent() { + return cent; + } } \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java index 50be6c4..38f7046 100644 --- a/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java +++ b/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java @@ -26,7 +26,9 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.ArrayList; import java.util.Currency; +import java.util.List; import java.util.Locale; /** @@ -98,6 +100,17 @@ public void test_case_4() throws IOException { } } + /** + * object list + * @throws IOException + */ + @Test + public void test_case_5() throws IOException { + if (isLessThanJdk17()) { + test_JavaCurrencyWrapperList(factory, factory); + } + } + /** * 确保 CurrencySerializer 和 JavaSerialize 序列化的产物完全一致 * @throws IOException @@ -157,6 +170,23 @@ private void test_JavaCurrencyDirectly(SerializerFactory serialize, SerializerFa } } + private void test_JavaCurrencyWrapperList(SerializerFactory serialize, SerializerFactory deserialize) + throws IOException { + if (isLessThanJdk17()) { + List cl = new ArrayList(); + cl.add(new CurrencyWrapper(Currency.getInstance(Locale.getAvailableLocales()[10]))); + cl.add(new CurrencyWrapper(Currency.getInstance(Locale.getAvailableLocales()[10]))); + cl.add(new CurrencyWrapper(Currency.getInstance(Locale.getAvailableLocales()[41]))); + + Object result = doEncodeNDecode(cl, serialize, deserialize); + Assert.assertTrue(result instanceof List); + + Assert.assertEquals(cl.get(0).getCurrency(), ((CurrencyWrapper) ((List) result).get(0)).getCurrency()); + Assert.assertEquals(cl.get(1).getCurrency(), ((CurrencyWrapper) ((List) result).get(1)).getCurrency()); + Assert.assertEquals(cl.get(2).getCurrency(), ((CurrencyWrapper) ((List) result).get(2)).getCurrency()); + } + } + private void test_Serialize(SerializerFactory originFactory, SerializerFactory newFactory) throws IOException { CurrencyWrapper cw = new CurrencyWrapper(); From 139667a2de559bff8f9145917d685da6205a2551 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Fri, 11 Aug 2023 19:32:01 +0800 Subject: [PATCH 28/32] format --- .../caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java index 37e04d2..efc6c1a 100644 --- a/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java @@ -21,7 +21,7 @@ public class JavaCurrencyDeserializer extends AbstractDeserializer { public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IOException { String currencyCode = in.readString(); Currency currency = null; - try { + try { // 如果该数据有问题, 保证至少塞入一个 null 作为 ref currency = Currency.getInstance(currencyCode); } finally { From 282ec537d649fd70d76f125cfa36fcb3b9499b31 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Mon, 14 Aug 2023 11:42:07 +0800 Subject: [PATCH 29/32] fix: ref bug on atomic --- .../hessian/io/atomic/AtomicDeserializer.java | 38 ++++++--- .../test/atomic/AtomicDeserializeTest.java | 77 +++++++++++++++++++ .../test/stacktrace/ExceptionClassTest.java | 33 ++++++++ 3 files changed, 138 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/caucho/hessian/io/atomic/AtomicDeserializer.java b/src/main/java/com/caucho/hessian/io/atomic/AtomicDeserializer.java index 072b255..d97ce8b 100644 --- a/src/main/java/com/caucho/hessian/io/atomic/AtomicDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/atomic/AtomicDeserializer.java @@ -6,7 +6,6 @@ import com.caucho.hessian.io.AbstractDeserializer; import com.caucho.hessian.io.AbstractHessianInput; -import com.caucho.hessian.io.ArrayDeserializer; import com.caucho.hessian.io.BasicDeserializer; import java.io.IOException; @@ -27,9 +26,8 @@ public class AtomicDeserializer extends AbstractDeserializer { private Class _type; - private BasicDeserializer intArrayDsr = new BasicDeserializer(BasicDeserializer.INTEGER_ARRAY); - private BasicDeserializer longArrayDsr = new BasicDeserializer(BasicDeserializer.LONG_ARRAY); - private BasicDeserializer objectArrayDsr = new BasicDeserializer(BasicDeserializer.OBJECT_ARRAY); + private BasicDeserializer intArrayDsr = new BasicDeserializer(BasicDeserializer.INTEGER_ARRAY); + private BasicDeserializer longArrayDsr = new BasicDeserializer(BasicDeserializer.LONG_ARRAY); public AtomicDeserializer(Class cl) { this._type = cl; @@ -39,42 +37,62 @@ public AtomicDeserializer(Class cl) { public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IOException { if (AtomicInteger.class.equals(_type)) { - return new AtomicInteger(in.readInt()); + AtomicInteger tmp = new AtomicInteger(); + in.addRef(tmp); + tmp.set(in.readInt()); + return tmp; } else if (AtomicBoolean.class.equals(_type)) { - return new AtomicBoolean(in.readInt() == 1); + AtomicBoolean tmp = new AtomicBoolean(); + in.addRef(tmp); + tmp.set(in.readInt() == 1); + return tmp; } else if (AtomicLong.class.equals(_type)) { - return new AtomicLong(in.readLong()); + AtomicLong tmp = new AtomicLong(); + in.addRef(tmp); + tmp.set(in.readLong()); + return tmp; } else if (AtomicReference.class.equals(_type)) { - return new AtomicReference(in.readObject()); + AtomicReference tmp = new AtomicReference(); + in.addRef(tmp); + tmp.set(in.readObject()); + return tmp; } else if (AtomicIntegerArray.class.equals(_type)) { + AtomicIntegerArray array = null; + int ref = in.addRef(array); int[] res = (int[]) intArrayDsr.readObject(in); int len = res.length; - AtomicIntegerArray array = new AtomicIntegerArray(len); + array = new AtomicIntegerArray(len); for (int i = 0; i < len; i++) { array.set(i, res[i]); } + in.setRef(ref, array); return array; } else if (AtomicLongArray.class.equals(_type)) { + AtomicLongArray array = null; + int ref = in.addRef(array); long[] res = (long[]) longArrayDsr.readObject(in); int len = res.length; - AtomicLongArray array = new AtomicLongArray(len); + array = new AtomicLongArray(len); for (int i = 0; i < len; i++) { array.set(i, res[i]); } + in.setRef(ref, array); return array; } else if (AtomicReferenceArray.class.equals(_type)) { + int ref = in.addRef(null); Object[] res = (Object[]) in.readObject((new Object[0]).getClass()); int len = res.length; AtomicReferenceArray array = new AtomicReferenceArray(len); for (int i = 0; i < len; i++) { array.set(i, res[i]); } + in.setRef(ref, array); return array; } diff --git a/src/test/java/com/caucho/hessian/test/atomic/AtomicDeserializeTest.java b/src/test/java/com/caucho/hessian/test/atomic/AtomicDeserializeTest.java index cd6f54d..1b73788 100644 --- a/src/test/java/com/caucho/hessian/test/atomic/AtomicDeserializeTest.java +++ b/src/test/java/com/caucho/hessian/test/atomic/AtomicDeserializeTest.java @@ -27,6 +27,8 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicIntegerArray; @@ -98,6 +100,81 @@ public void test_atomicWrapper() throws IOException { .get(1)); } + @Test + public void test_ref() throws IOException { + List l = new ArrayList(); + + // prepare + AtomicInteger ai = new AtomicInteger(17); + AtomicBoolean ab = new AtomicBoolean(true); + AtomicLong al = new AtomicLong(2147483649L); + AtomicReference at = new AtomicReference(new Integer(1)); + AtomicIntegerArray aia = new AtomicIntegerArray(2); + aia.set(0, 0); + aia.set(1, 1); + AtomicLongArray ala = new AtomicLongArray(2); + ala.set(0, 0L); + ala.set(1, 1L); + AtomicReferenceArray ara = new AtomicReferenceArray(2); + ara.set(0, new Integer(1)); + ara.set(1, new Integer(2)); + + AtomicWrapper atomicWrapper1 = new AtomicWrapper(); + atomicWrapper1.setaInteger(ai); + atomicWrapper1.setaBoolean(ab); + atomicWrapper1.setaLong(al); + atomicWrapper1.setaReference(at); + atomicWrapper1.setaIntegerArray(aia); + atomicWrapper1.setaLongArray(ala); + atomicWrapper1.setaReferenceArray(ara); + + AtomicWrapper atomicWrapper2 = new AtomicWrapper(); + atomicWrapper2.setaInteger(ai); + atomicWrapper2.setaBoolean(ab); + atomicWrapper2.setaLong(al); + atomicWrapper2.setaReference(at); + atomicWrapper2.setaIntegerArray(aia); + atomicWrapper2.setaLongArray(ala); + atomicWrapper2.setaReferenceArray(ara); + l.add(atomicWrapper1); + l.add(atomicWrapper2); + + Object result = doEncodeNDecode(l); + Assert.assertTrue(result instanceof List); + List resultInstance = (List) result; + + Assert.assertTrue(atomicWrapper1 instanceof AtomicWrapper); + AtomicWrapper actual = atomicWrapper1; + Assert.assertEquals(resultInstance.get(0).getaInteger().get(), actual.getaInteger().get()); + Assert.assertEquals(resultInstance.get(0).getaBoolean().get(), actual.getaBoolean().get()); + Assert.assertEquals(resultInstance.get(0).getaLong().get(), actual.getaLong().get()); + Assert.assertEquals(resultInstance.get(0).getaReference().get(), actual.getaReference().get()); + + Assert.assertEquals(resultInstance.get(0).getaIntegerArray().get(0), actual.getaIntegerArray().get(0)); + Assert.assertEquals(resultInstance.get(0).getaIntegerArray().get(1), actual.getaIntegerArray().get(1)); + Assert.assertEquals(resultInstance.get(0).getaLongArray().get(0), actual.getaLongArray().get(0)); + Assert.assertEquals(resultInstance.get(0).getaLongArray().get(1), actual.getaLongArray().get(1)); + Assert.assertEquals(resultInstance.get(0).getaReferenceArray().get(0), actual.getaReferenceArray() + .get(0)); + Assert.assertEquals(resultInstance.get(0).getaReferenceArray().get(1), actual.getaReferenceArray() + .get(1)); + + Assert.assertEquals(resultInstance.get(1).getaInteger().get(), actual.getaInteger().get()); + Assert.assertEquals(resultInstance.get(1).getaBoolean().get(), actual.getaBoolean().get()); + Assert.assertEquals(resultInstance.get(1).getaLong().get(), actual.getaLong().get()); + Assert.assertEquals(resultInstance.get(1).getaReference().get(), actual.getaReference().get()); + + Assert.assertEquals(resultInstance.get(1).getaIntegerArray().get(0), actual.getaIntegerArray().get(0)); + Assert.assertEquals(resultInstance.get(1).getaIntegerArray().get(1), actual.getaIntegerArray().get(1)); + Assert.assertEquals(resultInstance.get(1).getaLongArray().get(0), actual.getaLongArray().get(0)); + Assert.assertEquals(resultInstance.get(1).getaLongArray().get(1), actual.getaLongArray().get(1)); + Assert.assertEquals(resultInstance.get(1).getaReferenceArray().get(0), actual.getaReferenceArray() + .get(0)); + Assert.assertEquals(resultInstance.get(1).getaReferenceArray().get(1), actual.getaReferenceArray() + .get(1)); + + } + @Test public void test_atomic_unwrappedInteger() throws IOException { os.reset(); diff --git a/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java index ecbb936..30db446 100644 --- a/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java +++ b/src/test/java/com/caucho/hessian/test/stacktrace/ExceptionClassTest.java @@ -19,12 +19,15 @@ import com.caucho.hessian.io.Hessian2Input; import com.caucho.hessian.io.Hessian2Output; import com.caucho.hessian.io.SerializerFactory; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; /** * @@ -58,6 +61,36 @@ public void test_stacktrace() throws IOException { } + @Test + public void test_ref() throws IOException { + Throwable t = null; + try { + int x = 1 / 0; + } catch (Exception e) { + t = e; + } + + ExceptionWrapper w1 = new ExceptionWrapper(); + w1.setT(t); + + ExceptionWrapper w2 = new ExceptionWrapper(); + w2.setT(t); + + List l = new ArrayList(); + l.add(w1); + l.add(w2); + + Object result = doEncodeNDecode(l); + Assert.assertTrue(result instanceof List); + + List resultInstance = (List) result; + + Assert.assertEquals(l.get(0).t.getMessage(), resultInstance.get(0).t.getMessage()); + Assert.assertEquals(l.get(0).t.getStackTrace().length, resultInstance.get(0).t.getStackTrace().length); + + Assert.assertEquals(l.get(1).t.getMessage(), resultInstance.get(1).t.getMessage()); + } + protected Object doEncodeNDecode(Object origin) throws IOException { os.reset(); Hessian2Output output = new Hessian2Output(os); From 2b9fd8b0d8f5ea2e1562d28cfe077a8d24967a67 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Mon, 14 Aug 2023 11:49:01 +0800 Subject: [PATCH 30/32] version snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 997f4d9..6883920 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.alipay.sofa hessian - 3.5.0-beta1 + 3.5.0-SNAPSHOT jar ${project.groupId}:${project.artifactId} From b6d4f13b153599600b8cda2b0a62d642ba0b6138 Mon Sep 17 00:00:00 2001 From: lo1nt Date: Mon, 14 Aug 2023 14:13:23 +0800 Subject: [PATCH 31/32] enhance: ref --- .../hessian/io/java17/base/JavaCurrencyDeserializer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java index efc6c1a..9bbb8a1 100644 --- a/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java +++ b/src/main/java/com/caucho/hessian/io/java17/base/JavaCurrencyDeserializer.java @@ -19,14 +19,14 @@ public class JavaCurrencyDeserializer extends AbstractDeserializer { @Override public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IOException { + int ref = in.addRef(null); String currencyCode = in.readString(); Currency currency = null; try { // 如果该数据有问题, 保证至少塞入一个 null 作为 ref currency = Currency.getInstance(currencyCode); } finally { - in.addRef(currency); - + in.setRef(ref, currency); } return currency; } From 8d02768db019c609ba22ff1c0f4fce76f5e2e9cc Mon Sep 17 00:00:00 2001 From: lo1nt Date: Mon, 14 Aug 2023 14:38:59 +0800 Subject: [PATCH 32/32] fix: test --- pom.xml | 2 +- .../caucho/hessian/io/SerializerFactory.java | 31 ++++++++--- .../java17/base/SerializeCompatibleTest.java | 3 +- .../base/SerializeFactoryWithCurrency.java | 53 +++++++++++++++++++ 4 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithCurrency.java diff --git a/pom.xml b/pom.xml index 40678b0..6883920 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.alipay.sofa hessian - 3.5.0-SNAPSHOT-bug + 3.5.0-SNAPSHOT jar ${project.groupId}:${project.artifactId} diff --git a/src/main/java/com/caucho/hessian/io/SerializerFactory.java b/src/main/java/com/caucho/hessian/io/SerializerFactory.java index d64a08c..7536e4d 100644 --- a/src/main/java/com/caucho/hessian/io/SerializerFactory.java +++ b/src/main/java/com/caucho/hessian/io/SerializerFactory.java @@ -121,6 +121,7 @@ public class SerializerFactory extends AbstractSerializerFactory protected ClassNameResolver classNameResolver = ClassNameResolverBuilder.buildDefault(); protected final static boolean isHigherThanJdk8 = isJava8(); + protected final static boolean isHigherThanJdk17 = isJava17(); private Map> _typeNotFoundMap = new ConcurrentHashMap>( 8); @@ -702,13 +703,8 @@ protected static void addBasic(Class cl, String typeName, int type) log.warning(String.valueOf(t.getCause())); } - try { - JavaCurrencySerializer currencySerializer = new JavaCurrencySerializer(Currency.class); - JavaCurrencyDeserializer currencyDeserializer = new JavaCurrencyDeserializer(); - _staticSerializerMap.put(Currency.class, currencySerializer); - _staticDeserializerMap.put(Currency.class, currencyDeserializer); - } catch (Throwable t) { - log.warning(String.valueOf(t.getCause())); + if (isHigherThanJdk17) { + addCurrencySupport(); } } @@ -723,6 +719,27 @@ private static boolean isJava8() { return Double.valueOf(javaVersion) >= 1.8; } + /** + * check if the environment is java 17 or beyond + * + * @return if on java 17 + */ + private static boolean isJava17() { + String javaVersion = System.getProperty("java.specification.version"); + return Double.valueOf(javaVersion) >= 17; + } + + protected static void addCurrencySupport() { + try { + JavaCurrencySerializer currencySerializer = new JavaCurrencySerializer(Currency.class); + JavaCurrencyDeserializer currencyDeserializer = new JavaCurrencyDeserializer(); + _staticSerializerMap.put(Currency.class, currencySerializer); + _staticDeserializerMap.put(Currency.class, currencyDeserializer); + } catch (Throwable t) { + log.warning(String.valueOf(t.getCause())); + } + } + private static boolean isZoneId(Class cl) { try { return isHigherThanJdk8 && Class.forName("java.time.ZoneId").isAssignableFrom(cl); diff --git a/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java b/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java index 38f7046..5f11a18 100644 --- a/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java +++ b/src/test/java/com/caucho/hessian/io/java17/base/SerializeCompatibleTest.java @@ -40,6 +40,7 @@ public class SerializeCompatibleTest { // 把 currency 的去掉了 private static SerializerFactory originFactory; + // 把 currency 的加上 private static SerializerFactory factory; private static ByteArrayOutputStream os; @@ -52,7 +53,7 @@ private static boolean isLessThanJdk17() { @BeforeClass public static void setUp() { - factory = new SerializerFactory(); + factory = new SerializeFactoryWithCurrency(); originFactory = new SerializeFactoryWithoutCurrency(); os = new ByteArrayOutputStream(); diff --git a/src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithCurrency.java b/src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithCurrency.java new file mode 100644 index 0000000..c6a40bf --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/java17/base/SerializeFactoryWithCurrency.java @@ -0,0 +1,53 @@ +/* + * 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 com.caucho.hessian.io.java17.base; + +import com.caucho.hessian.io.Deserializer; +import com.caucho.hessian.io.HessianProtocolException; +import com.caucho.hessian.io.JavaDeserializer; +import com.caucho.hessian.io.JavaSerializer; +import com.caucho.hessian.io.Serializer; +import com.caucho.hessian.io.SerializerFactory; + +import java.util.Currency; + +/** + * + * @author junyuan + * @version SerializeFactoryWithoutCurrency.java, v 0.1 2023年08月09日 11:48 junyuan Exp $ + */ +public class SerializeFactoryWithCurrency extends SerializerFactory { + + private Serializer javaCurrencySerializer = new JavaCurrencySerializer(Currency.class); + private Deserializer javaCurrencyDeserializer = new JavaCurrencyDeserializer(); + + @Override + public Serializer getSerializer(Class cl) throws HessianProtocolException { + if (Currency.class.equals(cl)) { + return javaCurrencySerializer; + } + return super.getSerializer(cl); + } + + @Override + public Deserializer getDeserializer(Class cl) throws HessianProtocolException { + if (Currency.class.equals(cl)) { + return javaCurrencyDeserializer; + } + return super.getDeserializer(cl); + } +} \ No newline at end of file