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 package org.apache.hadoop.io; 019 020 import java.io.DataInput; 021 import java.io.DataOutput; 022 import java.io.IOException; 023 import java.util.Map; 024 import java.util.concurrent.ConcurrentHashMap; 025 import java.util.concurrent.atomic.AtomicReference; 026 027 import org.apache.hadoop.classification.InterfaceAudience; 028 import org.apache.hadoop.classification.InterfaceStability; 029 import org.apache.hadoop.conf.Configurable; 030 import org.apache.hadoop.conf.Configuration; 031 032 /** 033 * Abstract base class for MapWritable and SortedMapWritable 034 * 035 * Unlike org.apache.nutch.crawl.MapWritable, this class allows creation of 036 * MapWritable<Writable, MapWritable> so the CLASS_TO_ID and ID_TO_CLASS 037 * maps travel with the class instead of being static. 038 * 039 * Class ids range from 1 to 127 so there can be at most 127 distinct classes 040 * in any specific map instance. 041 */ 042 @InterfaceAudience.Public 043 @InterfaceStability.Stable 044 public abstract class AbstractMapWritable implements Writable, Configurable { 045 private AtomicReference<Configuration> conf; 046 047 /* Class to id mappings */ 048 private Map<Class, Byte> classToIdMap = new ConcurrentHashMap<Class, Byte>(); 049 050 /* Id to Class mappings */ 051 private Map<Byte, Class> idToClassMap = new ConcurrentHashMap<Byte, Class>(); 052 053 /* The number of new classes (those not established by the constructor) */ 054 private volatile byte newClasses = 0; 055 056 /** @return the number of known classes */ 057 byte getNewClasses() { 058 return newClasses; 059 } 060 061 /** 062 * Used to add "predefined" classes and by Writable to copy "new" classes. 063 */ 064 private synchronized void addToMap(Class clazz, byte id) { 065 if (classToIdMap.containsKey(clazz)) { 066 byte b = classToIdMap.get(clazz); 067 if (b != id) { 068 throw new IllegalArgumentException ("Class " + clazz.getName() + 069 " already registered but maps to " + b + " and not " + id); 070 } 071 } 072 if (idToClassMap.containsKey(id)) { 073 Class c = idToClassMap.get(id); 074 if (!c.equals(clazz)) { 075 throw new IllegalArgumentException("Id " + id + " exists but maps to " + 076 c.getName() + " and not " + clazz.getName()); 077 } 078 } 079 classToIdMap.put(clazz, id); 080 idToClassMap.put(id, clazz); 081 } 082 083 /** Add a Class to the maps if it is not already present. */ 084 protected synchronized void addToMap(Class clazz) { 085 if (classToIdMap.containsKey(clazz)) { 086 return; 087 } 088 if (newClasses + 1 > Byte.MAX_VALUE) { 089 throw new IndexOutOfBoundsException("adding an additional class would" + 090 " exceed the maximum number allowed"); 091 } 092 byte id = ++newClasses; 093 addToMap(clazz, id); 094 } 095 096 /** @return the Class class for the specified id */ 097 protected Class getClass(byte id) { 098 return idToClassMap.get(id); 099 } 100 101 /** @return the id for the specified Class */ 102 protected byte getId(Class clazz) { 103 return classToIdMap.containsKey(clazz) ? classToIdMap.get(clazz) : -1; 104 } 105 106 /** Used by child copy constructors. */ 107 protected synchronized void copy(Writable other) { 108 if (other != null) { 109 try { 110 DataOutputBuffer out = new DataOutputBuffer(); 111 other.write(out); 112 DataInputBuffer in = new DataInputBuffer(); 113 in.reset(out.getData(), out.getLength()); 114 readFields(in); 115 116 } catch (IOException e) { 117 throw new IllegalArgumentException("map cannot be copied: " + 118 e.getMessage()); 119 } 120 121 } else { 122 throw new IllegalArgumentException("source map cannot be null"); 123 } 124 } 125 126 /** constructor. */ 127 protected AbstractMapWritable() { 128 this.conf = new AtomicReference<Configuration>(); 129 130 addToMap(ArrayWritable.class, 131 Byte.valueOf(Integer.valueOf(-127).byteValue())); 132 addToMap(BooleanWritable.class, 133 Byte.valueOf(Integer.valueOf(-126).byteValue())); 134 addToMap(BytesWritable.class, 135 Byte.valueOf(Integer.valueOf(-125).byteValue())); 136 addToMap(FloatWritable.class, 137 Byte.valueOf(Integer.valueOf(-124).byteValue())); 138 addToMap(IntWritable.class, 139 Byte.valueOf(Integer.valueOf(-123).byteValue())); 140 addToMap(LongWritable.class, 141 Byte.valueOf(Integer.valueOf(-122).byteValue())); 142 addToMap(MapWritable.class, 143 Byte.valueOf(Integer.valueOf(-121).byteValue())); 144 addToMap(MD5Hash.class, 145 Byte.valueOf(Integer.valueOf(-120).byteValue())); 146 addToMap(NullWritable.class, 147 Byte.valueOf(Integer.valueOf(-119).byteValue())); 148 addToMap(ObjectWritable.class, 149 Byte.valueOf(Integer.valueOf(-118).byteValue())); 150 addToMap(SortedMapWritable.class, 151 Byte.valueOf(Integer.valueOf(-117).byteValue())); 152 addToMap(Text.class, 153 Byte.valueOf(Integer.valueOf(-116).byteValue())); 154 addToMap(TwoDArrayWritable.class, 155 Byte.valueOf(Integer.valueOf(-115).byteValue())); 156 157 // UTF8 is deprecated so we don't support it 158 159 addToMap(VIntWritable.class, 160 Byte.valueOf(Integer.valueOf(-114).byteValue())); 161 addToMap(VLongWritable.class, 162 Byte.valueOf(Integer.valueOf(-113).byteValue())); 163 164 } 165 166 /** @return the conf */ 167 @Override 168 public Configuration getConf() { 169 return conf.get(); 170 } 171 172 /** @param conf the conf to set */ 173 @Override 174 public void setConf(Configuration conf) { 175 this.conf.set(conf); 176 } 177 178 @Override 179 public void write(DataOutput out) throws IOException { 180 181 // First write out the size of the class table and any classes that are 182 // "unknown" classes 183 184 out.writeByte(newClasses); 185 186 for (byte i = 1; i <= newClasses; i++) { 187 out.writeByte(i); 188 out.writeUTF(getClass(i).getName()); 189 } 190 } 191 192 @Override 193 public void readFields(DataInput in) throws IOException { 194 195 // Get the number of "unknown" classes 196 197 newClasses = in.readByte(); 198 199 // Then read in the class names and add them to our tables 200 201 for (int i = 0; i < newClasses; i++) { 202 byte id = in.readByte(); 203 String className = in.readUTF(); 204 try { 205 addToMap(Class.forName(className), id); 206 207 } catch (ClassNotFoundException e) { 208 throw new IOException("can't find class: " + className + " because "+ 209 e.getMessage()); 210 } 211 } 212 } 213 }