diff --git a/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorSerializer.java b/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorSerializer.java index 0ea63f8..bcb50c5 100644 --- a/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorSerializer.java +++ b/src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorSerializer.java @@ -93,7 +93,8 @@ public void writeInstance(Object obj, AbstractHessianOutput out) * @return */ protected Field[] getFieldsForSerialize(Class cl) { - List fields = new ArrayList(); + ArrayList primitiveFields = new ArrayList(); + ArrayList compoundFields = new ArrayList(); for (; cl != null; cl = cl.getSuperclass()) { Field[] originFields = cl.getDeclaredFields(); for (int i = 0; i < originFields.length; i++) { @@ -101,9 +102,18 @@ protected Field[] getFieldsForSerialize(Class cl) { if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { continue; } - fields.add(field); + + if (field.getType().isPrimitive() || + field.getType().getName().startsWith("java.lang.") && + !field.getType().equals(Object.class)) + primitiveFields.add(field); + else + compoundFields.add(field); } } + List fields = new ArrayList(); + fields.addAll(primitiveFields); + fields.addAll(compoundFields); return fields.toArray(new Field[0]); } } diff --git a/src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java b/src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java new file mode 100644 index 0000000..65831bc --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java @@ -0,0 +1,164 @@ +/* + * Ant Group + * Copyright (c) 2004-2023 All Rights Reserved. + */ +package com.caucho.hessian.io; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author junyuan + * @version AbstractStringBuilderDeserializer.java, v 0.1 2023年10月20日 11:31 junyuan Exp $ + */ +public class AbstractStringBuilderDeserializer extends JavaDeserializer { + private static final Logger log = Logger.getLogger(AbstractStringBuilderDeserializer.class.getName()); + + private static final boolean ENABLE = judgeAvailability(); + /** String 的 value field, 用以判断是否需要用当前 deserializer */ + private static Field stringValueField; + /** String 的 coder field, 用以从中间 String 变量中获取 coder */ + private static Field stringCoderField; + + static { + try { + stringCoderField = String.class.getDeclaredField("coder"); + stringCoderField.setAccessible(true); + } catch (Throwable t) { + log.log(Level.WARNING, + "coder field not found or not accessible, will skip coder check, error is " + t.getMessage()); + } + } + + /** + * 判断是否要使用该反序列化器, 当 String.value 类型不为 char[] 时需要使用 + * @return + */ + private static boolean judgeAvailability() { + try { + stringValueField = String.class.getDeclaredField("value"); + stringValueField.setAccessible(true); + } catch (Throwable t) { + return false; + } + + if (byte[].class.equals(stringValueField.getType())) { + return true; + } + + return false; + } + + public static boolean isEnable() { + return ENABLE; + } + + public AbstractStringBuilderDeserializer(Class cl) { + super(cl); + } + + @Override + protected HashMap getFieldMap(Class cl) { + HashMap fieldMap = super.getFieldMap(cl); + Field valueField = null; + valueField = getAbstractStringBuilderField(cl, "value"); + if (valueField == null) { + log.log(Level.WARNING, "get value field failed"); + return fieldMap; + } + + Field coderField = null; + if (fieldMap.containsKey("coder")) { + coderField = getAbstractStringBuilderField(cl, "coder"); + } + + fieldMap.put("value", new StringBuilderValueFieldDeserializer(valueField, coderField)); + return fieldMap; + } + + /** + * 获取 AbstractStingBuilder 类内的 field + * @param cl + * @param fieldName + * @return + */ + private Field getAbstractStringBuilderField(Class cl, String fieldName) { + Field field = null; + try { + field = cl.getSuperclass().getDeclaredField(fieldName); + field.setAccessible(true); + } catch (Throwable t) { + log.log(Level.WARNING, "get " + fieldName + " field failed", t); + return null; + } + return field; + } + + /** + * 针对 value field 定制的 field deserializer + * 读取 value field 时, 根据传入数据进行读取, 读取到值后进行转换 + */ + static class StringBuilderValueFieldDeserializer extends FieldDeserializer { + /** + * 这个 _field 会是 AbstractStringBuilder.value + */ + private final Field _field; + + /** + * _coderField 会是 AbstractStringBuilder.coder 字段 + */ + private final Field _coderField; + + StringBuilderValueFieldDeserializer(Field value, Field coder) { + _field = value; + _coderField = coder; + } + + @Override + void deserialize(AbstractHessianInput in, Object obj) throws IOException { + Object value = null; + + try { + // hessian 在读取 char 时会用 utf-8 编码 + value = in.readObject(); + if (value == null) { + return; + } + + // 理论上获取到的值有两种情况: String 或者 byte[] + if (value instanceof String) { + dealWithStringValue((String) value, obj); + } else if (value instanceof byte[]) { + _field.set(obj, value); + } else { + throw new UnsupportedEncodingException("未知的编码类型" + value.getClass()); + } + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + + /** + * 如果读取到的是 String, 需要通过 String.coder 进行编码 + */ + public void dealWithStringValue(String value, Object obj) { + try { + byte[] res = (byte[]) stringValueField.get(value); + _field.set(obj, res); + + if (stringCoderField != null) { + byte coder = (byte) stringCoderField.getByte(value); + _coderField.set(obj, coder); + } + } catch (Throwable t) { + + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/caucho/hessian/io/AbstractStringBuilderSerializer.java b/src/main/java/com/caucho/hessian/io/AbstractStringBuilderSerializer.java new file mode 100644 index 0000000..dbce897 --- /dev/null +++ b/src/main/java/com/caucho/hessian/io/AbstractStringBuilderSerializer.java @@ -0,0 +1,111 @@ +/* + * 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.util.logging.Level; + +/** + * + * @author junyuan + * @version AbstractStringBuilderSerializer.java, v 0.1 2023年10月20日 11:31 junyuan Exp $ + */ +public class AbstractStringBuilderSerializer extends AbstractFieldAdaptorSerializer { + + private static final boolean ENABLE = judgeAvailability(); + + public static boolean isEnable() { + return ENABLE; + } + + public AbstractStringBuilderSerializer(Class cl) { + super(cl); + for (Field field : _fields) { + try { + field.setAccessible(true); + } catch (Throwable t) { + log.log(Level.WARNING, "unable to set field {} accessible", field.getName()); + } + } + + } + + @Override + protected void serializeField(AbstractHessianOutput out, Object obj, Field field) + throws IOException { + if ("value".equals(field.getName())) { + serializeValueArray(out, obj); + } else { + serializeNormalField(out, obj, field); + } + } + + /** + * 将底层 value 数组转为 char数组, 并以 writeString 方式进行序列化 + * 保证序列化结果与普通 JavaSerializer 保持一致 + * + * @param out + * @param obj + * @throws IOException + */ + protected void serializeValueArray(AbstractHessianOutput out, Object obj) + throws IOException { + if (obj instanceof StringBuilder) { + StringBuilder sb = (StringBuilder) obj; + // 要用实际底层 value 数据的长度以保持一致 + char[] dst = new char[sb.capacity()]; + sb.getChars(0, sb.length(), dst, 0); + + out.writeString(dst, 0, dst.length); + } else if (obj instanceof StringBuffer) { + StringBuffer sb = (StringBuffer) obj; + char[] dst = new char[sb.capacity()]; + sb.getChars(0, sb.length(), dst, 0); + + out.writeString(dst, 0, dst.length); + } else { + throw new UnsupportedOperationException("only support AbstractStringBuilder but got " + obj.getClass()); + } + } + + /** + * 常规字段以 object 的方式序列化 + * @param out + * @param obj + * @param field + * @throws IOException + */ + private void serializeNormalField(AbstractHessianOutput out, Object obj, Field field) throws IOException { + Object value = null; + + try { + value = field.get(obj); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeObject(value); + } + + /** + * 判断是否要使用该反序列化器, 当 String.value 类型为 byte[] 时需要使用 + * @return + */ + private static boolean judgeAvailability() { + Field field = null; + try { + field = String.class.getDeclaredField("value"); + } catch (Throwable t) { + return false; + } + + if (byte[].class.equals(field.getType())) { + return true; + } + + return false; + } +} \ 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 7536e4d..3579822 100644 --- a/src/main/java/com/caucho/hessian/io/SerializerFactory.java +++ b/src/main/java/com/caucho/hessian/io/SerializerFactory.java @@ -707,6 +707,8 @@ protected static void addBasic(Class cl, String typeName, int type) addCurrencySupport(); } + addAbstractStringBuilderSupport(); + } /** @@ -740,6 +742,24 @@ protected static void addCurrencySupport() { } } + protected static void addAbstractStringBuilderSupport() { + try { + if (AbstractStringBuilderSerializer.isEnable()) { + _staticSerializerMap.put(StringBuilder.class, new AbstractStringBuilderSerializer(StringBuilder.class)); + _staticSerializerMap.put(StringBuffer.class, new AbstractStringBuilderSerializer(StringBuffer.class)); + } + + if (AbstractStringBuilderDeserializer.isEnable()) { + _staticDeserializerMap.put(StringBuilder.class, new AbstractStringBuilderDeserializer( + StringBuilder.class)); + _staticDeserializerMap.put(StringBuffer.class, + new AbstractStringBuilderDeserializer(StringBuffer.class)); + } + } catch (Throwable t) { + log.info(String.valueOf(t.getCause())); + } + } + private static boolean isZoneId(Class cl) { try { return isHigherThanJdk8 && Class.forName("java.time.ZoneId").isAssignableFrom(cl); 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 cdf429d..c67568b 100644 --- a/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java +++ b/src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java @@ -90,7 +90,8 @@ protected void serializeField(AbstractHessianOutput out, Object obj, Field field @Override protected Field[] getFieldsForSerialize(Class cl) { - List fields = new ArrayList(); + ArrayList primitiveFields = new ArrayList(); + ArrayList compoundFields = new ArrayList(); for (; cl != null; cl = cl.getSuperclass()) { Field[] originFields = cl.getDeclaredFields(); for (int i = 0; i < originFields.length; i++) { @@ -102,9 +103,18 @@ protected Field[] getFieldsForSerialize(Class cl) { if ("format".equals(field.getName())) { continue; } - fields.add(field); + + if (field.getType().isPrimitive() || + field.getType().getName().startsWith("java.lang.") && + !field.getType().equals(Object.class)) + primitiveFields.add(field); + else + compoundFields.add(field); } } + List fields = new ArrayList(); + fields.addAll(primitiveFields); + fields.addAll(compoundFields); return fields.toArray(new Field[0]); } } diff --git a/src/test/java/com/caucho/hessian/io/java17/lang/AbstractStringBuilderEncodeTest.java b/src/test/java/com/caucho/hessian/io/java17/lang/AbstractStringBuilderEncodeTest.java new file mode 100644 index 0000000..9eaa50a --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/java17/lang/AbstractStringBuilderEncodeTest.java @@ -0,0 +1,166 @@ +/* + * 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.lang; + +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.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + *
    + *
  1. 确保新老两种序列化方式内容一致(由于类实现有变动, 无法保证 jdk17 和 jdk8 序列化结果完全一致)
  2. + *
  3. 确保 jdk8 下的序列化结果能被 jdk8 下的反序列化器正确解析
  4. + *
+ * + * + * @author junyuan + * @version AbstractStringBuilderEncodeTest.java, v 0.1 2023年10月20日 10:07 junyuan Exp $ + */ +public class AbstractStringBuilderEncodeTest { + + private static SerializerFactory factory; + private static SerializerFactory previousVersionFactory; + private static ByteArrayOutputStream os; + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + previousVersionFactory = new StringBuilderJavaSerializeFactory(); + + os = new ByteArrayOutputStream(); + } + + private void compatibleTest(Object inputData, SerializerFactory newFactory, SerializerFactory oldFactory) { + byte[] resNew = new byte[0]; + byte[] resOld = new byte[0]; + + try { + resNew = doEncodeWithStringBuilder(inputData, newFactory); + resOld = doEncodeWithStringBuilder(inputData, oldFactory); + } catch (Throwable e) { + e.printStackTrace(); + Assert.fail(); + } + + boolean result = Arrays.equals(resNew, resOld); + Assert.assertTrue(result); + } + + @Test + public void compatibleTest_short_no_warpper() { + StringBuilder sb = new StringBuilder(); + sb.append("test"); + + compatibleTest(sb, factory, previousVersionFactory); + } + + @Test + public void compatibleTest_long_no_warpper() { + StringBuilder sb = new StringBuilder(); + sb.append("100000000020000000003000000000400000000050000000006000000000"); + + compatibleTest(sb, factory, previousVersionFactory); + } + + /** + * short string with length lt 16 + */ + @Test + public void compatibleTest_short_wrapper() { + StringBuilder sb = new StringBuilder(); + sb.append("test"); + StringBuilderWrapper sbw = new StringBuilderWrapper(); + sbw.setStringBuilder(sb); + sbw.setStr(sb.toString()); + + compatibleTest(sbw, factory, previousVersionFactory); + } + + /** + * long string with length gt 31 + */ + @Test + public void compatibleTest_long_wrapper() { + StringBuilder sb = new StringBuilder(); + sb.append("100000000020000000003000000000400000000050000000006000000000"); + StringBuilderWrapper sbw = new StringBuilderWrapper(); + sbw.setStringBuilder(sb); + sbw.setStr(sb.toString()); + + compatibleTest(sbw, factory, previousVersionFactory); + } + + /** + * UTF16 + */ + @Test + public void compatibleTest_wrapper_short_utf16() { + StringBuilder sb = new StringBuilder(); + sb.append("test测试"); + StringBuilderWrapper sbw = new StringBuilderWrapper(); + sbw.setStringBuilder(sb); + sbw.setStr(sb.toString()); + + compatibleTest(sbw, factory, previousVersionFactory); + } + + /** + * UTF16 + */ + @Test + public void compatibleTest_wrapper_long_utf16() { + StringBuilder sb = new StringBuilder(); + sb.append("aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeefffffffffftest测试"); + StringBuilderWrapper sbw = new StringBuilderWrapper(); + sbw.setStringBuilder(sb); + sbw.setStr(sb.toString()); + + compatibleTest(sbw, factory, previousVersionFactory); + } + + protected byte[] doEncodeWithStringBuilder(Object data, SerializerFactory factory) + throws IOException { + os.reset(); + Hessian2Output out = new Hessian2Output(os); + out.setSerializerFactory(factory); + out.writeObject(data); + out.flush(); + + return os.toByteArray(); + } + + /* + *打印byte[],以逗号分隔 + */ + private static void printBytes(byte[] bytes) { + if (bytes == null || bytes.length == 0) { + System.out.println("null"); + return; + } + + for (int i = 0; i < bytes.length; i++) { + System.out.print(bytes[i] + ","); + } + System.out.println(); + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/io/java17/lang/AbstractStringBuilderTest.java b/src/test/java/com/caucho/hessian/io/java17/lang/AbstractStringBuilderTest.java new file mode 100644 index 0000000..ff64969 --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/java17/lang/AbstractStringBuilderTest.java @@ -0,0 +1,189 @@ +/* + * 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.lang; + +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; + +/** + * serialize test under same jdk + * @author junyuan + * @version Jdk17AbstractStringBuilderDecodeTest.java, v 0.1 2023年10月25日 11:41 junyuan Exp $ + */ +public class AbstractStringBuilderTest { + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + protected final static boolean isHigherThanJdk17 = isJava17(); + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + os = new ByteArrayOutputStream(); + } + + @Test + public void test_Ser_Des_wrapper() throws IOException { + StringBuilderWrapper sbw = buildStringBuilderWrapper("c12"); + + Object result = doEncodeNDecode(sbw, factory, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + @Test + public void test_Ser_Des_wrapper_utf16() throws IOException { + StringBuilderWrapper sbw = buildStringBuilderWrapper("c12测试"); + Object result = doEncodeNDecode(sbw, factory, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + @Test + public void test_Ser_Des() throws IOException { + StringBuilder sb = new StringBuilder(); + sb.append("c12"); + + Object result = doEncodeNDecode(sb, factory, factory); + + assertForStringBuilder(result, sb); + } + + @Test + public void test_Ser_Des_utf16() throws IOException { + StringBuilder sb = new StringBuilder(); + sb.append("c12测试"); + Object result = doEncodeNDecode(sb, factory, factory); + assertForStringBuilder(result, sb); + } + + @Test + public void test_Ser_Des_wrapper_long() throws IOException { + StringBuilderWrapper sbw = buildStringBuilderWrapper("100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc"); + + Object result = doEncodeNDecode(sbw, factory, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + @Test + public void test_Ser_Des_wrapper_utf16_long() throws IOException { + StringBuilderWrapper sbw = buildStringBuilderWrapper("100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc用来测试的数据"); + Object result = doEncodeNDecode(sbw, factory, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + @Test + public void test_Ser_Des_long() throws IOException { + StringBuilder sb = new StringBuilder(); + sb.append("100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc"); + + Object result = doEncodeNDecode(sb, factory, factory); + + assertForStringBuilder(result, sb); + } + + @Test + public void test_Ser_Des_utf16_long() throws IOException { + StringBuilder sb = new StringBuilder(); + sb.append("100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc用来测试的数据"); + Object result = doEncodeNDecode(sb, factory, factory); + assertForStringBuilder(result, sb); + } + + protected Object doEncodeNDecode(Object data, SerializerFactory ser, SerializerFactory des) throws IOException { + os.reset(); + Hessian2Output out = new Hessian2Output(os); + out.setSerializerFactory(ser); + out.writeObject(data); + out.flush(); + + byte[] res = os.toByteArray(); + + os.reset(); + ByteArrayInputStream is = new ByteArrayInputStream(res); + Hessian2Input input = new Hessian2Input(is); + input.setSerializerFactory(des); + return input.readObject(); + } + + private byte[] convertStringToByteArray(String str) { + String[] strArray = str.split(","); + byte[] byteArray = new byte[strArray.length]; + + for (int i = 0; i < strArray.length; i++) { + byteArray[i] = Byte.parseByte(strArray[i]); + } + + return byteArray; + } + + /** + * 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; + } + + /** + * StringBuilderWrapper with str and str builder as test data + * @param testData + * @return + */ + private StringBuilderWrapper buildStringBuilderWrapper(String testData) { + StringBuilder sb = new StringBuilder(); + sb.append(testData); + StringBuilderWrapper sbw = new StringBuilderWrapper(); + sbw.setStr(sb.toString()); + sbw.setStringBuilder(sb); + return sbw; + } + + /** + * + * @param result + * @param expected + */ + private void assertForStringBuilderWrapper(Object result, StringBuilderWrapper expected) { + Assert.assertTrue(result instanceof StringBuilderWrapper); + + StringBuilderWrapper sbwResult = (StringBuilderWrapper) result; + + Assert.assertEquals(expected.stringBuilder.toString(), sbwResult.stringBuilder.toString()); + Assert.assertEquals(expected.str, sbwResult.str); + } + + private void assertForStringBuilder(Object result, StringBuilder expected) { + Assert.assertTrue(result instanceof StringBuilder); + + StringBuilder sbResult = (StringBuilder) result; + + Assert.assertEquals(expected.toString(), sbResult.toString()); + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/io/java17/lang/Jdk17AbstractStringBuilderDecodeTest.java b/src/test/java/com/caucho/hessian/io/java17/lang/Jdk17AbstractStringBuilderDecodeTest.java new file mode 100644 index 0000000..5a8582f --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/java17/lang/Jdk17AbstractStringBuilderDecodeTest.java @@ -0,0 +1,242 @@ +/* + * 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.lang; + +import com.caucho.hessian.io.Hessian2Input; +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; + +/** + * may only work under jdk17 + * @author junyuan + * @version Jdk17AbstractStringBuilderDecodeTest.java, v 0.1 2023年10月25日 11:41 junyuan Exp $ + */ +public class Jdk17AbstractStringBuilderDecodeTest { + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + protected final static boolean lessThanJdk17 = !isJava17(); + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + os = new ByteArrayOutputStream(); + } + + @Test + public void test_Ser8_Des17_short() throws IOException { + if (lessThanJdk17) { + System.out.println("not jdk17, skip test"); + return; + } + + StringBuilder sb = new StringBuilder(); + sb.append("test"); + String testDataInHessianBytes = "79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-110,5,99,111,117,110,116,5,118,97,108,117,101,111,-112,-108,16,116,101,115,116,0,0,0,0,0,0,0,0,0,0,0,0"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + + Object result = doDecode(input, factory); + + assertForStringBuilder(result, sb); + } + + @Test + public void test_Ser8_Des17_long() throws IOException { + if (lessThanJdk17) { + System.out.println("not jdk17, skip test"); + return; + } + + StringBuilder sb = new StringBuilder(); + sb.append("100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc"); + String testDataInHessianBytes = "79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-110,5,99,111,117,110,116,5,118,97,108,117,101,111,-112,-56,78,83,0,78,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + + Object result = doDecode(input, factory); + + assertForStringBuilder(result, sb); + } + + @Test + public void test_Ser8_Des17_short_wrapper() throws IOException { + if (lessThanJdk17) { + System.out.println("not jdk17, skip test"); + return; + } + + StringBuilderWrapper sbw = buildStringBuilderWrapper("test"); + String testDataInHessianBytes = "79,-56,54,99,111,109,46,99,97,117,99,104,111,46,104,101,115,115,105,97,110,46,105,111,46,106,97,118,97,49,55,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,87,114,97,112,112,101,114,-110,13,115,116,114,105,110,103,66,117,105,108,100,101,114,3,115,116,114,111,-112,79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-110,5,99,111,117,110,116,5,118,97,108,117,101,111,-111,-108,16,116,101,115,116,0,0,0,0,0,0,0,0,0,0,0,0,4,116,101,115,116"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + + Object result = doDecode(input, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + @Test + public void test_Ser8_Des17_long_wrapper() throws IOException { + if (lessThanJdk17) { + System.out.println("not jdk17, skip test"); + return; + } + + StringBuilderWrapper sbw = buildStringBuilderWrapper("100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc"); + String testDataInHessianBytes = "79,-56,54,99,111,109,46,99,97,117,99,104,111,46,104,101,115,115,105,97,110,46,105,111,46,106,97,118,97,49,55,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,87,114,97,112,112,101,114,-110,13,115,116,114,105,110,103,66,117,105,108,100,101,114,3,115,116,114,111,-112,79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-110,5,99,111,117,110,116,5,118,97,108,117,101,111,-111,-56,78,83,0,78,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99,83,0,78,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + + Object result = doDecode(input, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + @Test + public void test_Ser8_Des17_short_utf16() throws IOException { + if (lessThanJdk17) { + System.out.println("not jdk17, skip test"); + return; + } + + StringBuilder sb = new StringBuilder(); + sb.append("test测试"); + String testDataInHessianBytes = "79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-110,5,99,111,117,110,116,5,118,97,108,117,101,111,-112,-106,16,116,101,115,116,-26,-75,-117,-24,-81,-107,0,0,0,0,0,0,0,0,0,0"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + + Object result = doDecode(input, factory); + + assertForStringBuilder(result, sb); + } + + @Test + public void test_Ser8_Des17_long_utf16() throws IOException { + if (lessThanJdk17) { + System.out.println("not jdk17, skip test"); + return; + } + + StringBuilder sb = new StringBuilder(); + sb.append("100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc测试用的长数据"); + String testDataInHessianBytes = "79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-110,5,99,111,117,110,116,5,118,97,108,117,101,111,-112,-56,85,83,0,85,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99,-26,-75,-117,-24,-81,-107,-25,-108,-88,-25,-102,-124,-23,-107,-65,-26,-107,-80,-26,-115,-82"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + + Object result = doDecode(input, factory); + + assertForStringBuilder(result, sb); + } + + @Test + public void test_Ser8_Des17_short_wrapper_utf16() throws IOException { + if (lessThanJdk17) { + System.out.println("not jdk17, skip test"); + return; + } + + StringBuilderWrapper sbw = buildStringBuilderWrapper("test测试"); + String testDataInHessianBytes = "79,-56,54,99,111,109,46,99,97,117,99,104,111,46,104,101,115,115,105,97,110,46,105,111,46,106,97,118,97,49,55,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,87,114,97,112,112,101,114,-110,13,115,116,114,105,110,103,66,117,105,108,100,101,114,3,115,116,114,111,-112,79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-110,5,99,111,117,110,116,5,118,97,108,117,101,111,-111,-106,16,116,101,115,116,-26,-75,-117,-24,-81,-107,0,0,0,0,0,0,0,0,0,0,6,116,101,115,116,-26,-75,-117,-24,-81,-107"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + + Object result = doDecode(input, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + @Test + public void test_Ser8_Des17_long_wrapper_utf16() throws IOException { + if (lessThanJdk17) { + System.out.println("not jdk17, skip test"); + return; + } + + StringBuilderWrapper sbw = buildStringBuilderWrapper("100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc测试用的长数据"); + String testDataInHessianBytes = "79,-56,54,99,111,109,46,99,97,117,99,104,111,46,104,101,115,115,105,97,110,46,105,111,46,106,97,118,97,49,55,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,87,114,97,112,112,101,114,-110,13,115,116,114,105,110,103,66,117,105,108,100,101,114,3,115,116,114,111,-112,79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-110,5,99,111,117,110,116,5,118,97,108,117,101,111,-111,-56,85,83,0,85,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99,-26,-75,-117,-24,-81,-107,-25,-108,-88,-25,-102,-124,-23,-107,-65,-26,-107,-80,-26,-115,-82,83,0,85,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99,-26,-75,-117,-24,-81,-107,-25,-108,-88,-25,-102,-124,-23,-107,-65,-26,-107,-80,-26,-115,-82"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + + Object result = doDecode(input, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + protected Object doDecode(byte[] data, SerializerFactory factory) throws IOException { + os.reset(); + ByteArrayInputStream is = new ByteArrayInputStream(data); + Hessian2Input input = new Hessian2Input(is); + input.setSerializerFactory(factory); + return input.readObject(); + } + + private byte[] convertStringToByteArray(String str) { + String[] strArray = str.split(","); + byte[] byteArray = new byte[strArray.length]; + + for (int i = 0; i < strArray.length; i++) { + byteArray[i] = Byte.parseByte(strArray[i]); + } + + return byteArray; + } + + /** + * 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; + } + + /** + * StringBuilderWrapper with str and str builder as test data + * @param testData + * @return + */ + private StringBuilderWrapper buildStringBuilderWrapper(String testData) { + StringBuilder sb = new StringBuilder(); + sb.append(testData); + StringBuilderWrapper sbw = new StringBuilderWrapper(); + sbw.setStr(sb.toString()); + sbw.setStringBuilder(sb); + return sbw; + } + + /** + * + * @param result + * @param expected + */ + private void assertForStringBuilderWrapper(Object result, StringBuilderWrapper expected) { + Assert.assertTrue(result instanceof StringBuilderWrapper); + + StringBuilderWrapper sbwResult = (StringBuilderWrapper) result; + + Assert.assertEquals(expected.stringBuilder.toString(), sbwResult.stringBuilder.toString()); + Assert.assertEquals(expected.str, sbwResult.str); + } + + private void assertForStringBuilder(Object result, StringBuilder expected) { + Assert.assertTrue(result instanceof StringBuilder); + + StringBuilder sbResult = (StringBuilder) result; + + Assert.assertEquals(expected.toString(), sbResult.toString()); + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/io/java17/lang/Jdk8AbstractStringBuilderDecodeTest.java b/src/test/java/com/caucho/hessian/io/java17/lang/Jdk8AbstractStringBuilderDecodeTest.java new file mode 100644 index 0000000..f8fc83e --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/java17/lang/Jdk8AbstractStringBuilderDecodeTest.java @@ -0,0 +1,220 @@ +/* + * 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.lang; + +import com.caucho.hessian.io.Hessian2Input; +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; + +/** + * decode test + * data encoded with hessian in jdk17 + * + * 确保 jdk17 下的序列化结果能被 jdk8 以及 jdk17 下的反序列化器正确解析 + * + * @author junyuan + * @version Jdk8AbstractStringBuilderDecodeTest.java, v 0.1 2023年11月10日 14:09 junyuan Exp $ + */ +public class Jdk8AbstractStringBuilderDecodeTest { + + private static SerializerFactory factory; + private static ByteArrayOutputStream os; + + @BeforeClass + public static void setUp() { + factory = new SerializerFactory(); + + os = new ByteArrayOutputStream(); + } + + @Test + public void test_Ser17_Des8_short() throws IOException { + String testData = "c12"; + String testDataInHessianBytes = "79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-109,5,99,111,100,101,114,5,99,111,117,110,116,5,118,97,108,117,101,111,-112,-112,-109,16,99,49,50"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + Object result = doDecode(input, factory); + + Assert.assertTrue(result instanceof StringBuilder); + + StringBuilder sbResult = (StringBuilder) result; + + Assert.assertEquals(testData, sbResult.toString()); + } + + /** + * input byte[] from jdk 17 + * @throws IOException + */ + @Test + public void test_Ser17_Des8_Long() throws IOException { + String testData = "100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc"; + String testDataInHessianBytes = "79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-109,5,99,111,100,101,114,5,99,111,117,110,116,5,118,97,108,117,101,111,-112,-112,-56,78,83,0,78,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + Object result = doDecode(input, factory); + + Assert.assertTrue(result instanceof StringBuilder); + + StringBuilder sbResult = (StringBuilder) result; + + Assert.assertEquals(testData, sbResult.toString()); + } + + /** + * input byte[] from jdk 17 + * complex data structure + * @throws IOException + */ + @Test + public void test_Wrapper_Ser17_Des8_Short() throws IOException { + String testData = "c12"; + StringBuilderWrapper sbw = buildStringBuilderWrapper(testData); + + String testDataInHessianBytes = "79,-56,54,99,111,109,46,99,97,117,99,104,111,46,104,101,115,115,105,97,110,46,105,111,46,106,97,118,97,49,55,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,87,114,97,112,112,101,114,-110,13,115,116,114,105,110,103,66,117,105,108,100,101,114,3,115,116,114,111,-112,79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-109,5,99,111,100,101,114,5,99,111,117,110,116,5,118,97,108,117,101,111,-111,-112,-109,16,99,49,50,0,0,0,0,0,0,0,0,0,0,0,0,0,3,99,49,50"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + + Object result = doDecode(input, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + @Test + public void test_Wrapper_Ser17_Des8_long() throws IOException { + String testData = "100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc"; + StringBuilderWrapper sbw = buildStringBuilderWrapper(testData); + + String testDataInHessianBytes = "79,-56,54,99,111,109,46,99,97,117,99,104,111,46,104,101,115,115,105,97,110,46,105,111,46,106,97,118,97,49,55,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,87,114,97,112,112,101,114,-110,13,115,116,114,105,110,103,66,117,105,108,100,101,114,3,115,116,114,111,-112,79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-109,5,99,111,100,101,114,5,99,111,117,110,116,5,118,97,108,117,101,111,-111,-112,-56,78,83,0,78,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99,83,0,78,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + + Object result = doDecode(input, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + @Test + public void test_Ser17_Des8_short_utf16() throws IOException { + String testData = "c12测试"; + String testDataInHessianBytes = "79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-109,5,99,111,100,101,114,5,99,111,117,110,116,5,118,97,108,117,101,111,-112,-111,-107,16,99,49,50,-26,-75,-117,-24,-81,-107,0,0,0,0,0,0,0,0,0,0,0"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + Object result = doDecode(input, factory); + + Assert.assertTrue(result instanceof StringBuilder); + + StringBuilder sbResult = (StringBuilder) result; + + Assert.assertEquals(testData, sbResult.toString()); + } + + /** + * input byte[] from jdk 17 + * @throws IOException + */ + @Test + public void test_Ser17_Des8_Long_utf16() throws IOException { + String testData = "100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc用来测试的数据"; + String testDataInHessianBytes = "79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-109,5,99,111,100,101,114,5,99,111,117,110,116,5,118,97,108,117,101,111,-112,-111,-56,85,83,0,85,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99,-25,-108,-88,-26,-99,-91,-26,-75,-117,-24,-81,-107,-25,-102,-124,-26,-107,-80,-26,-115,-82"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + Object result = doDecode(input, factory); + + Assert.assertTrue(result instanceof StringBuilder); + + StringBuilder sbResult = (StringBuilder) result; + + Assert.assertEquals(testData, sbResult.toString()); + } + + @Test + public void test_Ser17_Des8_short_wrapper_utf16() throws IOException { + String testData = "c12测试"; + StringBuilderWrapper sbw = buildStringBuilderWrapper(testData); + + String testDataInHessianBytes = "79,-56,54,99,111,109,46,99,97,117,99,104,111,46,104,101,115,115,105,97,110,46,105,111,46,106,97,118,97,49,55,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,87,114,97,112,112,101,114,-110,13,115,116,114,105,110,103,66,117,105,108,100,101,114,3,115,116,114,111,-112,79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-109,5,99,111,100,101,114,5,99,111,117,110,116,5,118,97,108,117,101,111,-111,-111,-107,16,99,49,50,-26,-75,-117,-24,-81,-107,0,0,0,0,0,0,0,0,0,0,0,5,99,49,50,-26,-75,-117,-24,-81,-107"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + Object result = doDecode(input, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + /** + * input byte[] from jdk 17 + * @throws IOException + */ + @Test + public void test_Ser17_Des8_Long_wrapper_utf16() throws IOException { + String testData = "100000000020000000003000000000400000000050000000006000000000aaaaaabbbbbbcccccc用来测试的数据"; + StringBuilderWrapper sbw = buildStringBuilderWrapper(testData); + + String testDataInHessianBytes = "79,-56,54,99,111,109,46,99,97,117,99,104,111,46,104,101,115,115,105,97,110,46,105,111,46,106,97,118,97,49,55,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,87,114,97,112,112,101,114,-110,13,115,116,114,105,110,103,66,117,105,108,100,101,114,3,115,116,114,111,-112,79,-89,106,97,118,97,46,108,97,110,103,46,83,116,114,105,110,103,66,117,105,108,100,101,114,-109,5,99,111,100,101,114,5,99,111,117,110,116,5,118,97,108,117,101,111,-111,-111,-56,85,83,0,85,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99,-25,-108,-88,-26,-99,-91,-26,-75,-117,-24,-81,-107,-25,-102,-124,-26,-107,-80,-26,-115,-82,83,0,85,49,48,48,48,48,48,48,48,48,48,50,48,48,48,48,48,48,48,48,48,51,48,48,48,48,48,48,48,48,48,52,48,48,48,48,48,48,48,48,48,53,48,48,48,48,48,48,48,48,48,54,48,48,48,48,48,48,48,48,48,97,97,97,97,97,97,98,98,98,98,98,98,99,99,99,99,99,99,-25,-108,-88,-26,-99,-91,-26,-75,-117,-24,-81,-107,-25,-102,-124,-26,-107,-80,-26,-115,-82"; + byte[] input = convertStringToByteArray(testDataInHessianBytes); + Object result = doDecode(input, factory); + + assertForStringBuilderWrapper(result, sbw); + } + + protected Object doDecode(byte[] data, SerializerFactory factory) throws IOException { + os.reset(); + ByteArrayInputStream is = new ByteArrayInputStream(data); + Hessian2Input input = new Hessian2Input(is); + input.setSerializerFactory(factory); + return input.readObject(); + } + + private byte[] convertStringToByteArray(String str) { + String[] strArray = str.split(","); + byte[] byteArray = new byte[strArray.length]; + + for (int i = 0; i < strArray.length; i++) { + byteArray[i] = Byte.parseByte(strArray[i]); + } + + return byteArray; + + } + + /** + * StringBuilderWrapper with str and str builder as test data + * @param testData + * @return + */ + private StringBuilderWrapper buildStringBuilderWrapper(String testData) { + StringBuilder sb = new StringBuilder(); + sb.append(testData); + StringBuilderWrapper sbw = new StringBuilderWrapper(); + sbw.setStr(sb.toString()); + sbw.setStringBuilder(sb); + return sbw; + } + + /** + * + * @param result + * @param expected + */ + private void assertForStringBuilderWrapper(Object result, StringBuilderWrapper expected) { + Assert.assertTrue(result instanceof StringBuilderWrapper); + + StringBuilderWrapper sbwResult = (StringBuilderWrapper) result; + + Assert.assertEquals(expected.stringBuilder.toString(), sbwResult.stringBuilder.toString()); + Assert.assertEquals(expected.str, sbwResult.str); + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderJDK8SerializeFactory.java b/src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderJDK8SerializeFactory.java new file mode 100644 index 0000000..17ae1aa --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderJDK8SerializeFactory.java @@ -0,0 +1,54 @@ +/* + * 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.lang; + +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; + +/** + * + * @author junyuan + * @version StringBuilderJDK8SerializeFactory.java, v 0.1 2023年10月24日 16:52 junyuan Exp $ + */ +public class StringBuilderJDK8SerializeFactory extends SerializerFactory { + @Override + public Serializer getSerializer(Class cl) throws HessianProtocolException { + Serializer serializer = super.getSerializer(cl); + + if (StringBuilder.class.isAssignableFrom(cl) || StringBuffer.class.isAssignableFrom(cl)) { + serializer = new JavaSerializer(cl); + } + + return serializer; + } + + @Override + public Deserializer getDeserializer(Class cl) throws HessianProtocolException { + Deserializer deserializer = super.getDeserializer(cl); + + if (StringBuilder.class.isAssignableFrom(cl) || StringBuffer.class.isAssignableFrom(cl)) { + deserializer = new JavaDeserializer(cl); + } + + _cachedDeserializerMap.put(cl, deserializer); + return deserializer; + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderJavaSerializeFactory.java b/src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderJavaSerializeFactory.java new file mode 100644 index 0000000..b4cc05e --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderJavaSerializeFactory.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.lang; + +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; + +/** + * java serializer only + * @author junyuan + * @version StringBuilderJavaSerializeFactory.java, v 0.1 2023年11月10日 14:43 junyuan Exp $ + */ +public class StringBuilderJavaSerializeFactory extends SerializerFactory { + @Override + public Serializer getSerializer(Class cl) throws HessianProtocolException { + Serializer serializer = super.getSerializer(cl); + + if (StringBuilder.class.isAssignableFrom(cl) || StringBuffer.class.isAssignableFrom(cl)) { + serializer = new JavaSerializer(cl); + } + + return serializer; + } + + @Override + public Deserializer getDeserializer(Class cl) throws HessianProtocolException { + Deserializer deserializer = super.getDeserializer(cl); + + if (StringBuilder.class.isAssignableFrom(cl) || StringBuffer.class.isAssignableFrom(cl)) { + deserializer = new JavaDeserializer(cl); + } + + return deserializer; + } +} \ No newline at end of file diff --git a/src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderWrapper.java b/src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderWrapper.java new file mode 100644 index 0000000..10f371b --- /dev/null +++ b/src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderWrapper.java @@ -0,0 +1,66 @@ +/* + * 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.lang; + +import java.io.Serializable; + +/** + * + * @author junyuan + * @version StringBuilderWrapper.java, v 0.1 2023年10月23日 16:59 junyuan Exp $ + */ +public class StringBuilderWrapper implements Serializable { + StringBuilder stringBuilder; + + String str; + + /** + * Getter method for property stringBuilder. + * + * @return property value of stringBuilder + */ + public StringBuilder getStringBuilder() { + return stringBuilder; + } + + /** + * Setter method for property stringBuilder. + * + * @param stringBuilder value to be assigned to property stringBuilder + */ + public void setStringBuilder(StringBuilder stringBuilder) { + this.stringBuilder = stringBuilder; + } + + /** + * Getter method for property str. + * + * @return property value of str + */ + public String getStr() { + return str; + } + + /** + * Setter method for property str. + * + * @param str value to be assigned to property str + */ + public void setStr(String str) { + this.str = str; + } +} \ No newline at end of file