001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package org.apache.hadoop.io;
020    
021    import java.io.DataInput;
022    import java.io.DataOutput;
023    import java.io.IOException;
024    
025    import org.apache.hadoop.classification.InterfaceAudience;
026    import org.apache.hadoop.classification.InterfaceStability;
027    import org.apache.hadoop.conf.Configurable;
028    import org.apache.hadoop.conf.Configuration;
029    import org.apache.hadoop.util.ReflectionUtils;
030    
031    /**
032     * A wrapper for Writable instances.
033     * <p>
034     * When two sequence files, which have same Key type but different Value
035     * types, are mapped out to reduce, multiple Value types is not allowed.
036     * In this case, this class can help you wrap instances with different types.
037     * </p>
038     * 
039     * <p>
040     * Compared with <code>ObjectWritable</code>, this class is much more effective,
041     * because <code>ObjectWritable</code> will append the class declaration as a String 
042     * into the output file in every Key-Value pair.
043     * </p>
044     * 
045     * <p>
046     * Generic Writable implements {@link Configurable} interface, so that it will be 
047     * configured by the framework. The configuration is passed to the wrapped objects
048     * implementing {@link Configurable} interface <i>before deserialization</i>. 
049     * </p>
050     * 
051     * how to use it: <br>
052     * 1. Write your own class, such as GenericObject, which extends GenericWritable.<br> 
053     * 2. Implements the abstract method <code>getTypes()</code>, defines 
054     *    the classes which will be wrapped in GenericObject in application.
055     *    Attention: this classes defined in <code>getTypes()</code> method, must
056     *    implement <code>Writable</code> interface.
057     * <br><br>
058     * 
059     * The code looks like this:
060     * <blockquote><pre>
061     * public class GenericObject extends GenericWritable {
062     * 
063     *   private static Class[] CLASSES = {
064     *               ClassType1.class, 
065     *               ClassType2.class,
066     *               ClassType3.class,
067     *               };
068     *
069     *   protected Class[] getTypes() {
070     *       return CLASSES;
071     *   }
072     *
073     * }
074     * </pre></blockquote>
075     * 
076     * @since Nov 8, 2006
077     */
078    @InterfaceAudience.Public
079    @InterfaceStability.Stable
080    public abstract class GenericWritable implements Writable, Configurable {
081    
082      private static final byte NOT_SET = -1;
083    
084      private byte type = NOT_SET;
085    
086      private Writable instance;
087    
088      private Configuration conf = null;
089      
090      /**
091       * Set the instance that is wrapped.
092       * 
093       * @param obj
094       */
095      public void set(Writable obj) {
096        instance = obj;
097        Class<? extends Writable> instanceClazz = instance.getClass();
098        Class<? extends Writable>[] clazzes = getTypes();
099        for (int i = 0; i < clazzes.length; i++) {
100          Class<? extends Writable> clazz = clazzes[i];
101          if (clazz.equals(instanceClazz)) {
102            type = (byte) i;
103            return;
104          }
105        }
106        throw new RuntimeException("The type of instance is: "
107                                   + instance.getClass() + ", which is NOT registered.");
108      }
109    
110      /**
111       * Return the wrapped instance.
112       */
113      public Writable get() {
114        return instance;
115      }
116      
117      @Override
118      public String toString() {
119        return "GW[" + (instance != null ? ("class=" + instance.getClass().getName() +
120            ",value=" + instance.toString()) : "(null)") + "]";
121      }
122    
123      @Override
124      public void readFields(DataInput in) throws IOException {
125        type = in.readByte();
126        Class<? extends Writable> clazz = getTypes()[type & 0xff];
127        try {
128          instance = ReflectionUtils.newInstance(clazz, conf);
129        } catch (Exception e) {
130          e.printStackTrace();
131          throw new IOException("Cannot initialize the class: " + clazz);
132        }
133        instance.readFields(in);
134      }
135    
136      @Override
137      public void write(DataOutput out) throws IOException {
138        if (type == NOT_SET || instance == null)
139          throw new IOException("The GenericWritable has NOT been set correctly. type="
140                                + type + ", instance=" + instance);
141        out.writeByte(type);
142        instance.write(out);
143      }
144    
145      /**
146       * Return all classes that may be wrapped.  Subclasses should implement this
147       * to return a constant array of classes.
148       */
149      abstract protected Class<? extends Writable>[] getTypes();
150    
151      @Override
152      public Configuration getConf() {
153        return conf;
154      }
155    
156      @Override
157      public void setConf(Configuration conf) {
158        this.conf = conf;
159      }
160      
161    }