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 }