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ö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 }