Bug 1637: Change Rpc actor calls to async
[controller.git] / opendaylight / md-sal / sal-remoterpc-connector / src / main / java / org / opendaylight / controller / remote / rpc / RpcBroker.java
index 4ec96c29cd2510175defa6327a235ffecb519b28..2aca655d2628eb9d89295d09419d0cd44f7491d7 100644 (file)
@@ -8,11 +8,14 @@
 
 package org.opendaylight.controller.remote.rpc;
 
+import static akka.pattern.Patterns.ask;
 import akka.actor.ActorRef;
 import akka.actor.Props;
+import akka.dispatch.OnComplete;
 import akka.japi.Creator;
 import akka.japi.Pair;
-import org.opendaylight.controller.remote.rpc.messages.ErrorResponse;
+import akka.util.Timeout;
+
 import org.opendaylight.controller.remote.rpc.messages.ExecuteRpc;
 import org.opendaylight.controller.remote.rpc.messages.InvokeRpc;
 import org.opendaylight.controller.remote.rpc.messages.RpcResponse;
@@ -23,12 +26,23 @@ import org.opendaylight.controller.remote.rpc.utils.RoutingLogic;
 import org.opendaylight.controller.xml.codec.XmlUtils;
 import org.opendaylight.controller.sal.connector.api.RpcRouter;
 import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
 import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Future;
 
@@ -38,81 +52,159 @@ import java.util.concurrent.Future;
 
 public class RpcBroker extends AbstractUntypedActor {
 
-  private static final Logger LOG = LoggerFactory.getLogger(RpcBroker.class);
-  private final Broker.ProviderSession brokerSession;
-  private final ActorRef rpcRegistry;
-  private SchemaContext schemaContext;
-
-  private RpcBroker(Broker.ProviderSession brokerSession, ActorRef rpcRegistry, SchemaContext schemaContext){
-    this.brokerSession = brokerSession;
-    this.rpcRegistry = rpcRegistry;
-    this.schemaContext = schemaContext;
-  }
-
-  public static Props props(final Broker.ProviderSession brokerSession, final ActorRef rpcRegistry, final SchemaContext schemaContext){
-    return Props.create(new Creator<RpcBroker>(){
-
-      @Override
-      public RpcBroker create() throws Exception {
-        return new RpcBroker(brokerSession, rpcRegistry, schemaContext);
-      }
-    });
-  }
-  @Override
-  protected void handleReceive(Object message) throws Exception {
-   if(message instanceof InvokeRpc) {
-      invokeRemoteRpc((InvokeRpc) message);
-    } else if(message instanceof ExecuteRpc) {
-      executeRpc((ExecuteRpc) message);
+    private static final Logger LOG = LoggerFactory.getLogger(RpcBroker.class);
+    private final Broker.ProviderSession brokerSession;
+    private final ActorRef rpcRegistry;
+    private final SchemaContext schemaContext;
+
+    private RpcBroker(Broker.ProviderSession brokerSession, ActorRef rpcRegistry,
+            SchemaContext schemaContext) {
+        this.brokerSession = brokerSession;
+        this.rpcRegistry = rpcRegistry;
+        this.schemaContext = schemaContext;
     }
-  }
-
-  private void invokeRemoteRpc(InvokeRpc msg) {
-    // Look up the remote actor to execute rpc
-    LOG.debug("Looking up the remote actor for route {}", msg);
-    try {
-      // Find router
-      RpcRouter.RouteIdentifier<?,?,?> routeId = new RouteIdentifierImpl(null, msg.getRpc(), msg.getIdentifier());
-      RpcRegistry.Messages.FindRouters rpcMsg = new RpcRegistry.Messages.FindRouters(routeId);
-      RpcRegistry.Messages.FindRoutersReply rpcReply =
-          (RpcRegistry.Messages.FindRoutersReply) ActorUtil.executeOperation(rpcRegistry, rpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION);
-
-      List<Pair<ActorRef, Long>> actorRefList = rpcReply.getRouterWithUpdateTime();
-
-      if(actorRefList == null || actorRefList.isEmpty()) {
-        LOG.debug("No remote actor found for rpc {{}}.", msg.getRpc());
-
-        getSender().tell(new ErrorResponse(
-            new IllegalStateException("No remote actor found for rpc execution of : " + msg.getRpc())), self());
-      } else {
-        RoutingLogic logic = new LatestEntryRoutingLogic(actorRefList);
 
-        ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(), schemaContext), msg.getRpc());
-        Object operationRes = ActorUtil.executeOperation(logic.select(),
-            executeMsg, ActorUtil.REMOTE_ASK_DURATION, ActorUtil.REMOTE_AWAIT_DURATION);
+    public static Props props(Broker.ProviderSession brokerSession, ActorRef rpcRegistry,
+            SchemaContext schemaContext) {
+        return Props.create(new RpcBrokerCreator(brokerSession, rpcRegistry, schemaContext));
+    }
 
-        getSender().tell(operationRes, self());
-      }
-    } catch (Exception e) {
-        LOG.error("invokeRemoteRpc: {}", e);
-        getSender().tell(new ErrorResponse(e), self());
+    @Override
+    protected void handleReceive(Object message) throws Exception {
+        if(message instanceof InvokeRpc) {
+            invokeRemoteRpc((InvokeRpc) message);
+        } else if(message instanceof ExecuteRpc) {
+            executeRpc((ExecuteRpc) message);
+        }
     }
-  }
 
+    private void invokeRemoteRpc(final InvokeRpc msg) {
+        LOG.debug("Looking up the remote actor for rpc {}", msg.getRpc());
+
+        RpcRouter.RouteIdentifier<?,?,?> routeId = new RouteIdentifierImpl(
+                null, msg.getRpc(), msg.getIdentifier());
+        RpcRegistry.Messages.FindRouters findMsg = new RpcRegistry.Messages.FindRouters(routeId);
+
+        scala.concurrent.Future<Object> future = ask(rpcRegistry, findMsg,
+                new Timeout(ActorUtil.LOCAL_ASK_DURATION));
 
+        final ActorRef sender = getSender();
+        final ActorRef self = self();
 
-  private void executeRpc(ExecuteRpc msg) {
-    LOG.debug("Executing rpc for rpc {}", msg.getRpc());
-    try {
-      Future<RpcResult<CompositeNode>> rpc = brokerSession.rpc(msg.getRpc(),
-          XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(), schemaContext));
-      RpcResult<CompositeNode> rpcResult = rpc != null ? rpc.get():null;
-      CompositeNode result = rpcResult != null ? rpcResult.getResult() : null;
-      getSender().tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result, schemaContext)), self());
-    } catch (Exception e) {
-      LOG.error("executeRpc: {}", e);
-      getSender().tell(new ErrorResponse(e), self());
+        OnComplete<Object> onComplete = new OnComplete<Object>() {
+            @Override
+            public void onComplete(Throwable failure, Object reply) throws Throwable {
+                if(failure != null) {
+                    LOG.error("FindRouters failed", failure);
+                    sender.tell(new akka.actor.Status.Failure(failure), self);
+                    return;
+                }
+
+                RpcRegistry.Messages.FindRoutersReply findReply =
+                                                (RpcRegistry.Messages.FindRoutersReply)reply;
+
+                List<Pair<ActorRef, Long>> actorRefList = findReply.getRouterWithUpdateTime();
+
+                if(actorRefList == null || actorRefList.isEmpty()) {
+                    String message = String.format(
+                            "No remote implementation found for rpc %s",  msg.getRpc());
+                    sender.tell(new akka.actor.Status.Failure(new RpcErrorsException(
+                            message, Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC,
+                                    "operation-not-supported", message)))), self);
+                    return;
+                }
+
+                finishInvokeRpc(actorRefList, msg, sender, self);
+            }
+        };
+
+        future.onComplete(onComplete, getContext().dispatcher());
     }
-  }
 
+    protected void finishInvokeRpc(final List<Pair<ActorRef, Long>> actorRefList,
+            final InvokeRpc msg, final ActorRef sender, final ActorRef self) {
+
+        RoutingLogic logic = new LatestEntryRoutingLogic(actorRefList);
+
+        ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(),
+                schemaContext), msg.getRpc());
+
+        scala.concurrent.Future<Object> future = ask(logic.select(), executeMsg,
+                new Timeout(ActorUtil.REMOTE_ASK_DURATION));
+
+        OnComplete<Object> onComplete = new OnComplete<Object>() {
+            @Override
+            public void onComplete(Throwable failure, Object reply) throws Throwable {
+                if(failure != null) {
+                    LOG.error("ExecuteRpc failed", failure);
+                    sender.tell(new akka.actor.Status.Failure(failure), self);
+                    return;
+                }
+
+                sender.tell(reply, self);
+            }
+        };
+
+        future.onComplete(onComplete, getContext().dispatcher());
+    }
+
+    private void executeRpc(final ExecuteRpc msg) {
+        LOG.debug("Executing rpc {}", msg.getRpc());
+
+        Future<RpcResult<CompositeNode>> future = brokerSession.rpc(msg.getRpc(),
+                XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(),
+                        schemaContext));
+
+        ListenableFuture<RpcResult<CompositeNode>> listenableFuture =
+                JdkFutureAdapters.listenInPoolThread(future);
+
+        final ActorRef sender = getSender();
+        final ActorRef self = self();
+
+        Futures.addCallback(listenableFuture, new FutureCallback<RpcResult<CompositeNode>>() {
+            @Override
+            public void onSuccess(RpcResult<CompositeNode> result) {
+                if(result.isSuccessful()) {
+                    sender.tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result.getResult(),
+                            schemaContext)), self);
+                } else {
+                    String message = String.format("Execution of RPC %s failed",  msg.getRpc());
+                    Collection<RpcError> errors = result.getErrors();
+                    if(errors == null || errors.size() == 0) {
+                        errors = Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC,
+                                null, message));
+                    }
+
+                    sender.tell(new akka.actor.Status.Failure(new RpcErrorsException(
+                            message, errors)), self);
+                }
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                LOG.error("executeRpc for {} failed: {}", msg.getRpc(), t);
+                sender.tell(new akka.actor.Status.Failure(t), self);
+            }
+        });
+    }
+
+    private static class RpcBrokerCreator implements Creator<RpcBroker> {
+        private static final long serialVersionUID = 1L;
+
+        final Broker.ProviderSession brokerSession;
+        final ActorRef rpcRegistry;
+        final SchemaContext schemaContext;
+
+        RpcBrokerCreator(ProviderSession brokerSession, ActorRef rpcRegistry,
+                SchemaContext schemaContext) {
+            this.brokerSession = brokerSession;
+            this.rpcRegistry = rpcRegistry;
+            this.schemaContext = schemaContext;
+        }
+
+        @Override
+        public RpcBroker create() throws Exception {
+            return new RpcBroker(brokerSession, rpcRegistry, schemaContext);
+        }
+    }
 }