Skip to content
This repository has been archived by the owner on Feb 21, 2022. It is now read-only.

Commit

Permalink
Use SerializationConverter as default for ThrowableConverter (closes x…
Browse files Browse the repository at this point in the history
  • Loading branch information
joehni committed May 6, 2019
1 parent 3f77c1b commit c40f72c
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 20 deletions.
9 changes: 7 additions & 2 deletions xstream-distribution/src/content/changes.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ <h2>Minor changes</h2>
<li>GHI:#134: Possible reuse of instances for Byte, Character, Short, Integer and Long while unmarshalling.</li>
<li>GHPR:#145: FileName parameter of StackTraceElement can be used for other source code units than file,
converter handles this.</li>
<li>GHI:#4: Cannot add suppressed exception to unmarshalled Throwable.</li>
<li>StackTraceElementConverter keeps now information about the source code unit for native methods if
available.</li>
<li>StackTraceElementConverter keeps for Java 9 or higher information about the class loader and module name
Expand All @@ -71,14 +72,18 @@ <h2>API changes</h2>
<li>Added c.t.x.io.xml.SimpleStaxDriver.</li>
<li>Added c.t.x.io.HierarchicalStreamReader.getLevel()</li>
<li>Deprecated obsolete method c.t.x.XStream.setupDefaultSecurity.</li>
<li>Added c.t.x.converters.extended.ThrowableConverter.ThrowableConverter(Mapper, ConverterLookup).</li>
<li>Deprecated c.t.x.converters.extended.ThrowableConverter.ThrowableConverter(ConverterLookup).</li>
</ul>

<h2>Stream compatibility</h2>

<ul>
<li>Drop automatic reference support at deserialization time for immutable types declared in the 1.4.x version
series (java.util.Currency, java.util.UUID, java.net.URI, java.nio.charset.Charset and the empty collection
types).</li>
series (java.util.Currency, java.util.UUID, java.net.URI, java.nio.charset.Charset and the empty collection
types).</li>
<li>ThrowableConverter uses by default now format of SerializableConverter, but can still read old format of
1.4.x version.</li>
<li>No support for Hibernate 3 collections.</li>
</ul>

Expand Down
20 changes: 12 additions & 8 deletions xstream-distribution/src/content/converters.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<html>
<!--
Copyright (C) 2005, 2006 Joe Walnes.
Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013, 2014, 2015, 2016, 2017 XStream committers.
Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013, 2014, 2015, 2016, 2017, 2019 XStream committers.
All rights reserved.
The software in this package is published under the terms of the BSD
Expand Down Expand Up @@ -174,13 +174,17 @@
<td>java.lang.Throwable<br/>java.lang.Exception<br/>java.lang.RuntimeException<br/>java.lang.Error<br/>...and all subclasses
of these.</td>
<td class="example">
&lt;java.io.IOException&gt;<br/>
&nbsp;&nbsp;&lt;detailMessage&gt;No file&lt;/detailMessage&gt;<br/>
&nbsp;&nbsp;&lt;stack-trace&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&lt;trace&gt;com.x.Foo.stuff(Foo.java:22)&lt;/trace&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&lt;trace&gt;com.x.Foo.blah(Foo.java:31)&lt;/trace&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&lt;trace&gt;com.x.Foo.main(Foo.java:43)&lt;/trace&gt;<br/>
&nbsp;&nbsp;&lt;/stack-trace&gt;<br/>
&lt;java.io.IOException&nbsp;serialization=&quot;custom&quot;&gt;<br/>
&nbsp;&lt;java.lang.Throwable&gt;<br/>
&nbsp;&nbsp;&lt;default&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;detailMessage&gt;No file&lt;/detailMessage&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;stack-trace&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;trace&gt;com.x.Foo.stuff(Foo.java:22)&lt;/trace&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;trace&gt;com.x.Foo.blah(Foo.java:31)&lt;/trace&gt;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;trace&gt;com.x.Foo.main(Foo.java:43)&lt;/trace&gt;<br/>
&nbsp;&nbsp;&nbsp;&lt;/stack-trace&gt;<br/>
&nbsp;&nbsp;&lt;/default&gt;<br/>
&nbsp;&lt;/java.lang.Throwable&gt;<br/>
&lt;/java.io.IOException&gt;
</td>
<td>This is only available with Java 1.4 or greater. It retains the full stack trace, including that of any
Expand Down
2 changes: 1 addition & 1 deletion xstream/src/java/com/thoughtworks/xstream/XStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ protected void setupConverters() {
registerConverter(new StringConverter(), PRIORITY_NORMAL);
registerConverter(new StringBufferConverter(), PRIORITY_NORMAL);
registerConverter(new StringBuilderConverter(), PRIORITY_NORMAL);
registerConverter(new ThrowableConverter(converterLookup), PRIORITY_NORMAL);
registerConverter(new ThrowableConverter(mapper, converterLookup), PRIORITY_NORMAL);
registerConverter(new StackTraceElementConverter(), PRIORITY_NORMAL);
registerConverter(new DateConverter(), PRIORITY_NORMAL);
registerConverter(new GregorianCalendarConverter(), PRIORITY_NORMAL);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
/*
* Copyright (C) 2004 Joe Walnes.
* Copyright (C) 2006, 2007, 2013, 2014, 2018 XStream Committers.
* Copyright (C) 2006, 2007, 2013, 2014, 2018, 2019 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 29. May 2004 by Joe Walnes
*/
package com.thoughtworks.xstream.converters.extended;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.ConverterLookup;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;


/**
* Converter for {@link Throwable} (and {@link Exception}) that retains stack trace.
*
*
* @author <a href="mailto:[email protected]">B. K. Oxley (binkley)</a>
* @author Joe Walnes
* @author J&ouml;rg Schaible
*/
public class ThrowableConverter implements Converter {

private Converter defaultConverter;
private static final String ATTRIBUTE_SERIALIZATION = "serialization";
private final Converter defaultConverter;
private final ConverterLookup lookup;
private final Mapper mapper;

/**
* @deprecated As of 1.4.5 use {@link #ThrowableConverter(ConverterLookup)}
Expand All @@ -38,13 +45,27 @@ public class ThrowableConverter implements Converter {
public ThrowableConverter(final Converter defaultConverter) {
this.defaultConverter = defaultConverter;
lookup = null;
mapper = null;
}

/**
* @since 1.4.5
* @deprecated As of upcoming use {@link #ThrowableConverter(Mapper, ConverterLookup)}
*/
@Deprecated
public ThrowableConverter(final ConverterLookup lookup) {
this.lookup = lookup;
mapper = null;
defaultConverter = null;
}

/**
* @since upcoming
*/
public ThrowableConverter(final Mapper mapper, final ConverterLookup lookup) {
this.mapper = mapper;
this.lookup = lookup;
defaultConverter = null;
}

@Override
Expand All @@ -68,11 +89,33 @@ public void marshal(final Object source, final HierarchicalStreamWriter writer,
}

private Converter getConverter() {
return defaultConverter != null ? defaultConverter : lookup.lookupConverterForType(Ser.class);
}

private Converter getConverter14() {
return defaultConverter != null ? defaultConverter : lookup.lookupConverterForType(Object.class);
}

@Override
public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) {
return getConverter().unmarshal(reader, context);
final String attributeName = mapper != null
? mapper.aliasForSystemAttribute(ATTRIBUTE_SERIALIZATION)
: ATTRIBUTE_SERIALIZATION;
final Converter converter = attributeName != null && reader.getAttribute(attributeName) == null
? getConverter14()
: getConverter();
return converter.unmarshal(reader, context);
}

private static class Ser implements Serializable {
private static final long serialVersionUID = 20190506L;

@SuppressWarnings("unused")
public Ser() {
}

private void readObject(final ObjectInputStream in) throws ClassNotFoundException, IOException {
in.defaultReadObject();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2004, 2005 Joe Walnes.
* Copyright (C) 2006, 2007, 2018 XStream Committers.
* Copyright (C) 2006, 2007, 2018, 2019 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
Expand All @@ -11,6 +11,8 @@
*/
package com.thoughtworks.xstream.converters.extended;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;

import com.thoughtworks.acceptance.AbstractAcceptanceTest;
Expand All @@ -35,7 +37,7 @@ public void testDeserializesException() {
}

public void testIncludesMessage() {
final Throwable expected = new Throwable("A MESSAGE");
final Throwable expected = new IOException("A MESSAGE");
final Throwable result = xstream.<Throwable>fromXML(xstream.toXML(expected));
assertThrowableEquals(expected, result);
}
Expand All @@ -52,6 +54,13 @@ public void testIncludesCauseAndMessage() {
assertThrowableEquals(expected, result);
}

public void testIncludesSuppressedExceptions() {
final Throwable expected = new Throwable("MESSAGE");
expected.addSuppressed(new Throwable("SUPPRESSED MESSAGE"));
final Throwable result = xstream.<Throwable>fromXML(xstream.toXML(expected));
assertThrowableEquals(expected, result);
}

public void testIncludesStackTrace() {
try {
throw new Exception();
Expand Down Expand Up @@ -91,7 +100,46 @@ public void testSerializesExtraFields() {
}
}

public void testSerializesWithNoSelfReferenceForUninitializedCauseInJdk14() {
public static class OtherException extends Exception {
private static final long serialVersionUID = 201905L;
private transient BigDecimal number;

public OtherException(final String msg, final BigDecimal number) {
super(msg);
this.number = number;
}

@Override
public boolean equals(final Object o) {
return super.equals(o) && o instanceof MyException && number.equals(((MyException)o).number);
}

@Override
public int hashCode() {
return number.hashCode() | super.hashCode();
}

private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
number = new BigDecimal(in.readDouble());
}

private void writeObject(final ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeDouble(number.doubleValue());
}
}

public void testSupportsSerializationMethods() {
try {
throw new OtherException("A MESSAGE", new BigDecimal(123.4));
} catch (final OtherException exception) {
final Throwable result = xstream.<Throwable>fromXML(xstream.toXML(exception));
assertThrowableEquals(exception, result);
}
}

public void testSerializesWithNoSelfReferenceForUninitializedCauseInPureMode() {
xstream.setMode(XStream.NO_REFERENCES);
try {
throw new RuntimeException("Without cause");
Expand All @@ -103,7 +151,7 @@ public void testSerializesWithNoSelfReferenceForUninitializedCauseInJdk14() {
}
}

public void testSerializesWithInitializedCauseInJdk14() {
public void testSerializesWithInitializedCauseInPureMode() {
xstream.setMode(XStream.NO_REFERENCES);
try {
throw new RuntimeException("Without cause", null);
Expand All @@ -115,6 +163,38 @@ public void testSerializesWithInitializedCauseInJdk14() {
}
}

public void testCanUnmarshalFormatOf14() {
final String xml = ""
+ "<java.lang.Throwable>\n"
+ " <detailMessage>A MESSAGE</detailMessage>\n"
+ " <stackTrace>\n"
+ " <trace>com.thoughtworks.xstream.converters.extended.ThrowableConverterTest.testDeserializesThrowable(ThrowableConverterTest.java:26)</trace>\n"
+ " <trace>sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)</trace>\n"
+ " <trace>sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)</trace>\n"
+ " <trace>sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)</trace>\n"
+ " <trace>java.lang.reflect.Method.invoke(Method.java:498)</trace>\n"
+ " <trace>junit.framework.TestCase.runTest(TestCase.java:154)</trace>\n"
+ " <trace>junit.framework.TestCase.runBare(TestCase.java:127)</trace>\n"
+ " <trace>junit.framework.TestResult$1.protect(TestResult.java:106)</trace>\n"
+ " <trace>junit.framework.TestResult.runProtected(TestResult.java:124)</trace>\n"
+ " <trace>junit.framework.TestResult.run(TestResult.java:109)</trace>\n"
+ " <trace>junit.framework.TestCase.run(TestCase.java:118)</trace>\n"
+ " <trace>junit.framework.TestSuite.runTest(TestSuite.java:208)</trace>\n"
+ " <trace>junit.framework.TestSuite.run(TestSuite.java:203)</trace>\n"
+ " </stackTrace>\n"
+ "</java.lang.Throwable>\n";
final Throwable result = xstream.<Throwable>fromXML(xml);
assertEquals("A MESSAGE", result.getMessage());
assertEquals(13, result.getStackTrace().length);
}

public void testCanAddSuppressedExceptionsLater() {
final Exception expected = new Exception();
final Throwable result = xstream.<Throwable>fromXML(xstream.toXML(expected));
assertThrowableEquals(expected, result);
result.addSuppressed(new RuntimeException());
}

private static void assertThrowableEquals(final Throwable a, final Throwable b) {
assertBoth(a, b, new MoreAssertions() {
@Override
Expand All @@ -124,6 +204,7 @@ public void assertMoreSafely(final Object a, final Object b) {
assertEquals(ta.getMessage(), tb.getMessage());
assertThrowableEquals(ta.getCause(), tb.getCause());
assertArrayEquals(ta.getStackTrace(), tb.getStackTrace());
assertArrayEquals(ta.getSuppressed(), tb.getSuppressed());
}
});
}
Expand Down

0 comments on commit c40f72c

Please sign in to comment.