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 Joerg Schaible                                           *
009     *****************************************************************************/
010    package org.picocontainer.gems.adapters;
011    
012    import com.thoughtworks.proxy.Invoker;
013    import com.thoughtworks.proxy.ProxyFactory;
014    import com.thoughtworks.proxy.factory.StandardProxyFactory;
015    import com.thoughtworks.proxy.kit.ReflectionUtils;
016    
017    import org.picocontainer.ComponentAdapter;
018    import org.picocontainer.PicoContainer;
019    import org.picocontainer.PicoInitializationException;
020    import org.picocontainer.PicoIntrospectionException;
021    import org.picocontainer.defaults.AssignabilityRegistrationException;
022    import org.picocontainer.defaults.CachingComponentAdapter;
023    import org.picocontainer.defaults.DecoratingComponentAdapter;
024    import org.picocontainer.defaults.NotConcreteRegistrationException;
025    
026    import java.lang.reflect.InvocationTargetException;
027    import java.lang.reflect.Method;
028    import java.lang.reflect.Proxy;
029    import java.util.Set;
030    
031    
032    /**
033     * A {@link ComponentAdapter} that realizes a {@link ThreadLocal} component instance.
034     * <p>
035     * The adapter creates proxy instances, that will create the necessary instances on-the-fly invoking the methods of the
036     * instance. Use this adapter, if you are instantiating your components in a single thread, but should be different when
037     * accessed from different threads. See {@link ThreadLocalComponentAdapterFactory} for details.
038     * </p>
039     * <p>
040     * Note: Because this implementation uses a {@link Proxy}, you can only access the methods exposed by the implemented
041     * interfaces of your component.
042     * </p>
043     * 
044     * @author J&ouml;rg Schaible
045     */
046    public class ThreadLocalComponentAdapter extends DecoratingComponentAdapter {
047    
048        private transient Class[] interfaces;
049        private ProxyFactory proxyFactory;
050    
051        /**
052         * Construct a ThreadLocalComponentAdapter.
053         * 
054         * @param delegate The {@link ComponentAdapter} to delegate.
055         * @param proxyFactory The {@link ProxyFactory} to use.
056         * @throws PicoIntrospectionException Thrown if the component does not implement any interface.
057         */
058        public ThreadLocalComponentAdapter(final ComponentAdapter delegate, final ProxyFactory proxyFactory)
059                throws PicoIntrospectionException {
060            super(new CachingComponentAdapter(delegate, new ThreadLocalReference()));
061            this.proxyFactory = proxyFactory;
062            interfaces = getInterfaces();
063        }
064    
065        /**
066         * Construct a ThreadLocalComponentAdapter using {@link Proxy} instances.
067         * 
068         * @param delegate The {@link ComponentAdapter} to delegate.
069         * @throws PicoIntrospectionException Thrown if the component does not implement any interface.
070         */
071        public ThreadLocalComponentAdapter(final ComponentAdapter delegate) throws PicoIntrospectionException {
072            this(new CachingComponentAdapter(delegate, new ThreadLocalReference()), new StandardProxyFactory());
073        }
074    
075        public Object getComponentInstance(final PicoContainer pico)
076                throws PicoInitializationException, PicoIntrospectionException, AssignabilityRegistrationException,
077                NotConcreteRegistrationException {
078    
079            if (interfaces == null) {
080                interfaces = getInterfaces();
081            }
082    
083            final ComponentAdapter delegate = getDelegate();
084            final Invoker invoker = new ThreadLocalInvoker(pico, delegate);
085            return proxyFactory.createProxy(interfaces, invoker);
086        }
087    
088        final private Class[] getInterfaces() {
089            final Object componentKey = getComponentKey();
090            final Class[] interfaces;
091            if (componentKey instanceof Class && ((Class)componentKey).isInterface()) {
092                interfaces = new Class[]{(Class)componentKey};
093            } else {
094                final Set allInterfaces = ReflectionUtils.getAllInterfaces(getComponentImplementation());
095                interfaces = (Class[])allInterfaces.toArray(new Class[allInterfaces.size()]);
096            }
097            if (interfaces.length == 0) {
098                throw new PicoIntrospectionException("Can't proxy implementation for "
099                        + getComponentImplementation().getName()
100                        + ". It does not implement any interfaces.");
101            }
102            return interfaces;
103        }
104    
105        final static private class ThreadLocalInvoker implements Invoker {
106    
107            private final PicoContainer pico;
108            private final ComponentAdapter delegate;
109    
110            private ThreadLocalInvoker(final PicoContainer pico, final ComponentAdapter delegate) {
111                this.pico = pico;
112                this.delegate = delegate;
113            }
114    
115            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
116                final Object delegatedInstance = delegate.getComponentInstance(pico);
117                if (method.equals(ReflectionUtils.equals)) { // necessary for JDK 1.3
118                    return new Boolean(args[0] != null && args[0].equals(delegatedInstance));
119                } else {
120                    try {
121                        return method.invoke(delegatedInstance, args);
122                    } catch (final InvocationTargetException e) {
123                        throw e.getTargetException();
124                    }
125                }
126            }
127        }
128    }