001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 The ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| 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+-------------------------------------------------------------------------*/
017package org.conqat.lib.commons.io;
018
019import java.io.ByteArrayInputStream;
020import java.io.ByteArrayOutputStream;
021import java.io.IOException;
022import java.io.ObjectInputStream;
023import java.io.ObjectOutputStream;
024import java.io.ObjectStreamClass;
025import java.io.Serializable;
026
027import org.conqat.lib.commons.assertion.CCSMAssert;
028import org.conqat.lib.commons.filesystem.FileSystemUtils;
029
030/**
031 * Utility methods for serialization.
032 * 
033 * @author hummelb
034 */
035public class SerializationUtils {
036
037        /** Serializes an object to byte array */
038        public static byte[] serializeToByteArray(Serializable object) throws IOException {
039                ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
040                ObjectOutputStream out = new ObjectOutputStream(outBuffer);
041                out.writeObject(object);
042                // no need to put this in finally, as we do not block file handles.
043                // Let the GC do the work
044                out.close();
045                return outBuffer.toByteArray();
046        }
047
048        /**
049         * <b>Make use of StorageUtils#deserialize(byte[]) whenever possible. Only
050         * use this method as last resort.</b> <br>
051         * <br>
052         * Deserializes an object from byte array using a custom/given class loader.
053         * <b>Important:</b>The caller has to ensure that the supplied class loader
054         * "knows" all classes to be deserialized. Returns <code>null</code> if a
055         * <code>null</code> value is passed.
056         * 
057         * @param classLoader
058         *            the class loader used. If this is null, the default behavior
059         *            of {@link ObjectInputStream} is used, i.e. if there is a
060         *            method on the current call stack whose class was loaded via a
061         *            custom class loader, this class loader is used. Otherwise the
062         *            default class loader is used. Also see the documentation of
063         *            {@link ObjectInputStream}s resolveClass() method.
064         */
065        public static Serializable deserializeFromByteArray(byte[] bytes, final ClassLoader classLoader)
066                        throws IOException, ClassNotFoundException {
067
068                if (bytes == null) {
069                        return null;
070                }
071
072                ObjectInputStream in;
073                if (classLoader == null) {
074                        in = new ObjectInputStream(new ByteArrayInputStream(bytes));
075                } else {
076                        // it seems that the only solution to use a custom class loader is
077                        // to override a method in the ObjectInputStream class. This is
078                        // confirmed by
079                        // http://blogs.sun.com/adventures/entry/desrializing_objects_custom_class_loaders
080                        // and seems plausible as the corresponding method is protected.
081
082                        in = new ObjectInputStream(new ByteArrayInputStream(bytes)) {
083
084                                /** {@inheritDoc} */
085                                @Override
086                                protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
087                                        try {
088                                                return Class.forName(desc.getName(), false, classLoader);
089                                        } catch (ClassNotFoundException e) {
090                                                // as a fallback we pass this to the super method, as
091                                                // for example primitive values are treated there.
092                                                return super.resolveClass(desc);
093                                        }
094                                }
095                        };
096                }
097
098                try {
099                        return (Serializable) in.readObject();
100                } finally {
101                        FileSystemUtils.close(in);
102                }
103        }
104
105        /**
106         * Returns a copy of the given object obtained by serialization and
107         * deserialization in memory.
108         * 
109         * @param classLoader
110         *            the class loader used. If this is null, the default behavior
111         *            of {@link ObjectInputStream} is used, i.e. if there is a
112         *            method on the current call stack whose class was loaded via a
113         *            custom class loader, this class loader is used. Otherwise the
114         *            default class loader is used. Also see the documentation of
115         *            {@link ObjectInputStream}s resolveClass() method.
116         */
117        @SuppressWarnings("unchecked")
118        public static <T extends Serializable> T cloneBySerialization(T t, ClassLoader classLoader) {
119                try {
120                        return (T) deserializeFromByteArray(serializeToByteArray(t), classLoader);
121                } catch (IOException e) {
122                        CCSMAssert.fail("This should be impossible as we are working in memory!");
123                        return null;
124                } catch (ClassNotFoundException e) {
125                        CCSMAssert.fail("This should be impossible as we just had the object available!");
126                        return null;
127                }
128        }
129
130        /**
131         * Inserts an int value to the given position in the byte array. The storage
132         * will require 4 bytes in big endian byte order.
133         */
134        public static void insertInt(int i, byte[] bytes, int position) {
135                bytes[position++] = (byte) (i >> 24 & 0xff);
136                bytes[position++] = (byte) (i >> 16 & 0xff);
137                bytes[position++] = (byte) (i >> 8 & 0xff);
138                bytes[position] = (byte) (i & 0xff);
139        }
140
141        /**
142         * Extracts an int value from the given array position (4 bytes in big
143         * endian). This is the counter part to {@link #insertInt(int, byte[], int)}
144         * .
145         */
146        public static int extractInt(byte[] bytes, int position) {
147                int result = bytes[position++] & 0xff;
148                result <<= 8;
149                result |= bytes[position++] & 0xff;
150                result <<= 8;
151                result |= bytes[position++] & 0xff;
152                result <<= 8;
153                result |= bytes[position++] & 0xff;
154                return result;
155        }
156
157}