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 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}