001 /*
002 * (c) 2003-2005, 2009, 2010 ThoughtWorks Ltd
003 * All rights reserved.
004 *
005 * The software in this package is published under the terms of the BSD
006 * style license a copy of which has been included with this distribution in
007 * the LICENSE.txt file.
008 *
009 * Created on 11-May-2004
010 */
011 package com.thoughtworks.proxy.toys.hotswap;
012
013 import java.io.IOException;
014 import java.io.ObjectInputStream;
015 import java.io.ObjectOutputStream;
016 import java.lang.reflect.Method;
017
018 import com.thoughtworks.proxy.ProxyFactory;
019 import com.thoughtworks.proxy.kit.ObjectReference;
020 import com.thoughtworks.proxy.toys.delegate.DelegatingInvoker;
021 import com.thoughtworks.proxy.toys.delegate.DelegationMode;
022
023
024 /**
025 * A {@link DelegatingInvoker} implementation that allows the exchange of the delegate.
026 *
027 * @author Aslak Hellesøy
028 * @author Dan North
029 * @author Paul Hammant
030 * @author Jörg Schaible
031 * @since 0.1
032 */
033 public class HotSwappingInvoker<T> extends DelegatingInvoker<Object> {
034 private static final long serialVersionUID = 1L;
035 private static final Method hotswap;
036 private static final Method checkForCycle;
037
038 static {
039 try {
040 hotswap = Swappable.class.getMethod("hotswap", new Class[]{Object.class});
041 checkForCycle = CycleCheck.class.getMethod("checkForCycle");
042 } catch (NoSuchMethodException e) {
043 throw new ExceptionInInitializerError(e.toString());
044 }
045 }
046
047 /**
048 * Internal interface used to detect cyclic swapping activity.
049 *
050 * @since 0.2
051 */
052 protected static interface CycleCheck {
053 /**
054 * Checks for a cyclic swap action.
055 *
056 * @throws IllegalStateException if cycle detected
057 * @since 0.2
058 */
059 void checkForCycle();
060 }
061
062 private Class<?>[] types;
063 private transient boolean executed = false;
064 private transient ThreadLocal<Object> delegate;
065
066 /**
067 * Construct a HotSwappingInvoker.
068 *
069 * @param types the types of the proxy
070 * @param proxyFactory the {@link ProxyFactory} to use
071 * @param delegateReference the {@link ObjectReference} with the delegate
072 * @param delegationMode {@link DelegationMode#DIRECT} or {@link DelegationMode#SIGNATURE}
073 * @since 1.0
074 */
075 public HotSwappingInvoker(
076 final Class<?>[] types, final ProxyFactory proxyFactory, final ObjectReference<Object> delegateReference,
077 final DelegationMode delegationMode) {
078 super(proxyFactory, delegateReference, delegationMode);
079 this.types = types;
080 this.delegate = new ThreadLocal<Object>();
081 }
082
083 @Override
084 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
085 Object result;
086 try {
087 delegate.set(delegate()); // ensure delegate will not change during invocation
088 if (method.equals(hotswap)) {
089 result = hotswap(args[0]);
090 } else if (method.equals(checkForCycle)) {
091 if (executed) {
092 throw new IllegalStateException("Cyclic dependency");
093 } else {
094 if (delegate() instanceof CycleCheck) {
095 executed = true;
096 CycleCheck.class.cast(delegate()).checkForCycle();
097 executed = false;
098 }
099 }
100 return Void.TYPE;
101 } else {
102 result = super.invoke(proxy, method, args);
103 }
104 } finally {
105 delegate.set(null);
106 }
107 return result;
108 }
109
110 @Override
111 protected Object delegate() {
112 final Object currentDelegate;
113 currentDelegate = delegate.get();
114 if (currentDelegate == null) {
115 return super.delegate();
116 } else {
117 return currentDelegate;
118 }
119 }
120
121 /**
122 * Exchange the current delegate.
123 *
124 * @param newDelegate the new delegate
125 * @return the old delegate
126 * @throws IllegalStateException if cyclic swapping action is detected
127 * @since 0.1
128 */
129 protected Object hotswap(final Object newDelegate) {
130 ObjectReference<Object> ref = getDelegateReference();
131 Object result = ref.get();
132 // Note, for the cycle detection the delegate has to be set first
133 delegate.set(newDelegate);
134 ref.set(newDelegate);
135 if (newDelegate instanceof CycleCheck) {
136 CycleCheck.class.cast(newDelegate).checkForCycle();
137 }
138 return result;
139 }
140
141 /**
142 * Create a proxy for this Invoker. The proxy implements all the types given as parameter to the constructor and
143 * implements additionally the {@link Swappable} interface.
144 *
145 * @return the new proxy
146 * @since 0.1
147 */
148 public T proxy() {
149 Class<?>[] typesWithSwappable = new Class[types.length + 2];
150 System.arraycopy(types, 0, typesWithSwappable, 0, types.length);
151 typesWithSwappable[types.length] = Swappable.class;
152 typesWithSwappable[types.length + 1] = CycleCheck.class;
153 return getProxyFactory().<T>createProxy(this, typesWithSwappable);
154 }
155
156 private void writeObject(final ObjectOutputStream out) throws IOException {
157 out.defaultWriteObject();
158 }
159
160 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
161 in.defaultReadObject();
162 this.delegate = new ThreadLocal<Object>();
163 }
164 }