Eliminate unnecessary blocking checks
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / utils / FutureCallbackTx.java
1 /*
2  * Copyright (c) 2016 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.restconf.nb.rfc8040.rests.utils;
9
10 import com.google.common.base.Throwables;
11 import com.google.common.collect.ImmutableList;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import java.util.List;
14 import java.util.concurrent.ExecutionException;
15 import org.eclipse.jdt.annotation.Nullable;
16 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
17 import org.opendaylight.mdsal.dom.api.DOMActionException;
18 import org.opendaylight.mdsal.dom.api.DOMRpcException;
19 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
20 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
21 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
22 import org.opendaylight.netconf.api.DocumentedException;
23 import org.opendaylight.netconf.api.NetconfDocumentedException;
24 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
25 import org.opendaylight.restconf.common.errors.RestconfError;
26 import org.opendaylight.yangtools.yang.common.RpcError;
27 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * Add callback for future objects and result set to the data factory.
34  */
35 final class FutureCallbackTx {
36     private static final Logger LOG = LoggerFactory.getLogger(FutureCallbackTx.class);
37
38     private FutureCallbackTx() {
39         throw new UnsupportedOperationException("Util class");
40     }
41
42     /**
43      * Add callback to the future object.
44      *
45      * @param listenableFuture
46      *             future object
47      * @param txType
48      *             type of operation (READ, POST, PUT, DELETE)
49      * @param dataFactory
50      *             factory setting result
51      * @throws RestconfDocumentedException
52      *             if the Future throws an exception
53      */
54     // FIXME: this is a *synchronous operation* and has to die
55     static <T> void addCallback(final ListenableFuture<T> listenableFuture, final String txType,
56                                 final FutureDataFactory<? super T> dataFactory) throws RestconfDocumentedException {
57         addCallback(listenableFuture, txType, dataFactory, null, null);
58     }
59
60     /**
61      * Add callback to the future object and close transaction chain.
62      *
63      * @param listenableFuture
64      *             future object
65      * @param txType
66      *             type of operation (READ, POST, PUT, DELETE)
67      * @param dataFactory
68      *             factory setting result
69      * @param transactionChain
70      *             transaction chain
71      * @throws RestconfDocumentedException
72      *             if the Future throws an exception
73      */
74     // FIXME: this is a *synchronous operation* and has to die
75     static <T> void addCallback(final ListenableFuture<T> listenableFuture, final String txType,
76                                 final FutureDataFactory<? super T> dataFactory,
77                                 @Nullable final DOMTransactionChain transactionChain)
78         throws RestconfDocumentedException {
79         addCallback(listenableFuture, txType, dataFactory, transactionChain, null);
80     }
81
82     /**
83      * Add callback to the future object and close transaction chain.
84      *
85      * @param listenableFuture
86      *             future object
87      * @param txType
88      *             type of operation (READ, POST, PUT, DELETE)
89      * @param dataFactory
90      *             factory setting result
91      * @param transactionChain
92      *             transaction chain
93      * @param path
94      *             unique identifier of a particular node instance in the data tree.
95      * @throws RestconfDocumentedException
96      *             if the Future throws an exception
97      */
98     static <T> void addCallback(final ListenableFuture<T> listenableFuture, final String txType,
99             final FutureDataFactory<? super T> dataFactory, @Nullable final DOMTransactionChain transactionChain,
100                                 YangInstanceIdentifier path) throws RestconfDocumentedException {
101         try {
102             final T result = listenableFuture.get();
103             dataFactory.setResult(result);
104             LOG.trace("Transaction({}) SUCCESSFUL", txType);
105         } catch (InterruptedException e) {
106             dataFactory.setFailureStatus();
107             LOG.warn("Transaction({}) FAILED!", txType, e);
108             throw new RestconfDocumentedException("Transaction failed", e);
109         } catch (ExecutionException e) {
110             dataFactory.setFailureStatus();
111             LOG.warn("Transaction({}) FAILED!", txType, e);
112
113             final Throwable cause = e.getCause();
114             if (cause instanceof DOMRpcException) {
115                 dataFactory.setResult((T) new DefaultDOMRpcResult(ImmutableList.of(
116                     RpcResultBuilder.newError(RpcError.ErrorType.RPC, "operation-failed", cause.getMessage()))));
117             } else if (cause instanceof DOMActionException) {
118                 dataFactory.setResult((T) new SimpleDOMActionResult(ImmutableList.of(
119                     RpcResultBuilder.newError(RpcError.ErrorType.RPC, "operation-failed", cause.getMessage()))));
120             } else if (cause instanceof TransactionCommitFailedException) {
121                 /* If device send some error message we want this message to get to client
122                    and not just to throw it away or override it with new generic message.
123                    We search for NetconfDocumentedException that was send from netconfSB
124                    and we create RestconfDocumentedException accordingly.
125                 */
126                 final List<Throwable> causalChain = Throwables.getCausalChain(cause);
127                 for (Throwable error : causalChain) {
128                     if (error instanceof DocumentedException) {
129                         final DocumentedException.ErrorTag errorTag = ((DocumentedException) error).getErrorTag();
130                         if (errorTag.equals(DocumentedException.ErrorTag.DATA_EXISTS)) {
131                             LOG.trace("Operation via Restconf was not executed because data at {} already exists",
132                                 path);
133                             throw new RestconfDocumentedException(e, new RestconfError(RestconfError.ErrorType.PROTOCOL,
134                                 RestconfError.ErrorTag.DATA_EXISTS, "Data already exists", path));
135                         } else if (errorTag.equals(DocumentedException.ErrorTag.DATA_MISSING)) {
136                             LOG.trace("Operation via Restconf was not executed because data at {} does not exist",
137                                 path);
138                             throw new RestconfDocumentedException(e, new RestconfError(RestconfError.ErrorType.PROTOCOL,
139                                 RestconfError.ErrorTag.DATA_MISSING, "Data does not exist", path));
140                         }
141                     }
142                     if (error instanceof NetconfDocumentedException) {
143                         throw new RestconfDocumentedException(error.getMessage(),
144                                 RestconfError.ErrorType.valueOfCaseInsensitive(
145                                         ((NetconfDocumentedException) error).getErrorType().getTypeValue()),
146                                 RestconfError.ErrorTag.valueOfCaseInsensitive(
147                                         ((NetconfDocumentedException) error).getErrorTag().getTagValue()), e);
148                     }
149                 }
150
151                 throw new RestconfDocumentedException("Transaction(" + txType + ") not committed correctly", e);
152             } else {
153                 throw new RestconfDocumentedException("Transaction failed", e);
154             }
155         } finally {
156             if (transactionChain != null) {
157                 transactionChain.close();
158             }
159         }
160     }
161 }