View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.lang3;
19  
20  import java.io.ByteArrayInputStream;
21  import java.io.ByteArrayOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.InvalidObjectException;
25  import java.io.ObjectInputStream;
26  import java.io.ObjectOutputStream;
27  import java.io.ObjectStreamClass;
28  import java.io.OutputStream;
29  import java.io.Serializable;
30  import java.util.Objects;
31  
32  /**
33   * Performs additional functionality for serialization.
34   *
35   * <ul>
36   * <li>Deep clone using serialization</li>
37   * <li>Serialize managing finally and IOException</li>
38   * <li>Deserialize managing finally and IOException</li>
39   * </ul>
40   *
41   * <p>
42   * This class throws exceptions for invalid {@code null} inputs. Each method documents its behavior in more detail.
43   * </p>
44   * <p>
45   * If you want to secure deserialization with a whitelist or blacklist, please use Apache Commons IO's
46   * {@link org.apache.commons.io.serialization.ValidatingObjectInputStream ValidatingObjectInputStream}.
47   * </p>
48   * <p>
49   * #ThreadSafe#
50   * </p>
51   *
52   * @see org.apache.commons.io.serialization.ValidatingObjectInputStream
53   * @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/serialization/">Java Object Serialization Specification</a>
54   * @since 1.0
55   */
56  public class SerializationUtils {
57  
58      /**
59       * Custom specialization of the standard JDK {@link ObjectInputStream} that uses a custom {@link ClassLoader} to resolve a class. If the specified
60       * {@link ClassLoader} is not able to resolve the class, the context classloader of the current thread will be used. This way, the standard deserialization
61       * also works in web application containers and application servers, regardless of which {@link ClassLoader} loaded the class that encapsulates
62       * serialization/deserialization.
63       *
64       * <p>
65       * For more in-depth information about the problem for which this class here is a workaround, see the JIRA issue LANG-626.
66       * </p>
67       */
68      static final class ClassLoaderAwareObjectInputStream extends ObjectInputStream {
69  
70          private final ClassLoader classLoader;
71  
72          /**
73           * Constructs a new instance.
74           *
75           * @param in          The {@link InputStream}.
76           * @param classLoader classloader to use
77           * @throws IOException Thrown if an I/O error occurs while reading stream header.
78           * @see java.io.ObjectInputStream
79           */
80          ClassLoaderAwareObjectInputStream(final InputStream in, final ClassLoader classLoader) throws IOException {
81              super(in);
82              this.classLoader = classLoader;
83          }
84  
85          /**
86           * Overridden version that uses the parameterized {@link ClassLoader} or the {@link ClassLoader} of the current {@link Thread} to resolve the class.
87           *
88           * @param desc An instance of class {@link ObjectStreamClass}.
89           * @return A {@link Class} object corresponding to {@code desc}.
90           * @throws IOException Thrown if an I/O error occurs.
91           * @throws ClassNotFoundException Thrown if class of a serialized object cannot be found.
92           */
93          @Override
94          protected Class<?> resolveClass(final ObjectStreamClass desc) throws IOException, ClassNotFoundException {
95              final String name = desc.getName();
96              try {
97                  return Class.forName(name, false, classLoader);
98              } catch (final ClassNotFoundException ex) {
99                  try {
100                     return Class.forName(name, false, Thread.currentThread().getContextClassLoader());
101                 } catch (final ClassNotFoundException cnfe) {
102                     final Class<?> cls = ClassUtils.getPrimitiveClass(name);
103                     if (cls != null) {
104                         return cls;
105                     }
106                     throw cnfe;
107                 }
108             }
109         }
110     }
111 
112     /**
113      * Deep clones an {@link Object} using serialization.
114      *
115      * <p>
116      * This is many times slower than writing clone methods by hand on all objects in your object graph. However, for complex object graphs, or for those that
117      * don't support deep cloning this can be a simple alternative implementation. Of course all the objects must be {@link Serializable}.
118      * </p>
119      *
120      * @param <T>    the type of the object involved.
121      * @param object The {@link Serializable} object to clone.
122      * @return The cloned object.
123      * @throws SerializationException Thrown if the serialization fails.
124      * @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/serialization/">Java Object Serialization Specification</a>
125      */
126     public static <T extends Serializable> T clone(final T object) {
127         if (object == null) {
128             return null;
129         }
130         final ByteArrayInputStream bais = new ByteArrayInputStream(serialize(object));
131         final Class<T> cls = ObjectUtils.getClass(object);
132         try (ClassLoaderAwareObjectInputStream in = new ClassLoaderAwareObjectInputStream(bais, cls.getClassLoader())) {
133             // When we serialize and deserialize an object, it is reasonable to assume the deserialized object is of the
134             // same type as the original serialized object
135             return (T) in.readObject();
136         } catch (final ClassNotFoundException | IOException ex) {
137             throw new SerializationException(String.format("%s while reading cloned object data", ex.getClass().getSimpleName()), ex);
138         }
139     }
140 
141     /**
142      * Deserializes a single {@link Object} from an array of bytes.
143      *
144      * <p>
145      * If the call site incorrectly types the return value, a {@link ClassCastException} is thrown from the call site. Without Generics in this declaration, the
146      * call site must type cast and can cause the same ClassCastException. Note that in both cases, the ClassCastException is in the call site, not in this
147      * method.
148      * </p>
149      * <p>
150      * If you want to secure deserialization with a whitelist or blacklist, please use Apache Commons IO's
151      * {@link org.apache.commons.io.serialization.ValidatingObjectInputStream ValidatingObjectInputStream}.
152      * </p>
153      *
154      * @param <T>        the object type to be deserialized.
155      * @param objectData The serialized object, must not be null.
156      * @return The deserialized object.
157      * @throws NullPointerException   Thrown if {@code objectData} is {@code null}.
158      * @throws SerializationException Thrown if the serialization fails.
159      * @see org.apache.commons.io.serialization.ValidatingObjectInputStream
160      * @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/serialization/">Java Object Serialization Specification</a>
161      */
162     public static <T> T deserialize(final byte[] objectData) {
163         Objects.requireNonNull(objectData, "objectData");
164         return deserialize(new ByteArrayInputStream(objectData));
165     }
166 
167     /**
168      * Deserializes an {@link Object} from the specified stream.
169      *
170      * <p>
171      * The stream will be closed once the object is written. This avoids the need for a finally clause, and maybe also exception handling, in the application
172      * code.
173      * </p>
174      *
175      * <p>
176      * The stream passed in is not buffered internally within this method. This is the responsibility of your application if desired.
177      * </p>
178      *
179      * <p>
180      * If the call site incorrectly types the return value, a {@link ClassCastException} is thrown from the call site. Without Generics in this declaration, the
181      * call site must type cast and can cause the same ClassCastException. Note that in both cases, the ClassCastException is in the call site, not in this
182      * method.
183      * </p>
184      *
185      * <p>
186      * If you want to secure deserialization with a whitelist or blacklist, please use Apache Commons IO's
187      * {@link org.apache.commons.io.serialization.ValidatingObjectInputStream ValidatingObjectInputStream}.
188      * </p>
189      *
190      * @param <T>         the object type to be deserialized.
191      * @param inputStream The serialized object input stream, must not be null.
192      * @return The deserialized object.
193      * @throws NullPointerException   Thrown if {@code inputStream} is {@code null}.
194      * @throws SerializationException Thrown if the serialization fails.
195      * @see org.apache.commons.io.serialization.ValidatingObjectInputStream
196      * @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/serialization/">Java Object Serialization Specification</a>
197      */
198     @SuppressWarnings("resource") // inputStream is managed by the caller
199     public static <T> T deserialize(final InputStream inputStream) {
200         Objects.requireNonNull(inputStream, "inputStream");
201         try (ObjectInputStream in = new ObjectInputStream(inputStream)) {
202             @SuppressWarnings("unchecked")
203             final T obj = (T) in.readObject();
204             return obj;
205         } catch (final ClassNotFoundException | IOException | NegativeArraySizeException ex) {
206             throw new SerializationException(ex);
207         }
208     }
209 
210     /**
211      * Checks that the specified object reference is not {@code null} and throws a customized {@link InvalidObjectException} if it is. This method is designed
212      * primarily for doing state validation in {@link Serializable} class's {@code readObject(ObjectInputStream)} methods.
213      *
214      * @param obj     The object reference to check for nullity.
215      * @param message detail message to be used in the event that a {@link InvalidObjectException} is thrown.
216      * @param <T>     the type of the reference.
217      * @return {@code obj} if not {@code null}.
218      * @throws InvalidObjectException Thrown if {@code obj} is {@code null}.
219      * @see Serializable
220      * @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/serialization/">Java Object Serialization Specification</a>
221      * @since 3.21.0
222      */
223     public static <T> T requireNonNull(final T obj, final String message) throws InvalidObjectException {
224         if (obj == null) {
225             throw new InvalidObjectException(message);
226         }
227         return obj;
228     }
229 
230     /**
231      * Performs a serialization roundtrip. Serializes and deserializes the given object, great for testing objects that implement {@link Serializable}.
232      *
233      * @param <T> The type of the object involved.
234      * @param obj The object to roundtrip.
235      * @return The serialized and deserialized object.
236      * @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/serialization/">Java Object Serialization Specification</a>
237      * @since 3.3
238      */
239     @SuppressWarnings("unchecked") // OK, because we serialized a type `T`
240     public static <T extends Serializable> T roundtrip(final T obj) {
241         return (T) deserialize(serialize(obj));
242     }
243 
244     /**
245      * Serializes an {@link Object} to a byte array for storage/serialization.
246      *
247      * @param obj The object to serialize to bytes.
248      * @return A byte[] with the converted Serializable.
249      * @throws SerializationException Thrown if the serialization fails.
250      * @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/serialization/">Java Object Serialization Specification</a>
251      */
252     public static byte[] serialize(final Serializable obj) {
253         final ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
254         serialize(obj, baos);
255         return baos.toByteArray();
256     }
257 
258     /**
259      * Serializes an {@link Object} to the specified stream.
260      *
261      * <p>
262      * The stream will be closed once the object is written. This avoids the need for a finally clause, and maybe also exception handling, in the application
263      * code.
264      * </p>
265      *
266      * <p>
267      * The stream passed in is not buffered internally within this method. This is the responsibility of your application if desired.
268      * </p>
269      *
270      * @param obj          The object to serialize to bytes, may be null.
271      * @param outputStream The stream to write to, must not be null.
272      * @throws NullPointerException   Thrown if {@code outputStream} is {@code null}.
273      * @throws SerializationException Thrown if the serialization fails.
274      * @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/serialization/">Java Object Serialization Specification</a>
275      */
276     @SuppressWarnings("resource") // outputStream is managed by the caller
277     public static void serialize(final Serializable obj, final OutputStream outputStream) {
278         Objects.requireNonNull(outputStream, "outputStream");
279         try (ObjectOutputStream out = new ObjectOutputStream(outputStream)) {
280             out.writeObject(obj);
281         } catch (final IOException ex) {
282             throw new SerializationException(ex);
283         }
284     }
285 
286     /**
287      * SerializationUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
288      * {@code SerializationUtils.clone(object)}.
289      *
290      * <p>
291      * This constructor is public to permit tools that require a JavaBean instance to operate.
292      * </p>
293      *
294      * @since 2.0
295      * @deprecated TODO Make private in 4.0.
296      */
297     @Deprecated
298     public SerializationUtils() {
299         // empty
300     }
301 }