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 static akka.pattern.Patterns.ask;
12 import akka.actor.ActorRef;
13 import akka.actor.Props;
14 import akka.dispatch.OnComplete;
15 import akka.japi.Creator;
16 import akka.japi.Pair;
17 import com.google.common.base.Preconditions;
18 import com.google.common.util.concurrent.CheckedFuture;
19 import com.google.common.util.concurrent.FutureCallback;
20 import com.google.common.util.concurrent.Futures;
21 import com.google.common.util.concurrent.JdkFutureAdapters;
22 import com.google.common.util.concurrent.ListenableFuture;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.List;
26 import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActor;
27 import org.opendaylight.controller.cluster.datastore.node.utils.serialization.NormalizedNodeSerializer;
28 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
29 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
30 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
31 import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node;
32 import org.opendaylight.controller.remote.rpc.messages.ExecuteRpc;
33 import org.opendaylight.controller.remote.rpc.messages.InvokeRpc;
34 import org.opendaylight.controller.remote.rpc.messages.RpcResponse;
35 import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
36 import org.opendaylight.controller.remote.rpc.utils.LatestEntryRoutingLogic;
37 import org.opendaylight.controller.remote.rpc.utils.RoutingLogic;
38 import org.opendaylight.controller.sal.connector.api.RpcRouter;
39 import org.opendaylight.yangtools.yang.common.RpcError;
40 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
41 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
42 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
43 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
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 ActorRef rpcRegistry;
55 private final RemoteRpcProviderConfig config;
56 private final DOMRpcService rpcService;
58 private RpcBroker(final DOMRpcService rpcService, final ActorRef rpcRegistry) {
59 this.rpcService = rpcService;
60 this.rpcRegistry = rpcRegistry;
61 config = new RemoteRpcProviderConfig(getContext().system().settings().config());
64 public static Props props(final DOMRpcService rpcService, final ActorRef rpcRegistry) {
65 Preconditions.checkNotNull(rpcRegistry, "ActorRef can not be null!");
66 Preconditions.checkNotNull(rpcService, "DOMRpcService can not be null");
67 return Props.create(new RpcBrokerCreator(rpcService, rpcRegistry));
71 protected void handleReceive(final Object message) throws Exception {
72 if(message instanceof InvokeRpc) {
73 invokeRemoteRpc((InvokeRpc) message);
74 } else if(message instanceof ExecuteRpc) {
75 executeRpc((ExecuteRpc) message);
79 private void invokeRemoteRpc(final InvokeRpc msg) {
80 if(LOG.isDebugEnabled()) {
81 LOG.debug("Looking up the remote actor for rpc {}", msg.getRpc());
83 final RpcRouter.RouteIdentifier<?,?,?> routeId = new RouteIdentifierImpl(
84 null, msg.getRpc(), msg.getIdentifier());
85 final RpcRegistry.Messages.FindRouters findMsg = new RpcRegistry.Messages.FindRouters(routeId);
87 final scala.concurrent.Future<Object> future = ask(rpcRegistry, findMsg, config.getAskDuration());
89 final ActorRef sender = getSender();
90 final ActorRef self = self();
92 final OnComplete<Object> onComplete = new OnComplete<Object>() {
94 public void onComplete(final Throwable failure, final Object reply) throws Throwable {
96 LOG.error("FindRouters failed", failure);
97 sender.tell(new akka.actor.Status.Failure(failure), self);
101 final RpcRegistry.Messages.FindRoutersReply findReply =
102 (RpcRegistry.Messages.FindRoutersReply)reply;
104 final List<Pair<ActorRef, Long>> actorRefList = findReply.getRouterWithUpdateTime();
106 if(actorRefList == null || actorRefList.isEmpty()) {
107 final String message = String.format(
108 "No remote implementation found for rpc %s", msg.getRpc());
109 sender.tell(new akka.actor.Status.Failure(new RpcErrorsException(
110 message, Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC,
111 "operation-not-supported", message)))), self);
115 finishInvokeRpc(actorRefList, msg, sender, self);
119 future.onComplete(onComplete, getContext().dispatcher());
122 protected void finishInvokeRpc(final List<Pair<ActorRef, Long>> actorRefList,
123 final InvokeRpc msg, final ActorRef sender, final ActorRef self) {
125 final RoutingLogic logic = new LatestEntryRoutingLogic(actorRefList);
127 final Node serializedNode = NormalizedNodeSerializer.serialize(msg.getInput());
128 final ExecuteRpc executeMsg = new ExecuteRpc(serializedNode, msg.getRpc());
130 final scala.concurrent.Future<Object> future = ask(logic.select(), executeMsg, config.getAskDuration());
132 final OnComplete<Object> onComplete = new OnComplete<Object>() {
134 public void onComplete(final Throwable failure, final Object reply) throws Throwable {
135 if(failure != null) {
136 LOG.error("ExecuteRpc failed", failure);
137 sender.tell(new akka.actor.Status.Failure(failure), self);
141 sender.tell(reply, self);
145 future.onComplete(onComplete, getContext().dispatcher());
148 private void executeRpc(final ExecuteRpc msg) {
149 if(LOG.isDebugEnabled()) {
150 LOG.debug("Executing rpc {}", msg.getRpc());
152 final NormalizedNode<?, ?> input = NormalizedNodeSerializer.deSerialize(msg.getInputNormalizedNode());
153 final SchemaPath schemaPath = SchemaPath.create(true, msg.getRpc());
155 final CheckedFuture<DOMRpcResult, DOMRpcException> future = rpcService.invokeRpc(schemaPath, input);
157 final ListenableFuture<DOMRpcResult> listenableFuture =
158 JdkFutureAdapters.listenInPoolThread(future);
160 final ActorRef sender = getSender();
161 final ActorRef self = self();
163 Futures.addCallback(listenableFuture, new FutureCallback<DOMRpcResult>() {
165 public void onSuccess(final DOMRpcResult result) {
166 if (result.getErrors() != null && ( ! result.getErrors().isEmpty())) {
167 final String message = String.format("Execution of RPC %s failed", msg.getRpc());
168 Collection<RpcError> errors = result.getErrors();
169 if(errors == null || errors.size() == 0) {
170 errors = Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC,
174 sender.tell(new akka.actor.Status.Failure(new RpcErrorsException(
175 message, errors)), self);
177 final Node serializedResultNode = NormalizedNodeSerializer.serialize(result.getResult());
178 sender.tell(new RpcResponse(serializedResultNode), self);
183 public void onFailure(final Throwable t) {
184 LOG.error("executeRpc for {} failed: {}", msg.getRpc(), t);
185 sender.tell(new akka.actor.Status.Failure(t), self);
190 private static class RpcBrokerCreator implements Creator<RpcBroker> {
191 private static final long serialVersionUID = 1L;
193 final DOMRpcService rpcService;
194 final ActorRef rpcRegistry;
196 RpcBrokerCreator(final DOMRpcService rpcService, final ActorRef rpcRegistry) {
197 this.rpcService = rpcService;
198 this.rpcRegistry = rpcRegistry;
202 public RpcBroker create() throws Exception {
203 return new RpcBroker(rpcService, rpcRegistry);