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 * https://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 018package org.apache.commons.lang3; 019 020import java.io.ByteArrayInputStream; 021import java.io.ByteArrayOutputStream; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.InvalidObjectException; 025import java.io.ObjectInputStream; 026import java.io.ObjectOutputStream; 027import java.io.ObjectStreamClass; 028import java.io.OutputStream; 029import java.io.Serializable; 030import java.util.Objects; 031 032/** 033 * Performs additional functionality for serialization. 034 * 035 * <ul> 036 * <li>Deep clone using serialization</li> 037 * <li>Serialize managing finally and IOException</li> 038 * <li>Deserialize managing finally and IOException</li> 039 * </ul> 040 * 041 * <p> 042 * This class throws exceptions for invalid {@code null} inputs. Each method documents its behavior in more detail. 043 * </p> 044 * <p> 045 * If you want to secure deserialization with a whitelist or blacklist, please use Apache Commons IO's 046 * {@link org.apache.commons.io.serialization.ValidatingObjectInputStream ValidatingObjectInputStream}. 047 * </p> 048 * <p> 049 * #ThreadSafe# 050 * </p> 051 * 052 * @see org.apache.commons.io.serialization.ValidatingObjectInputStream 053 * @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/serialization/">Java Object Serialization Specification</a> 054 * @since 1.0 055 */ 056public class SerializationUtils { 057 058 /** 059 * Custom specialization of the standard JDK {@link ObjectInputStream} that uses a custom {@link ClassLoader} to resolve a class. If the specified 060 * {@link ClassLoader} is not able to resolve the class, the context classloader of the current thread will be used. This way, the standard deserialization 061 * also works in web application containers and application servers, regardless of which {@link ClassLoader} loaded the class that encapsulates 062 * serialization/deserialization. 063 * 064 * <p> 065 * For more in-depth information about the problem for which this class here is a workaround, see the JIRA issue LANG-626. 066 * </p> 067 */ 068 static final class ClassLoaderAwareObjectInputStream extends ObjectInputStream { 069 070 private final ClassLoader classLoader; 071 072 /** 073 * Constructs a new instance. 074 * 075 * @param in The {@link InputStream}. 076 * @param classLoader classloader to use 077 * @throws IOException Thrown if an I/O error occurs while reading stream header. 078 * @see java.io.ObjectInputStream 079 */ 080 ClassLoaderAwareObjectInputStream(final InputStream in, final ClassLoader classLoader) throws IOException { 081 super(in); 082 this.classLoader = classLoader; 083 } 084 085 /** 086 * Overridden version that uses the parameterized {@link ClassLoader} or the {@link ClassLoader} of the current {@link Thread} to resolve the class. 087 * 088 * @param desc An instance of class {@link ObjectStreamClass}. 089 * @return A {@link Class} object corresponding to {@code desc}. 090 * @throws IOException Thrown if an I/O error occurs. 091 * @throws ClassNotFoundException Thrown if class of a serialized object cannot be found. 092 */ 093 @Override 094 protected Class<?> resolveClass(final ObjectStreamClass desc) throws IOException, ClassNotFoundException { 095 final String name = desc.getName(); 096 try { 097 return Class.forName(name, false, classLoader); 098 } catch (final ClassNotFoundException ex) { 099 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}