Teach RFC8040 restconf about actions
[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.opendaylight.mdsal.common.api.TransactionCommitFailedException;
16 import org.opendaylight.mdsal.dom.api.DOMActionException;
17 import org.opendaylight.mdsal.dom.api.DOMRpcException;
18 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
19 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
20 import org.opendaylight.netconf.api.NetconfDocumentedException;
21 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
22 import org.opendaylight.restconf.common.errors.RestconfError;
23 import org.opendaylight.yangtools.yang.common.RpcError;
24 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 /**
29  * Add callback for future objects and result set to the data factory.
30  */
31 final class FutureCallbackTx {
32
33     private static final Logger LOG = LoggerFactory.getLogger(FutureCallbackTx.class);
34
35     private FutureCallbackTx() {
36         throw new UnsupportedOperationException("Util class");
37     }
38
39     /**
40      * Add callback to the future object.
41      *
42      * @param listenableFuture
43      *             future object
44      * @param txType
45      *             type of operation (READ, POST, PUT, DELETE)
46      * @param dataFactory
47      *             factory setting result
48      * @throws RestconfDocumentedException
49      *             if the Future throws an exception
50      */
51     @SuppressWarnings("checkstyle:IllegalCatch")
52     static <T> void addCallback(final ListenableFuture<T> listenableFuture, final String txType,
53             final FutureDataFactory<? super T> dataFactory) throws RestconfDocumentedException {
54
55         try {
56             final T result = listenableFuture.get();
57             dataFactory.setResult(result);
58             LOG.trace("Transaction({}) SUCCESSFUL", txType);
59         } catch (InterruptedException e) {
60             dataFactory.setFailureStatus();
61             LOG.warn("Transaction({}) FAILED!", txType, e);
62             throw new RestconfDocumentedException("Transaction failed", e);
63         } catch (ExecutionException e) {
64             dataFactory.setFailureStatus();
65             LOG.warn("Transaction({}) FAILED!", txType, e);
66
67             final Throwable cause = e.getCause();
68             if (cause instanceof DOMRpcException) {
69                 dataFactory.setResult((T) new DefaultDOMRpcResult(ImmutableList.of(
70                     RpcResultBuilder.newError(RpcError.ErrorType.RPC, "operation-failed", cause.getMessage()))));
71             } else if (cause instanceof DOMActionException) {
72                 dataFactory.setResult((T) new SimpleDOMActionResult(ImmutableList.of(
73                     RpcResultBuilder.newError(RpcError.ErrorType.RPC, "operation-failed", cause.getMessage()))));
74             } else if (cause instanceof TransactionCommitFailedException) {
75                 /* If device send some error message we want this message to get to client
76                    and not just to throw it away or override it with new generic message.
77                    We search for NetconfDocumentedException that was send from netconfSB
78                    and we create RestconfDocumentedException accordingly.
79                 */
80                 final List<Throwable> causalChain = Throwables.getCausalChain(cause);
81                 for (Throwable error : causalChain) {
82                     if (error instanceof NetconfDocumentedException) {
83                         throw new RestconfDocumentedException(error.getMessage(),
84                                 RestconfError.ErrorType.valueOfCaseInsensitive(
85                                         ((NetconfDocumentedException) error).getErrorType().getTypeValue()),
86                                 RestconfError.ErrorTag.valueOfCaseInsensitive(
87                                         ((NetconfDocumentedException) error).getErrorTag().getTagValue()), e);
88                     }
89                 }
90
91                 throw new RestconfDocumentedException("Transaction(" + txType + ") not committed correctly", e);
92             } else {
93                 throw new RestconfDocumentedException("Transaction failed", e);
94             }
95         }
96     }
97 }