id:koichikさんのソースを参考にシリアライズの実装を考えてみました.
SerializebleObjectTestで,シリアライズもデシリアライズもうまくいっています.
しかし,SerializebleObjectTestをextends S2TestCaseBaseしてしまうと,例外が発生してしまうんだよなぁ.
なぜだろう.困った.
java.lang.RuntimeException: java.lang.RuntimeException: javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of org/seasar/framework/unit/UnitClassLoader): attempted duplicate class definition for name: "org/seasar/chronos/extension/persitense/TestTaskProxy" at org.seasar.chronos.extension.persitense.SerializableObject.readObject(SerializableObject.java:48) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974) at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1849) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351) at org.seasar.chronos.extension.persitense.SerializebleObjectTest.testDeserializebleObject(SerializebleObjectTest.java:62) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at junit.framework.TestCase.runTest(TestCase.java:164) at org.seasar.framework.unit.S2FrameworkTestCase.doRunTest(S2FrameworkTestCase.java:519) at org.seasar.extension.unit.S2TestCase.doRunTest(S2TestCase.java:103) at org.seasar.framework.unit.S2FrameworkTestCase.runBare(S2FrameworkTestCase.java:308) at junit.framework.TestResult$1.protect(TestResult.java:110) at junit.framework.TestResult.runProtected(TestResult.java:128) at junit.framework.TestResult.run(TestResult.java:113) at junit.framework.TestCase.run(TestCase.java:120) at junit.framework.TestSuite.runTest(TestSuite.java:228) at junit.framework.TestSuite.run(TestSuite.java:223) at org.junit.internal.runners.OldTestClassRunner.run(OldTestClassRunner.java:35) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196) Caused by: java.lang.RuntimeException: javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of org/seasar/framework/unit/UnitClassLoader): attempted duplicate class definition for name: "org/seasar/chronos/extension/persitense/TestTaskProxy" at org.seasar.chronos.extension.persitense.SerializableObject.readObject(SerializableObject.java:48) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974) at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1849) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351) at java.util.HashMap.readObject(HashMap.java:1030) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:974) at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1849) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351) at org.seasar.chronos.extension.persitense.SerializableObject.readObject(SerializableObject.java:45) ... 31 more Caused by: javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of org/seasar/framework/unit/UnitClassLoader): attempted duplicate class definition for name: "org/seasar/chronos/extension/persitense/TestTaskProxy" at javassist.ClassPool.toClass(ClassPool.java:904) at javassist.ClassPool.toClass(ClassPool.java:847) at javassist.ClassPool.toClass(ClassPool.java:805) at javassist.CtClass.toClass(CtClass.java:1037) at org.seasar.chronos.extension.persitense.SerializeFactory.createProxy(SerializeFactory.java:30) at org.seasar.chronos.extension.persitense.SerializableObject.readObject(SerializableObject.java:43) ... 51 more Caused by: java.lang.LinkageError: loader (instance of org/seasar/framework/unit/UnitClassLoader): attempted duplicate class definition for name: "org/seasar/chronos/extension/persitense/TestTaskProxy" at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:620) at java.lang.ClassLoader.defineClass(ClassLoader.java:465) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at javassist.ClassPool.toClass2(ClassPool.java:916) at javassist.ClassPool.toClass(ClassPool.java:898) ... 56 more
public class SerializeFactory { public static Class<?> createProxy(Class<?> target) throws NotFoundException, CannotCompileException { String proxyClassName = "org.seasar.chronos.extension.persitense." + target.getSimpleName() + "Proxy"; try { Class<?> clazz = Class.forName(proxyClassName); return clazz; } catch (ClassNotFoundException e) { } ClassPool cp = new ClassPool(); cp.appendClassPath(new LoaderClassPath(ProxyFactory.class .getClassLoader())); CtClass formCtClass = cp.get(target.getName()); CtClass formProxyCtClass = cp.makeClass(proxyClassName, formCtClass); formProxyCtClass.setSuperclass(formCtClass); addWriteReplaceMethod(target.getName(), formProxyCtClass); return formProxyCtClass.toClass(); } private static void addWriteReplaceMethod(String className, CtClass target) throws CannotCompileException { String body = "public Object writeReplace() throws java.io.ObjectStreamException { return org.seasar.chronos.extension.persitense.SerializableObject.create(\"" + className + "\", this); }"; CtMethod method = CtMethod.make(body, target); target.addMethod(method); } }
public class SerializableObject implements Serializable { private static final long serialVersionUID = 1L; private String className; private Object target; public static SerializableObject create(String className, Object target) { return new SerializableObject(className, target); } public SerializableObject(String className, Object target) { this.className = className; this.target = target; } private void writeObject(ObjectOutputStream stream) throws IOException { stream.writeObject(className); stream.writeObject(createFieldMap()); } @SuppressWarnings("unchecked") private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { try { className = String.class.cast(stream.readObject()); Class sourceClazz = Class.forName(className); Class clazz = SerializeFactory.createProxy(sourceClazz); target = clazz.newInstance(); Map<String, Object> fieldMap = Map.class.cast(stream.readObject()); restoreFields(fieldMap); } catch (Exception e) { throw new RuntimeException(e); } } private Object readResolve() throws ObjectStreamException { return target; } private Map createFieldMap() { try { Map<String, Object> fieldMap = new HashMap<String, Object>(); Class<? extends Object> clazz = target.getClass(); while (clazz != Object.class) { Field[] fields = clazz.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { if (!fields[i].isAccessible()) { fields[i].setAccessible(true); } Object value = fields[i].get(target); if (value != null) { fieldMap.put(fields[i].getName(), value); } } clazz = clazz.getSuperclass(); } return fieldMap; } catch (Exception e) { throw new RuntimeException(e); } } private void restoreFields(Map<String, Object> fieldMap) { try { Class clazz = target.getClass(); while (clazz != Object.class) { Field[] fields = clazz.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { int modifiers = fields[i].getModifiers(); if (Modifier.isFinal(modifiers)) { continue; } if (!fields[i].isAccessible()) { fields[i].setAccessible(true); } Object value = fieldMap.get(fields[i].getName()); if (value instanceof SerializableObject) { // ポイント!! value = SerializableObject.class.cast(value) .readResolve(); } fields[i].set(target, value); } clazz = clazz.getSuperclass(); } } catch (Exception e) { throw new RuntimeException(e); } } @Override public String toString() { return new ToStringBuilder(this).toString(); } public Object getTarget() { return target; } }
public class TestTask implements Serializable { private static final long serialVersionUID = 1L; public String name = "aaaa"; public Integer id = 10; public TestTask testTask = this; @Override public String toString() { return new ToStringBuilder(this).toString(); } }
public class SerializebleObjectTest { private static final long serialVersionUID = 1L; @Test public void testSerializebleObject() throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException { Class clazz = SerializeFactory.createProxy(TestTask.class); TestTask a = (TestTask) clazz.newInstance(); a.testTask = (TestTask) clazz.newInstance(); a.testTask.name = "hoge"; FileOutputStream fos = null; try { File targetFile = new File("C:\\temp\\", TestTask.class.getName()); fos = new FileOutputStream(targetFile); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(a); } catch (IOException e) { throw new IORuntimeException(e); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { throw new IORuntimeException(e); } } } } @Test public void testDeserializebleObject() { TestTask target = null; String className = TestTask.class.getName(); FileInputStream fis = null; try { File targetFile = new File("C:\\temp\\", className); if (targetFile.exists()) { fis = new FileInputStream(targetFile); ObjectInputStream ois = new ObjectInputStream(fis); target = (TestTask) ois.readObject(); } } catch (IOException e) { throw new IORuntimeException(e); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { throw new IORuntimeException(e); } } } System.out.println(target); System.out.println(target.name); System.out.println(target.id); System.out.println(target.testTask); System.out.println(target.testTask.name); System.out.println(target.testTask.id); } }
public class S2TestCaseBase extends S2TestCase { private static final String PATH = "test.dicon"; protected void setUp() throws Exception { super.setUp(); include(PATH); } }