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