Merge pull request #462 from akornev/master

SerializedConfigValue.writeExternal and readExternal are inconsistent
This commit is contained in:
Havoc Pennington 2017-05-16 08:21:56 -04:00 committed by GitHub
commit d0021d1d3b
3 changed files with 61 additions and 8 deletions

View File

@ -3,8 +3,10 @@
*/
package com.typesafe.config.impl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.Externalizable;
@ -465,19 +467,23 @@ class SerializedConfigValue extends AbstractConfigValue implements Externalizabl
SerializedField code = readCode(in);
if (code == SerializedField.END_MARKER) {
return;
} else if (code == SerializedField.ROOT_VALUE) {
in.readInt(); // discard length
this.value = readValue(in, null /* baseOrigin */);
}
DataInput input = fieldIn(in);
if (code == SerializedField.ROOT_VALUE) {
this.value = readValue(input, null /* baseOrigin */);
} else if (code == SerializedField.ROOT_WAS_CONFIG) {
in.readInt(); // discard length
this.wasConfig = in.readBoolean();
} else {
// ignore unknown field
skipField(in);
this.wasConfig = input.readBoolean();
}
}
}
private DataInput fieldIn(ObjectInput in) throws IOException {
byte[] bytes = new byte[in.readInt()];
in.readFully(bytes);
return new DataInputStream(new ByteArrayInputStream(bytes));
}
private static ConfigException shouldNotBeUsed() {
return new ConfigException.BugOrBroken(SerializedConfigValue.class.getName()
+ " should not exist outside of serialization");

View File

@ -279,6 +279,23 @@ class ConfigValueTest extends TestUtils {
assertTrue(b.root.toConfig eq b)
}
/**
* Reproduces the issue <a href=https://github.com/typesafehub/config/issues/461>#461</a>.
* <p>
* We use a custom de-/serializer that encodes String objects in a JDK-incompatible way. Encoding used here
* is rather simplistic: a long indicating the length in bytes (JDK uses a variable length integer) followed
* by the string's bytes. Running this test with the original SerializedConfigValue.readExternal()
* implementation results in an EOFException thrown during deserialization.
*/
@Test
def configConfigCustomSerializable() {
val aMap = configMap("a" -> 1, "b" -> 2, "c" -> 3)
val expected = new SimpleConfigObject(fakeOrigin(), aMap).toConfig
val actual = checkSerializableWithCustomSerializer(expected)
assertEquals(expected, actual)
}
@Test
def configListEquality() {
val aScalaSeq = Seq(1, 2, 3) map { intValue(_): AbstractConfigValue }

View File

@ -16,6 +16,8 @@ import java.io.ObjectOutputStream
import java.io.ByteArrayInputStream
import java.io.ObjectInputStream
import java.io.NotSerializableException
import java.io.OutputStream
import java.io.InputStream
import scala.annotation.tailrec
import java.net.URL
import java.util.Locale
@ -883,4 +885,32 @@ abstract trait TestUtils {
deleteRecursive(scratch)
}
}
protected def checkSerializableWithCustomSerializer[T: Manifest](o: T): T = {
val byteStream = new ByteArrayOutputStream()
val objectStream = new CustomObjectOutputStream(byteStream)
objectStream.writeObject(o)
objectStream.close()
val inStream = new ByteArrayInputStream(byteStream.toByteArray)
val inObjectStream = new CustomObjectInputStream(inStream)
val copy = inObjectStream.readObject()
inObjectStream.close()
copy.asInstanceOf[T]
}
class CustomObjectOutputStream(out: OutputStream) extends ObjectOutputStream(out) {
override def writeUTF(str: String): Unit = {
val bytes = str.getBytes
writeLong(bytes.length)
write(bytes)
}
}
class CustomObjectInputStream(in: InputStream) extends ObjectInputStream(in) {
override def readUTF(): String = {
val bytes = new Array[Byte](readLong().toByte)
read(bytes)
new String(bytes)
}
}
}