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