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}