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 }