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 *****************************************************************************/
010package org.picocontainer.behaviors;
011
012import java.lang.reflect.InvocationHandler;
013import java.lang.reflect.InvocationTargetException;
014import java.lang.reflect.Method;
015import java.lang.reflect.Proxy;
016import java.lang.reflect.Type;
017
018import org.picocontainer.ComponentAdapter;
019import org.picocontainer.ComponentMonitor;
020import org.picocontainer.PicoContainer;
021import 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")
035public 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}