    001    /*****************************************************************************
    002     * Copyright (C) PicoContainer 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     * Original code by                                                          *
    009     *****************************************************************************/
    010    package org.picocontainer.behaviors;
    011
    012    import java.lang.reflect.InvocationHandler;
    013    import java.lang.reflect.InvocationTargetException;
    014    import java.lang.reflect.Method;
    015    import java.lang.reflect.Proxy;
    016    import java.lang.reflect.Type;
    017
    018    import org.picocontainer.ComponentAdapter;
    019    import org.picocontainer.ComponentMonitor;
    020    import org.picocontainer.PicoContainer;
    021    import org.picocontainer.PicoCompositionException;
    022    import org.picocontainer.behaviors.AbstractBehavior;
    023
    024    /**
    025     * This component adapter makes it possible to hide the implementation
    026     * of a real subject (behind a proxy) provided the key is an interface.
    027     * <p/>
    028     * This class exists here, because a) it has no deps on external jars, b) dynamic proxy is quite easy.
    029     * The user is prompted to look at picocontainer-gems for alternate and bigger implementations.
    030     *
    031     * @author Aslak Helles&oslash;y
    032     * @author Paul Hammant
    033     * @see org.picocontainer.gems.adapters.HotSwappingComponentAdapter for a more feature-rich version of this class.
    034     */
    035    @SuppressWarnings("serial")
    036    public class HiddenImplementation<T> extends AbstractBehavior<T> {
    037
    038            /**
    039         * Creates an ImplementationHidingComponentAdapter with a delegate
    040         * @param delegate the component adapter to which this adapter delegates
    041         */
    042        public HiddenImplementation(ComponentAdapter<T> delegate) {
    043            super(delegate);
    044        }
    045
    046        public T getComponentInstance(final PicoContainer container, Type into) throws PicoCompositionException {
    047
    048            ComponentAdapter<T> delegate = getDelegate();
    049            Object componentKey = delegate.getComponentKey();
    050            Class<?>[] classes;
    051            if (componentKey instanceof Class && ((Class<?>) delegate.getComponentKey()).isInterface()) {
    052                classes = new Class[]{(Class<?>) delegate.getComponentKey()};
    053            } else if (componentKey instanceof Class[]) {
    054                classes = (Class[]) componentKey;
    055            } else {
    056                return delegate.getComponentInstance(container, into);
    057            }
    058
    059            verifyInterfacesOnly(classes);
    060            return createProxy(classes, container, delegate.getComponentImplementation().getClassLoader());
    061        }
    062
    063        public String getDescriptor() {
    064            return "Hidden";
    065        }
    066
    067
    068        @SuppressWarnings("unchecked")
    069            protected T createProxy(Class[] interfaces, final PicoContainer container, final ClassLoader classLoader) {
    070            return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
    071                public synchronized Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
    072                    return invokeMethod(getDelegate().getComponentInstance(container, NOTHING.class), method, args, container);
    073                }
    074            });
    075        }
    076
    077        protected Object invokeMethod(Object componentInstance, Method method, Object[] args, PicoContainer container) throws Throwable {
    078            ComponentMonitor componentMonitor = currentMonitor();
    079            try {
    080                componentMonitor.invoking(container, this, method, componentInstance, args);
    081                long startTime = System.currentTimeMillis();
    082                Object rv = method.invoke(componentInstance, args);
    083                componentMonitor.invoked(container, this,
    084                                         method, componentInstance, System.currentTimeMillis() - startTime, args, rv);
    085                return rv;
    086            } catch (final InvocationTargetException ite) {
    087                componentMonitor.invocationFailed(method, componentInstance, ite);
    088                throw ite.getTargetException();
    089            }
    090        }
    091
    092        private void verifyInterfacesOnly(Class<?>[] classes) {
    093            for (Class<?> clazz : classes) {
    094                if (!clazz.isInterface()) {
    095                    throw new PicoCompositionException(
    096                        "Class keys must be interfaces. " + clazz + " is not an interface.");
    097                }
    098            }
    099        }
    100
    101    }