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