Merge "Fix Akka exception: cannot use non-static local Creator to create actors"
[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
17 import com.google.common.util.concurrent.FutureCallback;
18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.JdkFutureAdapters;
20 import com.google.common.util.concurrent.ListenableFuture;
21
22 import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActor;
23 import org.opendaylight.controller.remote.rpc.messages.ExecuteRpc;
24 import org.opendaylight.controller.remote.rpc.messages.InvokeRpc;
25 import org.opendaylight.controller.remote.rpc.messages.RpcResponse;
26 import org.opendaylight.controller.remote.rpc.messages.UpdateSchemaContext;
27 import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
28 import org.opendaylight.controller.remote.rpc.utils.LatestEntryRoutingLogic;
29 import org.opendaylight.controller.remote.rpc.utils.RoutingLogic;
30 import org.opendaylight.controller.sal.connector.api.RpcRouter;
31 import org.opendaylight.controller.sal.core.api.Broker;
32 import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
33 import org.opendaylight.controller.xml.codec.XmlUtils;
34 import org.opendaylight.yangtools.yang.common.RpcError;
35 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
36 import org.opendaylight.yangtools.yang.common.RpcResult;
37 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
38 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
39 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import java.util.Arrays;
44 import java.util.Collection;
45 import java.util.List;
46 import java.util.concurrent.Future;
47
48 import static akka.pattern.Patterns.ask;
49
50 /**
51  * Actor to initiate execution of remote RPC on other nodes of the cluster.
52  */
53
54 public class RpcBroker extends AbstractUntypedActor {
55
56     private static final Logger LOG = LoggerFactory.getLogger(RpcBroker.class);
57     private final Broker.ProviderSession brokerSession;
58     private final ActorRef rpcRegistry;
59     private SchemaContext schemaContext;
60     private final RemoteRpcProviderConfig config;
61
62     private RpcBroker(Broker.ProviderSession brokerSession, ActorRef rpcRegistry,
63             SchemaContext schemaContext) {
64         this.brokerSession = brokerSession;
65         this.rpcRegistry = rpcRegistry;
66         this.schemaContext = schemaContext;
67         config = new RemoteRpcProviderConfig(getContext().system().settings().config());
68     }
69
70     public static Props props(Broker.ProviderSession brokerSession, ActorRef rpcRegistry,
71             SchemaContext schemaContext) {
72         return Props.create(new RpcBrokerCreator(brokerSession, rpcRegistry, schemaContext));
73     }
74
75     @Override
76     protected void handleReceive(Object message) throws Exception {
77         if(message instanceof InvokeRpc) {
78             invokeRemoteRpc((InvokeRpc) message);
79         } else if(message instanceof ExecuteRpc) {
80             executeRpc((ExecuteRpc) message);
81         } else if(message instanceof UpdateSchemaContext) {
82             updateSchemaContext((UpdateSchemaContext) message);
83         }
84     }
85
86     private void updateSchemaContext(UpdateSchemaContext message) {
87         this.schemaContext = message.getSchemaContext();
88     }
89
90     private void invokeRemoteRpc(final InvokeRpc msg) {
91         if(LOG.isDebugEnabled()) {
92             LOG.debug("Looking up the remote actor for rpc {}", msg.getRpc());
93         }
94         RpcRouter.RouteIdentifier<?,?,?> routeId = new RouteIdentifierImpl(
95                 null, msg.getRpc(), msg.getIdentifier());
96         RpcRegistry.Messages.FindRouters findMsg = new RpcRegistry.Messages.FindRouters(routeId);
97
98         scala.concurrent.Future<Object> future = ask(rpcRegistry, findMsg, config.getAskDuration());
99
100         final ActorRef sender = getSender();
101         final ActorRef self = self();
102
103         OnComplete<Object> onComplete = new OnComplete<Object>() {
104             @Override
105             public void onComplete(Throwable failure, Object reply) throws Throwable {
106                 if(failure != null) {
107                     LOG.error("FindRouters failed", failure);
108                     sender.tell(new akka.actor.Status.Failure(failure), self);
109                     return;
110                 }
111
112                 RpcRegistry.Messages.FindRoutersReply findReply =
113                                                 (RpcRegistry.Messages.FindRoutersReply)reply;
114
115                 List<Pair<ActorRef, Long>> actorRefList = findReply.getRouterWithUpdateTime();
116
117                 if(actorRefList == null || actorRefList.isEmpty()) {
118                     String message = String.format(
119                             "No remote implementation found for rpc %s",  msg.getRpc());
120                     sender.tell(new akka.actor.Status.Failure(new RpcErrorsException(
121                             message, Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC,
122                                     "operation-not-supported", message)))), self);
123                     return;
124                 }
125
126                 finishInvokeRpc(actorRefList, msg, sender, self);
127             }
128         };
129
130         future.onComplete(onComplete, getContext().dispatcher());
131     }
132
133     protected void finishInvokeRpc(final List<Pair<ActorRef, Long>> actorRefList,
134             final InvokeRpc msg, final ActorRef sender, final ActorRef self) {
135
136         RoutingLogic logic = new LatestEntryRoutingLogic(actorRefList);
137
138         ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(),
139                 schemaContext), msg.getRpc());
140
141         scala.concurrent.Future<Object> future = ask(logic.select(), executeMsg, config.getAskDuration());
142
143         OnComplete<Object> onComplete = new OnComplete<Object>() {
144             @Override
145             public void onComplete(Throwable failure, Object reply) throws Throwable {
146                 if(failure != null) {
147                     LOG.error("ExecuteRpc failed", failure);
148                     sender.tell(new akka.actor.Status.Failure(failure), self);
149                     return;
150                 }
151
152                 sender.tell(reply, self);
153             }
154         };
155
156         future.onComplete(onComplete, getContext().dispatcher());
157     }
158
159     private void executeRpc(final ExecuteRpc msg) {
160         if(LOG.isDebugEnabled()) {
161             LOG.debug("Executing rpc {}", msg.getRpc());
162         }
163         Future<RpcResult<CompositeNode>> future = brokerSession.rpc(msg.getRpc(),
164                 XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(),
165                         schemaContext));
166
167         ListenableFuture<RpcResult<CompositeNode>> listenableFuture =
168                 JdkFutureAdapters.listenInPoolThread(future);
169
170         final ActorRef sender = getSender();
171         final ActorRef self = self();
172
173         Futures.addCallback(listenableFuture, new FutureCallback<RpcResult<CompositeNode>>() {
174             @Override
175             public void onSuccess(RpcResult<CompositeNode> result) {
176                 if(result.isSuccessful()) {
177                     sender.tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result.getResult(),
178                             schemaContext)), self);
179                 } else {
180                     String message = String.format("Execution of RPC %s failed",  msg.getRpc());
181                     Collection<RpcError> errors = result.getErrors();
182                     if(errors == null || errors.size() == 0) {
183                         errors = Arrays.asList(RpcResultBuilder.newError(ErrorType.RPC,
184                                 null, message));
185                     }
186
187                     sender.tell(new akka.actor.Status.Failure(new RpcErrorsException(
188                             message, errors)), self);
189                 }
190             }
191
192             @Override
193             public void onFailure(Throwable t) {
194                 LOG.error("executeRpc for {} failed: {}", msg.getRpc(), t);
195                 sender.tell(new akka.actor.Status.Failure(t), self);
196             }
197         });
198     }
199
200     private static class RpcBrokerCreator implements Creator<RpcBroker> {
201         private static final long serialVersionUID = 1L;
202
203         final Broker.ProviderSession brokerSession;
204         final ActorRef rpcRegistry;
205         final SchemaContext schemaContext;
206
207         RpcBrokerCreator(ProviderSession brokerSession, ActorRef rpcRegistry,
208                 SchemaContext schemaContext) {
209             this.brokerSession = brokerSession;
210             this.rpcRegistry = rpcRegistry;
211             this.schemaContext = schemaContext;
212         }
213
214         @Override
215         public RpcBroker create() throws Exception {
216             return new RpcBroker(brokerSession, rpcRegistry, schemaContext);
217         }
218     }
219 }