1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 package org.apache.hadoop.io;
20
21 import java.io.DataInput;
22 import java.io.DataOutput;
23 import java.io.IOException;
24
25 /**
26 * A wrapper for Writable instances.
27 * <p>
28 * When two sequence files, which have same Key type but different Value
29 * types, are mapped out to reduce, multiple Value types is not allowed.
30 * In this case, this class can help you wrap instances with different types.
31 * </p>
32 *
33 * <p>
34 * Compared with <code>ObjectWritable</code>, this class is much more effective,
35 * because <code>ObjectWritable</code> will append the class declaration as a String
36 * into the output file in every Key-Value pair.
37 * </p>
38 *
39 * how to use it: <br>
40 * 1. Write your own class, such as GenericObject, which extends GenericWritable.<br>
41 * 2. Implements the abstract method <code>getTypes()</code>, defines
42 * the classes which will be wrapped in GenericObject in application.
43 * Attention: this classes defined in <code>getTypes()</code> method, must
44 * implement <code>Writable</code> interface.
45 * <br><br>
46 *
47 * The code looks like this:
48 * <blockquote><pre>
49 * public class GenericObject extends GenericWritable {
50 *
51 * private static Class[] CLASSES = {
52 * ClassType1.class,
53 * ClassType2.class,
54 * ClassType3.class,
55 * };
56 *
57 * protected Class[] getTypes() {
58 * return CLASSES;
59 * }
60 *
61 * }
62 * </pre></blockquote>
63 *
64 * @since Nov 8, 2006
65 */
66 public abstract class GenericWritable implements Writable {
67
68 private static final byte NOT_SET = -1;
69
70 private byte type = NOT_SET;
71
72 private Writable instance;
73
74 /**
75 * Set the instance that is wrapped.
76 *
77 * @param obj
78 */
79 public void set(Writable obj) {
80 instance = obj;
81 Class[] clazzes = getTypes();
82 for (int i = 0; i < clazzes.length; i++) {
83 Class clazz = clazzes[i];
84 if (clazz.isInstance(instance)) {
85 type = (byte) i;
86 return;
87 }
88 }
89 throw new RuntimeException("The type of instance is: "
90 + instance.getClass() + ", which is NOT registered.");
91 }
92
93 /**
94 * Return the wrapped instance.
95 */
96 public Writable get() {
97 return instance;
98 }
99
100 public void readFields(DataInput in) throws IOException {
101 type = in.readByte();
102 Class<? extends Writable> clazz = getTypes()[type & 0xff];
103 try {
104 instance = clazz.newInstance();
105 } catch (Exception e) {
106 e.printStackTrace();
107 throw new IOException("Cannot initialize the class: " + clazz);
108 }
109 instance.readFields(in);
110 }
111
112 public void write(DataOutput out) throws IOException {
113 if (type == NOT_SET || instance == null)
114 throw new IOException("The GenericWritable has NOT been set correctly. type="
115 + type + ", instance=" + instance);
116 out.writeByte(type);
117 instance.write(out);
118 }
119
120 /**
121 * Return all classes that may be wrapped. Subclasses should implement this
122 * to return a constant array of classes.
123 */
124 abstract protected Class<? extends Writable>[] getTypes();
125
126 }