Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: sofastack/sofa-hessian
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.5.0
Choose a base ref
...
head repository: sofastack/sofa-hessian
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.5.2
Choose a head ref
  • 6 commits
  • 20 files changed
  • 2 contributors

Commits on Sep 16, 2023

  1. Fix/throwable (#102)

    * fix: when ref works, ref to the fake self ref problem
    
    * fix: use null as self ref
    
    * format
    
    * chore: comment
    
    ---------
    
    Co-authored-by: lo1nt <[email protected]>
    Lo1nt and lo1nt authored Sep 16, 2023
    Copy the full SHA
    18d7299 View commit details
  2. use reflect on throwable if possible (#103)

    Co-authored-by: lo1nt <[email protected]>
    Lo1nt and lo1nt authored Sep 16, 2023
    Copy the full SHA
    fdbd4b1 View commit details

Commits on Sep 17, 2023

  1. release 3.5.1 (#104)

    Co-authored-by: lo1nt <[email protected]>
    Lo1nt and lo1nt authored Sep 17, 2023
    Copy the full SHA
    4d958a2 View commit details

Commits on Nov 9, 2023

  1. Fix/throw compatible (#108)

    * fix: stack trace
    
    * format
    
    * fix:
    
    ---------
    
    Co-authored-by: lo1nt <[email protected]>
    Lo1nt and lo1nt authored Nov 9, 2023
    Copy the full SHA
    8158da2 View commit details

Commits on Nov 10, 2023

  1. Feat/sb compatible (#107)

    * feat: add compatible support on string builder
    
    * add test case
    
    * feat: add test for jdk 17
    
    * format
    
    * chore log
    
    * fix: only use when value is bytes
    
    * fix: utf 16 support
    
    * chore: optimise import
    
    * fix: test
    
    * fix: log
    
    * fix: des actives only when value is bytes
    
    ---------
    
    Co-authored-by: lo1nt <[email protected]>
    Lo1nt and lo1nt authored Nov 10, 2023
    Copy the full SHA
    f35a6aa View commit details
  2. version 3.5.2 (#109)

    Co-authored-by: lo1nt <[email protected]>
    Lo1nt and lo1nt authored Nov 10, 2023
    Copy the full SHA
    da95611 View commit details
Showing with 1,482 additions and 30 deletions.
  1. +1 −1 pom.xml
  2. +12 −2 src/main/java/com/caucho/hessian/io/AbstractFieldAdaptorSerializer.java
  3. +164 −0 src/main/java/com/caucho/hessian/io/AbstractStringBuilderDeserializer.java
  4. +111 −0 src/main/java/com/caucho/hessian/io/AbstractStringBuilderSerializer.java
  5. +20 −0 src/main/java/com/caucho/hessian/io/SerializerFactory.java
  6. +5 −1 src/main/java/com/caucho/hessian/io/throwable/StackTraceElementDeserializer.java
  7. +12 −2 src/main/java/com/caucho/hessian/io/throwable/StackTraceElementSerializer.java
  8. +20 −21 src/main/java/com/caucho/hessian/io/throwable/ThrowableDeserializer.java
  9. +16 −3 src/main/java/com/caucho/hessian/io/throwable/ThrowableHelper.java
  10. +2 −0 src/main/java/com/caucho/hessian/io/throwable/ThrowableSerializer.java
  11. +166 −0 src/test/java/com/caucho/hessian/io/java17/lang/AbstractStringBuilderEncodeTest.java
  12. +189 −0 src/test/java/com/caucho/hessian/io/java17/lang/AbstractStringBuilderTest.java
  13. +242 −0 src/test/java/com/caucho/hessian/io/java17/lang/Jdk17AbstractStringBuilderDecodeTest.java
  14. +220 −0 src/test/java/com/caucho/hessian/io/java17/lang/Jdk8AbstractStringBuilderDecodeTest.java
  15. +54 −0 src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderJDK8SerializeFactory.java
  16. +53 −0 src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderJavaSerializeFactory.java
  17. +66 −0 src/test/java/com/caucho/hessian/io/java17/lang/StringBuilderWrapper.java
  18. +4 −0 src/test/java/com/caucho/hessian/io/throwable/SelfDefinedException.java
  19. +45 −0 src/test/java/com/caucho/hessian/io/throwable/SerializeCompatibleTest.java
  20. +80 −0 src/test/java/com/caucho/hessian/io/throwable/ThrowableJdk17DeserializeTest.java
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@

<groupId>com.alipay.sofa</groupId>
<artifactId>hessian</artifactId>
<version>3.5.0</version>
<version>3.5.2</version>
<packaging>jar</packaging>

<name>${project.groupId}:${project.artifactId}</name>
Original file line number Diff line number Diff line change
@@ -93,17 +93,27 @@ public void writeInstance(Object obj, AbstractHessianOutput out)
* @return
*/
protected Field[] getFieldsForSerialize(Class cl) {
List<Field> fields = new ArrayList<Field>();
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++) {
Field field = originFields[i];
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<Field> fields = new ArrayList<Field>();
fields.addAll(primitiveFields);
fields.addAll(compoundFields);
return fields.toArray(new Field[0]);
}
}
Original file line number Diff line number Diff line change
@@ -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) {

}
}
}

}
Original file line number Diff line number Diff line change
@@ -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;
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/caucho/hessian/io/SerializerFactory.java
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
@@ -114,7 +114,11 @@ public Object readObject(AbstractHessianInput in, String[] fieldNames) throws IO
String name = fieldNames[i];
Field field = _fields.get(name);

if (String.class.equals(field.getType())) {
// _fields 是基于当前加载的类的成员变量来创建的
// 如果出现当前不存在的属性也需要读出来, 但是不会使用
if (field == null) {
fieldValueMap.put(name, in.readObject());
} else if (String.class.equals(field.getType())) {
fieldValueMap.put(name, in.readString());
} else if (int.class.equals(field.getType())) {
fieldValueMap.put(name, in.readInt());
Original file line number Diff line number Diff line change
@@ -90,7 +90,8 @@ protected void serializeField(AbstractHessianOutput out, Object obj, Field field

@Override
protected Field[] getFieldsForSerialize(Class cl) {
List<Field> fields = new ArrayList<Field>();
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<Field> fields = new ArrayList<Field>();
fields.addAll(primitiveFields);
fields.addAll(compoundFields);
return fields.toArray(new Field[0]);
}
}
Loading