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 }