Merge pull request #437 from iantabolt/master

Make ValidationProblem implement Serializable
This commit is contained in:
Havoc Pennington 2018-03-29 11:26:37 -04:00 committed by GitHub
commit aea2ac4db8
2 changed files with 46 additions and 10 deletions

View File

@ -58,22 +58,22 @@ public abstract class ConfigException extends RuntimeException implements Serial
ConfigImplUtil.writeOrigin(out, origin); ConfigImplUtil.writeOrigin(out, origin);
} }
private void readObject(java.io.ObjectInputStream in) throws IOException, // For deserialization - uses reflection to set the final origin field on the object
ClassNotFoundException { private static <T> void setOriginField(T hasOriginField, Class<T> clazz,
in.defaultReadObject(); ConfigOrigin origin) throws IOException {
ConfigOrigin origin = ConfigImplUtil.readOrigin(in);
// circumvent "final" // circumvent "final"
Field f; Field f;
try { try {
f = ConfigException.class.getDeclaredField("origin"); f = clazz.getDeclaredField("origin");
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
throw new IOException("ConfigException has no origin field?", e); throw new IOException(clazz.getSimpleName() + " has no origin field?", e);
} catch (SecurityException e) { } catch (SecurityException e) {
throw new IOException("unable to fill out origin field in ConfigException", e); throw new IOException("unable to fill out origin field in " +
clazz.getSimpleName(), e);
} }
f.setAccessible(true); f.setAccessible(true);
try { try {
f.set(this, origin); f.set(hasOriginField, origin);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw new IOException("unable to set origin field", e); throw new IOException("unable to set origin field", e);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
@ -81,6 +81,13 @@ public abstract class ConfigException extends RuntimeException implements Serial
} }
} }
private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
ConfigOrigin origin = ConfigImplUtil.readOrigin(in);
setOriginField(this, ConfigException.class, origin);
}
/** /**
* Exception indicating that the type of a value does not match the type you * Exception indicating that the type of a value does not match the type you
* requested. * requested.
@ -310,10 +317,10 @@ public abstract class ConfigException extends RuntimeException implements Serial
* {@link ConfigException.ValidationFailed} exception thrown from * {@link ConfigException.ValidationFailed} exception thrown from
* <code>checkValid()</code> includes a list of problems encountered. * <code>checkValid()</code> includes a list of problems encountered.
*/ */
public static class ValidationProblem { public static class ValidationProblem implements Serializable {
final private String path; final private String path;
final private ConfigOrigin origin; final private transient ConfigOrigin origin;
final private String problem; final private String problem;
public ValidationProblem(String path, ConfigOrigin origin, String problem) { public ValidationProblem(String path, ConfigOrigin origin, String problem) {
@ -347,6 +354,20 @@ public abstract class ConfigException extends RuntimeException implements Serial
return problem; return problem;
} }
// We customize serialization because ConfigOrigin isn't
// serializable and we don't want it to be
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
ConfigImplUtil.writeOrigin(out, origin);
}
private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
ConfigOrigin origin = ConfigImplUtil.readOrigin(in);
setOriginField(this, ValidationProblem.class, origin);
}
@Override @Override
public String toString() { public String toString() {
return "ValidationProblem(" + path + "," + origin + "," + problem + ")"; return "ValidationProblem(" + path + "," + origin + "," + problem + ")";

View File

@ -97,6 +97,21 @@ class ValidationTest extends TestUtils {
checkValidationException(e, expecteds) checkValidationException(e, expecteds)
} }
@Test
def validationFailedSerializable(): Unit = {
// Reusing a previous test case to generate an error
val reference = parseConfig("""{ a : [{},{},{}] }""")
val conf = parseConfig("""{ a : 42 }""")
val e = intercept[ConfigException.ValidationFailed] {
conf.checkValid(reference)
}
val expecteds = Seq(WrongType("a", 1, "list", "number"))
val actual = checkSerializableNoMeaningfulEquals(e)
checkValidationException(actual, expecteds)
}
@Test @Test
def validationAllowsListOverriddenWithSameTypeList() { def validationAllowsListOverriddenWithSameTypeList() {
val reference = parseConfig("""{ a : [1,2,3] }""") val reference = parseConfig("""{ a : [1,2,3] }""")