Skip to content

Commit 818d8fb

Browse files
committed
#210 fix hessian JavaDeserializer constructor(null...) NPE
Example: org.springframework.jdbc.UncategorizedSQLException org.springframework.beans.MethodInvocationException com.alibaba.com.caucho.hessian.io.HessianProtocolException: 'com.alibaba.com.caucho.hessian.io.beans.ConstructNPE' could not be instantiated at com.alibaba.com.caucho.hessian.io.JavaDeserializer.instantiate(JavaDeserializer.java:313) Caused by: java.lang.reflect.InvocationTargetException at com.alibaba.com.caucho.hessian.io.JavaDeserializer.instantiate(JavaDeserializer.java:309) Caused by: java.lang.NullPointerException at com.alibaba.com.caucho.hessian.io.beans.ConstructNPE.<init>(ConstructNPE.java:15)
1 parent 4d885b8 commit 818d8fb

File tree

4 files changed

+150
-4
lines changed

4 files changed

+150
-4
lines changed

hessian-lite/src/main/java/com/alibaba/com/caucho/hessian/io/JavaDeserializer.java

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,36 @@
6767
public class JavaDeserializer extends AbstractMapDeserializer {
6868
private static final Logger log
6969
= Logger.getLogger(JavaDeserializer.class.getName());
70+
private static Object reflectionFactory;
71+
private static Method newConstructorForSerializationMethod;
72+
private static Constructor<Object> objConstructor;
73+
static {
74+
try {
75+
objConstructor = Object.class.getConstructor(new Class<?>[0]);
76+
77+
Class<?> factoryClass = Class.forName("sun.reflect.ReflectionFactory");
78+
Method getReflectionFactory = factoryClass.getDeclaredMethod("getReflectionFactory");
79+
if (!getReflectionFactory.isAccessible()) getReflectionFactory.setAccessible(true);
80+
reflectionFactory = getReflectionFactory.invoke(factoryClass);
81+
82+
newConstructorForSerializationMethod = factoryClass.getDeclaredMethod(
83+
"newConstructorForSerialization", Class.class, Constructor.class);
84+
if (!newConstructorForSerializationMethod.isAccessible()) {
85+
newConstructorForSerializationMethod.setAccessible(true);
86+
}
87+
} catch (Throwable e) {
88+
log.finest("Compatible constructor not supported for this JVM: "
89+
+ System.getProperty("java.vm.name"));
90+
}
91+
}
7092

7193
private Class _type;
7294
private HashMap _fieldMap;
7395
private Method _readResolve;
7496
private Constructor _constructor;
7597
private Object[] _constructorArgs;
98+
private boolean compatibleConstructNPE = newConstructorForSerializationMethod != null;
99+
private Constructor<?> compatibleConstructor;
76100

77101
public JavaDeserializer(Class cl) {
78102
_type = cl;
@@ -305,15 +329,41 @@ private Object resolve(Object obj)
305329
protected Object instantiate()
306330
throws Exception {
307331
try {
308-
if (_constructor != null)
309-
return _constructor.newInstance(_constructorArgs);
310-
else
311-
return _type.newInstance();
332+
return _constructor == null ? _type.newInstance() : construct();
312333
} catch (Exception e) {
313334
throw new HessianProtocolException("'" + _type.getName() + "' could not be instantiated", e);
314335
}
315336
}
316337

338+
protected Object construct() throws Exception {
339+
if (compatibleConstructor != null) return compatibleConstructor.newInstance();
340+
341+
InvocationTargetException ex;
342+
try {
343+
return _constructor.newInstance(_constructorArgs);
344+
} catch (InvocationTargetException e) {
345+
if (compatibleConstructNPE && e.getTargetException() instanceof NullPointerException) {
346+
ex = e;
347+
} else {
348+
throw e;
349+
}
350+
}
351+
352+
Constructor<?> ctor;
353+
try {
354+
ctor = (Constructor<?>) newConstructorForSerializationMethod.invoke(reflectionFactory, _type,
355+
objConstructor);
356+
if (!ctor.isAccessible()) ctor.setAccessible(true);
357+
Object obj = ctor.newInstance();
358+
compatibleConstructor = ctor;
359+
return obj;
360+
} catch (Throwable t) {
361+
log.finest(_type + " compatible construct for NullPointerException fail: " + t);
362+
}
363+
compatibleConstructNPE = false;
364+
throw ex;
365+
}
366+
317367
/**
318368
* Creates a map of the classes fields.
319369
*/
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.alibaba.com.caucho.hessian.io;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNotNull;
5+
6+
import java.beans.PropertyChangeEvent;
7+
import java.sql.SQLException;
8+
9+
import org.junit.Test;
10+
11+
import com.alibaba.com.caucho.hessian.io.base.SerializeTestBase;
12+
import com.alibaba.com.caucho.hessian.io.beans.ConstructNPE;
13+
14+
15+
public class JavaDeserializerTest extends SerializeTestBase {
16+
17+
/**
18+
* <a href="https://github.com/apache/incubator-dubbo/issues/210">#210</a>
19+
* @see org.springframework.jdbc.UncategorizedSQLException
20+
* @see org.springframework.beans.PropertyAccessException
21+
*/
22+
@Test
23+
public void testConstructorNPE() throws Exception {
24+
String sql = "select * from demo";
25+
SQLException sqlEx = new SQLException("just a sql exception");
26+
PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(new Object(), "name", "old", "new");
27+
ConstructNPE normalNPE = new ConstructNPE("junit", sql, sqlEx, propertyChangeEvent);
28+
29+
for (int repeat = 0; repeat < 2; repeat++) {
30+
assertDesEquals(normalNPE, baseHession2Serialize(normalNPE));
31+
assertCompatibleConstructNPE(factory.getDeserializer(normalNPE.getClass()), true);
32+
}
33+
}
34+
35+
private void assertDesEquals(ConstructNPE expected, ConstructNPE actual) {
36+
assertEquals(expected.getMessage(), actual.getMessage());
37+
assertEquals(expected.getCause().getClass(), actual.getCause().getClass());
38+
assertEquals(expected.getSql(), actual.getSql());
39+
}
40+
41+
private void assertCompatibleConstructNPE(Deserializer deserializer, boolean compatible) throws Exception {
42+
assertEquals(JavaDeserializer.class, deserializer.getClass());
43+
assertEquals(compatible, getFieldValue(deserializer, "compatibleConstructNPE"));
44+
if (compatible) assertNotNull(getFieldValue(deserializer, "compatibleConstructor"));
45+
}
46+
}

hessian-lite/src/test/java/com/alibaba/com/caucho/hessian/io/base/SerializeTestBase.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,20 @@
44
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
55
import com.alibaba.com.caucho.hessian.io.HessianInput;
66
import com.alibaba.com.caucho.hessian.io.HessianOutput;
7+
import com.alibaba.com.caucho.hessian.io.SerializerFactory;
78

89
import java.io.ByteArrayInputStream;
910
import java.io.ByteArrayOutputStream;
1011
import java.io.IOException;
12+
import java.lang.reflect.Field;
1113

1214
/**
1315
* hession base serialize utils
1416
*
1517
*/
1618
public class SerializeTestBase {
19+
protected SerializerFactory factory = new SerializerFactory();
20+
1721
/**
1822
* hession serialize util
1923
*
@@ -22,6 +26,7 @@ public class SerializeTestBase {
2226
* @return
2327
* @throws IOException
2428
*/
29+
@SuppressWarnings("unchecked")
2530
protected <T> T baseHessionSerialize(T data) throws IOException {
2631
ByteArrayOutputStream bout = new ByteArrayOutputStream();
2732
HessianOutput out = new HessianOutput(bout);
@@ -31,6 +36,7 @@ protected <T> T baseHessionSerialize(T data) throws IOException {
3136

3237
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
3338
HessianInput input = new HessianInput(bin);
39+
input.setSerializerFactory(factory);
3440
return (T) input.readObject();
3541
}
3642

@@ -42,6 +48,7 @@ protected <T> T baseHessionSerialize(T data) throws IOException {
4248
* @return
4349
* @throws IOException
4450
*/
51+
@SuppressWarnings("unchecked")
4552
protected <T> T baseHession2Serialize(T data) throws IOException {
4653
ByteArrayOutputStream bout = new ByteArrayOutputStream();
4754
Hessian2Output out = new Hessian2Output(bout);
@@ -51,6 +58,14 @@ protected <T> T baseHession2Serialize(T data) throws IOException {
5158

5259
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
5360
Hessian2Input input = new Hessian2Input(bin);
61+
input.setSerializerFactory(factory);
5462
return (T) input.readObject();
5563
}
64+
65+
@SuppressWarnings("unchecked")
66+
protected <T> T getFieldValue(Object bean, String fieldName) throws Exception {
67+
Field field = bean.getClass().getDeclaredField(fieldName);
68+
if (!field.isAccessible()) field.setAccessible(true);
69+
return (T) field.get(bean);
70+
}
5671
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.alibaba.com.caucho.hessian.io.beans;
2+
3+
import java.beans.PropertyChangeEvent;
4+
import java.sql.SQLException;
5+
6+
/**
7+
* <a href="https://github.com/apache/incubator-dubbo/issues/210">#210</a>
8+
* @see org.springframework.jdbc.UncategorizedSQLException
9+
* @see org.springframework.beans.PropertyAccessException
10+
*/
11+
public class ConstructNPE extends RuntimeException {
12+
private static final long serialVersionUID = 1L;
13+
private final String sql;
14+
private transient PropertyChangeEvent propertyChangeEvent;
15+
16+
public ConstructNPE(String task, String sql, SQLException ex, PropertyChangeEvent propertyChangeEvent) {
17+
super(task + "; uncategorized SQLException for SQL [" + sql + "]; SQL state ["
18+
+ ex.getSQLState() + "]; error code [" + ex.getErrorCode() + "]; " + ex.getMessage()
19+
+ propertyChangeEvent.getPropertyName(), ex);
20+
this.sql = sql;
21+
this.propertyChangeEvent = propertyChangeEvent;
22+
}
23+
24+
public SQLException getSQLException() {
25+
return (SQLException) getCause();
26+
}
27+
28+
public String getSql() {
29+
return this.sql;
30+
}
31+
32+
public PropertyChangeEvent getPropertyChangeEvent() {
33+
return this.propertyChangeEvent;
34+
}
35+
}

0 commit comments

Comments
 (0)