config-manager-facade-xml: final parameters
[controller.git] / opendaylight / config / config-manager-facade-xml / src / main / java / org / opendaylight / controller / config / facade / xml / transactions / TransactionProvider.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
9 package org.opendaylight.controller.config.facade.xml.transactions;
10
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import java.util.ArrayList;
14 import java.util.Collections;
15 import java.util.List;
16 import java.util.Set;
17 import javax.management.InstanceNotFoundException;
18 import javax.management.ObjectName;
19 import org.opendaylight.controller.config.api.ConflictingVersionException;
20 import org.opendaylight.controller.config.api.ValidationException;
21 import org.opendaylight.controller.config.api.jmx.CommitStatus;
22 import org.opendaylight.controller.config.util.ConfigRegistryClient;
23 import org.opendaylight.controller.config.util.ConfigTransactionClient;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 public class TransactionProvider implements AutoCloseable {
28     private static final Logger LOG = LoggerFactory.getLogger(TransactionProvider.class);
29
30     private final ConfigRegistryClient configRegistryClient;
31
32     private final String sessionIdForReporting;
33     private ObjectName candidateTx;
34     private ObjectName readTx;
35     private final List<ObjectName> allOpenedTransactions = new ArrayList<>();
36     private static final String NO_TRANSACTION_FOUND_FOR_SESSION = "No transaction found for session ";
37
38     public TransactionProvider(final ConfigRegistryClient configRegistryClient, final String sessionIdForReporting) {
39         this.configRegistryClient = configRegistryClient;
40         this.sessionIdForReporting = sessionIdForReporting;
41     }
42
43     @Override
44     public synchronized void close() {
45         for (ObjectName tx : allOpenedTransactions) {
46             try {
47                 if (isStillOpenTransaction(tx)) {
48                     configRegistryClient.getConfigTransactionClient(tx).abortConfig();
49                 }
50             } catch (final Exception e) {
51                 LOG.debug("Ignoring exception while closing transaction {}", tx, e);
52             }
53         }
54         allOpenedTransactions.clear();
55     }
56
57     public synchronized Optional<ObjectName> getTransaction() {
58
59         if (candidateTx == null){
60             return Optional.absent();
61         }
62
63         // Transaction was already closed somehow
64         if (!isStillOpenTransaction(candidateTx)) {
65             LOG.warn("Fixing illegal state: transaction {} was closed in {}", candidateTx, sessionIdForReporting);
66             candidateTx = null;
67             return Optional.absent();
68         }
69         return Optional.of(candidateTx);
70     }
71
72     public synchronized Optional<ObjectName> getReadTransaction() {
73
74         if (readTx == null){
75             return Optional.absent();
76         }
77
78         // Transaction was already closed somehow
79         if (!isStillOpenTransaction(readTx)) {
80             LOG.warn("Fixing illegal state: transaction {} was closed in {}", readTx, sessionIdForReporting);
81             readTx = null;
82             return Optional.absent();
83         }
84         return Optional.of(readTx);
85     }
86
87     private boolean isStillOpenTransaction(final ObjectName transaction) {
88         return configRegistryClient.getOpenConfigs().contains(transaction);
89     }
90
91     public synchronized ObjectName getOrCreateTransaction() {
92         Optional<ObjectName> ta = getTransaction();
93
94         if (ta.isPresent()) {
95             return ta.get();
96         }
97         candidateTx = configRegistryClient.beginConfig();
98         allOpenedTransactions.add(candidateTx);
99         return candidateTx;
100     }
101
102     public synchronized ObjectName getOrCreateReadTransaction() {
103         Optional<ObjectName> ta = getReadTransaction();
104
105         if (ta.isPresent()) {
106             return ta.get();
107         }
108         readTx = configRegistryClient.beginConfig();
109         allOpenedTransactions.add(readTx);
110         return readTx;
111     }
112
113     /**
114      * Used for editConfig test option
115      */
116     public synchronized ObjectName getTestTransaction() {
117         ObjectName testTx = configRegistryClient.beginConfig();
118         allOpenedTransactions.add(testTx);
119         return testTx;
120     }
121
122     /**
123      * Commit and notification send must be atomic
124      */
125     public CommitStatus commitTransaction() throws ValidationException, ConflictingVersionException {
126         return commitTransaction(configRegistryClient);
127     }
128
129     /**
130      * Commit and notification send must be atomic
131      * @param configRegistryClient
132      */
133     public synchronized CommitStatus commitTransaction(final ConfigRegistryClient configRegistryClient) throws ValidationException, ConflictingVersionException {
134         if (!getTransaction().isPresent()){
135             //making empty commit without prior opened transaction, just return commit status with empty lists
136             LOG.debug("Making commit without open candidate transaction for session {}", sessionIdForReporting);
137             return new CommitStatus(Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
138         }
139         final Optional<ObjectName> maybeTaON = getTransaction();
140         ObjectName taON = maybeTaON.get();
141         try {
142             CommitStatus status = configRegistryClient.commitConfig(taON);
143             // clean up
144             allOpenedTransactions.remove(candidateTx);
145             candidateTx = null;
146             return status;
147         } catch (final ValidationException validationException) {
148             // no clean up: user can reconfigure and recover this transaction
149             LOG.warn("Transaction {} failed on {}", taON, validationException.toString());
150             throw validationException;
151         } catch (final ConflictingVersionException e) {
152             LOG.debug("Exception while commit of {}, aborting transaction", taON, e);
153             // clean up
154             abortTransaction();
155             throw e;
156         }
157     }
158
159     public synchronized void abortTransaction() {
160         LOG.debug("Aborting current transaction");
161         Optional<ObjectName> taON = getTransaction();
162         Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
163
164         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
165         transactionClient.abortConfig();
166         allOpenedTransactions.remove(candidateTx);
167         candidateTx = null;
168     }
169
170     public synchronized void closeReadTransaction() {
171         LOG.debug("Closing read transaction");
172         Optional<ObjectName> taON = getReadTransaction();
173         Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
174
175         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
176         transactionClient.abortConfig();
177         allOpenedTransactions.remove(readTx);
178         readTx = null;
179     }
180
181     public synchronized void abortTestTransaction(final ObjectName testTx) {
182         LOG.debug("Aborting transaction {}", testTx);
183         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(testTx);
184         allOpenedTransactions.remove(testTx);
185         transactionClient.abortConfig();
186     }
187
188     public void validateTransaction() throws ValidationException {
189         Optional<ObjectName> taON = getTransaction();
190         Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
191
192         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
193         transactionClient.validateConfig();
194     }
195
196     public void validateTestTransaction(final ObjectName taON) throws ValidationException {
197         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
198         transactionClient.validateConfig();
199     }
200
201     public void wipeTestTransaction(final ObjectName taON) {
202         wipeInternal(taON, true);
203     }
204
205     /**
206      * Wiping means removing all module instances keeping the transaction open + service references.
207      */
208     synchronized void wipeInternal(final ObjectName taON, final boolean isTest) {
209         ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
210
211         Set<ObjectName> lookupConfigBeans = transactionClient.lookupConfigBeans();
212         int i = lookupConfigBeans.size();
213         for (ObjectName instance : lookupConfigBeans) {
214             try {
215                 transactionClient.destroyModule(instance);
216             } catch (final InstanceNotFoundException e) {
217                 if (isTest){
218                     LOG.debug("Unable to clean configuration in transactiom {}", taON, e);
219                 } else {
220                     LOG.warn("Unable to clean configuration in transactiom {}", taON, e);
221                 }
222
223                 throw new IllegalStateException("Unable to clean configuration in transactiom " + taON, e);
224             }
225         }
226         LOG.debug("Transaction {} wiped clean of {} config beans", taON, i);
227
228         transactionClient.removeAllServiceReferences();
229         LOG.debug("Transaction {} wiped clean of all service references", taON);
230     }
231
232     public void wipeTransaction() {
233         Optional<ObjectName> taON = getTransaction();
234         Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
235         wipeInternal(taON.get(), false);
236     }
237
238 }