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