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.IOException; 022import java.nio.charset.UnsupportedCharsetException; 023import java.util.ArrayList; 024 025import org.apache.commons.codec.binary.Base64; 026import org.apache.hadoop.classification.InterfaceAudience; 027import org.apache.hadoop.classification.InterfaceStability; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.io.serializer.Deserializer; 030import org.apache.hadoop.io.serializer.Serialization; 031import org.apache.hadoop.io.serializer.SerializationFactory; 032import org.apache.hadoop.io.serializer.Serializer; 033import 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 048public 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}