Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,36 @@
public class JavaDeserializer extends AbstractMapDeserializer {
private static final Logger log
= Logger.getLogger(JavaDeserializer.class.getName());
private static Object reflectionFactory;
private static Method newConstructorForSerializationMethod;
private static Constructor<Object> objConstructor;
static {
try {
objConstructor = Object.class.getConstructor(new Class<?>[0]);

Class<?> factoryClass = Class.forName("sun.reflect.ReflectionFactory");
Method getReflectionFactory = factoryClass.getDeclaredMethod("getReflectionFactory");
if (!getReflectionFactory.isAccessible()) getReflectionFactory.setAccessible(true);
reflectionFactory = getReflectionFactory.invoke(factoryClass);

newConstructorForSerializationMethod = factoryClass.getDeclaredMethod(
"newConstructorForSerialization", Class.class, Constructor.class);
if (!newConstructorForSerializationMethod.isAccessible()) {
newConstructorForSerializationMethod.setAccessible(true);
}
} catch (Throwable e) {
log.finest("Compatible constructor not supported for this JVM: "
+ System.getProperty("java.vm.name"));
}
}

private Class _type;
private HashMap _fieldMap;
private Method _readResolve;
private Constructor _constructor;
private Object[] _constructorArgs;
private boolean compatibleConstructNPE = newConstructorForSerializationMethod != null;
private Constructor<?> compatibleConstructor;

public JavaDeserializer(Class cl) {
_type = cl;
Expand Down Expand Up @@ -305,15 +329,41 @@ private Object resolve(Object obj)
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 {
if (compatibleConstructor != null) return compatibleConstructor.newInstance();

InvocationTargetException ex;
try {
return _constructor.newInstance(_constructorArgs);
} catch (InvocationTargetException e) {
if (compatibleConstructNPE && e.getTargetException() instanceof NullPointerException) {
ex = e;
} else {
throw e;
}
}

Constructor<?> ctor;
try {
ctor = (Constructor<?>) newConstructorForSerializationMethod.invoke(reflectionFactory, _type,
objConstructor);
if (!ctor.isAccessible()) ctor.setAccessible(true);
Object obj = ctor.newInstance();
compatibleConstructor = ctor;
return obj;
} catch (Throwable t) {
log.finest(_type + " compatible construct for NullPointerException fail: " + 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,46 @@
package com.alibaba.com.caucho.hessian.io;

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

import java.beans.PropertyChangeEvent;
import java.sql.SQLException;

import org.junit.Test;

import com.alibaba.com.caucho.hessian.io.base.SerializeTestBase;
import com.alibaba.com.caucho.hessian.io.beans.ConstructNPE;


public class JavaDeserializerTest extends SerializeTestBase {

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

for (int repeat = 0; repeat < 2; repeat++) {
assertDesEquals(normalNPE, baseHession2Serialize(normalNPE));
assertCompatibleConstructNPE(factory.getDeserializer(normalNPE.getClass()), true);
}
}

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"));
if (compatible) assertNotNull(getFieldValue(deserializer, "compatibleConstructor"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
import com.alibaba.com.caucho.hessian.io.HessianInput;
import com.alibaba.com.caucho.hessian.io.HessianOutput;
import com.alibaba.com.caucho.hessian.io.SerializerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;

/**
* hession base serialize utils
*
*/
public class SerializeTestBase {
protected SerializerFactory factory = new SerializerFactory();

/**
* hession serialize util
*
Expand All @@ -22,6 +26,7 @@ public class SerializeTestBase {
* @return
* @throws IOException
*/
@SuppressWarnings("unchecked")
protected <T> T baseHessionSerialize(T data) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
HessianOutput out = new HessianOutput(bout);
Expand All @@ -31,6 +36,7 @@ protected <T> T baseHessionSerialize(T data) throws IOException {

ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
HessianInput input = new HessianInput(bin);
input.setSerializerFactory(factory);
return (T) input.readObject();
}

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

ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
Hessian2Input input = new Hessian2Input(bin);
input.setSerializerFactory(factory);
return (T) input.readObject();
}

@SuppressWarnings("unchecked")
protected <T> T getFieldValue(Object bean, String fieldName) throws Exception {
Field field = bean.getClass().getDeclaredField(fieldName);
if (!field.isAccessible()) field.setAccessible(true);
return (T) field.get(bean);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.alibaba.com.caucho.hessian.io.beans;

import java.beans.PropertyChangeEvent;
import java.sql.SQLException;

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

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

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

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

public PropertyChangeEvent getPropertyChangeEvent() {
return this.propertyChangeEvent;
}
}