Fix race between BGP config topology DCN and topology-provider registration
[bgpcep.git] / bgp / topology-provider / src / main / java / org / opendaylight / bgpcep / bgp / topology / provider / config / BgpTopologyDeployerImpl.java
1 /*
2  * Copyright (c) 2016 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.bgpcep.bgp.topology.provider.config;
10
11 import static java.util.Objects.requireNonNull;
12
13 import java.util.Collection;
14 import java.util.Dictionary;
15 import java.util.HashSet;
16 import java.util.Hashtable;
17 import java.util.Set;
18 import java.util.stream.Collectors;
19 import javax.annotation.concurrent.GuardedBy;
20 import org.opendaylight.bgpcep.bgp.topology.provider.spi.BgpTopologyDeployer;
21 import org.opendaylight.bgpcep.bgp.topology.provider.spi.BgpTopologyProvider;
22 import org.opendaylight.bgpcep.bgp.topology.provider.spi.TopologyReferenceSingletonService;
23 import org.opendaylight.bgpcep.topology.TopologyReference;
24 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
25 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
26 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
27 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
28 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
31 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
32 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
33 import org.opendaylight.protocol.bgp.rib.spi.util.ClusterSingletonServiceRegistrationHelper;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
36 import org.opendaylight.yangtools.concepts.AbstractRegistration;
37 import org.opendaylight.yangtools.concepts.ListenerRegistration;
38 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
39 import org.osgi.framework.BundleContext;
40 import org.osgi.framework.ServiceRegistration;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 public final class BgpTopologyDeployerImpl implements BgpTopologyDeployer, AutoCloseable,
45         ClusteredDataTreeChangeListener<Topology> {
46
47     private static final Logger LOG = LoggerFactory.getLogger(BgpTopologyDeployerImpl.class);
48     private static final InstanceIdentifier<Topology> TOPOLOGY_IID =
49             InstanceIdentifier.create(NetworkTopology.class).child(Topology.class);
50
51     @GuardedBy("this")
52     private final Set<BgpTopologyProvider> topologyProviders = new HashSet<>();
53     @GuardedBy("this")
54     private final Set<Topology> topologies = new HashSet<>();
55     private final DataBroker dataBroker;
56     private final BundleContext context;
57     private final ClusterSingletonServiceProvider singletonProvider;
58     private ListenerRegistration<BgpTopologyDeployerImpl> registration;
59     @GuardedBy("this")
60     private boolean closed;
61
62     public BgpTopologyDeployerImpl(final BundleContext context, final DataBroker dataBroker,
63             final ClusterSingletonServiceProvider singletonProvider) {
64         this.context = requireNonNull(context);
65         this.dataBroker = requireNonNull(dataBroker);
66         this.singletonProvider = requireNonNull(singletonProvider);
67     }
68
69     public void init() {
70         this.registration = this.dataBroker.registerDataTreeChangeListener(
71                 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, TOPOLOGY_IID), this);
72         LOG.info("BGP topology deployer started.");
73     }
74
75     @Override
76     public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Topology>> changes) {
77         if (this.closed) {
78             LOG.trace("BGP Topology Provider Deployer was already closed, skipping changes.");
79             return;
80         }
81         for (final DataTreeModification<Topology> change : changes) {
82             final DataObjectModification<Topology> rootNode = change.getRootNode();
83             final Topology dataBefore = rootNode.getDataBefore();
84             final Topology dataAfter = rootNode.getDataAfter();
85             LOG.trace("BGP topology deployer configuration changed: modification type: [{}],"
86                     + " data before:[{}], data after: [{}]", rootNode.getModificationType(), dataBefore, dataAfter);
87             switch (rootNode.getModificationType()) {
88                 case DELETE:
89                     filterTopologyBuilders(dataBefore)
90                             .forEach(provider -> provider.onTopologyBuilderRemoved(dataBefore));
91                     this.topologies.remove(dataBefore);
92                     break;
93                 case SUBTREE_MODIFIED:
94                     filterTopologyBuilders(dataBefore).forEach(provider
95                         -> provider.onTopologyBuilderRemoved(dataBefore));
96                     this.topologies.remove(dataBefore);
97                     filterTopologyBuilders(dataAfter).forEach(provider
98                         -> provider.onTopologyBuilderCreated(dataAfter));
99                     this.topologies.add(dataAfter);
100                     break;
101                 case WRITE:
102                     filterTopologyBuilders(dataAfter).forEach(provider
103                         -> provider.onTopologyBuilderCreated(dataAfter));
104                     this.topologies.add(dataAfter);
105                     break;
106                 default:
107                     break;
108             }
109         }
110     }
111
112     @Override
113     public synchronized AbstractRegistration registerTopologyProvider(final BgpTopologyProvider topologyBuilder) {
114         filterTopologies(topologyBuilder).forEach(topology -> topologyBuilder.onTopologyBuilderCreated(topology));
115         this.topologyProviders.add(topologyBuilder);
116         return new AbstractRegistration() {
117             @Override
118             protected void removeRegistration() {
119                 synchronized (BgpTopologyDeployerImpl.this) {
120                     filterTopologies(topologyBuilder)
121                             .forEach(topology -> topologyBuilder.onTopologyBuilderRemoved(topology));
122                     BgpTopologyDeployerImpl.this.topologyProviders.remove(topologyBuilder);
123                 }
124             }
125         };
126     }
127
128     @Override
129     public DataBroker getDataBroker() {
130         return this.dataBroker;
131     }
132
133     @Override
134     @SuppressWarnings("checkstyle:IllegalCatch")
135     public AbstractRegistration registerService(final TopologyReferenceSingletonService topologyProviderService) {
136         final Dictionary<String, String> properties = new Hashtable<>();
137         properties.put("topology-id", topologyProviderService.getInstanceIdentifier()
138                 .firstKeyOf(Topology.class).getTopologyId().getValue());
139         final ServiceRegistration<?> registerService = this.context
140                 .registerService(new String[]{TopologyReference.class.getName()},
141                         topologyProviderService, properties);
142         final ClusterSingletonServiceRegistration registerClusterSingletonService =
143                 registerSingletonService(topologyProviderService);
144         return new AbstractRegistration() {
145             @Override
146             protected void removeRegistration() {
147                 try {
148                     registerClusterSingletonService.close();
149                 } catch (final Exception e) {
150                     LOG.warn("Failed to close ClusterSingletonServiceRegistration {} for TopologyBuilder {}",
151                             registerClusterSingletonService, topologyProviderService.getInstanceIdentifier(), e);
152                 } finally {
153                     registerService.unregister();
154                 }
155             }
156         };
157     }
158
159     @Override
160     public synchronized void close() {
161         if (this.registration != null) {
162             this.registration.close();
163             this.registration = null;
164         }
165
166         LOG.info("BGP topology deployer stopped.");
167         this.closed = true;
168     }
169
170     private Iterable<BgpTopologyProvider> filterTopologyBuilders(final Topology topology) {
171         return this.topologyProviders.stream().filter(input -> input.topologyTypeFilter(topology))
172                 .collect(Collectors.toList());
173     }
174
175     private Iterable<Topology> filterTopologies(final BgpTopologyProvider topologyBuilder) {
176         return this.topologies.stream().filter(topology -> topologyBuilder.topologyTypeFilter(topology))
177                 .collect(Collectors.toList());
178     }
179
180     private ClusterSingletonServiceRegistration registerSingletonService(
181             final ClusterSingletonService clusterSingletonService) {
182         return ClusterSingletonServiceRegistrationHelper
183                 .registerSingletonService(this.singletonProvider, clusterSingletonService);
184     }
185
186 }