    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    }