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    }