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 1153046 2011-08-02 06:57:04Z bayard $ 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 return (T) in.readObject(); 093 094 } catch (ClassNotFoundException ex) { 095 throw new SerializationException("ClassNotFoundException while reading cloned object data", ex); 096 } catch (IOException ex) { 097 throw new SerializationException("IOException while reading cloned object data", ex); 098 } finally { 099 try { 100 if (in != null) { 101 in.close(); 102 } 103 } catch (IOException ex) { 104 throw new SerializationException("IOException on closing cloned object data InputStream.", ex); 105 } 106 } 107 } 108 109 // Serialize 110 //----------------------------------------------------------------------- 111 /** 112 * <p>Serializes an {@code Object} to the specified stream.</p> 113 * 114 * <p>The stream will be closed once the object is written. 115 * This avoids the need for a finally clause, and maybe also exception 116 * handling, in the application code.</p> 117 * 118 * <p>The stream passed in is not buffered internally within this method. 119 * This is the responsibility of your application if desired.</p> 120 * 121 * @param obj the object to serialize to bytes, may be null 122 * @param outputStream the stream to write to, must not be null 123 * @throws IllegalArgumentException if {@code outputStream} is {@code null} 124 * @throws SerializationException (runtime) if the serialization fails 125 */ 126 public static void serialize(Serializable obj, OutputStream outputStream) { 127 if (outputStream == null) { 128 throw new IllegalArgumentException("The OutputStream must not be null"); 129 } 130 ObjectOutputStream out = null; 131 try { 132 // stream closed in the finally 133 out = new ObjectOutputStream(outputStream); 134 out.writeObject(obj); 135 136 } catch (IOException ex) { 137 throw new SerializationException(ex); 138 } finally { 139 try { 140 if (out != null) { 141 out.close(); 142 } 143 } catch (IOException ex) { // NOPMD 144 // ignore close exception 145 } 146 } 147 } 148 149 /** 150 * <p>Serializes an {@code Object} to a byte array for 151 * storage/serialization.</p> 152 * 153 * @param obj the object to serialize to bytes 154 * @return a byte[] with the converted Serializable 155 * @throws SerializationException (runtime) if the serialization fails 156 */ 157 public static byte[] serialize(Serializable obj) { 158 ByteArrayOutputStream baos = new ByteArrayOutputStream(512); 159 serialize(obj, baos); 160 return baos.toByteArray(); 161 } 162 163 // Deserialize 164 //----------------------------------------------------------------------- 165 /** 166 * <p>Deserializes an {@code Object} from the specified stream.</p> 167 * 168 * <p>The stream will be closed once the object is written. This 169 * avoids the need for a finally clause, and maybe also exception 170 * handling, in the application code.</p> 171 * 172 * <p>The stream passed in is not buffered internally within this method. 173 * This is the responsibility of your application if desired.</p> 174 * 175 * @param inputStream the serialized object input stream, must not be null 176 * @return the deserialized object 177 * @throws IllegalArgumentException if {@code inputStream} is {@code null} 178 * @throws SerializationException (runtime) if the serialization fails 179 */ 180 public static Object deserialize(InputStream inputStream) { 181 if (inputStream == null) { 182 throw new IllegalArgumentException("The InputStream must not be null"); 183 } 184 ObjectInputStream in = null; 185 try { 186 // stream closed in the finally 187 in = new ObjectInputStream(inputStream); 188 return in.readObject(); 189 190 } catch (ClassNotFoundException ex) { 191 throw new SerializationException(ex); 192 } catch (IOException ex) { 193 throw new SerializationException(ex); 194 } finally { 195 try { 196 if (in != null) { 197 in.close(); 198 } 199 } catch (IOException ex) { // NOPMD 200 // ignore close exception 201 } 202 } 203 } 204 205 /** 206 * <p>Deserializes a single {@code Object} from an array of bytes.</p> 207 * 208 * @param objectData the serialized object, must not be null 209 * @return the deserialized object 210 * @throws IllegalArgumentException if {@code objectData} is {@code null} 211 * @throws SerializationException (runtime) if the serialization fails 212 */ 213 public static Object deserialize(byte[] objectData) { 214 if (objectData == null) { 215 throw new IllegalArgumentException("The byte[] must not be null"); 216 } 217 ByteArrayInputStream bais = new ByteArrayInputStream(objectData); 218 return deserialize(bais); 219 } 220 221 /** 222 * <p>Custom specialization of the standard JDK {@link java.io.ObjectInputStream} 223 * that uses a custom <code>ClassLoader</code> to resolve a class. 224 * If the specified <code>ClassLoader</code> is not able to resolve the class, 225 * the context classloader of the current thread will be used. 226 * This way, the standard deserialization work also in web-application 227 * containers and application servers, no matter in which of the 228 * <code>ClassLoader</code> the particular class that encapsulates 229 * serialization/deserialization lives. </p> 230 * 231 * <p>For more in-depth information about the problem for which this 232 * class here is a workaround, see the JIRA issue LANG-626. </p> 233 */ 234 static class ClassLoaderAwareObjectInputStream extends ObjectInputStream { 235 private ClassLoader classLoader; 236 237 /** 238 * Constructor. 239 * @param in The <code>InputStream</code>. 240 * @param classLoader classloader to use 241 * @throws IOException if an I/O error occurs while reading stream header. 242 * @see java.io.ObjectInputStream 243 */ 244 public ClassLoaderAwareObjectInputStream(InputStream in, ClassLoader classLoader) throws IOException { 245 super(in); 246 this.classLoader = classLoader; 247 } 248 249 /** 250 * Overriden version that uses the parametrized <code>ClassLoader</code> or the <code>ClassLoader</code> 251 * of the current <code>Thread</code> to resolve the class. 252 * @param desc An instance of class <code>ObjectStreamClass</code>. 253 * @return A <code>Class</code> object corresponding to <code>desc</code>. 254 * @throws IOException Any of the usual Input/Output exceptions. 255 * @throws ClassNotFoundException If class of a serialized object cannot be found. 256 */ 257 @Override 258 protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { 259 String name = desc.getName(); 260 try { 261 return Class.forName(name, false, classLoader); 262 } catch (ClassNotFoundException ex) { 263 return Class.forName(name, false, Thread.currentThread().getContextClassLoader()); 264 } 265 } 266 267 } 268 269 }