Fix findbugs warnings
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / FrontendClientMetadataBuilder.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 package org.opendaylight.controller.cluster.datastore;
9
10 import com.google.common.base.MoreObjects;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Verify;
13 import com.google.common.collect.Collections2;
14 import java.util.HashMap;
15 import java.util.Map;
16 import javax.annotation.Nonnull;
17 import javax.annotation.concurrent.NotThreadSafe;
18 import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier;
19 import org.opendaylight.controller.cluster.access.concepts.LocalHistoryIdentifier;
20 import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
21 import org.opendaylight.controller.cluster.datastore.persisted.FrontendClientMetadata;
22 import org.opendaylight.controller.cluster.datastore.persisted.FrontendHistoryMetadata;
23 import org.opendaylight.controller.cluster.datastore.utils.UnsignedLongRangeSet;
24 import org.opendaylight.yangtools.concepts.Builder;
25 import org.opendaylight.yangtools.concepts.Identifiable;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 @NotThreadSafe
30 final class FrontendClientMetadataBuilder implements Builder<FrontendClientMetadata>, Identifiable<ClientIdentifier> {
31     private static final Logger LOG = LoggerFactory.getLogger(FrontendClientMetadataBuilder.class);
32
33     private final Map<LocalHistoryIdentifier, FrontendHistoryMetadataBuilder> currentHistories = new HashMap<>();
34     private final UnsignedLongRangeSet purgedHistories;
35     private final ClientIdentifier identifier;
36     private final String shardName;
37
38     FrontendClientMetadataBuilder(final String shardName, final ClientIdentifier identifier) {
39         this.shardName = Preconditions.checkNotNull(shardName);
40         this.identifier = Preconditions.checkNotNull(identifier);
41         purgedHistories = UnsignedLongRangeSet.create();
42
43         // History for stand-alone transactions is always present
44         final LocalHistoryIdentifier standaloneId = standaloneHistoryId();
45         currentHistories.put(standaloneId, new FrontendHistoryMetadataBuilder(standaloneId));
46     }
47
48     FrontendClientMetadataBuilder(final String shardName, final FrontendClientMetadata meta) {
49         this.shardName = Preconditions.checkNotNull(shardName);
50         this.identifier = Preconditions.checkNotNull(meta.getIdentifier());
51         purgedHistories = UnsignedLongRangeSet.create(meta.getPurgedHistories());
52
53         for (FrontendHistoryMetadata h : meta.getCurrentHistories()) {
54             final FrontendHistoryMetadataBuilder b = new FrontendHistoryMetadataBuilder(identifier, h);
55             currentHistories.put(b.getIdentifier(), b);
56         }
57
58         // Sanity check and recovery
59         final LocalHistoryIdentifier standaloneId = standaloneHistoryId();
60         if (!currentHistories.containsKey(standaloneId)) {
61             LOG.warn("{}: Client {} recovered histories {} do not contain stand-alone history, attempting recovery",
62                 shardName, identifier, currentHistories);
63             currentHistories.put(standaloneId, new FrontendHistoryMetadataBuilder(standaloneId));
64         }
65     }
66
67     private LocalHistoryIdentifier standaloneHistoryId() {
68         return new LocalHistoryIdentifier(identifier, 0);
69     }
70
71     @Override
72     public FrontendClientMetadata build() {
73         return new FrontendClientMetadata(identifier, purgedHistories.toImmutable(),
74             Collections2.transform(currentHistories.values(), FrontendHistoryMetadataBuilder::build));
75     }
76
77     @Override
78     public ClientIdentifier getIdentifier() {
79         return identifier;
80     }
81
82     void onHistoryCreated(final LocalHistoryIdentifier historyId) {
83         final FrontendHistoryMetadataBuilder newMeta = new FrontendHistoryMetadataBuilder(historyId);
84         final FrontendHistoryMetadataBuilder oldMeta = currentHistories.putIfAbsent(historyId, newMeta);
85         if (oldMeta != null) {
86             // This should not be happening, warn about it
87             LOG.warn("{}: Reused local history {}", shardName, historyId);
88         } else {
89             LOG.debug("{}: Created local history {}", shardName, historyId);
90         }
91     }
92
93     void onHistoryClosed(final LocalHistoryIdentifier historyId) {
94         final FrontendHistoryMetadataBuilder builder = currentHistories.get(historyId);
95         if (builder != null) {
96             builder.onHistoryClosed();
97             LOG.debug("{}: Closed history {}", shardName, historyId);
98         } else {
99             LOG.warn("{}: Closed unknown history {}, ignoring", shardName, historyId);
100         }
101     }
102
103     void onHistoryPurged(final LocalHistoryIdentifier historyId) {
104         final FrontendHistoryMetadataBuilder history = currentHistories.remove(historyId);
105         if (history == null) {
106             LOG.warn("{}: Purging unknown history {}", shardName, historyId);
107         }
108
109         // XXX: do we need to account for cookies?
110         purgedHistories.add(historyId.getHistoryId());
111         LOG.debug("{}: Purged history {}", shardName, historyId);
112     }
113
114     void onTransactionAborted(final TransactionIdentifier txId) {
115         final FrontendHistoryMetadataBuilder history = getHistory(txId);
116         if (history != null) {
117             history.onTransactionAborted(txId);
118             LOG.debug("{}: Aborted transaction {}", shardName, txId);
119         } else {
120             LOG.warn("{}: Unknown history for aborted transaction {}, ignoring", shardName, txId);
121         }
122     }
123
124     void onTransactionCommitted(final TransactionIdentifier txId) {
125         final FrontendHistoryMetadataBuilder history = getHistory(txId);
126         if (history != null) {
127             history.onTransactionCommitted(txId);
128             LOG.debug("{}: Committed transaction {}", shardName, txId);
129         } else {
130             LOG.warn("{}: Unknown history for commited transaction {}, ignoring", shardName, txId);
131         }
132     }
133
134     void onTransactionPurged(final TransactionIdentifier txId) {
135         final FrontendHistoryMetadataBuilder history = getHistory(txId);
136         if (history != null) {
137             history.onTransactionPurged(txId);
138             LOG.debug("{}: Purged transaction {}", shardName, txId);
139         } else {
140             LOG.warn("{}: Unknown history for purged transaction {}, ignoring", shardName, txId);
141         }
142     }
143
144     /**
145      * Transform frontend metadata for a particular client into its {@link LeaderFrontendState} counterpart.
146      *
147      * @param shard parent shard
148      * @return Leader frontend state
149      */
150     @Nonnull LeaderFrontendState toLeaderState(@Nonnull final Shard shard) {
151         // Note: we have to make sure to *copy* all current state and not leak any views, otherwise leader/follower
152         //       interactions would get intertwined leading to inconsistencies.
153         final Map<LocalHistoryIdentifier, LocalFrontendHistory> histories = new HashMap<>();
154         for (FrontendHistoryMetadataBuilder e : currentHistories.values()) {
155             if (e.getIdentifier().getHistoryId() != 0) {
156                 final AbstractFrontendHistory state = e.toLeaderState(shard);
157                 Verify.verify(state instanceof LocalFrontendHistory);
158                 histories.put(e.getIdentifier(), (LocalFrontendHistory) state);
159             }
160         }
161
162         final AbstractFrontendHistory singleHistory;
163         final FrontendHistoryMetadataBuilder singleHistoryMeta = currentHistories.get(
164             new LocalHistoryIdentifier(identifier, 0));
165         if (singleHistoryMeta == null) {
166             final ShardDataTree tree = shard.getDataStore();
167             singleHistory = StandaloneFrontendHistory.create(shard.persistenceId(), getIdentifier(), tree);
168         } else {
169             singleHistory = singleHistoryMeta.toLeaderState(shard);
170         }
171
172         return new LeaderFrontendState(shard.persistenceId(), getIdentifier(), shard.getDataStore(),
173             purgedHistories.copy(), singleHistory, histories);
174     }
175
176     private FrontendHistoryMetadataBuilder getHistory(final TransactionIdentifier txId) {
177         return currentHistories.get(txId.getHistoryId());
178     }
179
180     @Override
181     public String toString() {
182         return MoreObjects.toStringHelper(this).add("identifier", identifier).add("current", currentHistories)
183                 .add("purged", purgedHistories).toString();
184     }
185 }