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    }