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