Index: src/test/java/org/apache/hadoop/hbase/io/TestHbaseObjectWritable.java
===================================================================
--- src/test/java/org/apache/hadoop/hbase/io/TestHbaseObjectWritable.java (revision 1297245)
+++ src/test/java/org/apache/hadoop/hbase/io/TestHbaseObjectWritable.java (working copy)
@@ -19,7 +19,14 @@
*/
package org.apache.hadoop.hbase.io;
-import java.io.*;
+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.IOException;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@@ -33,11 +40,14 @@
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.PrefixFilter;
import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparator;
import org.junit.Assert;
+import com.google.common.collect.Lists;
+
public class TestHbaseObjectWritable extends TestCase {
@Override
@@ -56,14 +66,14 @@
/*
* This is the code used to generate byte[] where
* HbaseObjectWritable used byte for code
- *
+ *
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(byteStream);
HbaseObjectWritable.writeObject(out, bytes, byte[].class, conf);
byte[] ba = byteStream.toByteArray();
out.close();
*/
-
+
/*
* byte array generated by the folowing call
* HbaseObjectWritable.writeObject(out, new Text("Old"), Text.class, conf);
@@ -72,7 +82,7 @@
Text txt = (Text)readByteArray(conf, baForText);
Text oldTxt = new Text("Old");
assertEquals(txt, oldTxt);
-
+
final byte A = 'A';
byte [] bytes = new byte[1];
bytes[0] = A;
@@ -84,7 +94,7 @@
byte[] baOut = (byte[])readByteArray(conf, baForByteArray);
assertTrue(Bytes.equals(baOut, bytes));
}
-
+
/*
* helper method which reads byte array using HbaseObjectWritable.readObject()
*/
@@ -97,7 +107,7 @@
dis.close();
return product;
}
-
+
@SuppressWarnings("boxing")
public void testReadObjectDataInputConfiguration() throws IOException {
Configuration conf = HBaseConfiguration.create();
@@ -170,7 +180,7 @@
assertTrue(child instanceof CustomFilter);
assertEquals("mykey", ((CustomFilter)child).getKey());
}
-
+
public void testCustomSerializable() throws Exception {
Configuration conf = HBaseConfiguration.create();
@@ -196,26 +206,111 @@
dis.close();
return product;
}
-
+
+ public static class A extends IntWritable {
+ public A() {}
+ public A(int a) {super(a);}
+ }
+
+ public static class B extends A {
+ int b;
+ public B() { }
+ public B(int a, int b) {
+ super(a);
+ this.b = b;
+ }
+ @Override
+ public void write(DataOutput out) throws IOException {
+ super.write(out);
+ out.writeInt(b);
+ }
+
+ @Override
+ public void readFields(DataInput in) throws IOException {
+ super.readFields(in);
+ this.b = in.readInt();
+ }
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof B) {
+ return this.get() == ((B) o).get() && this.b == ((B) o).b;
+ }
+ return false;
+ }
+ }
+
+ /** Tests for serialization of List and Arrays */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public void testPolymorphismInSequences() throws Exception {
+ Configuration conf = HBaseConfiguration.create();
+ Object ret;
+
+ //test with lists
+ List list = Lists.newArrayList(new A(42), new B(10, 100));
+ ret = doType(conf, list, list.getClass());
+ assertEquals(ret, list);
+
+ //test with Writable[]
+ Writable[] warr = new Writable[] {new A(42), new B(10, 100)};
+ ret = doType(conf, warr, warr.getClass());
+ Assert.assertArrayEquals((Writable[])ret, warr);
+
+ //test with arrays
+ A[] arr = new A[] {new A(42), new B(10, 100)};
+ ret = doType(conf, arr, arr.getClass());
+ Assert.assertArrayEquals((A[])ret, arr);
+
+ //test with double array
+ A[][] darr = new A[][] {new A[] { new A(42), new B(10, 100)}, new A[] {new A(12)}};
+ ret = doType(conf, darr, darr.getClass());
+ Assert.assertArrayEquals((A[][])ret, darr);
+
+ //test with List of arrays
+ List larr = Lists.newArrayList(arr, new A[] {new A(99)});
+ ret = doType(conf, larr, larr.getClass());
+ List lret = (List) ret;
+ assertEquals(larr.size(), lret.size());
+ for (int i=0; i declaredClass;
@@ -329,26 +348,33 @@
}
}
- /**
- * Write out the code byte for passed Class.
- * @param out
- * @param c
- * @throws IOException
- */
- static void writeClassCode(final DataOutput out, final Class> c)
+ static Integer getClassCode(final Class> c)
throws IOException {
Integer code = CLASS_TO_CODE.get(c);
if (code == null ) {
- if ( List.class.isAssignableFrom(c)) {
+ if (List.class.isAssignableFrom(c)) {
code = CLASS_TO_CODE.get(List.class);
- }
- else if (Writable.class.isAssignableFrom(c)) {
+ } else if (Writable.class.isAssignableFrom(c)) {
code = CLASS_TO_CODE.get(Writable.class);
- }
- else if (Serializable.class.isAssignableFrom(c)){
+ } else if (c.isArray()) {
+ code = CLASS_TO_CODE.get(Array.class);
+ } else if (Serializable.class.isAssignableFrom(c)){
code = CLASS_TO_CODE.get(Serializable.class);
}
}
+ return code;
+ }
+
+ /**
+ * Write out the code for passed Class.
+ * @param out
+ * @param c
+ * @throws IOException
+ */
+ static void writeClassCode(final DataOutput out, final Class> c)
+ throws IOException {
+ Integer code = getClassCode(c);
+
if (code == null) {
LOG.error("Unsupported type " + c);
StackTraceElement[] els = new Exception().getStackTrace();
@@ -360,7 +386,6 @@
WritableUtils.writeVInt(out, code);
}
-
public static long getWritableSize(Object instance, Class declaredClass,
Configuration conf) {
long size = Bytes.SIZEOF_BYTE; // code
@@ -412,11 +437,18 @@
} else if(declClass.equals(Result [].class)) {
Result.writeArray(out, (Result [])instanceObj);
} else {
+ //if it is a Generic array, write the element's type
+ if (getClassCode(declaredClass) == GENERIC_ARRAY_CODE) {
+ Class> componentType = declaredClass.getComponentType();
+ writeClass(out, componentType);
+ }
+
int length = Array.getLength(instanceObj);
out.writeInt(length);
for (int i = 0; i < length; i++) {
- writeObject(out, Array.get(instanceObj, i),
- declClass.getComponentType(), conf);
+ Object item = Array.get(instanceObj, i);
+ writeObject(out, item,
+ item.getClass(), conf);
}
}
} else if (List.class.isAssignableFrom(declClass)) {
@@ -489,7 +521,37 @@
}
}
+ /** Writes the encoded class code as defined in CLASS_TO_CODE, or
+ * the whole class name if not defined in the mapping.
+ */
+ static void writeClass(DataOutput out, Class> c) throws IOException {
+ Integer code = CLASS_TO_CODE.get(c);
+ if (code == null) {
+ WritableUtils.writeVInt(out, NOT_ENCODED);
+ Text.writeString(out, c.getName());
+ } else {
+ WritableUtils.writeVInt(out, code);
+ }
+ }
+ /** Reads and returns the class as written by {@link #writeClass(DataOutput, Class)} */
+ static Class> readClass(Configuration conf, DataInput in) throws IOException {
+ Class> instanceClass = null;
+ int b = (byte)WritableUtils.readVInt(in);
+ if (b == NOT_ENCODED) {
+ String className = Text.readString(in);
+ try {
+ instanceClass = getClassByName(conf, className);
+ } catch (ClassNotFoundException e) {
+ LOG.error("Can't find class " + className, e);
+ throw new IOException("Can't find class " + className, e);
+ }
+ } else {
+ instanceClass = CODE_TO_CLASS.get(b);
+ }
+ return instanceClass;
+ }
+
/**
* Read a {@link Writable}, {@link String}, primitive type, or an array of
* the preceding.
@@ -552,6 +614,13 @@
Array.set(instance, i, readObject(in, conf));
}
}
+ } else if (declaredClass.equals(Array.class)) { //an array not declared in CLASS_TO_CODE
+ Class> componentType = readClass(conf, in);
+ int length = in.readInt();
+ instance = Array.newInstance(componentType, length);
+ for (int i = 0; i < length; i++) {
+ Array.set(instance, i, readObject(in, conf));
+ }
} else if (List.class.isAssignableFrom(declaredClass)) { // List
int length = in.readInt();
instance = new ArrayList(length);