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.InputStream;
26  import java.io.IOException;
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.DoNotRetryIOException;
46  import org.apache.hadoop.hbase.HColumnDescriptor;
47  import org.apache.hadoop.hbase.HConstants;
48  import org.apache.hadoop.hbase.HRegionInfo;
49  import org.apache.hadoop.hbase.HServerAddress;
50  import org.apache.hadoop.hbase.HServerInfo;
51  import org.apache.hadoop.hbase.HServerLoad;
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     // make sure that this is the last statement in this static block
279     NEXT_CLASS_CODE = code;
280   }
281 
282   private Class<?> declaredClass;
283   private Object instance;
284   private Configuration conf;
285 
286   /** default constructor for writable */
287   public HbaseObjectWritable() {
288     super();
289   }
290 
291   /**
292    * @param instance
293    */
294   public HbaseObjectWritable(Object instance) {
295     set(instance);
296   }
297 
298   /**
299    * @param declaredClass
300    * @param instance
301    */
302   public HbaseObjectWritable(Class<?> declaredClass, Object instance) {
303     this.declaredClass = declaredClass;
304     this.instance = instance;
305   }
306 
307   /** @return the instance, or null if none. */
308   public Object get() { return instance; }
309 
310   /** @return the class this is meant to be. */
311   public Class<?> getDeclaredClass() { return declaredClass; }
312 
313   /**
314    * Reset the instance.
315    * @param instance
316    */
317   public void set(Object instance) {
318     this.declaredClass = instance.getClass();
319     this.instance = instance;
320   }
321 
322   /**
323    * @see java.lang.Object#toString()
324    */
325   @Override
326   public String toString() {
327     return "OW[class=" + declaredClass + ",value=" + instance + "]";
328   }
329 
330 
331   public void readFields(DataInput in) throws IOException {
332     readObject(in, this, this.conf);
333   }
334 
335   public void write(DataOutput out) throws IOException {
336     writeObject(out, instance, declaredClass, conf);
337   }
338 
339   public long getWritableSize() {
340     return getWritableSize(instance, declaredClass, conf);
341   }
342 
343   private static class NullInstance extends Configured implements Writable {
344     Class<?> declaredClass;
345     /** default constructor for writable */
346     @SuppressWarnings("unused")
347     public NullInstance() { super(null); }
348 
349     /**
350      * @param declaredClass
351      * @param conf
352      */
353     public NullInstance(Class<?> declaredClass, Configuration conf) {
354       super(conf);
355       this.declaredClass = declaredClass;
356     }
357 
358     public void readFields(DataInput in) throws IOException {
359       this.declaredClass = CODE_TO_CLASS.get(WritableUtils.readVInt(in));
360     }
361 
362     public void write(DataOutput out) throws IOException {
363       writeClassCode(out, this.declaredClass);
364     }
365   }
366 
367   static Integer getClassCode(final Class<?> c)
368   throws IOException {
369     Integer code = CLASS_TO_CODE.get(c);
370     if (code == null ) {
371       if (List.class.isAssignableFrom(c)) {
372         code = CLASS_TO_CODE.get(List.class);
373       } else if (Writable.class.isAssignableFrom(c)) {
374         code = CLASS_TO_CODE.get(Writable.class);
375       } else if (c.isArray()) {
376         code = CLASS_TO_CODE.get(Array.class);
377       } else if (Message.class.isAssignableFrom(c)) {
378         code = CLASS_TO_CODE.get(Message.class);
379       } else if (Serializable.class.isAssignableFrom(c)){
380         code = CLASS_TO_CODE.get(Serializable.class);
381       }
382     }
383     return code;
384   }
385 
386   /**
387    * @return the next object code in the list.  Used in testing to verify that additional fields are not added 
388    */
389   static int getNextClassCode(){
390     return NEXT_CLASS_CODE;
391   }
392 
393   /**
394    * Write out the code for passed Class.
395    * @param out
396    * @param c
397    * @throws IOException
398    */
399   static void writeClassCode(final DataOutput out, final Class<?> c)
400       throws IOException {
401     Integer code = getClassCode(c);
402 
403     if (code == null) {
404       LOG.error("Unsupported type " + c);
405       StackTraceElement[] els = new Exception().getStackTrace();
406       for(StackTraceElement elem : els) {
407         LOG.error(elem.getMethodName());
408       }
409       throw new UnsupportedOperationException("No code for unexpected " + c);
410     }
411     WritableUtils.writeVInt(out, code);
412   }
413 
414   public static long getWritableSize(Object instance, Class declaredClass,
415                                      Configuration conf) {
416     long size = Bytes.SIZEOF_BYTE; // code
417     if (instance == null) {
418       return 0L;
419     }
420 
421     if (declaredClass.isArray()) {
422       if (declaredClass.equals(Result[].class)) {
423 
424         return size + Result.getWriteArraySize((Result[])instance);
425       }
426     }
427     if (declaredClass.equals(Result.class)) {
428       Result r = (Result) instance;
429       // one extra class code for writable instance.
430       return r.getWritableSize() + size + Bytes.SIZEOF_BYTE;
431     }
432     return 0L; // no hint is the default.
433   }
434   /**
435    * Write a {@link Writable}, {@link String}, primitive type, or an array of
436    * the preceding.
437    * @param out
438    * @param instance
439    * @param declaredClass
440    * @param conf
441    * @throws IOException
442    */
443   @SuppressWarnings("unchecked")
444   public static void writeObject(DataOutput out, Object instance,
445                                  Class declaredClass,
446                                  Configuration conf)
447   throws IOException {
448 
449     Object instanceObj = instance;
450     Class declClass = declaredClass;
451 
452     if (instanceObj == null) {                       // null
453       instanceObj = new NullInstance(declClass, conf);
454       declClass = Writable.class;
455     }
456     writeClassCode(out, declClass);
457     if (declClass.isArray()) {                // array
458       // If bytearray, just dump it out -- avoid the recursion and
459       // byte-at-a-time we were previously doing.
460       if (declClass.equals(byte [].class)) {
461         Bytes.writeByteArray(out, (byte [])instanceObj);
462       } else if(declClass.equals(Result [].class)) {
463         Result.writeArray(out, (Result [])instanceObj);
464       } else {
465         //if it is a Generic array, write the element's type
466         if (getClassCode(declaredClass) == GENERIC_ARRAY_CODE) {
467           Class<?> componentType = declaredClass.getComponentType();
468           writeClass(out, componentType);
469         }
470 
471         int length = Array.getLength(instanceObj);
472         out.writeInt(length);
473         for (int i = 0; i < length; i++) {
474           Object item = Array.get(instanceObj, i);
475           writeObject(out, item,
476                     item.getClass(), conf);
477         }
478       }
479     } else if (List.class.isAssignableFrom(declClass)) {
480       List list = (List)instanceObj;
481       int length = list.size();
482       out.writeInt(length);
483       for (int i = 0; i < length; i++) {
484         Object elem = list.get(i);
485         writeObject(out, elem,
486                   elem == null ? Writable.class : elem.getClass(), conf);
487       }
488     } else if (declClass == String.class) {   // String
489       Text.writeString(out, (String)instanceObj);
490     } else if (declClass.isPrimitive()) {     // primitive type
491       if (declClass == Boolean.TYPE) {        // boolean
492         out.writeBoolean(((Boolean)instanceObj).booleanValue());
493       } else if (declClass == Character.TYPE) { // char
494         out.writeChar(((Character)instanceObj).charValue());
495       } else if (declClass == Byte.TYPE) {    // byte
496         out.writeByte(((Byte)instanceObj).byteValue());
497       } else if (declClass == Short.TYPE) {   // short
498         out.writeShort(((Short)instanceObj).shortValue());
499       } else if (declClass == Integer.TYPE) { // int
500         out.writeInt(((Integer)instanceObj).intValue());
501       } else if (declClass == Long.TYPE) {    // long
502         out.writeLong(((Long)instanceObj).longValue());
503       } else if (declClass == Float.TYPE) {   // float
504         out.writeFloat(((Float)instanceObj).floatValue());
505       } else if (declClass == Double.TYPE) {  // double
506         out.writeDouble(((Double)instanceObj).doubleValue());
507       } else if (declClass == Void.TYPE) {    // void
508       } else {
509         throw new IllegalArgumentException("Not a primitive: "+declClass);
510       }
511     } else if (declClass.isEnum()) {         // enum
512       Text.writeString(out, ((Enum)instanceObj).name());
513     } else if (Message.class.isAssignableFrom(declaredClass)) {
514       Text.writeString(out, instanceObj.getClass().getName());
515       ((Message)instance).writeDelimitedTo(
516           DataOutputOutputStream.constructOutputStream(out));
517     } else if (Writable.class.isAssignableFrom(declClass)) { // Writable
518       Class <?> c = instanceObj.getClass();
519       Integer code = CLASS_TO_CODE.get(c);
520       if (code == null) {
521         out.writeByte(NOT_ENCODED);
522         Text.writeString(out, c.getName());
523       } else {
524         writeClassCode(out, c);
525       }
526       ((Writable)instanceObj).write(out);
527     } else if (Serializable.class.isAssignableFrom(declClass)) {
528       Class <?> c = instanceObj.getClass();
529       Integer code = CLASS_TO_CODE.get(c);
530       if (code == null) {
531         out.writeByte(NOT_ENCODED);
532         Text.writeString(out, c.getName());
533       } else {
534         writeClassCode(out, c);
535       }
536       ByteArrayOutputStream bos = null;
537       ObjectOutputStream oos = null;
538       try{
539         bos = new ByteArrayOutputStream();
540         oos = new ObjectOutputStream(bos);
541         oos.writeObject(instanceObj);
542         byte[] value = bos.toByteArray();
543         out.writeInt(value.length);
544         out.write(value);
545       } finally {
546         if(bos!=null) bos.close();
547         if(oos!=null) oos.close();
548       }
549     } else {
550       throw new IOException("Can't write: "+instanceObj+" as "+declClass);
551     }
552   }
553 
554   /** Writes the encoded class code as defined in CLASS_TO_CODE, or
555    * the whole class name if not defined in the mapping.
556    */
557   static void writeClass(DataOutput out, Class<?> c) throws IOException {
558     Integer code = CLASS_TO_CODE.get(c);
559     if (code == null) {
560       WritableUtils.writeVInt(out, NOT_ENCODED);
561       Text.writeString(out, c.getName());
562     } else {
563       WritableUtils.writeVInt(out, code);
564     }
565   }
566 
567   /** Reads and returns the class as written by {@link #writeClass(DataOutput, Class)} */
568   static Class<?> readClass(Configuration conf, DataInput in) throws IOException {
569     Class<?> instanceClass = null;
570     int b = (byte)WritableUtils.readVInt(in);
571     if (b == NOT_ENCODED) {
572       String className = Text.readString(in);
573       try {
574         instanceClass = getClassByName(conf, className);
575       } catch (ClassNotFoundException e) {
576         LOG.error("Can't find class " + className, e);
577         throw new IOException("Can't find class " + className, e);
578       }
579     } else {
580       instanceClass = CODE_TO_CLASS.get(b);
581     }
582     return instanceClass;
583   }
584 
585   /**
586    * Read a {@link Writable}, {@link String}, primitive type, or an array of
587    * the preceding.
588    * @param in
589    * @param conf
590    * @return the object
591    * @throws IOException
592    */
593   public static Object readObject(DataInput in, Configuration conf)
594     throws IOException {
595     return readObject(in, null, conf);
596   }
597 
598   /**
599    * Read a {@link Writable}, {@link String}, primitive type, or an array of
600    * the preceding.
601    * @param in
602    * @param objectWritable
603    * @param conf
604    * @return the object
605    * @throws IOException
606    */
607   @SuppressWarnings("unchecked")
608   public static Object readObject(DataInput in,
609       HbaseObjectWritable objectWritable, Configuration conf)
610   throws IOException {
611     Class<?> declaredClass = CODE_TO_CLASS.get(WritableUtils.readVInt(in));
612     Object instance;
613     if (declaredClass.isPrimitive()) {            // primitive types
614       if (declaredClass == Boolean.TYPE) {             // boolean
615         instance = Boolean.valueOf(in.readBoolean());
616       } else if (declaredClass == Character.TYPE) {    // char
617         instance = Character.valueOf(in.readChar());
618       } else if (declaredClass == Byte.TYPE) {         // byte
619         instance = Byte.valueOf(in.readByte());
620       } else if (declaredClass == Short.TYPE) {        // short
621         instance = Short.valueOf(in.readShort());
622       } else if (declaredClass == Integer.TYPE) {      // int
623         instance = Integer.valueOf(in.readInt());
624       } else if (declaredClass == Long.TYPE) {         // long
625         instance = Long.valueOf(in.readLong());
626       } else if (declaredClass == Float.TYPE) {        // float
627         instance = Float.valueOf(in.readFloat());
628       } else if (declaredClass == Double.TYPE) {       // double
629         instance = Double.valueOf(in.readDouble());
630       } else if (declaredClass == Void.TYPE) {         // void
631         instance = null;
632       } else {
633         throw new IllegalArgumentException("Not a primitive: "+declaredClass);
634       }
635     } else if (declaredClass.isArray()) {              // array
636       if (declaredClass.equals(byte [].class)) {
637         instance = Bytes.readByteArray(in);
638       } else if(declaredClass.equals(Result [].class)) {
639         instance = Result.readArray(in);
640       } else {
641         int length = in.readInt();
642         instance = Array.newInstance(declaredClass.getComponentType(), length);
643         for (int i = 0; i < length; i++) {
644           Array.set(instance, i, readObject(in, conf));
645         }
646       }
647     } else if (declaredClass.equals(Array.class)) { //an array not declared in CLASS_TO_CODE
648       Class<?> componentType = readClass(conf, in);
649       int length = in.readInt();
650       instance = Array.newInstance(componentType, length);
651       for (int i = 0; i < length; i++) {
652         Array.set(instance, i, readObject(in, conf));
653       }
654     } else if (List.class.isAssignableFrom(declaredClass)) {            // List
655       int length = in.readInt();
656       instance = new ArrayList(length);
657       for (int i = 0; i < length; i++) {
658         ((ArrayList)instance).add(readObject(in, conf));
659       }
660     } else if (declaredClass == String.class) {        // String
661       instance = Text.readString(in);
662     } else if (declaredClass.isEnum()) {         // enum
663       instance = Enum.valueOf((Class<? extends Enum>) declaredClass,
664         Text.readString(in));
665     } else if (declaredClass == Message.class) {
666       String className = Text.readString(in);
667       try {
668         declaredClass = getClassByName(conf, className);
669         instance = tryInstantiateProtobuf(declaredClass, in);
670       } catch (ClassNotFoundException e) {
671         LOG.error("Can't find class " + className, e);
672         throw new DoNotRetryIOException("Can't find class " + className, e);
673       }
674     } else {                                      // Writable or Serializable
675       Class instanceClass = null;
676       int b = (byte)WritableUtils.readVInt(in);
677       if (b == NOT_ENCODED) {
678         String className = Text.readString(in);
679         try {
680           instanceClass = getClassByName(conf, className);
681         } catch (ClassNotFoundException e) {
682           LOG.error("Can't find class " + className, e);
683           throw new DoNotRetryIOException("Can't find class " + className, e);
684         }
685       } else {
686         instanceClass = CODE_TO_CLASS.get(b);
687       }
688       if(Writable.class.isAssignableFrom(instanceClass)){
689         Writable writable = WritableFactories.newInstance(instanceClass, conf);
690         try {
691           writable.readFields(in);
692         } catch (IOException io) {
693           LOG.error("Error in readFields", io);
694           throw io;
695         } catch (Exception e) {
696           LOG.error("Error in readFields", e);
697           throw new IOException("Error in readFields" , e);
698         }
699         instance = writable;
700         if (instanceClass == NullInstance.class) {  // null
701           declaredClass = ((NullInstance)instance).declaredClass;
702           instance = null;
703         }
704       } else {
705         int length = in.readInt();
706         byte[] objectBytes = new byte[length];
707         in.readFully(objectBytes);
708         ByteArrayInputStream bis = null;
709         ObjectInputStream ois = null;
710         try {
711           bis = new ByteArrayInputStream(objectBytes);
712           ois = new ObjectInputStream(bis);
713           instance = ois.readObject();
714         } catch (ClassNotFoundException e) {
715           LOG.error("Class not found when attempting to deserialize object", e);
716           throw new DoNotRetryIOException("Class not found when attempting to " +
717               "deserialize object", e);
718         } finally {
719           if(bis!=null) bis.close();
720           if(ois!=null) ois.close();
721         }
722       }
723     }
724     if (objectWritable != null) {                 // store values
725       objectWritable.declaredClass = declaredClass;
726       objectWritable.instance = instance;
727     }
728     return instance;
729   }
730 
731   /**
732    * Try to instantiate a protocol buffer of the given message class
733    * from the given input stream.
734    *
735    * @param protoClass the class of the generated protocol buffer
736    * @param dataIn the input stream to read from
737    * @return the instantiated Message instance
738    * @throws IOException if an IO problem occurs
739    */
740   private static Message tryInstantiateProtobuf(
741       Class<?> protoClass,
742       DataInput dataIn) throws IOException {
743 
744     try {
745       if (dataIn instanceof InputStream) {
746         // We can use the built-in parseDelimitedFrom and not have to re-copy
747         // the data
748         Method parseMethod = getStaticProtobufMethod(protoClass,
749             "parseDelimitedFrom", InputStream.class);
750         return (Message)parseMethod.invoke(null, (InputStream)dataIn);
751       } else {
752         // Have to read it into a buffer first, since protobuf doesn't deal
753         // with the DataInput interface directly.
754 
755         // Read the size delimiter that writeDelimitedTo writes
756         int size = ProtoUtil.readRawVarint32(dataIn);
757         if (size < 0) {
758           throw new IOException("Invalid size: " + size);
759         }
760 
761         byte[] data = new byte[size];
762         dataIn.readFully(data);
763         Method parseMethod = getStaticProtobufMethod(protoClass,
764             "parseFrom", byte[].class);
765         return (Message)parseMethod.invoke(null, data);
766       }
767     } catch (InvocationTargetException e) {
768 
769       if (e.getCause() instanceof IOException) {
770         throw (IOException)e.getCause();
771       } else {
772         throw new IOException(e.getCause());
773       }
774     } catch (IllegalAccessException iae) {
775       throw new AssertionError("Could not access parse method in " +
776           protoClass);
777     }
778   }
779 
780   static Method getStaticProtobufMethod(Class<?> declaredClass, String method,
781       Class<?> ... args) {
782 
783     try {
784       return declaredClass.getMethod(method, args);
785     } catch (Exception e) {
786       // This is a bug in Hadoop - protobufs should all have this static method
787       throw new AssertionError("Protocol buffer class " + declaredClass +
788           " does not have an accessible parseFrom(InputStream) method!");
789     }
790   }
791 
792   @SuppressWarnings("unchecked")
793   private static Class getClassByName(Configuration conf, String className)
794   throws ClassNotFoundException {
795     if(conf != null) {
796       return conf.getClassByName(className);
797     }
798     ClassLoader cl = Thread.currentThread().getContextClassLoader();
799     if(cl == null) {
800       cl = HbaseObjectWritable.class.getClassLoader();
801     }
802     return Class.forName(className, true, cl);
803   }
804 
805   private static void addToMap(final Class<?> clazz, final int code) {
806     CLASS_TO_CODE.put(clazz, code);
807     CODE_TO_CLASS.put(code, clazz);
808   }
809 
810   public void setConf(Configuration conf) {
811     this.conf = conf;
812   }
813 
814   public Configuration getConf() {
815     return this.conf;
816   }
817 }