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 @Override
076 public T fromString(String str) throws IOException {
077 try {
078 byte[] bytes = Base64.decodeBase64(str.getBytes("UTF-8"));
079 inBuf.reset(bytes, bytes.length);
080 T restored = deserializer.deserialize(null);
081 return restored;
082 } catch (UnsupportedCharsetException ex) {
083 throw new IOException(ex.toString());
084 }
085 }
086
087 @Override
088 public String toString(T obj) throws IOException {
089 outBuf.reset();
090 serializer.serialize(obj);
091 byte[] buf = new byte[outBuf.getLength()];
092 System.arraycopy(outBuf.getData(), 0, buf, 0, buf.length);
093 return new String(Base64.encodeBase64(buf));
094 }
095
096 @Override
097 public void close() throws IOException {
098 inBuf.close();
099 outBuf.close();
100 deserializer.close();
101 serializer.close();
102 }
103
104 /**
105 * Stores the item in the configuration with the given keyName.
106 *
107 * @param <K> the class of the item
108 * @param conf the configuration to store
109 * @param item the object to be stored
110 * @param keyName the name of the key to use
111 * @throws IOException : forwards Exceptions from the underlying
112 * {@link Serialization} classes.
113 */
114 public static <K> void store(Configuration conf, K item, String keyName)
115 throws IOException {
116
117 DefaultStringifier<K> stringifier = new DefaultStringifier<K>(conf,
118 GenericsUtil.getClass(item));
119 conf.set(keyName, stringifier.toString(item));
120 stringifier.close();
121 }
122
123 /**
124 * Restores the object from the configuration.
125 *
126 * @param <K> the class of the item
127 * @param conf the configuration to use
128 * @param keyName the name of the key to use
129 * @param itemClass the class of the item
130 * @return restored object
131 * @throws IOException : forwards Exceptions from the underlying
132 * {@link Serialization} classes.
133 */
134 public static <K> K load(Configuration conf, String keyName,
135 Class<K> itemClass) throws IOException {
136 DefaultStringifier<K> stringifier = new DefaultStringifier<K>(conf,
137 itemClass);
138 try {
139 String itemStr = conf.get(keyName);
140 return stringifier.fromString(itemStr);
141 } finally {
142 stringifier.close();
143 }
144 }
145
146 /**
147 * Stores the array of items in the configuration with the given keyName.
148 *
149 * @param <K> the class of the item
150 * @param conf the configuration to use
151 * @param items the objects to be stored
152 * @param keyName the name of the key to use
153 * @throws IndexOutOfBoundsException if the items array is empty
154 * @throws IOException : forwards Exceptions from the underlying
155 * {@link Serialization} classes.
156 */
157 public static <K> void storeArray(Configuration conf, K[] items,
158 String keyName) throws IOException {
159
160 DefaultStringifier<K> stringifier = new DefaultStringifier<K>(conf,
161 GenericsUtil.getClass(items[0]));
162 try {
163 StringBuilder builder = new StringBuilder();
164 for (K item : items) {
165 builder.append(stringifier.toString(item)).append(SEPARATOR);
166 }
167 conf.set(keyName, builder.toString());
168 }
169 finally {
170 stringifier.close();
171 }
172 }
173
174 /**
175 * Restores the array of objects from the configuration.
176 *
177 * @param <K> the class of the item
178 * @param conf the configuration to use
179 * @param keyName the name of the key to use
180 * @param itemClass the class of the item
181 * @return restored object
182 * @throws IOException : forwards Exceptions from the underlying
183 * {@link Serialization} classes.
184 */
185 public static <K> K[] loadArray(Configuration conf, String keyName,
186 Class<K> itemClass) throws IOException {
187 DefaultStringifier<K> stringifier = new DefaultStringifier<K>(conf,
188 itemClass);
189 try {
190 String itemStr = conf.get(keyName);
191 ArrayList<K> list = new ArrayList<K>();
192 String[] parts = itemStr.split(SEPARATOR);
193
194 for (String part : parts) {
195 if (!part.isEmpty())
196 list.add(stringifier.fromString(part));
197 }
198
199 return GenericsUtil.toArray(itemClass, list);
200 }
201 finally {
202 stringifier.close();
203 }
204 }
205
206 }