Package co.paralleluniverse.actors.behaviors

Source Code of co.paralleluniverse.actors.behaviors.ProxyServerActor$Invocation

/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (c) 2013-2014, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*   or (per the licensee's choosing)
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.actors.behaviors;

import co.paralleluniverse.actors.ActorLoader;
import co.paralleluniverse.actors.ActorRef;
import co.paralleluniverse.actors.ActorRefDelegate;
import co.paralleluniverse.actors.LocalActor;
import co.paralleluniverse.actors.MailboxConfig;
import co.paralleluniverse.fibers.RuntimeSuspendExecution;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.strands.Strand;
import co.paralleluniverse.strands.channels.SendPort;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
//import java.lang.reflect.Proxy;
//import java.lang.reflect.InvocationHandler;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.NoOp;

/**
* Wraps a Java object in a {@link ServerActor} that exposes the object's methods as an interface and processes them in an actor
* (on a dedicated strand).
* <p/>
* You can either supply a target object to any of the public constructors, or extend this class and use the subclass itself as the target,
* in which case use the protected constructors that don't take a {@code target} argument.
* <p/>
* The interface(s) exposed must
*
* @author pron
*/
public class ProxyServerActor extends ServerActor<ProxyServerActor.Invocation, Object, ProxyServerActor.Invocation> {
    private final Class<?>[] interfaces;
    private Object target;
    private final boolean callOnVoidMethods;

    /**
     * Creates a new {@code ProxyServerActor}
     *
     * @param name              the actor's name (may be null)
     * @param strand            the actor's strand (may be null)
     * @param mailboxConfig     this actor's mailbox settings.
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param target            the object implementing the actor's behaviors, on which the exposed interface methods will be called.
     * @param interfaces        the interfaces this actor's {@link ActorRef} will implement; {@code target} must implement all these interfaces.
     */
    public ProxyServerActor(String name, Strand strand, MailboxConfig mailboxConfig, boolean callOnVoidMethods, Object target, Class<?>[] interfaces) {
        super(name, null, 0L, null, strand, mailboxConfig);
        this.callOnVoidMethods = callOnVoidMethods;
        this.target = ActorLoader.getReplacementFor(target != null ? target : this);
        this.interfaces = interfaces != null ? Arrays.copyOf(interfaces, interfaces.length) : this.target.getClass().getInterfaces();
        if (this.interfaces == null)
            throw new IllegalArgumentException("No interfaces provided, and target of class " + this.target.getClass().getName() + " implements no interfaces");
    }

    //<editor-fold defaultstate="collapsed" desc="Constructors">
    /////////// Constructors ///////////////////////////////////
    /**
     * Creates a new {@code ProxyServerActor}
     *
     * @param name              the actor's name (may be null)
     * @param mailboxConfig     this actor's mailbox settings.
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param target            the object implementing the actor's behaviors, on which the exposed interface methods will be called.
     * @param interfaces        the interfaces this actor's {@link ActorRef} will implement; {@code target} must implement all these interfaces.
     */
    public ProxyServerActor(String name, MailboxConfig mailboxConfig, boolean callOnVoidMethods, Object target, Class<?>... interfaces) {
        this(name, null, mailboxConfig, callOnVoidMethods, target, interfaces);
    }

    /**
     * Creates a new {@code ProxyServerActor} with the default mailbox settings.
     *
     * @param name              the actor's name (may be null)
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param target            the object implementing the actor's behaviors, on which the exposed interface methods will be called.
     * @param interfaces        the interfaces this actor's {@link ActorRef} will implement; {@code target} must implement all these interfaces.
     */
    public ProxyServerActor(String name, boolean callOnVoidMethods, Object target, Class<?>... interfaces) {
        this(name, null, null, callOnVoidMethods, target, interfaces);
    }

    /**
     * Creates a new {@code ProxyServerActor}
     *
     * @param mailboxConfig     this actor's mailbox settings.
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param target            the object implementing the actor's behaviors, on which the exposed interface methods will be called.
     * @param interfaces        the interfaces this actor's {@link ActorRef} will implement; {@code target} must implement all these interfaces.
     */
    public ProxyServerActor(MailboxConfig mailboxConfig, boolean callOnVoidMethods, Object target, Class<?>... interfaces) {
        this(null, null, mailboxConfig, callOnVoidMethods, target, interfaces);
    }

    /**
     * Creates a new {@code ProxyServerActor} with the default mailbox settings.
     *
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param target            the object implementing the actor's behaviors, on which the exposed interface methods will be called.
     * @param interfaces        the interfaces this actor's {@link ActorRef} will implement; {@code target} must implement all these interfaces.
     */
    public ProxyServerActor(boolean callOnVoidMethods, Object target, Class<?>... interfaces) {
        this(null, null, null, callOnVoidMethods, target, interfaces);
    }

    /**
     * Creates a new {@code ProxyServerActor}, which exposes all interfaces implemented by the given {@code target}.
     *
     * @param name              the actor's name (may be null)
     * @param mailboxConfig     this actor's mailbox settings.
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param target            the object implementing the actor's behaviors, on which the exposed interface methods will be called.
     * @param interfaces        the interfaces this actor's {@link ActorRef} will implement; {@code target} must implement all these interfaces.
     */
    public ProxyServerActor(String name, MailboxConfig mailboxConfig, boolean callOnVoidMethods, Object target) {
        this(name, null, mailboxConfig, callOnVoidMethods, target, null);
    }

    /**
     * Creates a new {@code ProxyServerActor} with the default mailbox settings,
     * which exposes all interfaces implemented by the given {@code target}.
     *
     * @param name              the actor's name (may be null)
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param target            the object implementing the actor's behaviors, on which the exposed interface methods will be called.
     */
    public ProxyServerActor(String name, boolean callOnVoidMethods, Object target) {
        this(name, null, null, callOnVoidMethods, target, null);
    }

    /**
     * Creates a new {@code ProxyServerActor}, which exposes all interfaces implemented by the given {@code target}.
     *
     * @param mailboxConfig     this actor's mailbox settings.
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param target            the object implementing the actor's behaviors, on which the exposed interface methods will be called.
     */
    public ProxyServerActor(MailboxConfig mailboxConfig, boolean callOnVoidMethods, Object target) {
        this(null, null, mailboxConfig, callOnVoidMethods, target, null);
    }

    /**
     * Creates a new {@code ProxyServerActor} with the default mailbox settings,
     * which exposes all interfaces implemented by the given {@code target}.
     *
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param target            the object implementing the actor's behaviors, on which the exposed interface methods will be called.
     */
    public ProxyServerActor(boolean callOnVoidMethods, Object target) {
        this(null, null, null, callOnVoidMethods, target, null);
    }

    /**
     * This constructor is for use by subclasses that are intended to serve as the target. This object will serve as the target
     * for the method calls.
     *
     * @param name              the actor's name (may be null)
     * @param mailboxConfig     this actor's mailbox settings.
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param interfaces        the interfaces this actor's {@link ActorRef} will implement; this class must implement all these interfaces.
     */
    protected ProxyServerActor(String name, MailboxConfig mailboxConfig, boolean callOnVoidMethods, Class<?>... interfaces) {
        this(name, null, mailboxConfig, callOnVoidMethods, null, interfaces);
    }

    /**
     * This constructor is for use by subclasses that are intended to serve as the target. This object will serve as the target
     * for the method calls. The default mailbox settings will be used.
     *
     * @param name              the actor's name (may be null)
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param interfaces        the interfaces this actor's {@link ActorRef} will implement; this class must implement all these interfaces.
     */
    protected ProxyServerActor(String name, boolean callOnVoidMethods, Class<?>... interfaces) {
        this(name, null, null, callOnVoidMethods, null, interfaces);
    }

    /**
     * This constructor is for use by subclasses that are intended to serve as the target. This object will serve as the target
     * for the method calls. The default mailbox settings will be used.
     *
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param interfaces        the interfaces this actor's {@link ActorRef} will implement; this class must implement all these interfaces.
     */
    protected ProxyServerActor(MailboxConfig mailboxConfig, boolean callOnVoidMethods, Class<?>... interfaces) {
        this(null, null, mailboxConfig, callOnVoidMethods, null, interfaces);
    }

    /**
     * This constructor is for use by subclasses that are intended to serve as the target. This object will serve as the target
     * for the method calls. The default mailbox settings will be used.
     *
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     * @param interfaces        the interfaces this actor's {@link ActorRef} will implement; this class must implement all these interfaces.
     */
    protected ProxyServerActor(boolean callOnVoidMethods, Class<?>... interfaces) {
        this(null, null, null, callOnVoidMethods, null, interfaces);
    }

    /**
     * This constructor is for use by subclasses that are intended to serve as the target. This object will serve as the target
     * for the method calls, and all of the interfaces implemented by the subclass will be exposed by the {@link ActorRef}.
     *
     * @param name              the actor's name (may be null)
     * @param mailboxConfig     this actor's mailbox settings.
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     */
    protected ProxyServerActor(String name, MailboxConfig mailboxConfig, boolean callOnVoidMethods) {
        this(name, null, mailboxConfig, callOnVoidMethods, null, null);
    }

    /**
     * This constructor is for use by subclasses that are intended to serve as the target. This object will serve as the target
     * for the method calls, and all of the interfaces implemented by the subclass will be exposed by the {@link ActorRef}.
     * The default mailbox settings will be used.
     *
     * @param name              the actor's name (may be null)
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     */
    protected ProxyServerActor(String name, boolean callOnVoidMethods) {
        this(name, null, null, callOnVoidMethods, null, null);
    }

    /**
     * This constructor is for use by subclasses that are intended to serve as the target. This object will serve as the target
     * for the method calls, and all of the interfaces implemented by the subclass will be exposed by the {@link ActorRef}.
     *
     * @param mailboxConfig     this actor's mailbox settings.
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     */
    protected ProxyServerActor(MailboxConfig mailboxConfig, boolean callOnVoidMethods) {
        this(null, null, mailboxConfig, callOnVoidMethods, null, null);
    }

    /**
     * This constructor is for use by subclasses that are intended to serve as the target. This object will serve as the target
     * for the method calls, and all of the interfaces implemented by the subclass will be exposed by the {@link ActorRef}.
     * The default mailbox settings will be used.
     *
     * @param callOnVoidMethods whether calling void methods will block until they have completed execution
     */
    protected ProxyServerActor(boolean callOnVoidMethods) {
        this(null, null, null, callOnVoidMethods, null, null);
    }
    //</editor-fold>

    @Override
    protected final Server<Invocation, Object, Invocation> makeRef(ActorRef<Object> ref) {
        return (Server<Invocation, Object, Invocation>) makeProxyRef(super.makeRef(ref));
    }

    private Object makeProxyRef(Server<Invocation, Object, Invocation> ref) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Server.class);
        enhancer.setInterfaces(combine(interfaces, standardInterfaces));
        enhancer.setCallbacks(new Callback[]{new ObjectProxyServerImpl(ref, callOnVoidMethods), NoOp.INSTANCE});
        enhancer.setCallbackFilter(new CallbackFilter() {

            @Override
            public int accept(Method method) {
                final Class<?> cls = method.getDeclaringClass();
                if (cls.isAssignableFrom(ActorRefDelegate.class))
                    return 1; // call super
                return 0;
            }
        });
        return enhancer.create(new Class[]{ActorRef.class}, new Object[]{ref});
//        return Enhancer.create(Server.class,
//                combine(interfaces, standardInterfaces),
//                new ObjectProxyServerImpl(ref, callOnVoidMethods));

//        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
//                combine(interfaces, standardInterfaces),
//                new ObjectProxyServerImpl(ref, callOnVoidMethods));
    }

    private static Class<?>[] standardInterfaces = new Class[]{
        SendPort.class,};

    private static class ObjectProxyServerImpl implements InvocationHandler, java.io.Serializable {
        private final boolean callOnVoidMethods;
        private final Server<Invocation, Object, Invocation> ref;

        ObjectProxyServerImpl(Server<Invocation, Object, Invocation> ref, boolean callOnVoidMethods) {
            this.ref = ref;
            this.callOnVoidMethods = callOnVoidMethods;
        }

        boolean isInActor() {
            return Objects.equals(ref, LocalActor.self());
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            final Class<?> cls = method.getDeclaringClass();
            assert !cls.isAssignableFrom(ActorRefDelegate.class);
            if (cls.isAssignableFrom(Server.class) || Arrays.asList(standardInterfaces).contains(cls)) {
                try {
                    return method.invoke(ref, args);
                } catch (InvocationTargetException e) {
                    throw e.getCause();
                }
            }

            try {
                if (isInActor()) {
                    try {
                        return method.invoke(ServerActor.currentServerActor(), args);
                    } catch (InvocationTargetException e) {
                        throw e.getCause();
                    }
                } else {
                    final Invocation m = new Invocation(method, args, false);
                    if (callOnVoidMethods || (method.getReturnType() != void.class && method.getReturnType() != Void.class))
                        return ref.call(m);
                    else {
                        ref.cast(m);
                        return null;
                    }
                }
            } catch (SuspendExecution e) {
                throw RuntimeSuspendExecution.of(e);
            }
        }
    }

    @Override
    protected void checkCodeSwap() throws SuspendExecution {
        verifyInActor();
        Object _target = ActorLoader.getReplacementFor(target);
        if (_target != target)
            log().info("Upgraded ProxyServerActor implementation: {}", _target);
        this.target = _target;
        super.checkCodeSwap();
    }

    @Override
    protected Object handleCall(ActorRef<?> from, Object id, Invocation m) throws Exception, SuspendExecution {
        try {
            Object res = m.invoke(target);
            return res == null ? NULL_RETURN_VALUE : res;
        } catch (InvocationTargetException e) {
            assert !(e.getCause() instanceof SuspendExecution);
            log().error("handleCall: Invocation " + m + " has thrown an exception.", e.getCause());
            throw rethrow(e.getCause());
        }
    }

    @Override
    protected void handleCast(ActorRef<?> from, Object id, Invocation m) throws SuspendExecution {
        try {
            m.invoke(target);
        } catch (InvocationTargetException e) {
            assert !(e.getCause() instanceof SuspendExecution);
            log().error("handleCast: Invocation " + m + " has thrown an exception.", e.getCause());
        }
    }

    protected static class Invocation implements java.io.Serializable {
        private final Method method;
        private final Object[] params;

        public Invocation(Method method, List<Object> params) {
            this.method = method;
            this.params = params.toArray(new Object[params.size()]);
        }

        public Invocation(Method method, Object... params) {
            this(method, params, false);
        }

        Invocation(Method method, Object[] params, boolean copy) {
            this.method = method;
            this.params = copy ? Arrays.copyOf(params, params.length) : params;
        }

        public Method getMethod() {
            return method;
        }

        public List<Object> getParams() {
            return Collections.unmodifiableList(Arrays.asList(params));
        }

        Object invoke(Object target) throws SuspendExecution, InvocationTargetException {
            try {
                return method.invoke(target, params);
            } catch (IllegalAccessException e) {
                throw new AssertionError(e);
            }
        }

        @Override
        public String toString() {
            return method.toString() + Arrays.toString(params);
        }
    }

    private static Class[] combine(Class[] ifaces1, Class[] ifaces2) {
        final Class[] ifaces = new Class[ifaces1.length + ifaces2.length];
        System.arraycopy(ifaces1, 0, ifaces, 0, ifaces1.length);
        System.arraycopy(ifaces2, 0, ifaces, ifaces1.length, ifaces2.length);
        return ifaces;
    }

    private static RuntimeException rethrow(Throwable t) throws Exception {
        if (t instanceof Exception)
            throw (Exception) t;
        if (t instanceof Error)
            throw (Error) t;
        throw new RuntimeException(t);
    }
}
TOP

Related Classes of co.paralleluniverse.actors.behaviors.ProxyServerActor$Invocation

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.