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