001 /*****************************************************************************
002 * Copyright (C) NanoContainer Organization. All rights reserved. *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file. *
007 * *
008 *****************************************************************************/
009 package org.nanocontainer.reflection;
010
011 import org.nanocontainer.integrationkit.ContainerRecorder;
012 import org.picocontainer.MutablePicoContainer;
013 import org.picocontainer.PicoException;
014
015 import java.io.IOException;
016 import java.io.ObjectInputStream;
017 import java.io.ObjectOutputStream;
018 import java.io.Serializable;
019 import java.lang.reflect.InvocationHandler;
020 import java.lang.reflect.InvocationTargetException;
021 import java.lang.reflect.Method;
022 import java.lang.reflect.Proxy;
023 import java.util.ArrayList;
024 import java.util.Iterator;
025 import java.util.List;
026
027 /**
028 * This class is serializable. The original container will not be serialized
029 * (for performance reasons), but the invocations will, so they can be replayed at the
030 * other end of the wire.
031 *
032 * @author Konstantin Pribluda ( konstantin.pribluda(at)infodesire.com )
033 * @author Aslak Hellesøy
034 * @author Mauro Talevi
035 */
036 public class DefaultContainerRecorder implements Serializable, ContainerRecorder {
037
038 private final List invocations = new ArrayList();
039 private transient MutablePicoContainer container;
040
041 private final InvocationHandler invocationRecorder = new InvocationRecorder();
042
043 public DefaultContainerRecorder(MutablePicoContainer container) {
044 this.container = container;
045 }
046
047 public MutablePicoContainer getContainerProxy() {
048 return (MutablePicoContainer) Proxy.newProxyInstance(getClass().getClassLoader(),
049 new Class[]{MutablePicoContainer.class}, invocationRecorder);
050 }
051
052 public void replay(MutablePicoContainer target) {
053 for (Iterator iter = invocations.iterator(); iter.hasNext();) {
054 Invocation invocation = (Invocation) iter.next();
055 try {
056 invocation.invoke(target);
057 } catch (IllegalAccessException e) {
058 throw new PicoException(e) {
059 };
060 } catch (InvocationTargetException e) {
061 throw new PicoException(e) {
062 };
063 }
064 }
065 }
066
067 private class Invocation implements Serializable {
068 private transient Method method;
069 private Object[] args;
070
071 Invocation(Method method, Object[] args) {
072 this.method = method;
073 this.args = args;
074 }
075
076 private void writeObject(ObjectOutputStream out) throws IOException {
077 out.defaultWriteObject();
078 out.writeUTF(method.getName());
079 out.writeObject(method.getDeclaringClass());
080 Class[] parameterTypes = method.getParameterTypes();
081 out.writeInt(parameterTypes.length);
082 for (int i = 0; i < parameterTypes.length; i++) {
083 out.writeObject(parameterTypes[i]);
084 }
085 }
086
087 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
088 in.defaultReadObject();
089 String methodName = in.readUTF();
090 Class declaringClass = (Class) in.readObject();
091 int n = in.readInt();
092 Class[] parameterTypes = new Class[n];
093 for (int i = 0; i < n; i++) {
094 parameterTypes[i] = (Class) in.readObject();
095 }
096 try {
097 method = declaringClass.getMethod(methodName, parameterTypes);
098 } catch (NoSuchMethodException e) {
099 throw new IOException("Couldn't load method " + methodName);
100 }
101 }
102
103 public void invoke(Object target) throws IllegalAccessException, InvocationTargetException {
104 method.invoke(target, args);
105 }
106 }
107
108 private class InvocationRecorder implements InvocationHandler, Serializable {
109 /**
110 * Record invocation and invoke on underlying container
111 */
112 public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, InvocationTargetException {
113 invocations.add(new Invocation(method, args));
114 return method.invoke(container, args);
115 }
116 };
117
118 }