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