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 }