Bug 6622 - ClusterSingletonService registration race condition
[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 com.google.common.base.Preconditions;
12 import com.google.common.collect.Iterables;
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.function.Function;
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 AutoCloseable, ClusteredDataTreeChangeListener<Topology>, BgpTopologyDeployer {
45
46     private static final Logger LOG = LoggerFactory.getLogger(BgpTopologyDeployerImpl.class);
47
48     private static final int MAX_REGISTRATION_ATTEMPTS = 10;
49     private static final int SLEEP_TIME = MAX_REGISTRATION_ATTEMPTS;
50
51     @GuardedBy("this")
52     private final Set<BgpTopologyProvider> topologyProviders = new HashSet<>();
53     private final DataBroker dataBroker;
54     private final ListenerRegistration<BgpTopologyDeployerImpl> registration;
55     private final BundleContext context;
56     private final ClusterSingletonServiceProvider singletonProvider;
57     @GuardedBy("this")
58     private boolean closed;
59
60     public BgpTopologyDeployerImpl(final BundleContext context, final DataBroker dataBroker, final ClusterSingletonServiceProvider singletonProvider) {
61         this.context = Preconditions.checkNotNull(context);
62         this.dataBroker = Preconditions.checkNotNull(dataBroker);
63         this.singletonProvider = Preconditions.checkNotNull(singletonProvider);
64         this.registration =
65                 this.dataBroker.registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(NetworkTopology.class).child(Topology.class)), this);
66         LOG.info("BGP topology deployer started.");
67     }
68
69     @Override
70     public synchronized void onDataTreeChanged(final Collection<DataTreeModification<Topology>> changes) {
71         if (this.closed) {
72             LOG.trace("BGP Topology Provider Deployer was already closed, skipping changes.");
73             return;
74         }
75         for (final DataTreeModification<Topology> change : changes) {
76             final DataObjectModification<Topology> rootNode = change.getRootNode();
77             final Topology dataBefore = rootNode.getDataBefore();
78             final Topology dataAfter = rootNode.getDataAfter();
79             LOG.trace("BGP topology deployer configuration changed: modification type: [{}], data before:[{}], data after: [{}]", rootNode.getModificationType(), dataBefore, dataAfter);
80             switch (rootNode.getModificationType()) {
81             case DELETE:
82                 filterTopologyBuilders(dataBefore).forEach(provider -> provider.onTopologyBuilderRemoved(dataBefore));
83                 break;
84             case SUBTREE_MODIFIED:
85                 filterTopologyBuilders(dataBefore).forEach(provider -> provider.onTopologyBuilderRemoved(dataBefore));
86                 filterTopologyBuilders(dataAfter).forEach(provider -> provider.onTopologyBuilderCreated(dataAfter, null));
87                 break;
88             case WRITE:
89                 filterTopologyBuilders(dataAfter).forEach(provider -> provider.onTopologyBuilderCreated(dataAfter, null));
90                 break;
91             default:
92                 break;
93             }
94         }
95     }
96
97     @Override
98     public synchronized AbstractRegistration registerTopologyProvider(final BgpTopologyProvider topologyBuilder) {
99         this.topologyProviders.add(topologyBuilder);
100         return new AbstractRegistration() {
101             @Override
102             protected void removeRegistration() {
103                 synchronized (BgpTopologyDeployerImpl.this) {
104                     BgpTopologyDeployerImpl.this.topologyProviders.remove(topologyBuilder);
105                 }
106             }
107         };
108     }
109
110     @Override
111     public synchronized void close() throws Exception {
112         this.registration.close();
113         LOG.info("BGP topology deployer stopped.");
114         this.closed = true;
115     }
116
117     @Override
118     public DataBroker getDataBroker() {
119         return this.dataBroker;
120     }
121
122     private Iterable<BgpTopologyProvider> filterTopologyBuilders(final Topology topology) {
123         return Iterables.filter(this.topologyProviders, input -> input.topologyTypeFilter(topology));
124     }
125
126     @Override
127     public AbstractRegistration registerService(final TopologyReferenceSingletonService topologyProviderService) {
128         final Dictionary<String, String> properties = new Hashtable<>();
129         properties.put("topology-id", topologyProviderService.getInstanceIdentifier().firstKeyOf(Topology.class).getTopologyId().getValue());
130         final ServiceRegistration<?> registerService = this.context.registerService(new String[] {TopologyReference.class.getName()}, topologyProviderService, properties);
131         final ClusterSingletonServiceRegistration registerClusterSingletonService = registerSingletonService(topologyProviderService);
132         return new AbstractRegistration() {
133             @Override
134             protected void removeRegistration() {
135                 try {
136                     registerClusterSingletonService.close();
137                 } catch (final Exception e) {
138                     LOG.warn("Failed to close ClusterSingletonServiceRegistration {} for TopologyBuilder {}",
139                             registerClusterSingletonService, topologyProviderService.getInstanceIdentifier(), e);
140                 }
141                 registerService.unregister();
142             }
143         };
144     }
145
146     @Override
147     public void createInstance(final Topology topology, final Function<Topology, Void> writeFunction) {
148         filterTopologyBuilders(topology).forEach(provider -> provider.onTopologyBuilderCreated(topology, writeFunction));
149     }
150
151     @Override
152     public void removeInstance(final Topology topology) {
153         filterTopologyBuilders(topology).forEach(provider -> provider.onTopologyBuilderRemoved(topology));
154     }
155
156     private ClusterSingletonServiceRegistration registerSingletonService(final ClusterSingletonService clusterSingletonService) {
157         return ClusterSingletonServiceRegistrationHelper.registerSingletonService(this.singletonProvider, clusterSingletonService, MAX_REGISTRATION_ATTEMPTS, SLEEP_TIME);
158     }
159
160 }