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