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    import org.picocontainer.behaviors.Stored;
024    
025    import java.lang.reflect.InvocationTargetException;
026    import java.lang.reflect.Method;
027    import java.lang.reflect.Proxy;
028    import java.lang.reflect.Type;
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 ThreadLocalizing} 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    @SuppressWarnings("serial")
047    public final class ThreadLocalized<T> extends AbstractBehavior<T> {
048    
049    
050            private transient Class[] interfaces;
051        private final ProxyFactory proxyFactory;
052    
053        /**
054         * Construct a ThreadLocalized.
055         * 
056         * @param delegate The {@link ComponentAdapter} to delegate.
057         * @param proxyFactory The {@link ProxyFactory} to use.
058         * @throws PicoCompositionException Thrown if the component does not implement any interface.
059         */
060        public ThreadLocalized(final ComponentAdapter<T> delegate, final ProxyFactory proxyFactory)
061                throws PicoCompositionException {
062            super(new Cached<T>(delegate, new ThreadLocalReference<Stored.Instance<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<Stored.Instance<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    }