001package org.javasimon.callback.async;
002
003import org.javasimon.SimonManager;
004import org.javasimon.callback.Callback;
005import org.javasimon.proxy.Delegating;
006import org.javasimon.proxy.DelegatingMethodInvocation;
007
008import java.lang.reflect.Method;
009
010/**
011 * Callback factory, produces a callback wrapper to make any callback asynchronous.
012 * <p/>
013 * Example: {@code Callback myAsyncCallback=new AsyncCallbackProxy(myCallback).newProxy(); }
014 * <p/>
015 * The purpose of this wrapping callback is to prevent the wrapped callback
016 * from being executed on the main thread. This can be useful when a concrete
017 * callback is time consuming, to reduce the impact on application performances.
018 * <p/>
019 * It can be used to disable/enable, at runtime, a callback without removing it from
020 * the {@link SimonManager}: {@code asyncCallbackProxy.setExecutor(Executors.disabled);}
021 *
022 * @author gerald
023 */
024public final class AsyncCallbackProxyFactory extends ExecutorProxyFactory<Callback> {
025
026        /** Interfaces implemented by callback proxy. */
027        private static final Class[] PROXY_INTERFACES = new Class[]{Callback.class, Delegating.class};
028        private final Method getDelegateMethod;
029
030        /**
031         * Constructor.
032         *
033         * @param delegate Wrapped object
034         */
035        public AsyncCallbackProxyFactory(Callback delegate) {
036                super(delegate);
037                getDelegateMethod = findGetDelegateMethod();
038        }
039
040        /**
041         * Constructor.
042         *
043         * @param delegate Wrapped object
044         * @param executor Executor used to run callback method, see {@link Executors}
045         */
046        public AsyncCallbackProxyFactory(Callback delegate, Executor executor) {
047                super(delegate, executor);
048                getDelegateMethod = findGetDelegateMethod();
049        }
050
051        /**
052         * Finds {@link Delegating#getDelegate()} method.
053         *
054         * @return delegate method
055         */
056        private Method findGetDelegateMethod() {
057                try {
058                        return Delegating.class.getDeclaredMethod("getDelegate", new Class[0]);
059                } catch (NoSuchMethodException | SecurityException noSuchMethodException) {
060                        throw new IllegalStateException("getDelegate method not found on Delegating interface", noSuchMethodException);
061                }
062        }
063
064        /**
065         * Creates a callback proxy.
066         *
067         * @param classLoader Class loader
068         * @return Callback proxy.
069         */
070        public Callback newProxy(ClassLoader classLoader) {
071                return (Callback) newProxy(classLoader, PROXY_INTERFACES);
072        }
073
074        /**
075         * Creates a callback proxy.
076         * The class loader for current thread is used as default class loader.
077         *
078         * @return Callback proxy.
079         */
080        public Callback newProxy() {
081                return (Callback) newProxy(PROXY_INTERFACES);
082        }
083
084        @Override
085        protected Object invoke(DelegatingMethodInvocation<Callback> delegatingMethodInvocation) throws Throwable {
086                Object result;
087                if (delegatingMethodInvocation.getMethod().equals(getDelegateMethod)) {
088                        result = getDelegate();
089                } else {
090                        result = super.invoke(delegatingMethodInvocation);
091                }
092                return result;
093        }
094}