Merge "BUG 1582 - PUT fails validate outer object name"
[controller.git] / opendaylight / md-sal / sal-remoterpc-connector / src / main / java / org / opendaylight / controller / remote / rpc / RpcBroker.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.controller.remote.rpc;
10
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;
39
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.List;
43 import java.util.concurrent.Future;
44
45 import static akka.pattern.Patterns.ask;
46
47 /**
48  * Actor to initiate execution of remote RPC on other nodes of the cluster.
49  */
50
51 public class RpcBroker extends AbstractUntypedActor {
52
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;
58
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());
65     }
66
67     public static Props props(Broker.ProviderSession brokerSession, ActorRef rpcRegistry,
68             SchemaContext schemaContext) {
69         return Props.create(new RpcBrokerCreator(brokerSession, rpcRegistry, schemaContext));
70     }
71
72     @Override
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);
78         }
79     }
80
81     private void invokeRemoteRpc(final InvokeRpc msg) {
82         LOG.debug("Looking up the remote actor for rpc {}", msg.getRpc());
83
84         RpcRouter.RouteIdentifier<?,?,?> routeId = new RouteIdentifierImpl(
85                 null, msg.getRpc(), msg.getIdentifier());
86         RpcRegistry.Messages.FindRouters findMsg = new RpcRegistry.Messages.FindRouters(routeId);
87
88         scala.concurrent.Future<Object> future = ask(rpcRegistry, findMsg, config.getAskDuration());
89
90         final ActorRef sender = getSender();
91         final ActorRef self = self();
92
93         OnComplete<Object> onComplete = new OnComplete<Object>() {
94             @Override
95             public void onComplete(Throwable failure, Object reply) throws Throwable {
96                 if(failure != null) {
97                     LOG.error("FindRouters failed", failure);
98                     sender.tell(new akka.actor.Status.Failure(failure), self);
99                     return;
100                 }
101
102                 RpcRegistry.Messages.FindRoutersReply findReply =
103                                                 (RpcRegistry.Messages.FindRoutersReply)reply;
104
105                 List<Pair<ActorRef, Long>> actorRefList = findReply.getRouterWithUpdateTime();
106
107                 if(actorRefList == null || actorRefList.isEmpty()) {
108                     String message = String.format(
109                             "No remote implementation found for rpc %s",  msg.getRpc());
110                     sender.tell(new akka.actor.Status.Failure(new RpcErrorsException(
111                             message, Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC,
112                                     "operation-not-supported", message)))), self);
113                     return;
114                 }
115
116                 finishInvokeRpc(actorRefList, msg, sender, self);
117             }
118         };
119
120         future.onComplete(onComplete, getContext().dispatcher());
121     }
122
123     protected void finishInvokeRpc(final List<Pair<ActorRef, Long>> actorRefList,
124             final InvokeRpc msg, final ActorRef sender, final ActorRef self) {
125
126         RoutingLogic logic = new LatestEntryRoutingLogic(actorRefList);
127
128         ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(),
129                 schemaContext), msg.getRpc());
130
131         scala.concurrent.Future<Object> future = ask(logic.select(), executeMsg, config.getAskDuration());
132
133         OnComplete<Object> onComplete = new OnComplete<Object>() {
134             @Override
135             public void onComplete(Throwable failure, Object reply) throws Throwable {
136                 if(failure != null) {
137                     LOG.error("ExecuteRpc failed", failure);
138                     sender.tell(new akka.actor.Status.Failure(failure), self);
139                     return;
140                 }
141
142                 sender.tell(reply, self);
143             }
144         };
145
146         future.onComplete(onComplete, getContext().dispatcher());
147     }
148
149     private void executeRpc(final ExecuteRpc msg) {
150         LOG.debug("Executing rpc {}", msg.getRpc());
151
152         Future<RpcResult<CompositeNode>> future = brokerSession.rpc(msg.getRpc(),
153                 XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(),
154                         schemaContext));
155
156         ListenableFuture<RpcResult<CompositeNode>> listenableFuture =
157                 JdkFutureAdapters.listenInPoolThread(future);
158
159         final ActorRef sender = getSender();
160         final ActorRef self = self();
161
162         Futures.addCallback(listenableFuture, new FutureCallback<RpcResult<CompositeNode>>() {
163             @Override
164             public void onSuccess(RpcResult<CompositeNode> result) {
165                 if(result.isSuccessful()) {
166                     sender.tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result.getResult(),
167                             schemaContext)), self);
168                 } else {
169                     String message = String.format("Execution of RPC %s failed",  msg.getRpc());
170                     Collection<RpcError> errors = result.getErrors();
171                     if(errors == null || errors.size() == 0) {
172                         errors = Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC,
173                                 null, message));
174                     }
175
176                     sender.tell(new akka.actor.Status.Failure(new RpcErrorsException(
177                             message, errors)), self);
178                 }
179             }
180
181             @Override
182             public void onFailure(Throwable t) {
183                 LOG.error("executeRpc for {} failed: {}", msg.getRpc(), t);
184                 sender.tell(new akka.actor.Status.Failure(t), self);
185             }
186         });
187     }
188
189     private static class RpcBrokerCreator implements Creator<RpcBroker> {
190         private static final long serialVersionUID = 1L;
191
192         final Broker.ProviderSession brokerSession;
193         final ActorRef rpcRegistry;
194         final SchemaContext schemaContext;
195
196         RpcBrokerCreator(ProviderSession brokerSession, ActorRef rpcRegistry,
197                 SchemaContext schemaContext) {
198             this.brokerSession = brokerSession;
199             this.rpcRegistry = rpcRegistry;
200             this.schemaContext = schemaContext;
201         }
202
203         @Override
204         public RpcBroker create() throws Exception {
205             return new RpcBroker(brokerSession, rpcRegistry, schemaContext);
206         }
207     }
208 }