mirror of
https://github.com/lightbend/config.git
synced 2025-03-22 15:20:26 +08:00
Make concrete Config and ConfigValue serializable
This commit is contained in:
parent
846c8c116a
commit
2d8e42686c
@ -3,6 +3,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.typesafe.config.impl;
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
import com.typesafe.config.ConfigException;
|
import com.typesafe.config.ConfigException;
|
||||||
import com.typesafe.config.ConfigMergeable;
|
import com.typesafe.config.ConfigMergeable;
|
||||||
import com.typesafe.config.ConfigOrigin;
|
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().
|
* 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;
|
final private SimpleConfigOrigin origin;
|
||||||
|
|
||||||
|
@ -3,12 +3,13 @@
|
|||||||
*/
|
*/
|
||||||
package com.typesafe.config.impl;
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.typesafe.config.ConfigException;
|
import com.typesafe.config.ConfigException;
|
||||||
|
|
||||||
final class Path {
|
final class Path implements Serializable {
|
||||||
|
|
||||||
final private String first;
|
final private String first;
|
||||||
final private Path remainder;
|
final private Path remainder;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.typesafe.config.impl;
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.AbstractMap;
|
import java.util.AbstractMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
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
|
* with a one-level java.util.Map from paths to non-null values. Null values are
|
||||||
* not "in" the map.
|
* not "in" the map.
|
||||||
*/
|
*/
|
||||||
final class SimpleConfig implements Config, MergeableValue {
|
final class SimpleConfig implements Config, MergeableValue, Serializable {
|
||||||
|
|
||||||
final private AbstractConfigObject object;
|
final private AbstractConfigObject object;
|
||||||
|
|
||||||
|
@ -21,7 +21,8 @@ final class SimpleConfigList extends AbstractConfigValue implements ConfigList {
|
|||||||
final private boolean resolved;
|
final private boolean resolved;
|
||||||
|
|
||||||
SimpleConfigList(ConfigOrigin origin, List<AbstractConfigValue> value) {
|
SimpleConfigList(ConfigOrigin origin, List<AbstractConfigValue> value) {
|
||||||
this(origin, value, ResolveStatus.fromValues(value));
|
this(origin, value, ResolveStatus
|
||||||
|
.fromValues(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleConfigList(ConfigOrigin origin, List<AbstractConfigValue> value,
|
SimpleConfigList(ConfigOrigin origin, List<AbstractConfigValue> value,
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package com.typesafe.config.impl;
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
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,
|
// 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.
|
// 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 String description;
|
||||||
final private int lineNumber;
|
final private int lineNumber;
|
||||||
final private int endLineNumber;
|
final private int endLineNumber;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.typesafe.config.impl;
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
final class SubstitutionExpression {
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
final class SubstitutionExpression implements Serializable {
|
||||||
|
|
||||||
final private Path path;
|
final private Path path;
|
||||||
final private boolean optional;
|
final private boolean optional;
|
||||||
|
@ -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.
|
// 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
|
// Now it just tests that if you override with system props, you can use system props
|
||||||
// in substitutions.
|
// in substitutions.
|
||||||
|
@ -792,6 +792,12 @@ class ConfigTest extends TestUtils {
|
|||||||
assertEquals(None, entries.get("nulls.null"))
|
assertEquals(None, entries.get("nulls.null"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def test01Serializable() {
|
||||||
|
val conf = ConfigFactory.load("test01")
|
||||||
|
val confCopy = checkSerializable(conf)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def test02SubstitutionsWithWeirdPaths() {
|
def test02SubstitutionsWithWeirdPaths() {
|
||||||
val conf = ConfigFactory.load("test02")
|
val conf = ConfigFactory.load("test02")
|
||||||
|
@ -28,6 +28,12 @@ class ConfigValueTest extends TestUtils {
|
|||||||
checkNotEqualObjects(a, b)
|
checkNotEqualObjects(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def configOriginSerializable() {
|
||||||
|
val a = SimpleConfigOrigin.newSimple("foo")
|
||||||
|
checkSerializable(a)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def configIntEquality() {
|
def configIntEquality() {
|
||||||
val a = intValue(42)
|
val a = intValue(42)
|
||||||
@ -39,6 +45,13 @@ class ConfigValueTest extends TestUtils {
|
|||||||
checkNotEqualObjects(a, b)
|
checkNotEqualObjects(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def configIntSerializable() {
|
||||||
|
val a = intValue(42)
|
||||||
|
val b = checkSerializable(a)
|
||||||
|
assertEquals(42, b.unwrapped)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def configLongEquality() {
|
def configLongEquality() {
|
||||||
val a = longValue(Integer.MAX_VALUE + 42L)
|
val a = longValue(Integer.MAX_VALUE + 42L)
|
||||||
@ -104,6 +117,16 @@ class ConfigValueTest extends TestUtils {
|
|||||||
checkNotEqualObjects(b, b.toConfig())
|
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
|
@Test
|
||||||
def configListEquality() {
|
def configListEquality() {
|
||||||
val aScalaSeq = Seq(1, 2, 3) map { intValue(_): AbstractConfigValue }
|
val aScalaSeq = Seq(1, 2, 3) map { intValue(_): AbstractConfigValue }
|
||||||
@ -117,6 +140,14 @@ class ConfigValueTest extends TestUtils {
|
|||||||
checkNotEqualObjects(aList, bList)
|
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
|
@Test
|
||||||
def configSubstitutionEquality() {
|
def configSubstitutionEquality() {
|
||||||
val a = subst("foo")
|
val a = subst("foo")
|
||||||
@ -128,6 +159,12 @@ class ConfigValueTest extends TestUtils {
|
|||||||
checkNotEqualObjects(a, b)
|
checkNotEqualObjects(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def configSubstitutionSerializable() {
|
||||||
|
val a = subst("foo")
|
||||||
|
val b = checkSerializable(a)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def configDelayedMergeEquality() {
|
def configDelayedMergeEquality() {
|
||||||
val s1 = subst("foo")
|
val s1 = subst("foo")
|
||||||
@ -141,6 +178,14 @@ class ConfigValueTest extends TestUtils {
|
|||||||
checkNotEqualObjects(a, b)
|
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
|
@Test
|
||||||
def configDelayedMergeObjectEquality() {
|
def configDelayedMergeObjectEquality() {
|
||||||
val empty = SimpleConfigObject.empty()
|
val empty = SimpleConfigObject.empty()
|
||||||
@ -155,6 +200,15 @@ class ConfigValueTest extends TestUtils {
|
|||||||
checkNotEqualObjects(a, b)
|
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
|
@Test
|
||||||
def valuesToString() {
|
def valuesToString() {
|
||||||
// just check that these don't throw, the exact output
|
// just check that these don't throw, the exact output
|
||||||
|
@ -13,6 +13,10 @@ import com.typesafe.config.Config
|
|||||||
import com.typesafe.config.ConfigSyntax
|
import com.typesafe.config.ConfigSyntax
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.ObjectOutputStream
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.ObjectInputStream
|
||||||
|
|
||||||
abstract trait TestUtils {
|
abstract trait TestUtils {
|
||||||
protected def intercept[E <: Throwable: Manifest](block: => Unit): E = {
|
protected def intercept[E <: Throwable: Manifest](block: => Unit): E = {
|
||||||
@ -82,6 +86,44 @@ abstract trait TestUtils {
|
|||||||
checkNotEqualToRandomOtherThing(b)
|
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() = {
|
def fakeOrigin() = {
|
||||||
SimpleConfigOrigin.newSimple("fake origin")
|
SimpleConfigOrigin.newSimple("fake origin")
|
||||||
}
|
}
|
||||||
@ -372,7 +414,7 @@ abstract trait TestUtils {
|
|||||||
protected def substInString(ref: String, optional: Boolean): ConfigSubstitution = {
|
protected def substInString(ref: String, optional: Boolean): ConfigSubstitution = {
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
val path = Path.newPath(ref)
|
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)
|
new ConfigSubstitution(fakeOrigin(), pieces.asJava)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user