001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.lang3; 018 019 import java.io.ByteArrayInputStream; 020 import java.io.ByteArrayOutputStream; 021 import java.io.IOException; 022 import java.io.InputStream; 023 import java.io.ObjectInputStream; 024 import java.io.ObjectOutputStream; 025 import java.io.ObjectStreamClass; 026 import java.io.OutputStream; 027 import java.io.Serializable; 028 029 /** 030 * <p>Assists with the serialization process and performs additional functionality based 031 * on serialization.</p> 032 * <p> 033 * <ul> 034 * <li>Deep clone using serialization 035 * <li>Serialize managing finally and IOException 036 * <li>Deserialize managing finally and IOException 037 * </ul> 038 * 039 * <p>This class throws exceptions for invalid {@code null} inputs. 040 * Each method documents its behaviour in more detail.</p> 041 * 042 * <p>#ThreadSafe#</p> 043 * @since 1.0 044 * @version $Id: SerializationUtils.java 1199718 2011-11-09 12:43:20Z sebb $ 045 */ 046 public class SerializationUtils { 047 048 /** 049 * <p>SerializationUtils instances should NOT be constructed in standard programming. 050 * Instead, the class should be used as {@code SerializationUtils.clone(object)}.</p> 051 * 052 * <p>This constructor is public to permit tools that require a JavaBean instance 053 * to operate.</p> 054 * @since 2.0 055 */ 056 public SerializationUtils() { 057 super(); 058 } 059 060 // Clone 061 //----------------------------------------------------------------------- 062 /** 063 * <p>Deep clone an {@code Object} using serialization.</p> 064 * 065 * <p>This is many times slower than writing clone methods by hand 066 * on all objects in your object graph. However, for complex object 067 * graphs, or for those that don't support deep cloning this can 068 * be a simple alternative implementation. Of course all the objects 069 * must be {@code Serializable}.</p> 070 * 071 * @param <T> the type of the object involved 072 * @param object the {@code Serializable} object to clone 073 * @return the cloned object 074 * @throws SerializationException (runtime) if the serialization fails 075 */ 076 public static <T extends Serializable> T clone(T object) { 077 if (object == null) { 078 return null; 079 } 080 byte[] objectData = serialize(object); 081 ByteArrayInputStream bais = new ByteArrayInputStream(objectData); 082 083 ClassLoaderAwareObjectInputStream in = null; 084 try { 085 // stream closed in the finally 086 in = new ClassLoaderAwareObjectInputStream(bais, object.getClass().getClassLoader()); 087 /* 088 * when we serialize and deserialize an object, 089 * it is reasonable to assume the deserialized object 090 * is of the same type as the original serialized object 091 */ 092 @SuppressWarnings("unchecked") // see above 093 T readObject = (T) in.readObject(); 094 return readObject; 095 096 } catch (ClassNotFoundException ex) { 097 throw new SerializationException("ClassNotFoundException while reading cloned object data", ex); 098 } catch (IOException ex) { 099 throw new SerializationException("IOException while reading cloned object data", ex); 100 } finally { 101 try { 102 if (in != null) { 103 in.close(); 104 } 105 } catch (IOException ex) { 106 throw new SerializationException("IOException on closing cloned object data InputStream.", ex); 107 } 108 } 109 } 110 111 // Serialize 112 //----------------------------------------------------------------------- 113 /** 114 * <p>Serializes an {@code Object} to the specified stream.</p> 115 * 116 * <p>The stream will be closed once the object is written. 117 * This avoids the need for a finally clause, and maybe also exception 118 * handling, in the application code.</p> 119 * 120 * <p>The stream passed in is not buffered internally within this method. 121 * This is the responsibility of your application if desired.</p> 122 * 123 * @param obj the object to serialize to bytes, may be null 124 * @param outputStream the stream to write to, must not be null 125 * @throws IllegalArgumentException if {@code outputStream} is {@code null} 126 * @throws SerializationException (runtime) if the serialization fails 127 */ 128 public static void serialize(Serializable obj, OutputStream outputStream) { 129 if (outputStream == null) { 130 throw new IllegalArgumentException("The OutputStream must not be null"); 131 } 132 ObjectOutputStream out = null; 133 try { 134 // stream closed in the finally 135 out = new ObjectOutputStream(outputStream); 136 out.writeObject(obj); 137 138 } catch (IOException ex) { 139 throw new SerializationException(ex); 140 } finally { 141 try { 142 if (out != null) { 143 out.close(); 144 } 145 } catch (IOException ex) { // NOPMD 146 // ignore close exception 147 } 148 } 149 } 150 151 /** 152 * <p>Serializes an {@code Object} to a byte array for 153 * storage/serialization.</p> 154 * 155 * @param obj the object to serialize to bytes 156 * @return a byte[] with the converted Serializable 157 * @throws SerializationException (runtime) if the serialization fails 158 */ 159 public static byte[] serialize(Serializable obj) { 160 ByteArrayOutputStream baos = new ByteArrayOutputStream(512); 161 serialize(obj, baos); 162 return baos.toByteArray(); 163 } 164 165 // Deserialize 166 //----------------------------------------------------------------------- 167 /** 168 * <p>Deserializes an {@code Object} from the specified stream.</p> 169 * 170 * <p>The stream will be closed once the object is written. This 171 * avoids the need for a finally clause, and maybe also exception 172 * handling, in the application code.</p> 173 * 174 * <p>The stream passed in is not buffered internally within this method. 175 * This is the responsibility of your application if desired.</p> 176 * 177 * @param inputStream the serialized object input stream, must not be null 178 * @return the deserialized object 179 * @throws IllegalArgumentException if {@code inputStream} is {@code null} 180 * @throws SerializationException (runtime) if the serialization fails 181 */ 182 public static Object deserialize(InputStream inputStream) { 183 if (inputStream == null) { 184 throw new IllegalArgumentException("The InputStream must not be null"); 185 } 186 ObjectInputStream in = null; 187 try { 188 // stream closed in the finally 189 in = new ObjectInputStream(inputStream); 190 return in.readObject(); 191 192 } catch (ClassNotFoundException ex) { 193 throw new SerializationException(ex); 194 } catch (IOException ex) { 195 throw new SerializationException(ex); 196 } finally { 197 try { 198 if (in != null) { 199 in.close(); 200 } 201 } catch (IOException ex) { // NOPMD 202 // ignore close exception 203 } 204 } 205 } 206 207 /** 208 * <p>Deserializes a single {@code Object} from an array of bytes.</p> 209 * 210 * @param objectData the serialized object, must not be null 211 * @return the deserialized object 212 * @throws IllegalArgumentException if {@code objectData} is {@code null} 213 * @throws SerializationException (runtime) if the serialization fails 214 */ 215 public static Object deserialize(byte[] objectData) { 216 if (objectData == null) { 217 throw new IllegalArgumentException("The byte[] must not be null"); 218 } 219 ByteArrayInputStream bais = new ByteArrayInputStream(objectData); 220 return deserialize(bais); 221 } 222 223 /** 224 * <p>Custom specialization of the standard JDK {@link java.io.ObjectInputStream} 225 * that uses a custom <code>ClassLoader</code> to resolve a class. 226 * If the specified <code>ClassLoader</code> is not able to resolve the class, 227 * the context classloader of the current thread will be used. 228 * This way, the standard deserialization work also in web-application 229 * containers and application servers, no matter in which of the 230 * <code>ClassLoader</code> the particular class that encapsulates 231 * serialization/deserialization lives. </p> 232 * 233 * <p>For more in-depth information about the problem for which this 234 * class here is a workaround, see the JIRA issue LANG-626. </p> 235 */ 236 static class ClassLoaderAwareObjectInputStream extends ObjectInputStream { 237 private ClassLoader classLoader; 238 239 /** 240 * Constructor. 241 * @param in The <code>InputStream</code>. 242 * @param classLoader classloader to use 243 * @throws IOException if an I/O error occurs while reading stream header. 244 * @see java.io.ObjectInputStream 245 */ 246 public ClassLoaderAwareObjectInputStream(InputStream in, ClassLoader classLoader) throws IOException { 247 super(in); 248 this.classLoader = classLoader; 249 } 250 251 /** 252 * Overriden version that uses the parametrized <code>ClassLoader</code> or the <code>ClassLoader</code> 253 * of the current <code>Thread</code> to resolve the class. 254 * @param desc An instance of class <code>ObjectStreamClass</code>. 255 * @return A <code>Class</code> object corresponding to <code>desc</code>. 256 * @throws IOException Any of the usual Input/Output exceptions. 257 * @throws ClassNotFoundException If class of a serialized object cannot be found. 258 */ 259 @Override 260 protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { 261 String name = desc.getName(); 262 try { 263 return Class.forName(name, false, classLoader); 264 } catch (ClassNotFoundException ex) { 265 return Class.forName(name, false, Thread.currentThread().getContextClassLoader()); 266 } 267 } 268 269 } 270 271 }