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.OutputStream;
026    import java.io.Serializable;
027    
028    /**
029     * <p>Assists with the serialization process and performs additional functionality based
030     * on serialization.</p>
031     * <p>
032     * <ul>
033     * <li>Deep clone using serialization
034     * <li>Serialize managing finally and IOException
035     * <li>Deserialize managing finally and IOException
036     * </ul>
037     *
038     * <p>This class throws exceptions for invalid {@code null} inputs.
039     * Each method documents its behaviour in more detail.</p>
040     *
041     * <p>#ThreadSafe#</p>
042     * @since 1.0
043     * @version $Id: SerializationUtils.java 1089736 2011-04-07 04:39:33Z bayard $
044     */
045    public class SerializationUtils {
046    
047        /**
048         * <p>SerializationUtils instances should NOT be constructed in standard programming.
049         * Instead, the class should be used as {@code SerializationUtils.clone(object)}.</p>
050         *
051         * <p>This constructor is public to permit tools that require a JavaBean instance
052         * to operate.</p>
053         * @since 2.0
054         */
055        public SerializationUtils() {
056            super();
057        }
058    
059        // Clone
060        //-----------------------------------------------------------------------
061        /**
062         * <p>Deep clone an {@code Object} using serialization.</p>
063         *
064         * <p>This is many times slower than writing clone methods by hand
065         * on all objects in your object graph. However, for complex object
066         * graphs, or for those that don't support deep cloning this can
067         * be a simple alternative implementation. Of course all the objects
068         * must be {@code Serializable}.</p>
069         *
070         * @param <T> the type of the object involved
071         * @param object  the {@code Serializable} object to clone
072         * @return the cloned object
073         * @throws SerializationException (runtime) if the serialization fails
074         */
075        public static <T extends Serializable> T clone(T object) {
076            /*
077             * when we serialize and deserialize an object,
078             * it is reasonable to assume the deserialized object
079             * is of the same type as the original serialized object
080             */
081            @SuppressWarnings("unchecked")
082            final T result = (T) deserialize(serialize(object));
083            return result;
084        }
085    
086        // Serialize
087        //-----------------------------------------------------------------------
088        /**
089         * <p>Serializes an {@code Object} to the specified stream.</p>
090         *
091         * <p>The stream will be closed once the object is written.
092         * This avoids the need for a finally clause, and maybe also exception
093         * handling, in the application code.</p>
094         *
095         * <p>The stream passed in is not buffered internally within this method.
096         * This is the responsibility of your application if desired.</p>
097         *
098         * @param obj  the object to serialize to bytes, may be null
099         * @param outputStream  the stream to write to, must not be null
100         * @throws IllegalArgumentException if {@code outputStream} is {@code null}
101         * @throws SerializationException (runtime) if the serialization fails
102         */
103        public static void serialize(Serializable obj, OutputStream outputStream) {
104            if (outputStream == null) {
105                throw new IllegalArgumentException("The OutputStream must not be null");
106            }
107            ObjectOutputStream out = null;
108            try {
109                // stream closed in the finally
110                out = new ObjectOutputStream(outputStream);
111                out.writeObject(obj);
112    
113            } catch (IOException ex) {
114                throw new SerializationException(ex);
115            } finally {
116                try {
117                    if (out != null) {
118                        out.close();
119                    }
120                } catch (IOException ex) { // NOPMD
121                    // ignore close exception
122                }
123            }
124        }
125    
126        /**
127         * <p>Serializes an {@code Object} to a byte array for
128         * storage/serialization.</p>
129         *
130         * @param obj  the object to serialize to bytes
131         * @return a byte[] with the converted Serializable
132         * @throws SerializationException (runtime) if the serialization fails
133         */
134        public static byte[] serialize(Serializable obj) {
135            ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
136            serialize(obj, baos);
137            return baos.toByteArray();
138        }
139    
140        // Deserialize
141        //-----------------------------------------------------------------------
142        /**
143         * <p>Deserializes an {@code Object} from the specified stream.</p>
144         *
145         * <p>The stream will be closed once the object is written. This
146         * avoids the need for a finally clause, and maybe also exception
147         * handling, in the application code.</p>
148         *
149         * <p>The stream passed in is not buffered internally within this method.
150         * This is the responsibility of your application if desired.</p>
151         *
152         * @param inputStream  the serialized object input stream, must not be null
153         * @return the deserialized object
154         * @throws IllegalArgumentException if {@code inputStream} is {@code null}
155         * @throws SerializationException (runtime) if the serialization fails
156         */
157        public static Object deserialize(InputStream inputStream) {
158            if (inputStream == null) {
159                throw new IllegalArgumentException("The InputStream must not be null");
160            }
161            ObjectInputStream in = null;
162            try {
163                // stream closed in the finally
164                in = new ObjectInputStream(inputStream);
165                return in.readObject();
166    
167            } catch (ClassNotFoundException ex) {
168                throw new SerializationException(ex);
169            } catch (IOException ex) {
170                throw new SerializationException(ex);
171            } finally {
172                try {
173                    if (in != null) {
174                        in.close();
175                    }
176                } catch (IOException ex) { // NOPMD
177                    // ignore close exception
178                }
179            }
180        }
181    
182        /**
183         * <p>Deserializes a single {@code Object} from an array of bytes.</p>
184         *
185         * @param objectData  the serialized object, must not be null
186         * @return the deserialized object
187         * @throws IllegalArgumentException if {@code objectData} is {@code null}
188         * @throws SerializationException (runtime) if the serialization fails
189         */
190        public static Object deserialize(byte[] objectData) {
191            if (objectData == null) {
192                throw new IllegalArgumentException("The byte[] must not be null");
193            }
194            ByteArrayInputStream bais = new ByteArrayInputStream(objectData);
195            return deserialize(bais);
196        }
197    
198    }