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 }