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