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    
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    @SuppressWarnings("serial")
035    public class HiddenImplementation<T> extends AbstractBehavior<T> {
036    
037            /**
038         * Creates an ImplementationHidingComponentAdapter with a delegate 
039         * @param delegate the component adapter to which this adapter delegates
040         */
041        public HiddenImplementation(ComponentAdapter<T> delegate) {
042            super(delegate);
043        }
044    
045        public T getComponentInstance(final PicoContainer container, Type into) throws PicoCompositionException {
046    
047            ComponentAdapter<T> delegate = getDelegate();
048            Object componentKey = delegate.getComponentKey();
049            Class<?>[] classes;
050            if (componentKey instanceof Class && ((Class<?>) delegate.getComponentKey()).isInterface()) {
051                classes = new Class[]{(Class<?>) delegate.getComponentKey()};
052            } else if (componentKey instanceof Class[]) {
053                classes = (Class[]) componentKey;
054            } else {
055                return delegate.getComponentInstance(container, into);
056            }
057    
058            verifyInterfacesOnly(classes);
059            return createProxy(classes, container, delegate.getComponentImplementation().getClassLoader());
060        }
061    
062        public String getDescriptor() {
063            return "Hidden";
064        }
065    
066        @SuppressWarnings("unchecked")
067        protected T createProxy(Class[] interfaces, final PicoContainer container, final ClassLoader classLoader) {
068            final PicoContainer container1 = container;
069            return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
070                private final PicoContainer container = container1;
071                private volatile Object instance;
072                public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
073                    if (instance == null) {
074                        synchronized (HiddenImplementation.this) {
075                            if (instance == null) {
076                                instance = getDelegate().getComponentInstance(container, NOTHING.class);
077                            }
078                        }
079                    }
080                    return invokeMethod(instance, method, args, container);
081                }
082            });
083        }
084    
085        protected Object invokeMethod(Object componentInstance, Method method, Object[] args, PicoContainer container) throws Throwable {
086            ComponentMonitor componentMonitor = currentMonitor();
087            try {
088                componentMonitor.invoking(container, this, method, componentInstance, args);
089                long startTime = System.currentTimeMillis();
090                Object rv = method.invoke(componentInstance, args);
091                componentMonitor.invoked(container, this,
092                                         method, componentInstance, System.currentTimeMillis() - startTime, args, rv);
093                return rv;
094            } catch (final InvocationTargetException ite) {
095                componentMonitor.invocationFailed(method, componentInstance, ite);
096                throw ite.getTargetException();
097            }
098        }
099    
100        private void verifyInterfacesOnly(Class<?>[] classes) {
101            for (Class<?> clazz : classes) {
102                if (!clazz.isInterface()) {
103                    throw new PicoCompositionException(
104                        "Class keys must be interfaces. " + clazz + " is not an interface.");
105                }
106            }
107        }
108    
109    }