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 14-May-2004
010 */
011package com.thoughtworks.proxy.toys.hotswap;
012
013import static com.thoughtworks.proxy.toys.delegate.DelegationMode.DIRECT;
014import static com.thoughtworks.proxy.toys.delegate.DelegationMode.SIGNATURE;
015
016import com.thoughtworks.proxy.ProxyFactory;
017import com.thoughtworks.proxy.factory.StandardProxyFactory;
018import com.thoughtworks.proxy.kit.ObjectReference;
019import com.thoughtworks.proxy.kit.ReflectionUtils;
020import com.thoughtworks.proxy.kit.SimpleReference;
021import com.thoughtworks.proxy.toys.delegate.DelegationMode;
022
023
024/**
025 * Factory for proxy instances that allow to exchange the delegated instance. Every created proxy will implement
026 * {@link Swappable}, that is used for the hot swap operation.
027 *
028 * @author Dan North
029 * @author Aslak Hellesøy
030 * @author Jörg Schaible
031 * @author Conrad Benham
032 * @author Paul Hammant
033 * @see com.thoughtworks.proxy.toys.hotswap
034 * @since 0.1
035 */
036public class HotSwapping<T> {
037
038    private Object instance;
039    private Class<?>[] types;
040    private DelegationMode delegationMode;
041
042    private HotSwapping(final Class<T> primaryType, Class<?>... types) {
043        this.types = ReflectionUtils.makeTypesArray(primaryType, types);
044    }
045
046    /**
047     * Creates a factory for proxy instances that allow the exchange of delegated instances.
048     *
049     * @param type the type of the proxy when it is finally created.
050     * @return a factory that will proxy instances of the supplied type.
051     * @since 1.0
052     */
053    public static <T> HotSwappingWith<T> proxy(final Class<T> type) {
054        return new HotSwappingWith<T>(new HotSwapping<T>(type));
055    }
056    
057    /**
058     * Creates a factory for proxy instances that allow the exchange of delegated instances.
059     *
060     * @param primaryType the primary type implemented by the proxy
061     * @param types other types that are implemented by the proxy
062     * @return a factory that will proxy instances of the supplied type.
063     * @since 1.0
064     */
065    public static <T> HotSwappingWith<T> proxy(final Class<T> primaryType, final Class<?> ... types) {
066        return new HotSwappingWith<T>(new HotSwapping<T>(primaryType, types));
067    }
068
069    /**
070     * Create a proxy with hot swapping capabilities for specific types of the delegate given with an
071     * {@link ObjectReference}. The delegate must implement the given types, if the invoker is in static typing mode,
072     * otherwise it must only have signature compatible methods. Proxies created by this method will implement
073     * {@link Swappable}
074     *
075     * @param factory the {@link ProxyFactory} to use.
076     * @return the created proxy implementing the <tt>types</tt> and {@link Swappable}
077     * @since 1.0
078     */
079    private T build(final ProxyFactory factory) {
080        final ObjectReference<Object> delegateReference = new SimpleReference<Object>(instance);
081        return new HotSwappingInvoker<T>(types, factory, delegateReference, delegationMode).proxy();
082    }
083
084    public static class HotSwappingWith<T> {
085        private final HotSwapping<T> hotswapping;
086
087        public HotSwappingWith(HotSwapping<T> hotswapping) {
088            this.hotswapping = hotswapping;
089        }
090
091        /**
092         * Defines the object that shall be proxied. This delegate must implement the types used to create the hot swap or
093         * have signature compatible methods.
094         *
095         * @param instance the object that shall be proxied.
096         * @return the factory that will proxy instances of the supplied type.
097         * @since 1.0
098         */
099        public HotSwappingBuildOrMode<T> with(final Object instance) {
100            hotswapping.instance = instance;
101            hotswapping.delegationMode = DIRECT;
102            for (Class<?> type : hotswapping.types) {
103                if (!type.isInstance(instance)) {
104                    hotswapping.delegationMode = SIGNATURE;
105                    break;
106                }
107            }
108            return new HotSwappingBuildOrMode<T>(hotswapping);
109        }
110    }
111
112    public static class HotSwappingBuildOrMode<T> extends HotSwappingBuild<T>{
113        public HotSwappingBuildOrMode(HotSwapping<T> hotswapping) {
114            super(hotswapping);
115        }
116
117        /**
118         * Forces a particular delegation mode to be used.
119         *
120         * @param delegationMode refer to {@link DelegationMode#DIRECT} or
121         *                       {@link DelegationMode#SIGNATURE} for allowed
122         *                       values.
123         * @return the factory that will proxy instances of the supplied type.
124         * @since 1.0
125         */
126        public HotSwappingBuild<T> mode(DelegationMode delegationMode) {
127            hotswapping.delegationMode = delegationMode;
128            return new HotSwappingBuild<T>(hotswapping);
129        }
130    }
131
132    public static class HotSwappingBuild<T> {
133        protected final HotSwapping<T> hotswapping;
134
135        public HotSwappingBuild(HotSwapping<T> hotswapping) {
136            this.hotswapping = hotswapping;
137        }
138
139        /**
140         * Create a proxy with hot swapping capabilities for specific types of the delegate given with an
141         * {@link ObjectReference}. The delegate must implement the given types, if the invoker is in static typing mode,
142         * otherwise it must only have signature compatible methods. Proxies created by this method will implement
143         * {@link Swappable}
144         *
145         * @return the created proxy implementing the <tt>types</tt> and {@link Swappable}
146         * @see com.thoughtworks.proxy.toys.hotswap
147         * @since 1.0
148         */
149        public T build() {
150            return build(new StandardProxyFactory());
151        }
152        
153        /**
154         * Create a proxy with hot swapping capabilities for specific types of the delegate given with an
155         * {@link ObjectReference}. The delegate must implement the given types, if the invoker is in static typing mode,
156         * otherwise it must only have signature compatible methods. Proxies created by this method will implement
157         * {@link Swappable}
158         *
159         * @param factory the {@link ProxyFactory} to use.
160         * @return the created proxy implementing the <tt>types</tt> and {@link Swappable}
161         * @see com.thoughtworks.proxy.toys.hotswap
162         * @since 1.0
163         */
164        public T build(final ProxyFactory factory) {
165            return hotswapping.build(factory);
166        }
167    }
168}