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}