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 *****************************************************************************/
010package org.picocontainer.gems.adapters;
011
012import com.thoughtworks.proxy.Invoker;
013import com.thoughtworks.proxy.ProxyFactory;
014import com.thoughtworks.proxy.factory.StandardProxyFactory;
015import com.thoughtworks.proxy.kit.ReflectionUtils;
016
017import org.picocontainer.ComponentAdapter;
018import org.picocontainer.PicoContainer;
019import org.picocontainer.PicoCompositionException;
020import org.picocontainer.references.ThreadLocalReference;
021import org.picocontainer.behaviors.Cached;
022import org.picocontainer.behaviors.AbstractBehavior;
023import org.picocontainer.behaviors.Stored;
024
025import java.lang.reflect.InvocationTargetException;
026import java.lang.reflect.Method;
027import java.lang.reflect.Proxy;
028import java.lang.reflect.Type;
029import 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")
047public 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}