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 }