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