Reduce exception guard
[netconf.git] / restconf / restconf-nb / 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.util.concurrent.ListenableFuture;
12 import java.util.List;
13 import java.util.concurrent.ExecutionException;
14 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
15 import org.opendaylight.netconf.api.DocumentedException;
16 import org.opendaylight.netconf.api.NetconfDocumentedException;
17 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
18 import org.opendaylight.restconf.common.errors.RestconfError;
19 import org.opendaylight.yangtools.yang.common.ErrorTag;
20 import org.opendaylight.yangtools.yang.common.ErrorType;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26  * Add callback for future objects and result set to the data factory.
27  */
28 final class FutureCallbackTx {
29     private static final Logger LOG = LoggerFactory.getLogger(FutureCallbackTx.class);
30
31     private FutureCallbackTx() {
32         // Hidden on purpose
33     }
34
35     /**
36      * Add callback to the future object.
37      *
38      * @param listenableFuture
39      *             future object
40      * @param txType
41      *             type of operation (READ, POST, PUT, DELETE)
42      * @param dataFactory
43      *             factory setting result
44      * @throws RestconfDocumentedException
45      *             if the Future throws an exception
46      */
47     // FIXME: this is a *synchronous operation* and has to die
48     static <T> void addCallback(final ListenableFuture<T> listenableFuture, final String txType,
49                                 final FutureDataFactory<? super T> dataFactory) throws RestconfDocumentedException {
50         addCallback(listenableFuture, txType, dataFactory, null);
51     }
52
53     /**
54      * Add callback to the future object and close transaction chain.
55      *
56      * @param listenableFuture
57      *             future object
58      * @param txType
59      *             type of operation (READ, POST, PUT, DELETE)
60      * @param dataFactory
61      *             factory setting result
62      * @param path unique identifier of a particular node instance in the data tree
63      * @throws RestconfDocumentedException
64      *             if the Future throws an exception
65      */
66     // FIXME: this is a *synchronous operation* and has to die
67     static <T> void addCallback(final ListenableFuture<T> listenableFuture, final String txType,
68             final FutureDataFactory<? super T> dataFactory, final YangInstanceIdentifier path)
69                 throws RestconfDocumentedException {
70         try {
71             final T result = listenableFuture.get();
72             dataFactory.setResult(result);
73             LOG.trace("Transaction({}) SUCCESSFUL", txType);
74         } catch (InterruptedException e) {
75             dataFactory.setFailureStatus();
76             LOG.warn("Transaction({}) FAILED!", txType, e);
77             throw new RestconfDocumentedException("Transaction failed", e);
78         } catch (ExecutionException e) {
79             dataFactory.setFailureStatus();
80             LOG.warn("Transaction({}) FAILED!", txType, e);
81
82             final Throwable cause = e.getCause();
83             if (cause instanceof TransactionCommitFailedException) {
84                 /* If device send some error message we want this message to get to client
85                    and not just to throw it away or override it with new generic message.
86                    We search for NetconfDocumentedException that was send from netconfSB
87                    and we create RestconfDocumentedException accordingly.
88                 */
89                 final List<Throwable> causalChain = Throwables.getCausalChain(cause);
90                 for (Throwable error : causalChain) {
91                     if (error instanceof DocumentedException) {
92                         final ErrorTag errorTag = ((DocumentedException) error).getErrorTag();
93                         if (errorTag.equals(ErrorTag.DATA_EXISTS)) {
94                             LOG.trace("Operation via Restconf was not executed because data at {} already exists",
95                                 path);
96                             throw new RestconfDocumentedException(e, new RestconfError(ErrorType.PROTOCOL,
97                                 ErrorTag.DATA_EXISTS, "Data already exists", path));
98                         } else if (errorTag.equals(ErrorTag.DATA_MISSING)) {
99                             LOG.trace("Operation via Restconf was not executed because data at {} does not exist",
100                                 path);
101                             throw new RestconfDocumentedException(e, new RestconfError(ErrorType.PROTOCOL,
102                                 ErrorTag.DATA_MISSING, "Data does not exist", path));
103                         }
104                     }
105                     if (error instanceof NetconfDocumentedException) {
106                         throw new RestconfDocumentedException(error.getMessage(),
107                                 ((NetconfDocumentedException) error).getErrorType(),
108                                 ((NetconfDocumentedException) error).getErrorTag(), e);
109                     }
110                 }
111
112                 throw new RestconfDocumentedException("Transaction(" + txType + ") not committed correctly", e);
113             } else {
114                 throw new RestconfDocumentedException("Transaction failed", e);
115             }
116         }
117     }
118 }