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