Make concrete Config and ConfigValue serializable

This commit is contained in:
Havoc Pennington 2012-02-01 12:15:05 -05:00
parent 846c8c116a
commit 2d8e42686c
10 changed files with 122 additions and 7 deletions

View File

@ -3,6 +3,8 @@
*/
package com.typesafe.config.impl;
import java.io.Serializable;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigMergeable;
import com.typesafe.config.ConfigOrigin;
@ -16,7 +18,7 @@ import com.typesafe.config.ConfigValue;
* improperly-factored and non-modular code. Please don't add parent().
*
*/
abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
abstract class AbstractConfigValue implements ConfigValue, MergeableValue, Serializable {
final private SimpleConfigOrigin origin;

View File

@ -3,12 +3,13 @@
*/
package com.typesafe.config.impl;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import com.typesafe.config.ConfigException;
final class Path {
final class Path implements Serializable {
final private String first;
final private Path remainder;

View File

@ -3,6 +3,7 @@
*/
package com.typesafe.config.impl;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
@ -28,7 +29,7 @@ import com.typesafe.config.ConfigValueType;
* with a one-level java.util.Map from paths to non-null values. Null values are
* not "in" the map.
*/
final class SimpleConfig implements Config, MergeableValue {
final class SimpleConfig implements Config, MergeableValue, Serializable {
final private AbstractConfigObject object;

View File

@ -21,7 +21,8 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList {
final private boolean resolved;
SimpleConfigList(ConfigOrigin origin, List<AbstractConfigValue> value) {
this(origin, value, ResolveStatus.fromValues(value));
this(origin, value, ResolveStatus
.fromValues(value));
}
SimpleConfigList(ConfigOrigin origin, List<AbstractConfigValue> value,

View File

@ -4,6 +4,7 @@
package com.typesafe.config.impl;
import java.io.File;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
@ -17,7 +18,7 @@ import com.typesafe.config.ConfigOrigin;
// it would be cleaner to have a class hierarchy for various origin types,
// but was hoping this would be enough simpler to be a little messy. eh.
final class SimpleConfigOrigin implements ConfigOrigin {
final class SimpleConfigOrigin implements ConfigOrigin, Serializable {
final private String description;
final private int lineNumber;
final private int endLineNumber;

View File

@ -1,6 +1,8 @@
package com.typesafe.config.impl;
final class SubstitutionExpression {
import java.io.Serializable;
final class SubstitutionExpression implements Serializable {
final private Path path;
final private boolean optional;

View File

@ -322,6 +322,11 @@ class ConfigSubstitutionTest extends TestUtils {
""")
}
@Test
def serializeUnresolvedObject() {
checkSerializable(substComplexObject)
}
// this is a weird test, it used to test fallback to system props which made more sense.
// Now it just tests that if you override with system props, you can use system props
// in substitutions.

View File

@ -792,6 +792,12 @@ class ConfigTest extends TestUtils {
assertEquals(None, entries.get("nulls.null"))
}
@Test
def test01Serializable() {
val conf = ConfigFactory.load("test01")
val confCopy = checkSerializable(conf)
}
@Test
def test02SubstitutionsWithWeirdPaths() {
val conf = ConfigFactory.load("test02")

View File

@ -28,6 +28,12 @@ class ConfigValueTest extends TestUtils {
checkNotEqualObjects(a, b)
}
@Test
def configOriginSerializable() {
val a = SimpleConfigOrigin.newSimple("foo")
checkSerializable(a)
}
@Test
def configIntEquality() {
val a = intValue(42)
@ -39,6 +45,13 @@ class ConfigValueTest extends TestUtils {
checkNotEqualObjects(a, b)
}
@Test
def configIntSerializable() {
val a = intValue(42)
val b = checkSerializable(a)
assertEquals(42, b.unwrapped)
}
@Test
def configLongEquality() {
val a = longValue(Integer.MAX_VALUE + 42L)
@ -104,6 +117,16 @@ class ConfigValueTest extends TestUtils {
checkNotEqualObjects(b, b.toConfig())
}
@Test
def configObjectSerializable() {
val aMap = configMap("a" -> 1, "b" -> 2, "c" -> 3)
val a = new SimpleConfigObject(fakeOrigin(), aMap)
val b = checkSerializable(a)
assertEquals(1, b.toConfig.getInt("a"))
// check that deserialized Config and ConfigObject refer to each other
assertTrue(b.toConfig.root eq b)
}
@Test
def configListEquality() {
val aScalaSeq = Seq(1, 2, 3) map { intValue(_): AbstractConfigValue }
@ -117,6 +140,14 @@ class ConfigValueTest extends TestUtils {
checkNotEqualObjects(aList, bList)
}
@Test
def configListSerializable() {
val aScalaSeq = Seq(1, 2, 3) map { intValue(_): AbstractConfigValue }
val aList = new SimpleConfigList(fakeOrigin(), aScalaSeq.asJava)
val bList = checkSerializable(aList)
assertEquals(1, bList.get(0).unwrapped())
}
@Test
def configSubstitutionEquality() {
val a = subst("foo")
@ -128,6 +159,12 @@ class ConfigValueTest extends TestUtils {
checkNotEqualObjects(a, b)
}
@Test
def configSubstitutionSerializable() {
val a = subst("foo")
val b = checkSerializable(a)
}
@Test
def configDelayedMergeEquality() {
val s1 = subst("foo")
@ -141,6 +178,14 @@ class ConfigValueTest extends TestUtils {
checkNotEqualObjects(a, b)
}
@Test
def configDelayedMergeSerializable() {
val s1 = subst("foo")
val s2 = subst("bar")
val a = new ConfigDelayedMerge(fakeOrigin(), List[AbstractConfigValue](s1, s2).asJava)
val b = checkSerializable(a)
}
@Test
def configDelayedMergeObjectEquality() {
val empty = SimpleConfigObject.empty()
@ -155,6 +200,15 @@ class ConfigValueTest extends TestUtils {
checkNotEqualObjects(a, b)
}
@Test
def configDelayedMergeObjectSerializable() {
val empty = SimpleConfigObject.empty()
val s1 = subst("foo")
val s2 = subst("bar")
val a = new ConfigDelayedMergeObject(fakeOrigin(), List[AbstractConfigValue](empty, s1, s2).asJava)
val b = checkSerializable(a)
}
@Test
def valuesToString() {
// just check that these don't throw, the exact output

View File

@ -13,6 +13,10 @@ import com.typesafe.config.Config
import com.typesafe.config.ConfigSyntax
import com.typesafe.config.ConfigFactory
import java.io.File
import java.io.ByteArrayOutputStream
import java.io.ObjectOutputStream
import java.io.ByteArrayInputStream
import java.io.ObjectInputStream
abstract trait TestUtils {
protected def intercept[E <: Throwable: Manifest](block: => Unit): E = {
@ -82,6 +86,44 @@ abstract trait TestUtils {
checkNotEqualToRandomOtherThing(b)
}
private def copyViaSerialize(o: java.io.Serializable): AnyRef = {
val byteStream = new ByteArrayOutputStream()
val objectStream = new ObjectOutputStream(byteStream)
objectStream.writeObject(o)
objectStream.close()
val inStream = new ByteArrayInputStream(byteStream.toByteArray())
val inObjectStream = new ObjectInputStream(inStream)
val copy = inObjectStream.readObject()
inObjectStream.close()
copy
}
protected def checkSerializable[T: Manifest](o: T): T = {
checkEqualObjects(o, o)
assertTrue(o.getClass.getSimpleName + " not an instance of Serializable", o.isInstanceOf[java.io.Serializable])
val a = o.asInstanceOf[java.io.Serializable]
val b = try {
copyViaSerialize(a)
} catch {
case nf: ClassNotFoundException =>
throw new AssertionError("failed to make a copy via serialization, " +
"possibly caused by http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6446627",
nf)
case e: Exception =>
throw new AssertionError("failed to make a copy via serialization", e)
}
assertTrue("deserialized type " + b.getClass.getSimpleName + " doesn't match serialized type " + a.getClass.getSimpleName,
manifest[T].erasure.isAssignableFrom(b.getClass))
checkEqualObjects(a, b)
b.asInstanceOf[T]
}
def fakeOrigin() = {
SimpleConfigOrigin.newSimple("fake origin")
}
@ -372,7 +414,7 @@ abstract trait TestUtils {
protected def substInString(ref: String, optional: Boolean): ConfigSubstitution = {
import scala.collection.JavaConverters._
val path = Path.newPath(ref)
val pieces = List("start<", new SubstitutionExpression(path, optional), ">end")
val pieces = List[AnyRef]("start<", new SubstitutionExpression(path, optional), ">end")
new ConfigSubstitution(fakeOrigin(), pieces.asJava)
}