Improve segmented journal actor metrics
[controller.git] / opendaylight / md-sal / sal-remoterpc-connector / src / main / java / org / opendaylight / controller / remote / rpc / OpsRegistrar.java
1 /*
2  * Copyright (c) 2017 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 package org.opendaylight.controller.remote.rpc;
9
10 import static java.util.Objects.requireNonNull;
11
12 import akka.actor.Address;
13 import akka.actor.Props;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import java.util.Optional;
19 import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActor;
20 import org.opendaylight.controller.remote.rpc.registry.ActionRegistry.Messages.UpdateRemoteActionEndpoints;
21 import org.opendaylight.controller.remote.rpc.registry.ActionRegistry.RemoteActionEndpoint;
22 import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.UpdateRemoteEndpoints;
23 import org.opendaylight.controller.remote.rpc.registry.RpcRegistry.RemoteRpcEndpoint;
24 import org.opendaylight.mdsal.dom.api.DOMActionProviderService;
25 import org.opendaylight.mdsal.dom.api.DOMRpcProviderService;
26 import org.opendaylight.yangtools.concepts.Registration;
27
28 /**
29  * Actor handling registration of RPCs and Actions available on remote nodes with the local
30  * {@link DOMRpcProviderService} and {@link DOMActionProviderService}.
31  */
32 final class OpsRegistrar extends AbstractUntypedActor {
33     private final Map<Address, Registration> rpcRegs = new HashMap<>();
34     private final Map<Address, Registration> actionRegs = new HashMap<>();
35     private final DOMRpcProviderService rpcProviderService;
36     private final RemoteOpsProviderConfig config;
37     private final DOMActionProviderService actionProviderService;
38
39     OpsRegistrar(final RemoteOpsProviderConfig config, final DOMRpcProviderService rpcProviderService,
40                  final DOMActionProviderService actionProviderService) {
41         this.config = requireNonNull(config);
42         this.rpcProviderService = requireNonNull(rpcProviderService);
43         this.actionProviderService = requireNonNull(actionProviderService);
44     }
45
46     public static Props props(final RemoteOpsProviderConfig config, final DOMRpcProviderService rpcProviderService,
47                               final DOMActionProviderService actionProviderService) {
48         return Props.create(OpsRegistrar.class, requireNonNull(config),
49             requireNonNull(rpcProviderService, "DOMRpcProviderService cannot be null"),
50             requireNonNull(actionProviderService, "DOMActionProviderService cannot be null"));
51     }
52
53     @Override
54     public void postStop() throws Exception {
55         rpcRegs.values().forEach(Registration::close);
56         rpcRegs.clear();
57         actionRegs.values().forEach(Registration::close);
58         actionRegs.clear();
59
60         super.postStop();
61     }
62
63     @Override
64     protected void handleReceive(final Object message) {
65         if (message instanceof UpdateRemoteEndpoints updateEndpoints) {
66             LOG.debug("Handling updateRemoteEndpoints message");
67             updateRemoteRpcEndpoints(updateEndpoints.getRpcEndpoints());
68         } else if (message instanceof UpdateRemoteActionEndpoints updateEndpoints) {
69             LOG.debug("Handling updateRemoteActionEndpoints message");
70             updateRemoteActionEndpoints(updateEndpoints.getActionEndpoints());
71         } else {
72             unknownMessage(message);
73         }
74     }
75
76     private void updateRemoteRpcEndpoints(final Map<Address, Optional<RemoteRpcEndpoint>> rpcEndpoints) {
77         /*
78          * Updating RPC providers is a two-step process. We first add the newly-discovered RPCs and then close
79          * the old registration. This minimizes churn observed by listeners, as they will not observe RPC
80          * unavailability which would occur if we were to do it the other way around.
81          *
82          * Note that when an RPC moves from one remote node to another, we also do not want to expose the gap,
83          * hence we register all new implementations before closing all registrations.
84          */
85         final var prevRegs = new ArrayList<Registration>(rpcEndpoints.size());
86
87         for (Entry<Address, Optional<RemoteRpcEndpoint>> e : rpcEndpoints.entrySet()) {
88             LOG.debug("Updating RPC registrations for {}", e.getKey());
89
90             final Registration prevReg;
91             final Optional<RemoteRpcEndpoint> maybeEndpoint = e.getValue();
92             if (maybeEndpoint.isPresent()) {
93                 final RemoteRpcEndpoint endpoint = maybeEndpoint.orElseThrow();
94                 final RemoteRpcImplementation impl = new RemoteRpcImplementation(endpoint.getRouter(), config);
95                 prevReg = rpcRegs.put(e.getKey(), rpcProviderService.registerRpcImplementation(impl,
96                     endpoint.getRpcs()));
97             } else {
98                 prevReg = rpcRegs.remove(e.getKey());
99             }
100
101             if (prevReg != null) {
102                 prevRegs.add(prevReg);
103             }
104         }
105
106         prevRegs.forEach(Registration::close);
107     }
108
109     /**
110      * Updates the action endpoints, Adding new registrations first before removing previous registrations.
111      */
112     private void updateRemoteActionEndpoints(final Map<Address, Optional<RemoteActionEndpoint>> actionEndpoints) {
113         /*
114          * Updating Action providers is a two-step process. We first add the newly-discovered RPCs and then close
115          * the old registration. This minimizes churn observed by listeners, as they will not observe RPC
116          * unavailability which would occur if we were to do it the other way around.
117          *
118          * Note that when an Action moves from one remote node to another, we also do not want to expose the gap,
119          * hence we register all new implementations before closing all registrations.
120          */
121         final var prevRegs = new ArrayList<Registration>(actionEndpoints.size());
122
123         for (Entry<Address, Optional<RemoteActionEndpoint>> e : actionEndpoints.entrySet()) {
124             LOG.debug("Updating action registrations for {}", e.getKey());
125
126             final Registration prevReg;
127             final Optional<RemoteActionEndpoint> maybeEndpoint = e.getValue();
128             if (maybeEndpoint.isPresent()) {
129                 final RemoteActionEndpoint endpoint = maybeEndpoint.orElseThrow();
130                 final RemoteActionImplementation impl = new RemoteActionImplementation(endpoint.getRouter(), config);
131                 prevReg = actionRegs.put(e.getKey(), actionProviderService.registerActionImplementation(impl,
132                     endpoint.getActions()));
133             } else {
134                 prevReg = actionRegs.remove(e.getKey());
135             }
136
137             if (prevReg != null) {
138                 prevRegs.add(prevReg);
139             }
140         }
141
142         prevRegs.forEach(Registration::close);
143     }
144 }