Use java.util.Optional
[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 static com.google.common.base.Preconditions.checkState;
11
12 import com.google.common.util.concurrent.FluentFuture;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.Optional;
16 import java.util.concurrent.ExecutionException;
17 import org.opendaylight.mdsal.common.api.CommitInfo;
18 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
19 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
20 import org.opendaylight.netconf.api.DocumentedException;
21 import org.opendaylight.netconf.api.DocumentedException.ErrorSeverity;
22 import org.opendaylight.netconf.api.DocumentedException.ErrorTag;
23 import org.opendaylight.netconf.api.DocumentedException.ErrorType;
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         return Optional.ofNullable(candidateTransaction);
58     }
59
60     public synchronized DOMDataTreeReadWriteTransaction getOrCreateTransaction() {
61         if (getCandidateTransaction().isPresent()) {
62             return getCandidateTransaction().get();
63         }
64
65         candidateTransaction = dataBroker.newReadWriteTransaction();
66         allOpenReadWriteTransactions.add(candidateTransaction);
67         return candidateTransaction;
68     }
69
70     public synchronized void validateTransaction() throws DocumentedException {
71         if (transactionValidator == null) {
72             LOG.error("Validate capability is not supported");
73             throw new DocumentedException("Validate capability is not supported",
74                 ErrorType.PROTOCOL, ErrorTag.OPERATION_NOT_SUPPORTED, ErrorSeverity.ERROR);
75         }
76
77         if (!getCandidateTransaction().isPresent()) {
78             // Validating empty transaction, just return true
79             LOG.debug("Validating empty candidate transaction for session {}", netconfSessionIdForReporting);
80             return;
81         }
82
83         try {
84             transactionValidator.validate(candidateTransaction).get();
85         } catch (final InterruptedException | ExecutionException e) {
86             LOG.debug("Candidate transaction validation {} failed on session {}", candidateTransaction,
87                 netconfSessionIdForReporting, e);
88             final String cause = e.getCause() != null ? " Cause: " + e.getCause().getMessage() : "";
89             throw new DocumentedException("Candidate transaction validate failed [sessionId="
90                     + netconfSessionIdForReporting + "]: " + e.getMessage() + cause, e, ErrorType.APPLICATION,
91                     ErrorTag.OPERATION_FAILED, ErrorSeverity.ERROR);
92         }
93     }
94
95     public synchronized boolean commitTransaction() throws DocumentedException {
96         if (!getCandidateTransaction().isPresent()) {
97             //making empty commit without prior opened transaction, just return true
98             LOG.debug("Making commit without open candidate transaction for session {}", netconfSessionIdForReporting);
99             return true;
100         }
101
102         final FluentFuture<? extends CommitInfo> future = candidateTransaction.commit();
103         try {
104             future.get();
105         } catch (final InterruptedException | ExecutionException e) {
106             LOG.debug("Transaction {} failed on", candidateTransaction, e);
107             final String cause = e.getCause() != null ? " Cause: " + e.getCause().getMessage() : "";
108             throw new DocumentedException("Transaction commit failed on " + e.getMessage() + " "
109                     + netconfSessionIdForReporting + cause, e, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
110                     ErrorSeverity.ERROR);
111         } finally {
112             allOpenReadWriteTransactions.remove(candidateTransaction);
113             candidateTransaction = null;
114         }
115
116         return true;
117     }
118
119     public synchronized void abortTransaction() {
120         LOG.debug("Aborting current candidateTransaction");
121         final Optional<DOMDataTreeReadWriteTransaction> otx = getCandidateTransaction();
122         if (!otx.isPresent()) {
123             LOG.warn("discard-changes triggerd on an empty transaction for session: {}", netconfSessionIdForReporting);
124             return;
125         }
126         candidateTransaction.cancel();
127         allOpenReadWriteTransactions.remove(candidateTransaction);
128         candidateTransaction = null;
129     }
130
131     public synchronized DOMDataTreeReadWriteTransaction createRunningTransaction() {
132         runningTransaction = dataBroker.newReadWriteTransaction();
133         allOpenReadWriteTransactions.add(runningTransaction);
134         return runningTransaction;
135     }
136
137     public synchronized void abortRunningTransaction(final DOMDataTreeReadWriteTransaction tx) {
138         LOG.debug("Aborting current running Transaction");
139         checkState(runningTransaction != null, NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting);
140         tx.cancel();
141         allOpenReadWriteTransactions.remove(tx);
142     }
143 }