Skip to content

Commit c674b76

Browse files
committed
Further improvement to #1195 fix
1 parent 35362a0 commit c674b76

File tree

3 files changed

+110
-59
lines changed

3 files changed

+110
-59
lines changed

src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java

+48-34
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ public static class Reference implements Serializable
6969
*/
7070
protected int _index = -1;
7171

72+
/**
73+
* Lazily-constructed description of this instance; needed mostly to
74+
* allow JDK serialization to work in case where {@link #_from} is
75+
* non-serializable (and has to be dropped) but we still want to pass
76+
* actual description along.
77+
*
78+
* @since 2.7.4
79+
*/
80+
protected String _asString;
81+
7282
/**
7383
* Default constructor for deserialization/sub-classing purposes
7484
*/
@@ -89,7 +99,8 @@ public Reference(Object from, int index) {
8999
_index = index;
90100
}
91101

92-
private Reference(Reference src, Object newFrom) {
102+
private Reference(Reference src, String asString, Object newFrom) {
103+
_asString = asString;
93104
_from = newFrom;
94105
_fieldName = src._fieldName;
95106
_index = src._index;
@@ -104,31 +115,38 @@ private Reference(Reference src, Object newFrom) {
104115
public int getIndex() { return _index; }
105116

106117
@Override public String toString() {
107-
StringBuilder sb = new StringBuilder();
108-
Class<?> cls = (_from instanceof Class<?>) ?
109-
((Class<?>)_from) : _from.getClass();
110-
/* Hmmh. Although Class.getName() is mostly ok, it does look
111-
* butt-ugly for arrays. So let's use getSimpleName() instead;
112-
* but have to prepend package name too.
113-
*/
114-
String pkgName = ClassUtil.getPackageName(cls);
115-
if (pkgName != null) {
116-
sb.append(pkgName);
117-
sb.append('.');
118-
}
119-
sb.append(cls.getSimpleName());
120-
sb.append('[');
121-
if (_fieldName != null) {
122-
sb.append('"');
123-
sb.append(_fieldName);
124-
sb.append('"');
125-
} else if (_index >= 0) {
126-
sb.append(_index);
127-
} else {
128-
sb.append('?');
118+
if (_asString == null) {
119+
StringBuilder sb = new StringBuilder();
120+
121+
if (_from == null) { // can this ever occur?
122+
sb.append("UNKNOWN");
123+
} else {
124+
Class<?> cls = (_from instanceof Class<?>) ? (Class<?>)_from : _from.getClass();
125+
/* Hmmh. Although Class.getName() is mostly ok, it does look
126+
* butt-ugly for arrays. So let's use getSimpleName() instead;
127+
* but have to prepend package name too.
128+
*/
129+
String pkgName = ClassUtil.getPackageName(cls);
130+
if (pkgName != null) {
131+
sb.append(pkgName);
132+
sb.append('.');
133+
}
134+
sb.append(cls.getSimpleName());
135+
}
136+
sb.append('[');
137+
if (_fieldName != null) {
138+
sb.append('"');
139+
sb.append(_fieldName);
140+
sb.append('"');
141+
} else if (_index >= 0) {
142+
sb.append(_index);
143+
} else {
144+
sb.append('?');
145+
}
146+
sb.append(']');
147+
_asString = sb.toString();
129148
}
130-
sb.append(']');
131-
return sb.toString();
149+
return _asString;
132150
}
133151

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

152166
/*
153167
/**********************************************************
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,82 @@
11
package com.fasterxml.jackson.databind.interop;
22

33
import java.io.*;
4+
import java.util.List;
45

56
import com.fasterxml.jackson.databind.*;
67

78
public class ExceptionSerializableTest1195 extends BaseMapTest
89
{
9-
abstract static class ClassToRead {
10+
static class ClassToRead {
1011
public int x;
1112
}
1213

1314
static class ContainerClassToRead {
1415
public ClassToRead classToRead;
1516
}
1617

17-
public void testExceptionSerializability() throws Exception
18+
static class ContainerClassesToRead {
19+
public List<ClassToRead> classesToRead;
20+
}
21+
22+
final ObjectMapper MAPPER = new ObjectMapper();
23+
24+
public void testExceptionSerializabilitySimple() throws Exception
1825
{
19-
final ObjectMapper mapper = new ObjectMapper();
2026
try {
21-
mapper.readValue("{\"type\": \"B\"}", ClassToRead.class);
27+
MAPPER.readValue("{\"x\": \"B\"}", ClassToRead.class);
2228
fail("Should not have passed");
2329
} catch (JsonMappingException e) {
24-
ObjectOutputStream stream = new ObjectOutputStream(new ByteArrayOutputStream());
25-
try {
26-
stream.writeObject(e);
27-
stream.close();
28-
} catch (Exception e2) {
29-
fail("Failed to serialize "+e.getClass().getName()+": "+e2);
30-
}
30+
verifyException(e, "not a valid Integer");
31+
_testSerializability(e);
3132
}
3233
try {
33-
mapper.readValue("{\"classToRead\": {\"type\": \"B\"}}", ContainerClassToRead.class);
34+
MAPPER.readValue("{\"classToRead\": {\"x\": \"B\"}}", ContainerClassToRead.class);
35+
fail("Should not have passed");
36+
} catch (JsonMappingException e) {
37+
verifyException(e, "not a valid Integer");
38+
_testSerializability(e);
39+
}
40+
}
41+
42+
public void testExceptionSerializabilityStructured() throws Exception
43+
{
44+
try {
45+
MAPPER.readValue("{\"classesToRead\": [{\"x\": 1}, {\"x\": \"B\"}]}",
46+
ContainerClassesToRead.class);
3447
fail("Should not have passed");
3548
} catch (JsonMappingException e) {
36-
ObjectOutputStream stream = new ObjectOutputStream(new ByteArrayOutputStream());
37-
try {
38-
stream.writeObject(e);
39-
stream.close();
40-
} catch (Exception e2) {
41-
fail("Failed to serialize "+e.getClass().getName()+": "+e2);
42-
}
49+
verifyException(e, "not a valid Integer");
50+
_testSerializability(e);
51+
}
52+
}
53+
54+
/*
55+
/**********************************************************
56+
/* Helper methods
57+
/**********************************************************
58+
*/
59+
60+
private void _testSerializability(Exception e) throws IOException
61+
{
62+
ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000);
63+
ObjectOutputStream stream = new ObjectOutputStream(bytes);
64+
try {
65+
stream.writeObject(e);
66+
stream.close();
67+
} catch (Exception e2) {
68+
fail("Failed to JDK serialize "+e.getClass().getName()+": "+e2);
69+
}
70+
// and then back...
71+
byte[] b = bytes.toByteArray();
72+
ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(b));
73+
Object result = null;
74+
try {
75+
result = objIn.readObject();
76+
} catch (Exception e2) {
77+
fail("Failed to JDK deserialize "+e.getClass().getName()+": "+e2);
4378
}
79+
objIn.close();
80+
assertNotNull(result);
4481
}
4582
}

src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ static class Issue728 {
3030
public <C extends CharSequence> C method(C input) { return null; }
3131
}
3232

33-
public interface Generic1195 {
33+
public interface Generic1194 {
3434
public AtomicReference<String> getGeneric();
3535
public List<String> getList();
3636
public Map<String,String> getMap();
@@ -127,23 +127,23 @@ public void testJavaTypeAsJLRType()
127127
assertSame(t1, t2);
128128
}
129129

130-
// [databind#1195]
131-
public void testGenericSignature1195() throws Exception
130+
// [databind#1194]
131+
public void testGenericSignature1194() throws Exception
132132
{
133133
TypeFactory tf = TypeFactory.defaultInstance();
134134
Method m;
135135
JavaType t;
136136

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

141-
m = Generic1195.class.getMethod("getMap");
141+
m = Generic1194.class.getMethod("getMap");
142142
t = tf.constructType(m.getGenericReturnType());
143143
assertEquals("Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;",
144144
t.getGenericSignature());
145145

146-
m = Generic1195.class.getMethod("getGeneric");
146+
m = Generic1194.class.getMethod("getGeneric");
147147
t = tf.constructType(m.getGenericReturnType());
148148
assertEquals("Ljava/util/concurrent/atomic/AtomicReference<Ljava/lang/String;>;", t.getGenericSignature());
149149
}

0 commit comments

Comments
 (0)