2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.restconf.nb.rfc8040.rests.utils;
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;
33 * Add callback for future objects and result set to the data factory.
35 final class FutureCallbackTx {
36 private static final Logger LOG = LoggerFactory.getLogger(FutureCallbackTx.class);
38 private FutureCallbackTx() {
39 throw new UnsupportedOperationException("Util class");
43 * Add callback to the future object.
45 * @param listenableFuture
48 * type of operation (READ, POST, PUT, DELETE)
50 * factory setting result
51 * @throws RestconfDocumentedException
52 * if the Future throws an exception
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);
61 * Add callback to the future object and close transaction chain.
63 * @param listenableFuture
66 * type of operation (READ, POST, PUT, DELETE)
68 * factory setting result
69 * @param transactionChain
71 * @throws RestconfDocumentedException
72 * if the Future throws an exception
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);
83 * Add callback to the future object and close transaction chain.
85 * @param listenableFuture
88 * type of operation (READ, POST, PUT, DELETE)
90 * factory setting result
91 * @param transactionChain
94 * unique identifier of a particular node instance in the data tree.
95 * @throws RestconfDocumentedException
96 * if the Future throws an exception
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 {
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);
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.
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",
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",
138 throw new RestconfDocumentedException(e, new RestconfError(RestconfError.ErrorType.PROTOCOL,
139 RestconfError.ErrorTag.DATA_MISSING, "Data does not exist", path));
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);
151 throw new RestconfDocumentedException("Transaction(" + txType + ") not committed correctly", e);
153 throw new RestconfDocumentedException("Transaction failed", e);
156 if (transactionChain != null) {
157 transactionChain.close();