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.IOException;
022 import java.nio.charset.UnsupportedCharsetException;
023 import java.util.ArrayList;
024
025 import org.apache.commons.codec.binary.Base64;
026 import org.apache.hadoop.classification.InterfaceAudience;
027 import org.apache.hadoop.classification.InterfaceStability;
028 import org.apache.hadoop.conf.Configuration;
029 import org.apache.hadoop.io.serializer.Deserializer;
030 import org.apache.hadoop.io.serializer.Serialization;
031 import org.apache.hadoop.io.serializer.SerializationFactory;
032 import org.apache.hadoop.io.serializer.Serializer;
033 import org.apache.hadoop.util.GenericsUtil;
034
035 /**
036 * DefaultStringifier is the default implementation of the {@link Stringifier}
037 * interface which stringifies the objects using base64 encoding of the
038 * serialized version of the objects. The {@link Serializer} and
039 * {@link Deserializer} are obtained from the {@link SerializationFactory}.
040 * <br>
041 * DefaultStringifier offers convenience methods to store/load objects to/from
042 * the configuration.
043 *
044 * @param <T> the class of the objects to stringify
045 */
046 @InterfaceAudience.Public
047 @InterfaceStability.Stable
048 public class DefaultStringifier<T> implements Stringifier<T> {
049
050 private static final String SEPARATOR = ",";
051
052 private Serializer<T> serializer;
053
054 private Deserializer<T> deserializer;
055
056 private DataInputBuffer inBuf;
057
058 private DataOutputBuffer outBuf;
059
060 public DefaultStringifier(Configuration conf, Class<T> c) {
061
062 SerializationFactory factory = new SerializationFactory(conf);
063 this.serializer = factory.getSerializer(c);
064 this.deserializer = factory.getDeserializer(c);
065 this.inBuf = new DataInputBuffer();
066 this.outBuf = new DataOutputBuffer();
067 try {
068 serializer.open(outBuf);
069 deserializer.open(inBuf);
070 } catch (IOException ex) {
071 throw new RuntimeException(ex);
072 }
073 }
074
075 public T fromString(String str) throws IOException {
076 try {
077 byte[] bytes = Base64.decodeBase64(str.getBytes("UTF-8"));
078 inBuf.reset(bytes, bytes.length);
079 T restored = deserializer.deserialize(null);
080 return restored;
081 } catch (UnsupportedCharsetException ex) {
082 throw new IOException(ex.toString());
083 }
084 }
085
086 public String toString(T obj) throws IOException {
087 outBuf.reset();
088 serializer.serialize(obj);
089 byte[] buf = new byte[outBuf.getLength()];
090 System.arraycopy(outBuf.getData(), 0, buf, 0, buf.length);
091 return new String(Base64.encodeBase64(buf));
092 }
093
094 public void close() throws IOException {
095 inBuf.close();
096 outBuf.close();
097 deserializer.close();
098 serializer.close();
099 }
100
101 /**
102 * Stores the item in the configuration with the given keyName.
103 *
104 * @param <K> the class of the item
105 * @param conf the configuration to store
106 * @param item the object to be stored
107 * @param keyName the name of the key to use
108 * @throws IOException : forwards Exceptions from the underlying
109 * {@link Serialization} classes.
110 */
111 public static <K> void store(Configuration conf, K item, String keyName)
112 throws IOException {
113
114 DefaultStringifier<K> stringifier = new DefaultStringifier<K>(conf,
115 GenericsUtil.getClass(item));
116 conf.set(keyName, stringifier.toString(item));
117 stringifier.close();
118 }
119
120 /**
121 * Restores the object from the configuration.
122 *
123 * @param <K> the class of the item
124 * @param conf the configuration to use
125 * @param keyName the name of the key to use
126 * @param itemClass the class of the item
127 * @return restored object
128 * @throws IOException : forwards Exceptions from the underlying
129 * {@link Serialization} classes.
130 */
131 public static <K> K load(Configuration conf, String keyName,
132 Class<K> itemClass) throws IOException {
133 DefaultStringifier<K> stringifier = new DefaultStringifier<K>(conf,
134 itemClass);
135 try {
136 String itemStr = conf.get(keyName);
137 return stringifier.fromString(itemStr);
138 } finally {
139 stringifier.close();
140 }
141 }
142
143 /**
144 * Stores the array of items in the configuration with the given keyName.
145 *
146 * @param <K> the class of the item
147 * @param conf the configuration to use
148 * @param items the objects to be stored
149 * @param keyName the name of the key to use
150 * @throws IndexOutOfBoundsException if the items array is empty
151 * @throws IOException : forwards Exceptions from the underlying
152 * {@link Serialization} classes.
153 */
154 public static <K> void storeArray(Configuration conf, K[] items,
155 String keyName) throws IOException {
156
157 DefaultStringifier<K> stringifier = new DefaultStringifier<K>(conf,
158 GenericsUtil.getClass(items[0]));
159 try {
160 StringBuilder builder = new StringBuilder();
161 for (K item : items) {
162 builder.append(stringifier.toString(item)).append(SEPARATOR);
163 }
164 conf.set(keyName, builder.toString());
165 }
166 finally {
167 stringifier.close();
168 }
169 }
170
171 /**
172 * Restores the array of objects from the configuration.
173 *
174 * @param <K> the class of the item
175 * @param conf the configuration to use
176 * @param keyName the name of the key to use
177 * @param itemClass the class of the item
178 * @return restored object
179 * @throws IOException : forwards Exceptions from the underlying
180 * {@link Serialization} classes.
181 */
182 public static <K> K[] loadArray(Configuration conf, String keyName,
183 Class<K> itemClass) throws IOException {
184 DefaultStringifier<K> stringifier = new DefaultStringifier<K>(conf,
185 itemClass);
186 try {
187 String itemStr = conf.get(keyName);
188 ArrayList<K> list = new ArrayList<K>();
189 String[] parts = itemStr.split(SEPARATOR);
190
191 for (String part : parts) {
192 if (!part.equals(""))
193 list.add(stringifier.fromString(part));
194 }
195
196 return GenericsUtil.toArray(itemClass, list);
197 }
198 finally {
199 stringifier.close();
200 }
201 }
202
203 }