Fix up release old producers to avoid memory leak
[mdsal.git] / dom / mdsal-dom-broker / src / main / java / org / opendaylight / mdsal / dom / broker / ProducerLayout.java
1 /*
2  * Copyright (c) 2015 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.mdsal.dom.broker;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.ArrayListMultimap;
14 import com.google.common.collect.BiMap;
15 import com.google.common.collect.ImmutableBiMap;
16 import com.google.common.collect.ImmutableBiMap.Builder;
17 import com.google.common.collect.ImmutableMap;
18 import com.google.common.collect.Maps;
19 import com.google.common.collect.Multimap;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.Set;
25 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
26 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeShard;
28 import org.opendaylight.mdsal.dom.spi.shard.DOMDataTreeShardProducer;
29 import org.opendaylight.mdsal.dom.spi.shard.DOMDataTreeShardWriteTransaction;
30 import org.opendaylight.mdsal.dom.spi.shard.WriteableDOMDataTreeShard;
31 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 final class ProducerLayout {
37     private static final Logger LOG = LoggerFactory.getLogger(ProducerLayout.class);
38
39     private final BiMap<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducer;
40     private final Map<DOMDataTreeIdentifier, DOMDataTreeProducer> children;
41     private final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap;
42
43     private ProducerLayout(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap,
44             final BiMap<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducer,
45             final Map<DOMDataTreeIdentifier, DOMDataTreeProducer> children) {
46         this.shardMap = ImmutableMap.copyOf(shardMap);
47         this.idToProducer = requireNonNull(idToProducer);
48         this.children = requireNonNull(children);
49     }
50
51     static ProducerLayout create(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
52         return new ProducerLayout(shardMap, mapIdsToProducer(shardMap), ImmutableMap.of());
53     }
54
55     private static BiMap<DOMDataTreeIdentifier, DOMDataTreeShardProducer> mapIdsToProducer(
56             final Map<DOMDataTreeIdentifier, DOMDataTreeShard> shardMap) {
57         final Multimap<DOMDataTreeShard, DOMDataTreeIdentifier> shardToId = ArrayListMultimap.create();
58         // map which identifier belongs to which shard
59         for (final Entry<DOMDataTreeIdentifier, DOMDataTreeShard> entry : shardMap.entrySet()) {
60             shardToId.put(entry.getValue(), entry.getKey());
61         }
62
63         final Builder<DOMDataTreeIdentifier, DOMDataTreeShardProducer> idToProducerBuilder = ImmutableBiMap.builder();
64         for (final Entry<DOMDataTreeShard, Collection<DOMDataTreeIdentifier>> entry : shardToId.asMap().entrySet()) {
65             if (entry.getKey() instanceof WriteableDOMDataTreeShard) {
66                 //create a single producer for all prefixes in a single shard
67                 final DOMDataTreeShardProducer producer = ((WriteableDOMDataTreeShard) entry.getKey())
68                         .createProducer(entry.getValue());
69                 // id mapped to producers
70                 for (final DOMDataTreeIdentifier id : entry.getValue()) {
71                     idToProducerBuilder.put(id, producer);
72                 }
73             } else {
74                 LOG.error("Unable to create a producer for shard that's not a WriteableDOMDataTreeShard");
75             }
76         }
77
78         return idToProducerBuilder.build();
79     }
80
81     ProducerLayout addChild(final DOMDataTreeProducer producer, final Collection<DOMDataTreeIdentifier> subtrees) {
82         final ImmutableMap.Builder<DOMDataTreeIdentifier, DOMDataTreeProducer> cb = ImmutableMap.builder();
83         cb.putAll(children);
84         for (final DOMDataTreeIdentifier s : subtrees) {
85             cb.put(s, producer);
86         }
87
88         return new ProducerLayout(shardMap, idToProducer, cb.build());
89     }
90
91     ProducerLayout reshard(final Map<DOMDataTreeIdentifier, DOMDataTreeShard> newShardMap) {
92         close();
93         return new ProducerLayout(newShardMap, mapIdsToProducer(newShardMap), children);
94     }
95
96     boolean haveSubtree(final DOMDataTreeIdentifier subtree) {
97         for (final DOMDataTreeIdentifier i : shardMap.keySet()) {
98             if (i.contains(subtree)) {
99                 return true;
100             }
101         }
102
103         return false;
104     }
105
106     DOMDataTreeProducer lookupChild(final DOMDataTreeIdentifier path) {
107         for (final Entry<DOMDataTreeIdentifier, DOMDataTreeProducer> e : children.entrySet()) {
108             if (e.getKey().contains(path)) {
109                 // FIXME: does this match wildcards?
110                 return e.getValue();
111             }
112         }
113
114         return null;
115     }
116
117     Set<DOMDataTreeIdentifier> getChildTrees() {
118         return children.keySet();
119     }
120
121     void checkAvailable(final Collection<PathArgument> base, final PathArgument child) {
122         if (!children.isEmpty()) {
123             final Collection<PathArgument> args = new ArrayList<>(base.size() + 1);
124             args.addAll(base);
125             args.add(child);
126
127             final YangInstanceIdentifier path = YangInstanceIdentifier.create(args);
128             for (final DOMDataTreeIdentifier c : children.keySet()) {
129                 Preconditions.checkArgument(!c.getRootIdentifier().contains(path),
130                     "Path {%s} is not available to this cursor since it's already claimed by a child producer", path);
131             }
132         }
133     }
134
135     Map<DOMDataTreeIdentifier, DOMDataTreeShardWriteTransaction> createTransactions() {
136         Preconditions.checkState(!idToProducer.isEmpty(),
137                 "Cannot create transaction since the producer is not mapped to any shard");
138         return Maps.transformValues(idToProducer, DOMDataTreeShardProducer::createTransaction);
139     }
140
141     void close() {
142         idToProducer.values().forEach(DOMDataTreeShardProducer::close);
143     }
144
145 }