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 */ 017package org.apache.commons.lang3; 018 019import java.io.ByteArrayInputStream; 020import java.io.ByteArrayOutputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.ObjectInputStream; 024import java.io.ObjectOutputStream; 025import java.io.ObjectStreamClass; 026import java.io.OutputStream; 027import java.io.Serializable; 028import java.util.Objects; 029 030/** 031 * Performs additional functionality for serialization. 032 * 033 * <ul> 034 * <li>Deep clone using serialization</li> 035 * <li>Serialize managing finally and IOException</li> 036 * <li>Deserialize managing finally and IOException</li> 037 * </ul> 038 * 039 * <p> 040 * This class throws exceptions for invalid {@code null} inputs. Each method documents its behavior in more detail. 041 * </p> 042 * <p> 043 * If you want to secure deserialization with a whitelist or blacklist, please use Apache Commons IO's 044 * {@link org.apache.commons.io.serialization.ValidatingObjectInputStream ValidatingObjectInputStream}. 045 * </p> 046 * <p> 047 * #ThreadSafe# 048 * </p> 049 * 050 * @see org.apache.commons.io.serialization.ValidatingObjectInputStream 051 * @since 1.0 052 */ 053public class SerializationUtils { 054 055 /** 056 * Custom specialization of the standard JDK {@link ObjectInputStream} 057 * that uses a custom {@link ClassLoader} to resolve a class. 058 * If the specified {@link ClassLoader} is not able to resolve the class, 059 * the context classloader of the current thread will be used. 060 * This way, the standard deserialization work also in web-application 061 * containers and application servers, no matter in which of the 062 * {@link ClassLoader} the particular class that encapsulates 063 * serialization/deserialization lives. 064 * 065 * <p>For more in-depth information about the problem for which this 066 * class here is a workaround, see the JIRA issue LANG-626.</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 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} 087 * of the current {@link Thread} to resolve the class. 088 * 089 * @param desc An instance of class {@link ObjectStreamClass}. 090 * @return A {@link Class} object corresponding to {@code desc}. 091 * @throws IOException Any of the usual Input/Output exceptions. 092 * @throws ClassNotFoundException If class of a serialized object cannot be found. 093 */ 094 @Override 095 protected Class<?> resolveClass(final ObjectStreamClass desc) throws IOException, ClassNotFoundException { 096 final String name = desc.getName(); 097 try { 098 return Class.forName(name, false, classLoader); 099 } catch (final ClassNotFoundException ex) { 100 try { 101 return Class.forName(name, false, Thread.currentThread().getContextClassLoader()); 102 } catch (final ClassNotFoundException cnfe) { 103 final Class<?> cls = ClassUtils.getPrimitiveClass(name); 104 if (cls != null) { 105 return cls; 106 } 107 throw cnfe; 108 } 109 } 110 } 111 112 } 113 114 /** 115 * Deep clones an {@link Object} using serialization. 116 * 117 * <p>This is many times slower than writing clone methods by hand 118 * on all objects in your object graph. However, for complex object 119 * graphs, or for those that don't support deep cloning this can 120 * be a simple alternative implementation. Of course all the objects 121 * must be {@link Serializable}.</p> 122 * 123 * @param <T> the type of the object involved. 124 * @param object the {@link Serializable} object to clone. 125 * @return the cloned object. 126 * @throws SerializationException (runtime) if the serialization fails. 127 */ 128 public static <T extends Serializable> T clone(final T object) { 129 if (object == null) { 130 return null; 131 } 132 final ByteArrayInputStream bais = new ByteArrayInputStream(serialize(object)); 133 final Class<T> cls = ObjectUtils.getClass(object); 134 try (ClassLoaderAwareObjectInputStream in = new ClassLoaderAwareObjectInputStream(bais, cls.getClassLoader())) { 135 // When we serialize and deserialize an object, it is reasonable to assume the deserialized object is of the 136 // same type as the original serialized object 137 return (T) in.readObject(); 138 139 } catch (final ClassNotFoundException | IOException ex) { 140 throw new SerializationException(String.format("%s while reading cloned object data", ex.getClass().getSimpleName()), ex); 141 } 142 } 143 144 /** 145 * Deserializes a single {@link Object} from an array of bytes. 146 * 147 * <p> 148 * If the call site incorrectly types the return value, a {@link ClassCastException} is thrown from the call site. 149 * Without Generics in this declaration, the call site must type cast and can cause the same ClassCastException. 150 * Note that in both cases, the ClassCastException is in the call site, not in this method. 151 * </p> 152 * <p> 153 * If you want to secure deserialization with a whitelist or blacklist, please use Apache Commons IO's 154 * {@link org.apache.commons.io.serialization.ValidatingObjectInputStream ValidatingObjectInputStream}. 155 * </p> 156 * 157 * @param <T> the object type to be deserialized. 158 * @param objectData 159 * the serialized object, must not be null. 160 * @return the deserialized object. 161 * @throws NullPointerException if {@code objectData} is {@code null}. 162 * @throws SerializationException (runtime) if the serialization fails. 163 * @see org.apache.commons.io.serialization.ValidatingObjectInputStream 164 */ 165 public static <T> T deserialize(final byte[] objectData) { 166 Objects.requireNonNull(objectData, "objectData"); 167 return deserialize(new ByteArrayInputStream(objectData)); 168 } 169 170 /** 171 * Deserializes an {@link Object} from the specified stream. 172 * 173 * <p> 174 * The stream will be closed once the object is written. This avoids the need for a finally clause, and maybe also 175 * exception handling, in the application code. 176 * </p> 177 * 178 * <p> 179 * The stream passed in is not buffered internally within this method. This is the responsibility of your 180 * application if desired. 181 * </p> 182 * 183 * <p> 184 * If the call site incorrectly types the return value, a {@link ClassCastException} is thrown from the call site. 185 * Without Generics in this declaration, the call site must type cast and can cause the same ClassCastException. 186 * Note that in both cases, the ClassCastException is in the call site, not in this method. 187 * </p> 188 * 189 * <p> 190 * If you want to secure deserialization with a whitelist or blacklist, please use Apache Commons IO's 191 * {@link org.apache.commons.io.serialization.ValidatingObjectInputStream ValidatingObjectInputStream}. 192 * </p> 193 * 194 * @param <T> the object type to be deserialized. 195 * @param inputStream the serialized object input stream, must not be null. 196 * @return the deserialized object. 197 * @throws NullPointerException if {@code inputStream} is {@code null}. 198 * @throws SerializationException (runtime) if the serialization fails. 199 * @see org.apache.commons.io.serialization.ValidatingObjectInputStream 200 */ 201 @SuppressWarnings("resource") // inputStream is managed by the caller 202 public static <T> T deserialize(final InputStream inputStream) { 203 Objects.requireNonNull(inputStream, "inputStream"); 204 try (ObjectInputStream in = new ObjectInputStream(inputStream)) { 205 @SuppressWarnings("unchecked") 206 final T obj = (T) in.readObject(); 207 return obj; 208 } catch (final ClassNotFoundException | IOException | NegativeArraySizeException ex) { 209 throw new SerializationException(ex); 210 } 211 } 212 213 /** 214 * Performs a serialization roundtrip. Serializes and deserializes the given object, great for testing objects that 215 * implement {@link Serializable}. 216 * 217 * @param <T> 218 * the type of the object involved. 219 * @param obj 220 * the object to roundtrip. 221 * @return the serialized and deserialized object. 222 * @since 3.3 223 */ 224 @SuppressWarnings("unchecked") // OK, because we serialized a type `T` 225 public static <T extends Serializable> T roundtrip(final T obj) { 226 return (T) deserialize(serialize(obj)); 227 } 228 229 /** 230 * Serializes an {@link Object} to a byte array for 231 * storage/serialization. 232 * 233 * @param obj the object to serialize to bytes. 234 * @return a byte[] with the converted Serializable. 235 * @throws SerializationException (runtime) if the serialization fails. 236 */ 237 public static byte[] serialize(final Serializable obj) { 238 final ByteArrayOutputStream baos = new ByteArrayOutputStream(512); 239 serialize(obj, baos); 240 return baos.toByteArray(); 241 } 242 243 /** 244 * Serializes an {@link Object} to the specified stream. 245 * 246 * <p>The stream will be closed once the object is written. 247 * This avoids the need for a finally clause, and maybe also exception 248 * handling, in the application code.</p> 249 * 250 * <p>The stream passed in is not buffered internally within this method. 251 * This is the responsibility of your application if desired.</p> 252 * 253 * @param obj the object to serialize to bytes, may be null. 254 * @param outputStream the stream to write to, must not be null. 255 * @throws NullPointerException if {@code outputStream} is {@code null}. 256 * @throws SerializationException (runtime) if the serialization fails. 257 */ 258 @SuppressWarnings("resource") // outputStream is managed by the caller 259 public static void serialize(final Serializable obj, final OutputStream outputStream) { 260 Objects.requireNonNull(outputStream, "outputStream"); 261 try (ObjectOutputStream out = new ObjectOutputStream(outputStream)) { 262 out.writeObject(obj); 263 } catch (final IOException ex) { 264 throw new SerializationException(ex); 265 } 266 } 267 268 /** 269 * SerializationUtils instances should NOT be constructed in standard programming. 270 * Instead, the class should be used as {@code SerializationUtils.clone(object)}. 271 * 272 * <p>This constructor is public to permit tools that require a JavaBean instance 273 * to operate.</p> 274 * 275 * @since 2.0 276 * @deprecated TODO Make private in 4.0. 277 */ 278 @Deprecated 279 public SerializationUtils() { 280 // empty 281 } 282 283}