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