Skip to content

Commit

Permalink
Feat/sb compatible (#107)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
Lo1nt and lo1nt authored Nov 10, 2023
1 parent 8158da2 commit f35a6aa
Show file tree
Hide file tree
Showing 12 changed files with 1,309 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -707,6 +707,8 @@ protected static void addBasic(Class cl, String typeName, int type)
addCurrencySupport();
}

addAbstractStringBuilderSupport();

}

/**
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand All @@ -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

0 comments on commit f35a6aa

Please sign in to comment.