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