package org.yajul.serialization;
import org.junit.Assert;
import org.junit.Test;
import org.yajul.io.CountingObjectOutputStream;
import org.yajul.io.NullOutputStream;
import org.yajul.io.SerializationStats;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.Deflater;
import static org.junit.Assert.*;
/**
* Tests object serialization utilities.
* <br>
* User: josh
* Date: Aug 21, 2009
* Time: 4:31:41 PM
*/
public class SerializationTest {
private final static Logger log = Logger.getLogger(SerializationTest.class.getName());
@Test
public void testObjectSerialization() throws Exception {
Foo f = new Foo("one", 1);
byte[] bytes = SerializationHelper.toByteArray(f);
assertNotNull(bytes);
Foo f2 = (Foo) SerializationHelper.fromByteArray(bytes);
assertNotNull(f2);
assertEquals(f, f2);
assertNotSame(f, f2);
int size = SerializationStats.sizeOf(f);
log.log(Level.FINE,"size = " + size);
assertEquals(size, bytes.length);
ByteArrayWrapper<Foo> wrapper = new ByteArrayWrapper<Foo>(f);
assertTrue(wrapper.isUnwrapped());
ByteArrayWrapper<Foo> clone = SerializationHelper.clone(wrapper);
byte[] bytes1 = wrapper.wrap();
assertTrue(wrapper.isWrapped());
assertTrue(Arrays.equals(bytes, bytes1));
assertTrue(Arrays.equals(bytes, clone.wrap()));
f2 = (Foo) SerializationStats.autoUnwrap(clone);
assertNotSame(clone, f2);
assertEquals(f, f2);
assertNotSame(f, f2);
}
@Test
public void testCountingOutputStream() throws Exception {
Thing t = createThing();
NullOutputStream nos = new NullOutputStream();
CountingObjectOutputStream oos = new CountingObjectOutputStream(nos);
oos.writeObject(t);
oos.flush();
assertEquals(10, oos.getCounter(Foo.class.getName()).getCount());
assertEquals(1, oos.getCounter(Bar.class.getName()).getCount());
assertEquals(1, oos.getCounter(Thing.class.getName()).getCount());
SerializationStats.Stats stats = SerializationStats.getStats(t);
assertEquals(10, stats.getCounter(Foo.class.getName()).getCount());
assertEquals(1, stats.getCounter(Bar.class.getName()).getCount());
assertEquals(1, stats.getCounter(Thing.class.getName()).getCount());
}
private Thing createThing() {
Thing t = new Thing();
List<Foo> list = t.getFoos();
addFoos(list);
t.setBar(new Bar(3.14159, t));
return t;
}
private static void addFoos(List<Foo> list) {
for (int i = 0; i < 10; i++) {
list.add(new Foo("foo-" + i, i));
}
}
@Test
public void testReplacingAndResolving() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectReplacingOutputStream oros = new ObjectReplacingOutputStream(baos, new FooStubifier());
Thing t = createThing();
oros.writeObject(t);
oros.flush();
byte[] bytes = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectResolvingInputStream oris = new ObjectResolvingInputStream(bais, new FooDeStubifier());
Thing t2 = (Thing) oris.readObject();
checkFooList(t2.getFoos(), t.getFoos());
ThingEx tex = new ThingEx();
addFoos(tex.getFoos());
ThingEx tex2 = SerializationHelper.clone(tex);
checkFooList(tex2.getFoos(), tex.getFoos());
}
private void checkFooList(List<Foo> listA, List<Foo> listB) {
assertEquals(listB.size(), listA.size());
for (int i = 0; i < listA.size(); i++) {
Foo f1 = listA.get(i);
Foo f2 = listB.get(i);
assertEquals(f1.getNumber(), f2.getNumber());
Assert.assertFalse(f1.getName().equals(f2.getName()));
assertTrue(f1.getName().startsWith("fromstub-"));
}
}
@Test
public void testExternalizable() throws Exception {
Baz b = new Baz();
BazEx be = new BazEx();
assertEquals(b,be);
log.info("b = " + SerializationStats.sizeOf(b));
log.info("be = " + SerializationStats.sizeOf(be));
BazEx be2 = SerializationHelper.clone(be);
assertEquals(be, be2);
}
@Test
public void testCompress() throws Exception {
Baz b = new Baz();
byte[] bu = SerializationHelper.toByteArray(b);
byte[] bc = SerializationHelper.toCompressedByteArray(b, 512, Deflater.BEST_SPEED, 512);
log.info(String.format("b (comp) = %d bytes %d/%d %.2f%%",bc.length,
bc.length,bu.length,
((double)bc.length / (double)bu.length) * 100.0));
Baz clone = (Baz) SerializationHelper.fromCompressedByteArray(bc, 512);
assertEquals(b, clone);
long start = System.currentTimeMillis();
int iterations = 2000;
for (int i = 0; i < iterations; i++) {
byte[] bytes = SerializationHelper.toCompressedByteArray(b, 512, Deflater.BEST_SPEED, 512);
Baz another = (Baz) SerializationHelper.fromCompressedByteArray(bytes, 512);
assertEquals(another, b);
}
long end = System.currentTimeMillis();
log.info("with compression, elapsed = " + (end - start));
start = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
byte[] bytes = SerializationHelper.toByteArray(b);
Baz another = (Baz) SerializationHelper.fromByteArray(bytes);
assertEquals(another, b);
}
end = System.currentTimeMillis();
log.info("no compression, elapsed = " + (end - start));
}
public static enum MyEnum {
VALUE1,
VALUE2,
VALUE3
}
public static class Baz implements Serializable {
protected Long nullLong;
protected Long notNullLong = 123L;
protected Integer nullInt;
protected Integer notNullInt = 123;
protected MyEnum nullEnum;
protected MyEnum myEnum = MyEnum.VALUE1;
protected FooEx nullEx;
protected FooEx notNullEx = new FooEx("test",456);
protected List<FooEx> list = Arrays.asList(new FooEx[] {
new FooEx("one",1),
new FooEx("two",2),
new FooEx("three",3),
});
public Baz() {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Baz)) return false;
Baz baz = (Baz) o;
if (list != null ? !list.equals(baz.list) : baz.list != null) return false;
if (myEnum != baz.myEnum) return false;
if (!notNullEx.equals(baz.notNullEx)) return false;
if (!notNullInt.equals(baz.notNullInt)) return false;
if (!notNullLong.equals(baz.notNullLong)) return false;
if (nullEnum != baz.nullEnum) return false;
if (nullEx != null ? !nullEx.equals(baz.nullEx) : baz.nullEx != null) return false;
if (nullInt != null ? !nullInt.equals(baz.nullInt) : baz.nullInt != null) return false;
if (nullLong != null ? !nullLong.equals(baz.nullLong) : baz.nullLong != null) return false;
return true;
}
@Override
public int hashCode() {
int result = nullLong != null ? nullLong.hashCode() : 0;
result = 31 * result + notNullLong.hashCode();
result = 31 * result + (nullInt != null ? nullInt.hashCode() : 0);
result = 31 * result + notNullInt.hashCode();
result = 31 * result + (nullEnum != null ? nullEnum.hashCode() : 0);
result = 31 * result + (myEnum != null ? myEnum.hashCode() : 0);
result = 31 * result + (nullEx != null ? nullEx.hashCode() : 0);
result = 31 * result + notNullEx.hashCode();
result = 31 * result + (list != null ? list.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Baz{" +
"nullLong=" + nullLong +
", notNullLong=" + notNullLong +
", nullInt=" + nullInt +
", notNullInt=" + notNullInt +
", nullEnum=" + nullEnum +
", myEnum=" + myEnum +
", nullEx=" + nullEx +
", notNullEx=" + notNullEx +
", list=" + list +
'}';
}
}
public static class BazEx extends Baz implements Externalizable {
public void writeExternal(ObjectOutput out) throws IOException {
ExternalizableHelper.writeNullableLong(out, nullLong);
ExternalizableHelper.writeNullableLong(out, notNullLong);
ExternalizableHelper.writeNullableInteger(out, nullInt);
ExternalizableHelper.writeNullableInteger(out, notNullInt);
ExternalizableHelper.writeNullableEnum(out, nullEnum);
ExternalizableHelper.writeNullableEnum(out, myEnum);
ExternalizableHelper.writeNullable(out,nullEx);
ExternalizableHelper.writeNullable(out,notNullEx);
ExternalizableHelper.writeList(out,list);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
nullLong = ExternalizableHelper.readNullableLong(in);
notNullLong = ExternalizableHelper.readNullableLong(in);
nullInt = ExternalizableHelper.readNullableInteger(in);
notNullInt = ExternalizableHelper.readNullableInteger(in);
nullEnum = ExternalizableHelper.readNullableEnum(in,MyEnum.values());
myEnum = ExternalizableHelper.readNullableEnum(in,MyEnum.values());
nullEx = ExternalizableHelper.readNullable(in,FooEx.class);
notNullEx = ExternalizableHelper.readNullable(in,FooEx.class);
list = ExternalizableHelper.readArrayList(in,FooEx.class);
}
}
public static class Foo implements Serializable {
protected String name;
protected int number;
public Foo() {
}
public Foo(String name, int number) {
this.name = name;
this.number = number;
}
public String getName() {
return name;
}
public int getNumber() {
return number;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Foo)) return false;
Foo foo = (Foo) o;
if (number != foo.number) return false;
if (name != null ? !name.equals(foo.name) : foo.name != null) return false;
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + number;
return result;
}
@Override
public String toString() {
return "Foo{" +
"name='" + name + '\'' +
", number=" + number +
'}';
}
}
public static class FooEx extends Foo implements Externalizable {
public FooEx() {
}
public FooEx(String name, int number) {
super(name, number);
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(number);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String) in.readObject();
number = in.readInt();
}
}
public static class FooStub implements Serializable {
private int num;
public FooStub(int num) {
this.num = num;
}
}
public static class Bar implements Serializable {
private double factor;
private Thing parent;
public Bar(double factor, Thing parent) {
this.factor = factor;
this.parent = parent;
}
public double getFactor() {
return factor;
}
public Thing getParent() {
return parent;
}
}
public static class Thing implements Serializable {
private List<Foo> foos = new ArrayList<Foo>();
private Bar bar;
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
public List<Foo> getFoos() {
return foos;
}
}
public static class ThingEx implements Externalizable {
private List<Foo> foos = new ArrayList<Foo>();
public List<Foo> getFoos() {
return foos;
}
public void writeExternal(ObjectOutput out) throws IOException {
ObjectReplacingOutputStream oros = new ObjectReplacingOutputStream(out, new FooStubifier());
oros.writeObject(foos);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
ObjectResolvingInputStream oris = new ObjectResolvingInputStream(in, new FooDeStubifier());
foos = (List<Foo>) oris.readObject();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ThingEx)) return false;
ThingEx thingEx = (ThingEx) o;
if (foos != null ? !foos.equals(thingEx.foos) : thingEx.foos != null) return false;
return true;
}
@Override
public int hashCode() {
return foos != null ? foos.hashCode() : 0;
}
}
private static class FooDeStubifier implements ObjectResolver {
public Object resolveObject(Object obj) {
if (obj instanceof FooStub) {
FooStub fs = (FooStub) obj;
return new Foo("fromstub-", fs.num);
} else
return obj;
}
}
private static class FooStubifier implements ObjectReplacer {
public Object replaceObject(Object obj) {
if (obj instanceof Foo) {
Foo f = (Foo) obj;
return new FooStub(f.getNumber());
} else
return obj;
}
}
public static class FooBar implements Serializable {
private String one;
private MyEnum two;
private Date three;
public FooBar(String one, MyEnum two, Date three) {
this.one = one;
this.two = two;
this.three = three;
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException
{
int bits = ois.readInt();
if (ExternalizableHelper.isNotNullBit(bits,0))
one = ExternalizableHelper.readNullableString(ois);
if (ExternalizableHelper.isNotNullBit(bits,1))
two = ExternalizableHelper.readNullableEnumByte(ois,MyEnum.values());
if (ExternalizableHelper.isNotNullBit(bits,2))
three = new Date(ois.readLong());
}
private void writeObject(ObjectOutputStream oos) throws IOException
{
int bits = ExternalizableHelper.getNullBits(one,two,three);
oos.writeInt(bits);
if (ExternalizableHelper.isNotNullBit(bits,0))
ExternalizableHelper.writeNullableString(oos,one);
if (ExternalizableHelper.isNotNullBit(bits,1))
ExternalizableHelper.writeEnumByte(oos,two);
if (ExternalizableHelper.isNotNullBit(bits,2))
oos.writeLong(three.getTime());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof FooBar)) return false;
FooBar fooBar = (FooBar) o;
if (one != null ? !one.equals(fooBar.one) : fooBar.one != null) return false;
if (three != null ? !three.equals(fooBar.three) : fooBar.three != null) return false;
if (two != fooBar.two) return false;
return true;
}
@Override
public int hashCode() {
int result = one != null ? one.hashCode() : 0;
result = 31 * result + (two != null ? two.hashCode() : 0);
result = 31 * result + (three != null ? three.hashCode() : 0);
return result;
}
}
public static class Three implements Serializable {
private String one;
private MyEnum two;
private Date three;
public Three(String one, MyEnum two, Date three) {
this.one = one;
this.two = two;
this.three = three;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Three)) return false;
Three other = (Three) o;
if (one != null ? !one.equals(other.one) : other.one != null) return false;
if (three != null ? !three.equals(other.three) : other.three != null) return false;
if (two != other.two) return false;
return true;
}
@Override
public int hashCode() {
int result = one != null ? one.hashCode() : 0;
result = 31 * result + (two != null ? two.hashCode() : 0);
result = 31 * result + (three != null ? three.hashCode() : 0);
return result;
}
}
@Test
public void testNullableBits() throws Exception {
FooBar fooBar1 = new FooBar("one",MyEnum.VALUE1,new Date());
FooBar fooBar2 = SerializationHelper.clone(fooBar1);
assertNotSame(fooBar1,fooBar2);
assertEquals(fooBar1,fooBar2);
int size = SerializationStats.sizeOf(fooBar1);
System.out.println("size (nothing null) = " + size);
Three three = new Three("one",MyEnum.VALUE1,new Date());
size = SerializationStats.sizeOf(three);
System.out.println("size (nothing null, default serialization) = " + size);
fooBar1 = new FooBar(null, null, null);
fooBar2 = SerializationHelper.clone(fooBar1);
assertNotSame(fooBar1,fooBar2);
assertEquals(fooBar1,fooBar2);
size = SerializationStats.sizeOf(fooBar1);
System.out.println("size (all null) = " + size);
three = new Three(null,null,null);
size = SerializationStats.sizeOf(three);
System.out.println("size (all null, default serialization) = " + size);
}
@Test
public void testNullSerialization() throws Exception {
Baz b = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(b);
oos.close();
byte[] bytes = baos.toByteArray();
System.out.println("null object size = " + bytes.length);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
Baz b2 = (Baz) ois.readObject();
}
}