2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.remote.rpc;
11 import akka.actor.ActorRef;
12 import akka.actor.Props;
13 import akka.dispatch.OnComplete;
14 import akka.japi.Creator;
15 import akka.japi.Pair;
16 import com.google.common.util.concurrent.FutureCallback;
17 import com.google.common.util.concurrent.Futures;
18 import com.google.common.util.concurrent.JdkFutureAdapters;
19 import com.google.common.util.concurrent.ListenableFuture;
20 import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActor;
21 import org.opendaylight.controller.remote.rpc.messages.ExecuteRpc;
22 import org.opendaylight.controller.remote.rpc.messages.InvokeRpc;
23 import org.opendaylight.controller.remote.rpc.messages.RpcResponse;
24 import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
25 import org.opendaylight.controller.remote.rpc.utils.LatestEntryRoutingLogic;
26 import org.opendaylight.controller.remote.rpc.utils.RoutingLogic;
27 import org.opendaylight.controller.sal.connector.api.RpcRouter;
28 import org.opendaylight.controller.sal.core.api.Broker;
29 import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
30 import org.opendaylight.controller.xml.codec.XmlUtils;
31 import org.opendaylight.yangtools.yang.common.RpcError;
32 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
33 import org.opendaylight.yangtools.yang.common.RpcResult;
34 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
35 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.List;
43 import java.util.concurrent.Future;
45 import static akka.pattern.Patterns.ask;
48 * Actor to initiate execution of remote RPC on other nodes of the cluster.
51 public class RpcBroker extends AbstractUntypedActor {
53 private static final Logger LOG = LoggerFactory.getLogger(RpcBroker.class);
54 private final Broker.ProviderSession brokerSession;
55 private final ActorRef rpcRegistry;
56 private final SchemaContext schemaContext;
57 private final RemoteRpcProviderConfig config;
59 private RpcBroker(Broker.ProviderSession brokerSession, ActorRef rpcRegistry,
60 SchemaContext schemaContext) {
61 this.brokerSession = brokerSession;
62 this.rpcRegistry = rpcRegistry;
63 this.schemaContext = schemaContext;
64 config = new RemoteRpcProviderConfig(getContext().system().settings().config());
67 public static Props props(Broker.ProviderSession brokerSession, ActorRef rpcRegistry,
68 SchemaContext schemaContext) {
69 return Props.create(new RpcBrokerCreator(brokerSession, rpcRegistry, schemaContext));
73 protected void handleReceive(Object message) throws Exception {
74 if(message instanceof InvokeRpc) {
75 invokeRemoteRpc((InvokeRpc) message);
76 } else if(message instanceof ExecuteRpc) {
77 executeRpc((ExecuteRpc) message);
81 private void invokeRemoteRpc(final InvokeRpc msg) {
82 if(LOG.isDebugEnabled()) {
83 LOG.debug("Looking up the remote actor for rpc {}", msg.getRpc());
85 RpcRouter.RouteIdentifier<?,?,?> routeId = new RouteIdentifierImpl(
86 null, msg.getRpc(), msg.getIdentifier());
87 RpcRegistry.Messages.FindRouters findMsg = new RpcRegistry.Messages.FindRouters(routeId);
89 scala.concurrent.Future<Object> future = ask(rpcRegistry, findMsg, config.getAskDuration());
91 final ActorRef sender = getSender();
92 final ActorRef self = self();
94 OnComplete<Object> onComplete = new OnComplete<Object>() {
96 public void onComplete(Throwable failure, Object reply) throws Throwable {
98 LOG.error("FindRouters failed", failure);
99 sender.tell(new akka.actor.Status.Failure(failure), self);
103 RpcRegistry.Messages.FindRoutersReply findReply =
104 (RpcRegistry.Messages.FindRoutersReply)reply;
106 List<Pair<ActorRef, Long>> actorRefList = findReply.getRouterWithUpdateTime();
108 if(actorRefList == null || actorRefList.isEmpty()) {
109 String message = String.format(
110 "No remote implementation found for rpc %s", msg.getRpc());
111 sender.tell(new akka.actor.Status.Failure(new RpcErrorsException(
112 message, Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC,
113 "operation-not-supported", message)))), self);
117 finishInvokeRpc(actorRefList, msg, sender, self);
121 future.onComplete(onComplete, getContext().dispatcher());
124 protected void finishInvokeRpc(final List<Pair<ActorRef, Long>> actorRefList,
125 final InvokeRpc msg, final ActorRef sender, final ActorRef self) {
127 RoutingLogic logic = new LatestEntryRoutingLogic(actorRefList);
129 ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(),
130 schemaContext), msg.getRpc());
132 scala.concurrent.Future<Object> future = ask(logic.select(), executeMsg, config.getAskDuration());
134 OnComplete<Object> onComplete = new OnComplete<Object>() {
136 public void onComplete(Throwable failure, Object reply) throws Throwable {
137 if(failure != null) {
138 LOG.error("ExecuteRpc failed", failure);
139 sender.tell(new akka.actor.Status.Failure(failure), self);
143 sender.tell(reply, self);
147 future.onComplete(onComplete, getContext().dispatcher());
150 private void executeRpc(final ExecuteRpc msg) {
151 if(LOG.isDebugEnabled()) {
152 LOG.debug("Executing rpc {}", msg.getRpc());
154 Future<RpcResult<CompositeNode>> future = brokerSession.rpc(msg.getRpc(),
155 XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(),
158 ListenableFuture<RpcResult<CompositeNode>> listenableFuture =
159 JdkFutureAdapters.listenInPoolThread(future);
161 final ActorRef sender = getSender();
162 final ActorRef self = self();
164 Futures.addCallback(listenableFuture, new FutureCallback<RpcResult<CompositeNode>>() {
166 public void onSuccess(RpcResult<CompositeNode> result) {
167 if(result.isSuccessful()) {
168 sender.tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result.getResult(),
169 schemaContext)), self);
171 String message = String.format("Execution of RPC %s failed", msg.getRpc());
172 Collection<RpcError> errors = result.getErrors();
173 if(errors == null || errors.size() == 0) {
174 errors = Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC,
178 sender.tell(new akka.actor.Status.Failure(new RpcErrorsException(
179 message, errors)), self);
184 public void onFailure(Throwable t) {
185 LOG.error("executeRpc for {} failed: {}", msg.getRpc(), t);
186 sender.tell(new akka.actor.Status.Failure(t), self);
191 private static class RpcBrokerCreator implements Creator<RpcBroker> {
192 private static final long serialVersionUID = 1L;
194 final Broker.ProviderSession brokerSession;
195 final ActorRef rpcRegistry;
196 final SchemaContext schemaContext;
198 RpcBrokerCreator(ProviderSession brokerSession, ActorRef rpcRegistry,
199 SchemaContext schemaContext) {
200 this.brokerSession = brokerSession;
201 this.rpcRegistry = rpcRegistry;
202 this.schemaContext = schemaContext;
206 public RpcBroker create() throws Exception {
207 return new RpcBroker(brokerSession, rpcRegistry, schemaContext);