Skip to content

Commit d1890d5

Browse files
committed
#210 fix hessian JavaDeserializer constructor(null...) NPE
Example: org.springframework.jdbc.UncategorizedSQLException 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 1e4ff6f commit d1890d5

File tree

5 files changed

+185
-4
lines changed

5 files changed

+185
-4
lines changed

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

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public class JavaDeserializer extends AbstractMapDeserializer {
6969
private Method _readResolve;
7070
private Constructor _constructor;
7171
private Object[] _constructorArgs;
72+
private boolean compatibleConstructNPE = true;
7273

7374
public JavaDeserializer(Class cl) {
7475
_type = cl;
@@ -301,15 +302,51 @@ private Object resolve(Object obj)
301302
protected Object instantiate()
302303
throws Exception {
303304
try {
304-
if (_constructor != null)
305-
return _constructor.newInstance(_constructorArgs);
306-
else
307-
return _type.newInstance();
305+
return _constructor == null ? _type.newInstance() : construct();
308306
} catch (Exception e) {
309307
throw new HessianProtocolException("'" + _type.getName() + "' could not be instantiated", e);
310308
}
311309
}
312310

311+
@SuppressWarnings({ "rawtypes", "unchecked" })
312+
protected Object construct() throws Exception {
313+
InvocationTargetException ex;
314+
try {
315+
return _constructor.newInstance(_constructorArgs);
316+
} catch (InvocationTargetException e) {
317+
if (!compatibleConstructNPE) throw e;
318+
319+
if (e.getTargetException() instanceof NullPointerException) {
320+
ex = e;
321+
} else {
322+
throw e;
323+
}
324+
}
325+
326+
Class[] types = _constructor.getParameterTypes();
327+
Object[] args = new Object[types.length];
328+
System.arraycopy(_constructorArgs, 0, args, 0, types.length);
329+
try {
330+
for (int i = 0; i < types.length; i++) {
331+
if (args[i] == null) {
332+
try {
333+
Constructor ctor = types[i].getDeclaredConstructor(new Class[0]);
334+
if (!ctor.isAccessible()) ctor.setAccessible(true);
335+
args[i] = ctor.newInstance(new Object[0]);
336+
} catch (Exception e) {
337+
}
338+
}
339+
}
340+
Object ret = _constructor.newInstance(args);
341+
_constructorArgs = args;
342+
return ret;
343+
} catch (Throwable t) {
344+
}
345+
346+
compatibleConstructNPE = false;
347+
throw ex;
348+
}
349+
313350
/**
314351
* Creates a map of the classes fields.
315352
*/
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.alibaba.com.caucho.hessian.io;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNotNull;
5+
import static org.junit.Assert.assertNull;
6+
import static org.junit.Assert.fail;
7+
8+
import java.lang.reflect.InvocationTargetException;
9+
import java.sql.SQLException;
10+
11+
import org.junit.Test;
12+
13+
import com.alibaba.com.caucho.hessian.io.base.SerializeTestBase;
14+
import com.alibaba.com.caucho.hessian.io.beans.ConstructAlwaysNPE;
15+
import com.alibaba.com.caucho.hessian.io.beans.ConstructNPE;
16+
17+
public class JavaDeserializerTest extends SerializeTestBase {
18+
19+
/**
20+
* <a href="https://github.com/apache/incubator-dubbo/issues/210">#210</a>
21+
* @see org.springframework.jdbc.UncategorizedSQLException
22+
*/
23+
@Test
24+
public void testConstructorNPE() throws Exception {
25+
String sql = "select * from demo";
26+
SQLException sqlEx = new SQLException("just a sql exception");
27+
28+
for (int repeat = 0; repeat < 2; repeat++) {
29+
ConstructNPE normalNPE = new ConstructNPE("junit", sql, sqlEx);
30+
assertDesEquals(normalNPE, baseHession2Serialize(normalNPE));
31+
assertCompatibleConstructNPE(factory.getDeserializer(normalNPE.getClass()), true);
32+
33+
assertDesEquals(normalNPE, baseHessionSerialize(normalNPE));
34+
assertCompatibleConstructNPE(factory.getDeserializer(normalNPE.getClass()), true);
35+
36+
37+
ConstructAlwaysNPE alwaysNPE = new ConstructAlwaysNPE("junit", sql, sqlEx);
38+
try {
39+
baseHession2Serialize(alwaysNPE);
40+
fail("must be always throw NullPointerException");
41+
} catch (HessianProtocolException e) {
42+
assertEquals(InvocationTargetException.class, e.getCause().getClass());
43+
assertEquals(NullPointerException.class, e.getCause().getCause().getClass());
44+
}
45+
assertCompatibleConstructNPE(factory.getDeserializer(alwaysNPE.getClass()), false);
46+
47+
try {
48+
baseHessionSerialize(alwaysNPE);
49+
fail("must be always throw NullPointerException");
50+
} catch (HessianProtocolException e) {
51+
assertEquals(InvocationTargetException.class, e.getCause().getClass());
52+
assertEquals(NullPointerException.class, e.getCause().getCause().getClass());
53+
}
54+
assertCompatibleConstructNPE(factory.getDeserializer(alwaysNPE.getClass()), false);
55+
}
56+
}
57+
58+
private void assertDesEquals(ConstructNPE expected, ConstructNPE actual) {
59+
assertEquals(expected.getMessage(), actual.getMessage());
60+
assertEquals(expected.getCause().getClass(), actual.getCause().getClass());
61+
assertEquals(expected.getSql(), actual.getSql());
62+
}
63+
64+
private void assertCompatibleConstructNPE(Deserializer deserializer, boolean compatible) throws Exception {
65+
assertEquals(JavaDeserializer.class, deserializer.getClass());
66+
assertEquals(compatible, getFieldValue(deserializer, "compatibleConstructNPE"));
67+
Object[] args = (Object[]) getFieldValue(deserializer, "_constructorArgs");
68+
for (int i = 0; i < args.length; i++) {
69+
if (compatible) {
70+
assertNotNull(args[i]);
71+
} else {
72+
assertNull(args[i]);
73+
}
74+
}
75+
}
76+
77+
}

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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.alibaba.com.caucho.hessian.io.beans;
2+
3+
import java.sql.SQLException;
4+
5+
/**
6+
* <a href="https://github.com/apache/incubator-dubbo/issues/210">#210</a>
7+
*/
8+
public class ConstructAlwaysNPE extends RuntimeException {
9+
private static final long serialVersionUID = 1L;
10+
private final String sql;
11+
12+
public ConstructAlwaysNPE(String task, String sql, SQLException ex) {
13+
super(task + "; uncategorized SQLException for SQL [" + sql + "]; SQL state [" +
14+
ex.getSQLState() + "]; error code [" + ex.getErrorCode() + "]; " + ex.getMessage(), ex);
15+
if (sql.length() == 0) throw new NullPointerException("sql=" + sql);
16+
this.sql = sql;
17+
}
18+
19+
public SQLException getSQLException() {
20+
return (SQLException) getCause();
21+
}
22+
23+
public String getSql() {
24+
return this.sql;
25+
}
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.alibaba.com.caucho.hessian.io.beans;
2+
3+
import java.sql.SQLException;
4+
5+
/**
6+
* <a href="https://github.com/apache/incubator-dubbo/issues/210">#210</a>
7+
* @see org.springframework.jdbc.UncategorizedSQLException
8+
*/
9+
public class ConstructNPE extends RuntimeException {
10+
private static final long serialVersionUID = 1L;
11+
private final String sql;
12+
13+
public ConstructNPE(String task, String sql, SQLException ex) {
14+
super(task + "; uncategorized SQLException for SQL [" + sql + "]; SQL state [" +
15+
ex.getSQLState() + "]; error code [" + ex.getErrorCode() + "]; " + ex.getMessage(), ex);
16+
this.sql = sql;
17+
}
18+
19+
public SQLException getSQLException() {
20+
return (SQLException) getCause();
21+
}
22+
23+
public String getSql() {
24+
return this.sql;
25+
}
26+
}

0 commit comments

Comments
 (0)