001    /*--------------------------------------------------------------------------+
002    $Id: SerializationUtils.java 29381 2010-07-27 11:29:07Z deissenb $
003    |                                                                          |
004    | Copyright 2005-2010 Technische Universitaet Muenchen                     |
005    |                                                                          |
006    | Licensed under the Apache License, Version 2.0 (the "License");          |
007    | you may not use this file except in compliance with the License.         |
008    | You may obtain a copy of the License at                                  |
009    |                                                                          |
010    |    http://www.apache.org/licenses/LICENSE-2.0                            |
011    |                                                                          |
012    | Unless required by applicable law or agreed to in writing, software      |
013    | distributed under the License is distributed on an "AS IS" BASIS,        |
014    | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
015    | See the License for the specific language governing permissions and      |
016    | limitations under the License.                                           |
017    +--------------------------------------------------------------------------*/
018    package edu.tum.cs.commons.io;
019    
020    import java.io.ByteArrayInputStream;
021    import java.io.ByteArrayOutputStream;
022    import java.io.IOException;
023    import java.io.ObjectInputStream;
024    import java.io.ObjectOutputStream;
025    import java.io.ObjectStreamClass;
026    import java.io.Serializable;
027    
028    import edu.tum.cs.commons.assertion.CCSMAssert;
029    import edu.tum.cs.commons.filesystem.FileSystemUtils;
030    
031    /**
032     * Utility methods for serialization.
033     * 
034     * @author hummelb
035     * @author $Author: deissenb $
036     * @version $Rev: 29381 $
037     * @levd.rating GREEN Hash: CE4B1240AC4E61F91653F44688B311C5
038     */
039    public class SerializationUtils {
040    
041            /** Serializes an object to byte array */
042            public static byte[] serializeToByteArray(Serializable object)
043                            throws IOException {
044                    ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
045                    ObjectOutputStream out = new ObjectOutputStream(outBuffer);
046                    out.writeObject(object);
047                    // no need to put this in finally, as we do not block file handles.
048                    // Let the GC do the work
049                    out.close();
050                    return outBuffer.toByteArray();
051            }
052    
053            /**
054             * Deserializes an object from byte array using the default class loader
055             * used in {@link ObjectInputStream}, i.e. if there is a method on the
056             * current call stack whose class was loaded via a custom class loader, this
057             * class loader is used. Otherwise the default class loader is used. Also
058             * see the documentation of {@link ObjectInputStream}s resolveClass()
059             * method.
060             */
061            public static Serializable deserializeFromByteArray(byte[] bytes)
062                            throws IOException, ClassNotFoundException {
063                    return deserializeFromByteArray(bytes, null);
064            }
065    
066            /**
067             * Deserializes an object from byte array using a custom/given class loader.
068             * 
069             * @param classLoader
070             *            the class loader used. If this is null, the default behavior
071             *            of {@link ObjectInputStream} is used, i.e. if there is a
072             *            method on the current call stack whose class was loaded via a
073             *            custom class loader, this class loader is used. Otherwise the
074             *            default class loader is used. Also see the documentation of
075             *            {@link ObjectInputStream}s resolveClass() method.
076             */
077            public static Serializable deserializeFromByteArray(byte[] bytes,
078                            final ClassLoader classLoader) throws IOException,
079                            ClassNotFoundException {
080    
081                    ObjectInputStream in;
082                    if (classLoader == null) {
083                            in = new ObjectInputStream(new ByteArrayInputStream(bytes));
084                    } else {
085                            // it seems that the only solution to use a custom class loader is
086                            // to override a method in the ObjectInputStream class. This is
087                            // confirmed by
088                            // http://blogs.sun.com/adventures/entry/desrializing_objects_custom_class_loaders
089                            // and seems plausible as the corresponding method is protected.
090    
091                            in = new ObjectInputStream(new ByteArrayInputStream(bytes)) {
092    
093                                    /** {@inheritDoc} */
094                                    @Override
095                                    protected Class<?> resolveClass(ObjectStreamClass desc)
096                                                    throws IOException, ClassNotFoundException {
097                                            try {
098                                                    return Class
099                                                                    .forName(desc.getName(), false, classLoader);
100                                            } catch (ClassNotFoundException e) {
101                                                    // as a fallback we pass this to the super method, as
102                                                    // for example primitive values are treated there.
103                                                    return super.resolveClass(desc);
104                                            }
105                                    }
106                            };
107                    }
108    
109                    try {
110                            return (Serializable) in.readObject();
111                    } finally {
112                            FileSystemUtils.close(in);
113                    }
114            }
115    
116            /**
117             * Returns a copy of the given object obtained by serialization and
118             * deserialization in memory.
119             * 
120             * @param classLoader
121             *            the class loader used. If this is null, the default behavior
122             *            of {@link ObjectInputStream} is used, i.e. if there is a
123             *            method on the current call stack whose class was loaded via a
124             *            custom class loader, this class loader is used. Otherwise the
125             *            default class loader is used. Also see the documentation of
126             *            {@link ObjectInputStream}s resolveClass() method.
127             */
128            @SuppressWarnings("unchecked")
129            public static <T extends Serializable> T cloneBySerialization(T t,
130                            ClassLoader classLoader) {
131                    try {
132                            return (T) deserializeFromByteArray(serializeToByteArray(t),
133                                            classLoader);
134                    } catch (IOException e) {
135                            CCSMAssert
136                                            .fail("This should be impossible as we are working in memory!");
137                            return null;
138                    } catch (ClassNotFoundException e) {
139                            CCSMAssert
140                                            .fail("This should be impossible as we just had the object available!");
141                            return null;
142                    }
143            }
144    
145            /**
146             * Inserts an int value to the given position in the byte array. The storage
147             * will require 4 bytes in big endian byte order.
148             */
149            public static void insertInt(int i, byte[] bytes, int position) {
150                    bytes[position++] = (byte) (i >> 24 & 0xff);
151                    bytes[position++] = (byte) (i >> 16 & 0xff);
152                    bytes[position++] = (byte) (i >> 8 & 0xff);
153                    bytes[position] = (byte) (i & 0xff);
154            }
155    
156            /**
157             * Extracts an int value from the given array position (4 bytes in big
158             * endian). This is the counter part to {@link #insertInt(int, byte[], int)}
159             * .
160             */
161            public static int extractInt(byte[] bytes, int position) {
162                    int result = bytes[position++] & 0xff;
163                    result <<= 8;
164                    result |= bytes[position++] & 0xff;
165                    result <<= 8;
166                    result |= bytes[position++] & 0xff;
167                    result <<= 8;
168                    result |= bytes[position++] & 0xff;
169                    return result;
170            }
171    
172    }