    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&oslash;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    }