Skip to content

Commit

Permalink
fix apache#210 JavaDeserializer._constructor.newInstance([null, null]…
Browse files Browse the repository at this point in the history
…) NPE

Example: org.springframework.jdbc.UncategorizedSQLException
  • Loading branch information
takeseem committed Apr 14, 2018
1 parent 07d3b30 commit 674905a
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public class JavaDeserializer extends AbstractMapDeserializer {
private Method _readResolve;
private Constructor _constructor;
private Object []_constructorArgs;
private boolean compatibleConstructNPE = true;

public JavaDeserializer(Class cl)
{
Expand Down Expand Up @@ -267,15 +268,49 @@ protected Object instantiate()
throws Exception
{
try {
if (_constructor != null)
return _constructor.newInstance(_constructorArgs);
else
return _type.newInstance();
return _constructor == null ? _type.newInstance() : construct();
} catch (Exception e) {
throw new HessianProtocolException("'" + _type.getName() + "' could not be instantiated", e);
}
}

protected Object construct() throws Exception {
InvocationTargetException ex;
try {
return _constructor.newInstance(_constructorArgs);
} catch (InvocationTargetException e) {
if (!compatibleConstructNPE
|| !(e.getTargetException() instanceof NullPointerException)) {
throw e;
}

ex = e;
}

Class[] types = _constructor.getParameterTypes();
Object[] args = new Object[types.length];
System.arraycopy(_constructorArgs, 0, args, 0, types.length);
try {
for (int i = 0; i < types.length; i++) {
if (args[i] == null) {
try {
Constructor ctor = types[i].getDeclaredConstructor(new Class[0]);
if (!ctor.isAccessible()) ctor.setAccessible(true);
args[i] = ctor.newInstance(new Object[0]);
} catch (Exception e) {
}
}
}
Object ret = _constructor.newInstance(args);
_constructorArgs = args;
return ret;
} catch (Throwable t) {
}

compatibleConstructNPE = false;
throw ex;
}

/**
* Creates a map of the classes fields.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.alibaba.com.caucho.hessian.io;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;

import org.junit.Test;

import com.alibaba.com.caucho.hessian.io.model.ConstructAlwaysNPE;
import com.alibaba.com.caucho.hessian.io.model.ConstructNPE;

public class JavaDeserializerTest {

/**
* <a href="https://github.com/apache/incubator-dubbo/issues/210">#210</a>
* @see org.springframework.jdbc.UncategorizedSQLException
*/
@Test
public void testConstructorNPE() throws Exception {
String sql = "select * from demo";
SQLException sqlEx = new SQLException("just a sql exception");
ConstructNPE normalNPE = new ConstructNPE("junit", sql, sqlEx);
ConstructAlwaysNPE alwaysNPE = new ConstructAlwaysNPE("junit", sql, sqlEx);

ByteArrayOutputStream bout = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(bout);
out.writeObject(normalNPE);
out.writeObject(alwaysNPE);
out.flush();

SerializerFactory factory = new SerializerFactory();
for (int repeat = 0; repeat < 2; repeat++) {
Hessian2Input input = new Hessian2Input(new ByteArrayInputStream(bout.toByteArray()));
input.setSerializerFactory(factory);

assertDesEquals(normalNPE, (ConstructNPE) input.readObject());
assertCompatibleConstructNPE(factory.getDeserializer(normalNPE.getClass()), true);

try {
input.readObject();
fail("must be always throw NullPointerException");
} catch (HessianProtocolException e) {
assertEquals(InvocationTargetException.class, e.getCause().getClass());
assertEquals(NullPointerException.class, e.getCause().getCause().getClass());
}
assertCompatibleConstructNPE(factory.getDeserializer(alwaysNPE.getClass()), false);
}
}

private void assertDesEquals(ConstructNPE expected, ConstructNPE actual) {
assertEquals(expected.getMessage(), actual.getMessage());
assertEquals(expected.getCause().getClass(), actual.getCause().getClass());
assertEquals(expected.getSql(), actual.getSql());
}

private void assertCompatibleConstructNPE(Deserializer deserializer, boolean compatible) throws Exception {
assertEquals(JavaDeserializer.class, deserializer.getClass());
assertEquals(compatible, getFieldValue(deserializer, "compatibleConstructNPE"));
Object[] args = (Object[]) getFieldValue(deserializer, "_constructorArgs");
for (int i = 0; i < args.length; i++) {
if (compatible) {
assertNotNull(args[i]);
} else {
assertNull(args[i]);
}
}
}

public Object getFieldValue(Object bean, String fieldName) throws Exception {
Field field = bean.getClass().getDeclaredField(fieldName);
if (!field.isAccessible()) field.setAccessible(true);
return field.get(bean);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.alibaba.com.caucho.hessian.io.model;

import java.sql.SQLException;

/**
* <a href="https://github.com/apache/incubator-dubbo/issues/210">#210</a>
*/
public class ConstructAlwaysNPE extends RuntimeException {
private static final long serialVersionUID = 1L;
private final String sql;

public ConstructAlwaysNPE(String task, String sql, SQLException ex) {
super(task + "; uncategorized SQLException for SQL [" + sql + "]; SQL state [" +
ex.getSQLState() + "]; error code [" + ex.getErrorCode() + "]; " + ex.getMessage(), ex);
if (sql.length() == 0) throw new NullPointerException("sql=" + sql);
this.sql = sql;
}

public SQLException getSQLException() {
return (SQLException) getCause();
}

public String getSql() {
return this.sql;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.alibaba.com.caucho.hessian.io.model;

import java.sql.SQLException;

/**
* <a href="https://github.com/apache/incubator-dubbo/issues/210">#210</a>
* @see org.springframework.jdbc.UncategorizedSQLException
*/
public class ConstructNPE extends RuntimeException {
private static final long serialVersionUID = 1L;
private final String sql;

public ConstructNPE(String task, String sql, SQLException ex) {
super(task + "; uncategorized SQLException for SQL [" + sql + "]; SQL state [" +
ex.getSQLState() + "]; error code [" + ex.getErrorCode() + "]; " + ex.getMessage(), ex);
this.sql = sql;
}

public SQLException getSQLException() {
return (SQLException) getCause();
}

public String getSql() {
return this.sql;
}
}

0 comments on commit 674905a

Please sign in to comment.