View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.io;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.DataInput;
24  import java.io.DataOutput;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.ObjectInputStream;
28  import java.io.ObjectOutputStream;
29  import java.io.Serializable;
30  import java.lang.reflect.Array;
31  import java.lang.reflect.InvocationTargetException;
32  import java.lang.reflect.Method;
33  import java.util.ArrayList;
34  import java.util.HashMap;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.NavigableSet;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  import org.apache.hadoop.conf.Configurable;
42  import org.apache.hadoop.conf.Configuration;
43  import org.apache.hadoop.conf.Configured;
44  import org.apache.hadoop.hbase.ClusterStatus;
45  import org.apache.hadoop.hbase.HColumnDescriptor;
46  import org.apache.hadoop.hbase.HConstants;
47  import org.apache.hadoop.hbase.HRegionInfo;
48  import org.apache.hadoop.hbase.HServerAddress;
49  import org.apache.hadoop.hbase.HServerInfo;
50  import org.apache.hadoop.hbase.HServerLoad;
51  import org.apache.hadoop.hbase.HServerLoadWithSeqIds;
52  import org.apache.hadoop.hbase.HTableDescriptor;
53  import org.apache.hadoop.hbase.KeyValue;
54  import org.apache.hadoop.hbase.client.Action;
55  import org.apache.hadoop.hbase.client.Append;
56  import org.apache.hadoop.hbase.client.Delete;
57  import org.apache.hadoop.hbase.client.Get;
58  import org.apache.hadoop.hbase.client.Increment;
59  import org.apache.hadoop.hbase.client.MultiAction;
60  import org.apache.hadoop.hbase.client.MultiResponse;
61  import org.apache.hadoop.hbase.client.Put;
62  import org.apache.hadoop.hbase.client.Result;
63  import org.apache.hadoop.hbase.client.Row;
64  import org.apache.hadoop.hbase.client.RowMutations;
65  import org.apache.hadoop.hbase.client.Scan;
66  import org.apache.hadoop.hbase.client.coprocessor.Exec;
67  import org.apache.hadoop.hbase.filter.BinaryComparator;
68  import org.apache.hadoop.hbase.filter.BitComparator;
69  import org.apache.hadoop.hbase.filter.ColumnCountGetFilter;
70  import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;
71  import org.apache.hadoop.hbase.filter.ColumnRangeFilter;
72  import org.apache.hadoop.hbase.filter.CompareFilter;
73  import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
74  import org.apache.hadoop.hbase.filter.DependentColumnFilter;
75  import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
76  import org.apache.hadoop.hbase.filter.FuzzyRowFilter;
77  import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
78  import org.apache.hadoop.hbase.filter.KeyOnlyFilter;
79  import org.apache.hadoop.hbase.filter.PageFilter;
80  import org.apache.hadoop.hbase.filter.PrefixFilter;
81  import org.apache.hadoop.hbase.filter.QualifierFilter;
82  import org.apache.hadoop.hbase.filter.RandomRowFilter;
83  import org.apache.hadoop.hbase.filter.RowFilter;
84  import org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter;
85  import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
86  import org.apache.hadoop.hbase.filter.SkipFilter;
87  import org.apache.hadoop.hbase.filter.ValueFilter;
88  import org.apache.hadoop.hbase.filter.WhileMatchFilter;
89  import org.apache.hadoop.hbase.filter.WritableByteArrayComparable;
90  import org.apache.hadoop.hbase.regionserver.HRegion;
91  import org.apache.hadoop.hbase.regionserver.RegionOpeningState;
92  import org.apache.hadoop.hbase.regionserver.wal.HLog;
93  import org.apache.hadoop.hbase.regionserver.wal.HLogKey;
94  import org.apache.hadoop.hbase.snapshot.HSnapshotDescription;
95  import org.apache.hadoop.hbase.util.Bytes;
96  import org.apache.hadoop.hbase.util.ProtoUtil;
97  import org.apache.hadoop.io.MapWritable;
98  import org.apache.hadoop.io.ObjectWritable;
99  import org.apache.hadoop.io.Text;
100 import org.apache.hadoop.io.Writable;
101 import org.apache.hadoop.io.WritableFactories;
102 import org.apache.hadoop.io.WritableUtils;
103 
104 import com.google.protobuf.Message;
105 
106 /**
107  * This is a customized version of the polymorphic hadoop
108  * {@link ObjectWritable}.  It removes UTF8 (HADOOP-414).
109  * Using {@link Text} intead of UTF-8 saves ~2% CPU between reading and writing
110  * objects running a short sequentialWrite Performance Evaluation test just in
111  * ObjectWritable alone; more when we're doing randomRead-ing.  Other
112  * optimizations include our passing codes for classes instead of the
113  * actual class names themselves.  This makes it so this class needs amendment
114  * if non-Writable classes are introduced -- if passed a Writable for which we
115  * have no code, we just do the old-school passing of the class name, etc. --
116  * but passing codes the  savings are large particularly when cell
117  * data is small (If < a couple of kilobytes, the encoding/decoding of class
118  * name and reflection to instantiate class was costing in excess of the cell
119  * handling).
120  */
121 public class HbaseObjectWritable implements Writable, WritableWithSize, Configurable {
122   protected final static Log LOG = LogFactory.getLog(HbaseObjectWritable.class);
123 
124   // Here we maintain two static maps of classes to code and vice versa.
125   // Add new classes+codes as wanted or figure way to auto-generate these
126   // maps from the HMasterInterface.
127   static final Map<Integer, Class<?>> CODE_TO_CLASS =
128     new HashMap<Integer, Class<?>>();
129   static final Map<Class<?>, Integer> CLASS_TO_CODE =
130     new HashMap<Class<?>, Integer>();
131   // Special code that means 'not-encoded'; in this case we do old school
132   // sending of the class name using reflection, etc.
133   private static final byte NOT_ENCODED = 0;
134   //Generic array means that the array type is not one of the pre-defined arrays
135   //in the CLASS_TO_CODE map, but we have to still encode the array since it's
136   //elements are serializable by this class.
137   private static final int GENERIC_ARRAY_CODE;
138   private static final int NEXT_CLASS_CODE;
139   static {
140     ////////////////////////////////////////////////////////////////////////////
141     // WARNING: Please do not insert, remove or swap any line in this static  //
142     // block.  Doing so would change or shift all the codes used to serialize //
143     // objects, which makes backwards compatibility very hard for clients.    //
144     // New codes should always be added at the end. Code removal is           //
145     // discouraged because code is a short now.                               //
146     ////////////////////////////////////////////////////////////////////////////
147 
148     int code = NOT_ENCODED + 1;
149     // Primitive types.
150     addToMap(Boolean.TYPE, code++);
151     addToMap(Byte.TYPE, code++);
152     addToMap(Character.TYPE, code++);
153     addToMap(Short.TYPE, code++);
154     addToMap(Integer.TYPE, code++);
155     addToMap(Long.TYPE, code++);
156     addToMap(Float.TYPE, code++);
157     addToMap(Double.TYPE, code++);
158     addToMap(Void.TYPE, code++);
159 
160     // Other java types
161     addToMap(String.class, code++);
162     addToMap(byte [].class, code++);
163     addToMap(byte [][].class, code++);
164 
165     // Hadoop types
166     addToMap(Text.class, code++);
167     addToMap(Writable.class, code++);
168     addToMap(Writable [].class, code++);
169     addToMap(HbaseMapWritable.class, code++);
170     addToMap(NullInstance.class, code++);
171 
172     // Hbase types
173     addToMap(HColumnDescriptor.class, code++);
174     addToMap(HConstants.Modify.class, code++);
175 
176     // We used to have a class named HMsg but its been removed.  Rather than
177     // just axe it, use following random Integer class -- we just chose any
178     // class from java.lang -- instead just so codes that follow stay
179     // in same relative place.
180     addToMap(Integer.class, code++);
181     addToMap(Integer[].class, code++);
182 
183     addToMap(HRegion.class, code++);
184     addToMap(HRegion[].class, code++);
185     addToMap(HRegionInfo.class, code++);
186     addToMap(HRegionInfo[].class, code++);
187     addToMap(HServerAddress.class, code++);
188     addToMap(HServerInfo.class, code++);
189     addToMap(HTableDescriptor.class, code++);
190     addToMap(MapWritable.class, code++);
191 
192     //
193     // HBASE-880
194     //
195     addToMap(ClusterStatus.class, code++);
196     addToMap(Delete.class, code++);
197     addToMap(Get.class, code++);
198     addToMap(KeyValue.class, code++);
199     addToMap(KeyValue[].class, code++);
200     addToMap(Put.class, code++);
201     addToMap(Put[].class, code++);
202     addToMap(Result.class, code++);
203     addToMap(Result[].class, code++);
204     addToMap(Scan.class, code++);
205 
206     addToMap(WhileMatchFilter.class, code++);
207     addToMap(PrefixFilter.class, code++);
208     addToMap(PageFilter.class, code++);
209     addToMap(InclusiveStopFilter.class, code++);
210     addToMap(ColumnCountGetFilter.class, code++);
211     addToMap(SingleColumnValueFilter.class, code++);
212     addToMap(SingleColumnValueExcludeFilter.class, code++);
213     addToMap(BinaryComparator.class, code++);
214     addToMap(BitComparator.class, code++);
215     addToMap(CompareFilter.class, code++);
216     addToMap(RowFilter.class, code++);
217     addToMap(ValueFilter.class, code++);
218     addToMap(QualifierFilter.class, code++);
219     addToMap(SkipFilter.class, code++);
220     addToMap(WritableByteArrayComparable.class, code++);
221     addToMap(FirstKeyOnlyFilter.class, code++);
222     addToMap(DependentColumnFilter.class, code++);
223 
224     addToMap(Delete [].class, code++);
225 
226     addToMap(HLog.Entry.class, code++);
227     addToMap(HLog.Entry[].class, code++);
228     addToMap(HLogKey.class, code++);
229 
230     addToMap(List.class, code++);
231 
232     addToMap(NavigableSet.class, code++);
233     addToMap(ColumnPrefixFilter.class, code++);
234 
235     // Multi
236     addToMap(Row.class, code++);
237     addToMap(Action.class, code++);
238     addToMap(MultiAction.class, code++);
239     addToMap(MultiResponse.class, code++);
240 
241     // coprocessor execution
242     addToMap(Exec.class, code++);
243     addToMap(Increment.class, code++);
244 
245     addToMap(KeyOnlyFilter.class, code++);
246 
247     // serializable
248     addToMap(Serializable.class, code++);
249 
250     addToMap(RandomRowFilter.class, code++);
251 
252     addToMap(CompareOp.class, code++);
253 
254     addToMap(ColumnRangeFilter.class, code++);
255 
256     addToMap(HServerLoad.class, code++);
257 
258     addToMap(RegionOpeningState.class, code++);
259 
260     addToMap(HTableDescriptor[].class, code++);
261 
262     addToMap(Append.class, code++);
263 
264     addToMap(RowMutations.class, code++);
265 
266     addToMap(Message.class, code++);
267 
268     //java.lang.reflect.Array is a placeholder for arrays not defined above
269     GENERIC_ARRAY_CODE = code++;
270     addToMap(Array.class, GENERIC_ARRAY_CODE);
271     
272     addToMap(FuzzyRowFilter.class, code++);
273 
274     // we aren't going to bump the rpc version number.
275     // we don't want to cause incompatiblity with older 0.94/0.92 clients.
276     addToMap(HSnapshotDescription.class, code);
277 
278     // for keeping backward-forward compat, we are using
279     // negative codes for HDP-specific classes
280     int negativeCode = -2;
281     addToMap(HServerLoadWithSeqIds.class, negativeCode--);
282 
283     // make sure that this is the last statement in this static block
284     NEXT_CLASS_CODE = code;
285   }
286 
287   private Class<?> declaredClass;
288   private Object instance;
289   private Configuration conf;
290 
291   /** default constructor for writable */
292   public HbaseObjectWritable() {
293     super();
294   }
295 
296   /**
297    * @param instance
298    */
299   public HbaseObjectWritable(Object instance) {
300     set(instance);
301   }
302 
303   /**
304    * @param declaredClass
305    * @param instance
306    */
307   public HbaseObjectWritable(Class<?> declaredClass, Object instance) {
308     this.declaredClass = declaredClass;
309     this.instance = instance;
310   }
311 
312   /** @return the instance, or null if none. */
313   public Object get() { return instance; }
314 
315   /** @return the class this is meant to be. */
316   public Class<?> getDeclaredClass() { return declaredClass; }
317 
318   /**
319    * Reset the instance.
320    * @param instance
321    */
322   public void set(Object instance) {
323     this.declaredClass = instance.getClass();
324     this.instance = instance;
325   }
326 
327   /**
328    * @see java.lang.Object#toString()
329    */
330   @Override
331   public String toString() {
332     return "OW[class=" + declaredClass + ",value=" + instance + "]";
333   }
334 
335 
336   public void readFields(DataInput in) throws IOException {
337     readObject(in, this, this.conf);
338   }
339 
340   public void write(DataOutput out) throws IOException {
341     writeObject(out, instance, declaredClass, conf);
342   }
343 
344   public long getWritableSize() {
345     return getWritableSize(instance, declaredClass, conf);
346   }
347 
348   private static class NullInstance extends Configured implements Writable {
349     Class<?> declaredClass;
350     /** default constructor for writable */
351     @SuppressWarnings("unused")
352     public NullInstance() { super(null); }
353 
354     /**
355      * @param declaredClass
356      * @param conf
357      */
358     public NullInstance(Class<?> declaredClass, Configuration conf) {
359       super(conf);
360       this.declaredClass = declaredClass;
361     }
362 
363     public void readFields(DataInput in) throws IOException {
364       this.declaredClass = CODE_TO_CLASS.get(WritableUtils.readVInt(in));
365     }
366 
367     public void write(DataOutput out) throws IOException {
368       writeClassCode(out, this.declaredClass);
369     }
370   }
371 
372   static Integer getClassCode(final Class<?> c)
373   throws IOException {
374     Integer code = CLASS_TO_CODE.get(c);
375     if (code == null ) {
376       if (List.class.isAssignableFrom(c)) {
377         code = CLASS_TO_CODE.get(List.class);
378       } else if (Writable.class.isAssignableFrom(c)) {
379         code = CLASS_TO_CODE.get(Writable.class);
380       } else if (c.isArray()) {
381         code = CLASS_TO_CODE.get(Array.class);
382       } else if (Message.class.isAssignableFrom(c)) {
383         code = CLASS_TO_CODE.get(Message.class);
384       } else if (Serializable.class.isAssignableFrom(c)){
385         code = CLASS_TO_CODE.get(Serializable.class);
386       }
387     }
388     return code;
389   }
390 
391   /**
392    * @return the next object code in the list.  Used in testing to verify that additional fields are not added
393    */
394   static int getNextClassCode(){
395     return NEXT_CLASS_CODE;
396   }
397 
398   /**
399    * Write out the code for passed Class.
400    * @param out
401    * @param c
402    * @throws IOException
403    */
404   static void writeClassCode(final DataOutput out, final Class<?> c)
405       throws IOException {
406     Integer code = getClassCode(c);
407 
408     if (code == null) {
409       LOG.error("Unsupported type " + c);
410       StackTraceElement[] els = new Exception().getStackTrace();
411       for(StackTraceElement elem : els) {
412         LOG.error(elem.getMethodName());
413       }
414       throw new UnsupportedOperationException("No code for unexpected " + c);
415     }
416     WritableUtils.writeVInt(out, code);
417   }
418 
419   public static long getWritableSize(Object instance, Class declaredClass,
420                                      Configuration conf) {
421     long size = Bytes.SIZEOF_BYTE; // code
422     if (instance == null) {
423       return 0L;
424     }
425 
426     if (declaredClass.isArray()) {
427       if (declaredClass.equals(Result[].class)) {
428 
429         return size + Result.getWriteArraySize((Result[])instance);
430       }
431     }
432     if (declaredClass.equals(Result.class)) {
433       Result r = (Result) instance;
434       // one extra class code for writable instance.
435       return r.getWritableSize() + size + Bytes.SIZEOF_BYTE;
436     }
437     return 0L; // no hint is the default.
438   }
439   /**
440    * Write a {@link Writable}, {@link String}, primitive type, or an array of
441    * the preceding.
442    * @param out
443    * @param instance
444    * @param declaredClass
445    * @param conf
446    * @throws IOException
447    */
448   @SuppressWarnings("unchecked")
449   public static void writeObject(DataOutput out, Object instance,
450                                  Class declaredClass,
451                                  Configuration conf)
452   throws IOException {
453 
454     Object instanceObj = instance;
455     Class declClass = declaredClass;
456 
457     if (instanceObj == null) {                       // null
458       instanceObj = new NullInstance(declClass, conf);
459       declClass = Writable.class;
460     }
461     writeClassCode(out, declClass);
462     if (declClass.isArray()) {                // array
463       // If bytearray, just dump it out -- avoid the recursion and
464       // byte-at-a-time we were previously doing.
465       if (declClass.equals(byte [].class)) {
466         Bytes.writeByteArray(out, (byte [])instanceObj);
467       } else if(declClass.equals(Result [].class)) {
468         Result.writeArray(out, (Result [])instanceObj);
469       } else {
470         //if it is a Generic array, write the element's type
471         if (getClassCode(declaredClass) == GENERIC_ARRAY_CODE) {
472           Class<?> componentType = declaredClass.getComponentType();
473           writeClass(out, componentType);
474         }
475 
476         int length = Array.getLength(instanceObj);
477         out.writeInt(length);
478         for (int i = 0; i < length; i++) {
479           Object item = Array.get(instanceObj, i);
480           writeObject(out, item,
481                     item.getClass(), conf);
482         }
483       }
484     } else if (List.class.isAssignableFrom(declClass)) {
485       List list = (List)instanceObj;
486       int length = list.size();
487       out.writeInt(length);
488       for (int i = 0; i < length; i++) {
489         Object elem = list.get(i);
490         writeObject(out, elem,
491                   elem == null ? Writable.class : elem.getClass(), conf);
492       }
493     } else if (declClass == String.class) {   // String
494       Text.writeString(out, (String)instanceObj);
495     } else if (declClass.isPrimitive()) {     // primitive type
496       if (declClass == Boolean.TYPE) {        // boolean
497         out.writeBoolean(((Boolean)instanceObj).booleanValue());
498       } else if (declClass == Character.TYPE) { // char
499         out.writeChar(((Character)instanceObj).charValue());
500       } else if (declClass == Byte.TYPE) {    // byte
501         out.writeByte(((Byte)instanceObj).byteValue());
502       } else if (declClass == Short.TYPE) {   // short
503         out.writeShort(((Short)instanceObj).shortValue());
504       } else if (declClass == Integer.TYPE) { // int
505         out.writeInt(((Integer)instanceObj).intValue());
506       } else if (declClass == Long.TYPE) {    // long
507         out.writeLong(((Long)instanceObj).longValue());
508       } else if (declClass == Float.TYPE) {   // float
509         out.writeFloat(((Float)instanceObj).floatValue());
510       } else if (declClass == Double.TYPE) {  // double
511         out.writeDouble(((Double)instanceObj).doubleValue());
512       } else if (declClass == Void.TYPE) {    // void
513       } else {
514         throw new IllegalArgumentException("Not a primitive: "+declClass);
515       }
516     } else if (declClass.isEnum()) {         // enum
517       Text.writeString(out, ((Enum)instanceObj).name());
518     } else if (Message.class.isAssignableFrom(declaredClass)) {
519       Text.writeString(out, instanceObj.getClass().getName());
520       ((Message)instance).writeDelimitedTo(
521           DataOutputOutputStream.constructOutputStream(out));
522     } else if (Writable.class.isAssignableFrom(declClass)) { // Writable
523       Class <?> c = instanceObj.getClass();
524       Integer code = CLASS_TO_CODE.get(c);
525       if (code == null) {
526         out.writeByte(NOT_ENCODED);
527         Text.writeString(out, c.getName());
528       } else {
529         writeClassCode(out, c);
530       }
531       ((Writable)instanceObj).write(out);
532     } else if (Serializable.class.isAssignableFrom(declClass)) {
533       Class <?> c = instanceObj.getClass();
534       Integer code = CLASS_TO_CODE.get(c);
535       if (code == null) {
536         out.writeByte(NOT_ENCODED);
537         Text.writeString(out, c.getName());
538       } else {
539         writeClassCode(out, c);
540       }
541       ByteArrayOutputStream bos = null;
542       ObjectOutputStream oos = null;
543       try{
544         bos = new ByteArrayOutputStream();
545         oos = new ObjectOutputStream(bos);
546         oos.writeObject(instanceObj);
547         byte[] value = bos.toByteArray();
548         out.writeInt(value.length);
549         out.write(value);
550       } finally {
551         if(bos!=null) bos.close();
552         if(oos!=null) oos.close();
553       }
554     } else {
555       throw new IOException("Can't write: "+instanceObj+" as "+declClass);
556     }
557   }
558 
559   /** Writes the encoded class code as defined in CLASS_TO_CODE, or
560    * the whole class name if not defined in the mapping.
561    */
562   static void writeClass(DataOutput out, Class<?> c) throws IOException {
563     Integer code = CLASS_TO_CODE.get(c);
564     if (code == null) {
565       WritableUtils.writeVInt(out, NOT_ENCODED);
566       Text.writeString(out, c.getName());
567     } else {
568       WritableUtils.writeVInt(out, code);
569     }
570   }
571 
572   /** Reads and returns the class as written by {@link #writeClass(DataOutput, Class)} */
573   static Class<?> readClass(Configuration conf, DataInput in) throws IOException {
574     Class<?> instanceClass = null;
575     int b = WritableUtils.readVInt(in);
576     if (b == NOT_ENCODED) {
577       String className = Text.readString(in);
578       try {
579         instanceClass = getClassByName(conf, className);
580       } catch (ClassNotFoundException e) {
581         LOG.error("Can't find class " + className, e);
582         throw new IOException("Can't find class " + className, e);
583       }
584     } else {
585       instanceClass = CODE_TO_CLASS.get(b);
586     }
587     return instanceClass;
588   }
589 
590   /**
591    * Read a {@link Writable}, {@link String}, primitive type, or an array of
592    * the preceding.
593    * @param in
594    * @param conf
595    * @return the object
596    * @throws IOException
597    */
598   public static Object readObject(DataInput in, Configuration conf)
599     throws IOException {
600     return readObject(in, null, conf);
601   }
602 
603   /**
604    * Read a {@link Writable}, {@link String}, primitive type, or an array of
605    * the preceding.
606    * @param in
607    * @param objectWritable
608    * @param conf
609    * @return the object
610    * @throws IOException
611    */
612   @SuppressWarnings("unchecked")
613   public static Object readObject(DataInput in,
614       HbaseObjectWritable objectWritable, Configuration conf)
615   throws IOException {
616     Class<?> declaredClass = CODE_TO_CLASS.get(WritableUtils.readVInt(in));
617     Object instance;
618     if (declaredClass.isPrimitive()) {            // primitive types
619       if (declaredClass == Boolean.TYPE) {             // boolean
620         instance = Boolean.valueOf(in.readBoolean());
621       } else if (declaredClass == Character.TYPE) {    // char
622         instance = Character.valueOf(in.readChar());
623       } else if (declaredClass == Byte.TYPE) {         // byte
624         instance = Byte.valueOf(in.readByte());
625       } else if (declaredClass == Short.TYPE) {        // short
626         instance = Short.valueOf(in.readShort());
627       } else if (declaredClass == Integer.TYPE) {      // int
628         instance = Integer.valueOf(in.readInt());
629       } else if (declaredClass == Long.TYPE) {         // long
630         instance = Long.valueOf(in.readLong());
631       } else if (declaredClass == Float.TYPE) {        // float
632         instance = Float.valueOf(in.readFloat());
633       } else if (declaredClass == Double.TYPE) {       // double
634         instance = Double.valueOf(in.readDouble());
635       } else if (declaredClass == Void.TYPE) {         // void
636         instance = null;
637       } else {
638         throw new IllegalArgumentException("Not a primitive: "+declaredClass);
639       }
640     } else if (declaredClass.isArray()) {              // array
641       if (declaredClass.equals(byte [].class)) {
642         instance = Bytes.readByteArray(in);
643       } else if(declaredClass.equals(Result [].class)) {
644         instance = Result.readArray(in);
645       } else {
646         int length = in.readInt();
647         instance = Array.newInstance(declaredClass.getComponentType(), length);
648         for (int i = 0; i < length; i++) {
649           Array.set(instance, i, readObject(in, conf));
650         }
651       }
652     } else if (declaredClass.equals(Array.class)) { //an array not declared in CLASS_TO_CODE
653       Class<?> componentType = readClass(conf, in);
654       int length = in.readInt();
655       instance = Array.newInstance(componentType, length);
656       for (int i = 0; i < length; i++) {
657         Array.set(instance, i, readObject(in, conf));
658       }
659     } else if (List.class.isAssignableFrom(declaredClass)) {            // List
660       int length = in.readInt();
661       instance = new ArrayList(length);
662       for (int i = 0; i < length; i++) {
663         ((ArrayList)instance).add(readObject(in, conf));
664       }
665     } else if (declaredClass == String.class) {        // String
666       instance = Text.readString(in);
667     } else if (declaredClass.isEnum()) {         // enum
668       instance = Enum.valueOf((Class<? extends Enum>) declaredClass,
669         Text.readString(in));
670     } else if (declaredClass == Message.class) {
671       String className = Text.readString(in);
672       try {
673         declaredClass = getClassByName(conf, className);
674         instance = tryInstantiateProtobuf(declaredClass, in);
675       } catch (ClassNotFoundException e) {
676         LOG.error("Can't find class " + className, e);
677         throw new IOException("Can't find class " + className, e);
678       }
679     } else {                                      // Writable or Serializable
680       Class instanceClass = null;
681       int b = (byte)WritableUtils.readVInt(in);
682       if (b == NOT_ENCODED) {
683         String className = Text.readString(in);
684         try {
685           instanceClass = getClassByName(conf, className);
686         } catch (ClassNotFoundException e) {
687           LOG.error("Can't find class " + className, e);
688           throw new IOException("Can't find class " + className, e);
689         }
690       } else {
691         instanceClass = CODE_TO_CLASS.get(b);
692       }
693       if(Writable.class.isAssignableFrom(instanceClass)){
694         Writable writable = WritableFactories.newInstance(instanceClass, conf);
695         try {
696           writable.readFields(in);
697         } catch (Exception e) {
698           LOG.error("Error in readFields", e);
699           throw new IOException("Error in readFields" , e);
700         }
701         instance = writable;
702         if (instanceClass == NullInstance.class) {  // null
703           declaredClass = ((NullInstance)instance).declaredClass;
704           instance = null;
705         }
706       } else {
707         int length = in.readInt();
708         byte[] objectBytes = new byte[length];
709         in.readFully(objectBytes);
710         ByteArrayInputStream bis = null;
711         ObjectInputStream ois = null;
712         try {
713           bis = new ByteArrayInputStream(objectBytes);
714           ois = new ObjectInputStream(bis);
715           instance = ois.readObject();
716         } catch (ClassNotFoundException e) {
717           LOG.error("Class not found when attempting to deserialize object", e);
718           throw new IOException("Class not found when attempting to " +
719               "deserialize object", e);
720         } finally {
721           if(bis!=null) bis.close();
722           if(ois!=null) ois.close();
723         }
724       }
725     }
726     if (objectWritable != null) {                 // store values
727       objectWritable.declaredClass = declaredClass;
728       objectWritable.instance = instance;
729     }
730     return instance;
731   }
732 
733   /**
734    * Try to instantiate a protocol buffer of the given message class
735    * from the given input stream.
736    *
737    * @param protoClass the class of the generated protocol buffer
738    * @param dataIn the input stream to read from
739    * @return the instantiated Message instance
740    * @throws IOException if an IO problem occurs
741    */
742   private static Message tryInstantiateProtobuf(
743       Class<?> protoClass,
744       DataInput dataIn) throws IOException {
745 
746     try {
747       if (dataIn instanceof InputStream) {
748         // We can use the built-in parseDelimitedFrom and not have to re-copy
749         // the data
750         Method parseMethod = getStaticProtobufMethod(protoClass,
751             "parseDelimitedFrom", InputStream.class);
752         return (Message)parseMethod.invoke(null, (InputStream)dataIn);
753       } else {
754         // Have to read it into a buffer first, since protobuf doesn't deal
755         // with the DataInput interface directly.
756 
757         // Read the size delimiter that writeDelimitedTo writes
758         int size = ProtoUtil.readRawVarint32(dataIn);
759         if (size < 0) {
760           throw new IOException("Invalid size: " + size);
761         }
762 
763         byte[] data = new byte[size];
764         dataIn.readFully(data);
765         Method parseMethod = getStaticProtobufMethod(protoClass,
766             "parseFrom", byte[].class);
767         return (Message)parseMethod.invoke(null, data);
768       }
769     } catch (InvocationTargetException e) {
770 
771       if (e.getCause() instanceof IOException) {
772         throw (IOException)e.getCause();
773       } else {
774         throw new IOException(e.getCause());
775       }
776     } catch (IllegalAccessException iae) {
777       throw new AssertionError("Could not access parse method in " +
778           protoClass);
779     }
780   }
781 
782   static Method getStaticProtobufMethod(Class<?> declaredClass, String method,
783       Class<?> ... args) {
784 
785     try {
786       return declaredClass.getMethod(method, args);
787     } catch (Exception e) {
788       // This is a bug in Hadoop - protobufs should all have this static method
789       throw new AssertionError("Protocol buffer class " + declaredClass +
790           " does not have an accessible parseFrom(InputStream) method!");
791     }
792   }
793 
794   @SuppressWarnings("unchecked")
795   private static Class getClassByName(Configuration conf, String className)
796   throws ClassNotFoundException {
797     if(conf != null) {
798       return conf.getClassByName(className);
799     }
800     ClassLoader cl = Thread.currentThread().getContextClassLoader();
801     if(cl == null) {
802       cl = HbaseObjectWritable.class.getClassLoader();
803     }
804     return Class.forName(className, true, cl);
805   }
806 
807   private static void addToMap(final Class<?> clazz, final int code) {
808     CLASS_TO_CODE.put(clazz, code);
809     CODE_TO_CLASS.put(code, clazz);
810   }
811 
812   public void setConf(Configuration conf) {
813     this.conf = conf;
814   }
815 
816   public Configuration getConf() {
817     return this.conf;
818   }
819 }