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
019package org.apache.hadoop.io;
020
021import java.io.DataInput;
022import java.io.DataOutput;
023import java.io.IOException;
024
025import org.apache.hadoop.classification.InterfaceAudience;
026import org.apache.hadoop.classification.InterfaceStability;
027import org.apache.hadoop.conf.Configurable;
028import org.apache.hadoop.conf.Configuration;
029import 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
080public 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}