diff --git a/src/main/java/org/apache/hadoop/hbase/io/HbaseObjectWritable.java b/src/main/java/org/apache/hadoop/hbase/io/HbaseObjectWritable.java index 8c5ecd6..f347772 100644 --- a/src/main/java/org/apache/hadoop/hbase/io/HbaseObjectWritable.java +++ b/src/main/java/org/apache/hadoop/hbase/io/HbaseObjectWritable.java @@ -380,7 +380,9 @@ public class HbaseObjectWritable implements Writable, WritableWithSize, Configur declClass = Writable.class; } writeClassCode(out, declClass); - if (declClass.isArray()) { // array + // used to just be arrays. Now check for ones for which there's a + // class code, and let the others get scooped up by serializable. + if (declClass.isArray() && CLASS_TO_CODE.get(declClass)!=null) { // array // If bytearray, just dump it out -- avoid the recursion and // byte-at-a-time we were previously doing. if (declClass.equals(byte [].class)) { @@ -447,18 +449,27 @@ public class HbaseObjectWritable implements Writable, WritableWithSize, Configur } else { writeClassCode(out, c); } - ByteArrayOutputStream bos = null; - ObjectOutputStream oos = null; - try{ - bos = new ByteArrayOutputStream(); - oos = new ObjectOutputStream(bos); - oos.writeObject(instanceObj); - byte[] value = bos.toByteArray(); - out.writeInt(value.length); - out.write(value); - } finally { - if(bos!=null) bos.close(); - if(oos!=null) oos.close(); + if(declClass.isArray()){ + int length = Array.getLength(instanceObj); + out.writeInt(length); + for (int i = 0; i < length; i++) { + writeObject(out, Array.get(instanceObj, i), + declClass.getComponentType(), conf); + } + } else { + ByteArrayOutputStream bos = null; + ObjectOutputStream oos = null; + try{ + bos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(bos); + oos.writeObject(instanceObj); + byte[] value = bos.toByteArray(); + out.writeInt(value.length); + out.write(value); + } finally { + if(bos!=null) bos.close(); + if(oos!=null) oos.close(); + } } } else { throw new IOException("Can't write: "+instanceObj+" as "+declClass); @@ -567,21 +578,29 @@ public class HbaseObjectWritable implements Writable, WritableWithSize, Configur instance = null; } } else { - int length = in.readInt(); - byte[] objectBytes = new byte[length]; - in.readFully(objectBytes); - ByteArrayInputStream bis = null; - ObjectInputStream ois = null; - try { - bis = new ByteArrayInputStream(objectBytes); - ois = new ObjectInputStream(bis); - instance = ois.readObject(); - } catch (ClassNotFoundException e) { - LOG.error("Error in readFields", e); - throw new IOException("Error in readFields", e); - } finally { - if(bis!=null) bis.close(); - if(ois!=null) ois.close(); + if(instanceClass.isArray()){ + int length = in.readInt(); + instance = Array.newInstance(instanceClass.getComponentType(), length); + for(int i = 0; i< length; i++){ + Array.set(instance, i, HbaseObjectWritable.readObject(in, conf)); + } + } else { + int length = in.readInt(); + byte[] objectBytes = new byte[length]; + in.readFully(objectBytes); + ByteArrayInputStream bis = null; + ObjectInputStream ois = null; + try { + bis = new ByteArrayInputStream(objectBytes); + ois = new ObjectInputStream(bis); + instance = ois.readObject(); + } catch (ClassNotFoundException e) { + LOG.error("Error in readFields", e); + throw new IOException("Error in readFields", e); + } finally { + if(bis!=null) bis.close(); + if(ois!=null) ois.close(); + } } } } diff --git a/src/test/java/org/apache/hadoop/hbase/io/TestHbaseObjectWritable.java b/src/test/java/org/apache/hadoop/hbase/io/TestHbaseObjectWritable.java index 6bce5cd..2f0fa7d 100644 --- a/src/test/java/org/apache/hadoop/hbase/io/TestHbaseObjectWritable.java +++ b/src/test/java/org/apache/hadoop/hbase/io/TestHbaseObjectWritable.java @@ -21,6 +21,7 @@ package org.apache.hadoop.hbase.io; import java.io.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import junit.framework.TestCase; @@ -122,6 +123,20 @@ public class TestHbaseObjectWritable extends TestCase { assertEquals("mykey", ((CustomFilter)child).getKey()); } + public void testCustomWritableArray() throws Exception { + Configuration conf = HBaseConfiguration.create(); + + // test proper serialization of un-encoded custom writables + CustomWritable custom1 = new CustomWritable("test phrase"); + CustomWritable custom2 = new CustomWritable("test phrase2"); + CustomWritable[] customs = {custom1, custom2}; + Object obj = doType(conf, customs, CustomWritable[].class); + + assertTrue("Arrays should match " + Arrays.toString(customs) + ", " + + Arrays.toString((Object[]) obj), + Arrays.equals(customs, (Object[])obj)); + } + public void testCustomSerializable() throws Exception { Configuration conf = HBaseConfiguration.create(); @@ -132,6 +147,20 @@ public class TestHbaseObjectWritable extends TestCase { assertTrue(obj instanceof CustomSerializable); assertEquals("test phrase", ((CustomSerializable)obj).getValue()); } + + + public void testCustomSerializableArray() throws Exception { + Configuration conf = HBaseConfiguration.create(); + + // test proper serialization of un-encoded serialized java objects + CustomSerializable custom1 = new CustomSerializable("test phrase"); + CustomSerializable custom2 = new CustomSerializable("test phrase2"); + CustomSerializable[] custom = {custom1, custom2}; + Object obj = doType(conf, custom, CustomSerializable[].class); + assertTrue("Arrays should match " + Arrays.toString(custom) + ", " + + Arrays.toString((Object[]) obj), + Arrays.equals(custom, (Object[]) obj)); + } private Object doType(final Configuration conf, final Object value, final Class clazz) @@ -149,7 +178,7 @@ public class TestHbaseObjectWritable extends TestCase { } public static class CustomSerializable implements Serializable { - private static final long serialVersionUID = 1048445561865740632L; + private static final long serialVersionUID = 1048445561865740633L; private String value = null; public CustomSerializable() { @@ -167,6 +196,21 @@ public class TestHbaseObjectWritable extends TestCase { this.value = value; } + @Override + public boolean equals(Object obj) { + return (obj instanceof CustomSerializable) && ((CustomSerializable)obj).value.equals(value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return "<" + value + ">"; + } + } public static class CustomWritable implements Writable { @@ -190,6 +234,21 @@ public class TestHbaseObjectWritable extends TestCase { public void readFields(DataInput in) throws IOException { this.value = Text.readString(in); } + + @Override + public boolean equals(Object obj) { + return (obj instanceof CustomWritable) && ((CustomWritable)obj).value.equals(value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return "<" + value + ">"; + } } public static class CustomFilter extends FilterBase {