1 5 package org.easymock.classextension.internal; 6 7 import java.io.ByteArrayInputStream ; 8 import java.io.ByteArrayOutputStream ; 9 import java.io.DataOutputStream ; 10 import java.io.IOException ; 11 import java.io.ObjectInputStream ; 12 import java.io.ObjectStreamClass ; 13 import java.io.ObjectStreamConstants ; 14 import java.io.Serializable ; 15 import java.lang.reflect.Constructor ; 16 import java.lang.reflect.Field ; 17 import java.lang.reflect.InvocationTargetException ; 18 import java.lang.reflect.Method ; 19 import java.lang.reflect.Modifier ; 20 21 import org.easymock.MockControl; 22 import org.easymock.classextension.MockClassControl; 23 import org.easymock.internal.RecordState; 24 25 29 public class DefaultClassInstantiator implements IClassInstantiator { 30 31 38 public Object newInstance(Class c) throws InstantiationException { 39 40 if (isSerializable(c)) { 41 try { 42 return readObject(getSerializedBytes(c)); 43 } catch (IOException e) { 45 throw new RuntimeException ("Failed to instantiate " 46 + c.getName() + "'s mock: " + e.getMessage()); 47 } catch (ClassNotFoundException e) { 48 throw new RuntimeException ("Failed to instantiate " 49 + c.getName() + "'s mock: " + e.getMessage()); 50 } 51 } 53 54 Constructor constructor = getConstructorToUse(c); 55 Object [] params = getArgsForTypes(constructor.getParameterTypes()); 56 try { 57 return constructor.newInstance(params); 58 } catch (IllegalArgumentException e) { 60 throw new RuntimeException ("Failed to instantiate " + c.getName() 61 + "'s mock: " + e.getMessage()); 62 } catch (IllegalAccessException e) { 63 throw new RuntimeException ("Failed to instantiate " + c.getName() 64 + "'s mock: " + e.getMessage()); 65 } catch (InvocationTargetException e) { 67 throw new RuntimeException ("Failed to instantiate " + c.getName() 68 + "'s mock: " + e.getMessage()); 69 } 70 } 71 72 79 private boolean isSerializable(Class clazz) { 80 return Serializable .class.isAssignableFrom(clazz); 81 } 82 83 92 private Constructor getConstructorToUse(Class clazz) { 93 try { 95 return clazz.getConstructor(new Class [0]); 96 } catch (NoSuchMethodException e) { 97 if (clazz.getConstructors().length == 0) { 99 throw new IllegalArgumentException ( 100 "No visible constructors in class " + clazz.getName()); 101 } 102 return clazz.getConstructors()[0]; 103 } 104 } 105 106 113 private Object [] getArgsForTypes(Class [] methodTypes) 114 throws InstantiationException { 115 Object [] methodArgs = new Object [methodTypes.length]; 116 117 for (int i = 0; i < methodTypes.length; i++) { 118 119 if (methodTypes[i].isPrimitive()) { 120 methodArgs[i] = RecordState.emptyReturnValueFor(methodTypes[i]); 122 } else if (Modifier.isFinal(methodTypes[i].getModifiers())) { 123 methodArgs[i] = newInstance(methodTypes[i]); 127 } else { 128 MockControl ctrl = MockClassControl 130 .createNiceControl(methodTypes[i]); 131 ctrl.replay(); 132 methodArgs[i] = ctrl.getMock(); 133 } 134 } 135 return methodArgs; 136 } 137 138 private static byte[] getSerializedBytes(Class clazz) throws IOException { 139 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 140 DataOutputStream data = new DataOutputStream (baos); 141 data.writeShort(ObjectStreamConstants.STREAM_MAGIC); 142 data.writeShort(ObjectStreamConstants.STREAM_VERSION); 143 data.writeByte(ObjectStreamConstants.TC_OBJECT); 144 data.writeByte(ObjectStreamConstants.TC_CLASSDESC); 145 data.writeUTF(clazz.getName()); 146 147 Long suid = getSerializableUID(clazz); 148 149 data.writeLong(suid.longValue()); 150 151 data.writeByte(2); data.writeShort(0); data.writeByte(ObjectStreamConstants.TC_ENDBLOCKDATA); 154 data.writeByte(ObjectStreamConstants.TC_NULL); 155 return baos.toByteArray(); 156 } 157 158 private static Long getSerializableUID(Class clazz) { 159 160 try { 161 Field f = clazz.getDeclaredField("serialVersionUID"); 162 final int mask = Modifier.STATIC | Modifier.FINAL; 163 if ((f.getModifiers() & mask) == mask) { 164 f.setAccessible(true); 165 return new Long (f.getLong(null)); 166 } 167 } catch (NoSuchFieldException e) { 168 } catch (IllegalAccessException e) { 170 throw new RuntimeException ( 172 "Should have been able to get serialVersionUID since it's there"); 173 } 175 return callLongMethod(clazz, ClassInstantiatorFactory 177 .is1_3Specifications() ? "computeSerialVersionUID" 178 : "computeDefaultSUID"); 179 } 181 182 private static Long callLongMethod(Class clazz, String methodName) { 183 184 Method method; 185 try { 187 method = ObjectStreamClass .class.getDeclaredMethod(methodName, 188 new Class [] { Class .class }); 189 } catch (NoSuchMethodException e) { 190 throw new InternalError ("ObjectStreamClass." + methodName 191 + " seems to have vanished"); 192 } 193 boolean accessible = method.isAccessible(); 194 method.setAccessible(true); 195 Long suid; 196 try { 197 suid = (Long ) method.invoke(null, new Object [] { clazz }); 198 } catch (IllegalAccessException e) { 199 throw new InternalError ("ObjectStreamClass." + methodName 200 + " should have been accessible"); 201 } catch (InvocationTargetException e) { 202 throw new InternalError ("ObjectStreamClass." + methodName 203 + " failled to be called: " + e.getMessage()); 204 } 205 method.setAccessible(accessible); 206 return suid; 208 } 209 210 private static Object readObject(byte[] bytes) throws IOException , 211 ClassNotFoundException { 212 ObjectInputStream in = new ObjectInputStream (new ByteArrayInputStream ( 213 bytes)); 214 return in.readObject(); 215 } 216 217 } 218 | Popular Tags |