Skip to content

Commit

Permalink
Further improvement to #1195 fix
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Apr 15, 2016
1 parent 35362a0 commit c674b76
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ public static class Reference implements Serializable
*/
protected int _index = -1;

/**
* Lazily-constructed description of this instance; needed mostly to
* allow JDK serialization to work in case where {@link #_from} is
* non-serializable (and has to be dropped) but we still want to pass
* actual description along.
*
* @since 2.7.4
*/
protected String _asString;

/**
* Default constructor for deserialization/sub-classing purposes
*/
Expand All @@ -89,7 +99,8 @@ public Reference(Object from, int index) {
_index = index;
}

private Reference(Reference src, Object newFrom) {
private Reference(Reference src, String asString, Object newFrom) {
_asString = asString;
_from = newFrom;
_fieldName = src._fieldName;
_index = src._index;
Expand All @@ -104,31 +115,38 @@ private Reference(Reference src, Object newFrom) {
public int getIndex() { return _index; }

@Override public String toString() {
StringBuilder sb = new StringBuilder();
Class<?> cls = (_from instanceof Class<?>) ?
((Class<?>)_from) : _from.getClass();
/* Hmmh. Although Class.getName() is mostly ok, it does look
* butt-ugly for arrays. So let's use getSimpleName() instead;
* but have to prepend package name too.
*/
String pkgName = ClassUtil.getPackageName(cls);
if (pkgName != null) {
sb.append(pkgName);
sb.append('.');
}
sb.append(cls.getSimpleName());
sb.append('[');
if (_fieldName != null) {
sb.append('"');
sb.append(_fieldName);
sb.append('"');
} else if (_index >= 0) {
sb.append(_index);
} else {
sb.append('?');
if (_asString == null) {
StringBuilder sb = new StringBuilder();

if (_from == null) { // can this ever occur?
sb.append("UNKNOWN");
} else {
Class<?> cls = (_from instanceof Class<?>) ? (Class<?>)_from : _from.getClass();
/* Hmmh. Although Class.getName() is mostly ok, it does look
* butt-ugly for arrays. So let's use getSimpleName() instead;
* but have to prepend package name too.
*/
String pkgName = ClassUtil.getPackageName(cls);
if (pkgName != null) {
sb.append(pkgName);
sb.append('.');
}
sb.append(cls.getSimpleName());
}
sb.append('[');
if (_fieldName != null) {
sb.append('"');
sb.append(_fieldName);
sb.append('"');
} else if (_index >= 0) {
sb.append(_index);
} else {
sb.append('?');
}
sb.append(']');
_asString = sb.toString();
}
sb.append(']');
return sb.toString();
return _asString;
}

/**
Expand All @@ -138,16 +156,12 @@ private Reference(Reference src, Object newFrom) {
*/
Object writeReplace() {
// as per [databind#1195], reference may cause trouble, if non-serializable
// instance. What to replace it with is trickier; Class is most natural, but
// would recipient have that available? Assume this is the case, for now, because
//
if ((_from != null) && !(_from instanceof Serializable)) {
Object from = _from.getClass();
return new Reference(this, from);
}
return this;
// instance (either directly or transitively); and even use of Class would often
// be problematic. Because of this, clear up `_from` always, but ensure that
// description is preserved
return new Reference(this, toString(), null);
}
}
}

/*
/**********************************************************
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,82 @@
package com.fasterxml.jackson.databind.interop;

import java.io.*;
import java.util.List;

import com.fasterxml.jackson.databind.*;

public class ExceptionSerializableTest1195 extends BaseMapTest
{
abstract static class ClassToRead {
static class ClassToRead {
public int x;
}

static class ContainerClassToRead {
public ClassToRead classToRead;
}

public void testExceptionSerializability() throws Exception
static class ContainerClassesToRead {
public List<ClassToRead> classesToRead;
}

final ObjectMapper MAPPER = new ObjectMapper();

public void testExceptionSerializabilitySimple() throws Exception
{
final ObjectMapper mapper = new ObjectMapper();
try {
mapper.readValue("{\"type\": \"B\"}", ClassToRead.class);
MAPPER.readValue("{\"x\": \"B\"}", ClassToRead.class);
fail("Should not have passed");
} catch (JsonMappingException e) {
ObjectOutputStream stream = new ObjectOutputStream(new ByteArrayOutputStream());
try {
stream.writeObject(e);
stream.close();
} catch (Exception e2) {
fail("Failed to serialize "+e.getClass().getName()+": "+e2);
}
verifyException(e, "not a valid Integer");
_testSerializability(e);
}
try {
mapper.readValue("{\"classToRead\": {\"type\": \"B\"}}", ContainerClassToRead.class);
MAPPER.readValue("{\"classToRead\": {\"x\": \"B\"}}", ContainerClassToRead.class);
fail("Should not have passed");
} catch (JsonMappingException e) {
verifyException(e, "not a valid Integer");
_testSerializability(e);
}
}

public void testExceptionSerializabilityStructured() throws Exception
{
try {
MAPPER.readValue("{\"classesToRead\": [{\"x\": 1}, {\"x\": \"B\"}]}",
ContainerClassesToRead.class);
fail("Should not have passed");
} catch (JsonMappingException e) {
ObjectOutputStream stream = new ObjectOutputStream(new ByteArrayOutputStream());
try {
stream.writeObject(e);
stream.close();
} catch (Exception e2) {
fail("Failed to serialize "+e.getClass().getName()+": "+e2);
}
verifyException(e, "not a valid Integer");
_testSerializability(e);
}
}

/*
/**********************************************************
/* Helper methods
/**********************************************************
*/

private void _testSerializability(Exception e) throws IOException
{
ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000);
ObjectOutputStream stream = new ObjectOutputStream(bytes);
try {
stream.writeObject(e);
stream.close();
} catch (Exception e2) {
fail("Failed to JDK serialize "+e.getClass().getName()+": "+e2);
}
// and then back...
byte[] b = bytes.toByteArray();
ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(b));
Object result = null;
try {
result = objIn.readObject();
} catch (Exception e2) {
fail("Failed to JDK deserialize "+e.getClass().getName()+": "+e2);
}
objIn.close();
assertNotNull(result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ static class Issue728 {
public <C extends CharSequence> C method(C input) { return null; }
}

public interface Generic1195 {
public interface Generic1194 {
public AtomicReference<String> getGeneric();
public List<String> getList();
public Map<String,String> getMap();
Expand Down Expand Up @@ -127,23 +127,23 @@ public void testJavaTypeAsJLRType()
assertSame(t1, t2);
}

// [databind#1195]
public void testGenericSignature1195() throws Exception
// [databind#1194]
public void testGenericSignature1194() throws Exception
{
TypeFactory tf = TypeFactory.defaultInstance();
Method m;
JavaType t;

m = Generic1195.class.getMethod("getList");
m = Generic1194.class.getMethod("getList");
t = tf.constructType(m.getGenericReturnType());
assertEquals("Ljava/util/List<Ljava/lang/String;>;", t.getGenericSignature());

m = Generic1195.class.getMethod("getMap");
m = Generic1194.class.getMethod("getMap");
t = tf.constructType(m.getGenericReturnType());
assertEquals("Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;",
t.getGenericSignature());

m = Generic1195.class.getMethod("getGeneric");
m = Generic1194.class.getMethod("getGeneric");
t = tf.constructType(m.getGenericReturnType());
assertEquals("Ljava/util/concurrent/atomic/AtomicReference<Ljava/lang/String;>;", t.getGenericSignature());
}
Expand Down

0 comments on commit c674b76

Please sign in to comment.