Migrate netconf to MD-SAL APIs
[netconf.git] / netconf / mdsal-netconf-connector / src / main / java / org / opendaylight / netconf / mdsal / connector / 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 package org.opendaylight.netconf.mdsal.connector;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.FluentFuture;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.concurrent.ExecutionException;
16 import org.opendaylight.mdsal.common.api.CommitInfo;
17 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
18 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
19 import org.opendaylight.netconf.api.DocumentedException;
20 import org.opendaylight.netconf.api.DocumentedException.ErrorSeverity;
21 import org.opendaylight.netconf.api.DocumentedException.ErrorTag;
22 import org.opendaylight.netconf.api.DocumentedException.ErrorType;
23 import org.opendaylight.netconf.mdsal.connector.DOMDataTransactionValidator.ValidationFailedException;
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 DOMDataBroker dataBroker;
31
32     private DOMDataTreeReadWriteTransaction candidateTransaction = null;
33     private DOMDataTreeReadWriteTransaction runningTransaction = null;
34     private final List<DOMDataTreeReadWriteTransaction> allOpenReadWriteTransactions = new ArrayList<>();
35     private final DOMDataTransactionValidator transactionValidator;
36
37     private final String netconfSessionIdForReporting;
38
39     private static final String NO_TRANSACTION_FOUND_FOR_SESSION = "No candidateTransaction found for session ";
40
41     public TransactionProvider(final DOMDataBroker dataBroker, final String netconfSessionIdForReporting) {
42         this.dataBroker = dataBroker;
43         this.netconfSessionIdForReporting = netconfSessionIdForReporting;
44         this.transactionValidator = dataBroker.getExtensions().getInstance(DOMDataTransactionValidator.class);
45     }
46
47     @Override
48     public synchronized void close() {
49         for (final DOMDataTreeReadWriteTransaction rwt : allOpenReadWriteTransactions) {
50             rwt.cancel();
51         }
52
53         allOpenReadWriteTransactions.clear();
54     }
55
56     public synchronized Optional<DOMDataTreeReadWriteTransaction> getCandidateTransaction() {
57         if (candidateTransaction == null) {
58             return Optional.absent();
59         }
60
61         return Optional.of(candidateTransaction);
62     }
63
64     public synchronized DOMDataTreeReadWriteTransaction getOrCreateTransaction() {
65         if (getCandidateTransaction().isPresent()) {
66             return getCandidateTransaction().get();
67         }
68
69         candidateTransaction = dataBroker.newReadWriteTransaction();
70         allOpenReadWriteTransactions.add(candidateTransaction);
71         return candidateTransaction;
72     }
73
74     public synchronized void validateTransaction() throws DocumentedException {
75         if (transactionValidator == null) {
76             LOG.error("Validate capability is not supported");
77             throw new DocumentedException("Validate capability is not supported",
78                 ErrorType.PROTOCOL, ErrorTag.OPERATION_NOT_SUPPORTED, ErrorSeverity.ERROR);
79         }
80
81         if (!getCandidateTransaction().isPresent()) {
82             // Validating empty transaction, just return true
83             LOG.debug("Validating empty candidate transaction for session {}", netconfSessionIdForReporting);
84             return;
85         }
86
87         try {
88             transactionValidator.validate(candidateTransaction).checkedGet();
89         } catch (final ValidationFailedException e) {
90             LOG.debug("Candidate transaction validation {} failed on session {}", candidateTransaction,
91                 netconfSessionIdForReporting, e);
92             final String cause = e.getCause() != null ? " Cause: " + e.getCause().getMessage() : "";
93             throw new DocumentedException("Candidate transaction validate failed [sessionId="
94                     + netconfSessionIdForReporting + "]: " + e.getMessage() + cause, e, ErrorType.APPLICATION,
95                     ErrorTag.OPERATION_FAILED, ErrorSeverity.ERROR);
96         }
97     }
98
99     public synchronized boolean commitTransaction() throws DocumentedException {
100         if (!getCandidateTransaction().isPresent()) {
101             //making empty commit without prior opened transaction, just return true
102             LOG.debug("Making commit without open candidate transaction for session {}", netconfSessionIdForReporting);
103             return true;
104         }
105
106         final FluentFuture<? extends CommitInfo> future = candidateTransaction.commit();
107         try {
108             future.get();
109         } catch (final InterruptedException | ExecutionException e) {
110             LOG.debug("Transaction {} failed on", candidateTransaction, e);
111             final String cause = e.getCause() != null ? " Cause: " + e.getCause().getMessage() : "";
112             throw new DocumentedException("Transaction commit failed on " + e.getMessage() + " "
113                     + netconfSessionIdForReporting + cause, e, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
114                     ErrorSeverity.ERROR);
115         } finally {
116             allOpenReadWriteTransactions.remove(candidateTransaction);
117             candidateTransaction = null;
118         }
119
120         return true;
121     }
122
123     public synchronized void abortTransaction() {
124         LOG.debug("Aborting current candidateTransaction");
125         final Optional<DOMDataTreeReadWriteTransaction> otx = getCandidateTransaction();
126         if (!otx.isPresent()) {
127             LOG.warn("discard-changes triggerd on an empty transaction for session: {}", netconfSessionIdForReporting);
128             return;
129         }
130         candidateTransaction.cancel();
131         allOpenReadWriteTransactions.remove(candidateTransaction);
132         candidateTransaction = null;
133     }
134
135     public synchronized DOMDataTreeReadWriteTransaction createRunningTransaction() {
136         runningTransaction = dataBroker.newReadWriteTransaction();
137         allOpenReadWriteTransactions.add(runningTransaction);
138         return runningTransaction;
139     }
140
141     public synchronized void abortRunningTransaction(final DOMDataTreeReadWriteTransaction tx) {
142         LOG.debug("Aborting current running Transaction");
143         Preconditions.checkState(runningTransaction != null,
144                 NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting);
145         tx.cancel();
146         allOpenReadWriteTransactions.remove(tx);
147     }
148 }