* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
+
package org.opendaylight.restconf;
import com.google.common.base.Preconditions;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
-import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
};
private ListenerRegistration<SchemaContextListener> listenerRegistration;
+ private static TransactionChainHandler transactionChainHandler;
private static DOMDataBroker dataBroker;
- private static DOMTransactionChain transactionChain;
@Override
public void onSessionInitiated(final ProviderSession session) {
final DOMMountPointServiceHandler domMountPointServiceHandler = new DOMMountPointServiceHandler(
session.getService(DOMMountPointService.class));
- dataBroker = session.getService(DOMDataBroker.class);
- final DOMDataBrokerHandler brokerHandler = new DOMDataBrokerHandler(dataBroker);
+ RestConnectorProvider.dataBroker = session.getService(DOMDataBroker.class);
+ final DOMDataBrokerHandler brokerHandler = new DOMDataBrokerHandler(RestConnectorProvider.dataBroker);
- transactionChain = dataBroker.createTransactionChain(transactionListener);
- final TransactionChainHandler transactionChainHandler = new TransactionChainHandler(transactionChain);
+ RestConnectorProvider.transactionChainHandler = new TransactionChainHandler(RestConnectorProvider.dataBroker
+ .createTransactionChain(RestConnectorProvider.transactionListener));
final DOMRpcService rpcService = session.getService(DOMRpcService.class);
final RpcServiceHandler rpcServiceHandler = new RpcServiceHandler(rpcService);
- wrapperServices.setHandlers(schemaCtxHandler, domMountPointServiceHandler, transactionChainHandler,
- brokerHandler, rpcServiceHandler);
+ wrapperServices.setHandlers(schemaCtxHandler, domMountPointServiceHandler,
+ RestConnectorProvider.transactionChainHandler, brokerHandler, rpcServiceHandler);
}
/**
- * After {@link TransactionChain} failed, this is creating new transaction
- * with listener.
+ * After {@link TransactionChain} failed, this updates {@link TransactionChainHandler} with new transaction chain.
*
* @param chain
* - old {@link TransactionChain}
*/
public static void resetTransactionChainForAdapaters(final TransactionChain<?, ?> chain) {
- LOG.trace("Resetting TransactionChain({}) to {}", chain, transactionChain);
+ LOG.trace("Resetting TransactionChain({})", chain);
chain.close();
- transactionChain = Preconditions.checkNotNull(dataBroker).createTransactionChain(transactionListener);
+ RestConnectorProvider.transactionChainHandler.update(
+ Preconditions.checkNotNull(RestConnectorProvider.dataBroker).createTransactionChain(
+ RestConnectorProvider.transactionListener)
+ );
}
@Override
@Override
public void close() throws Exception {
+ // close registration
if (this.listenerRegistration != null) {
this.listenerRegistration.close();
}
- if (transactionChain != null) {
- transactionChain.close();
+
+ // close transaction chain
+ if (RestConnectorProvider.transactionChainHandler != null) {
+ RestConnectorProvider.transactionChainHandler.get().close();
}
}
}
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.restconf.handlers;
-import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+package org.opendaylight.restconf.handlers;
/**
* Handler for handling object prepared by provider for Restconf services
/**
* Get prepared object
*
- * @return {@link DOMTransactionChain}
+ * @return T
*/
T get();
+
+ /**
+ * Update object
+ *
+ * @param object
+ * - new object to update old object
+ */
+ default void update(T object) {}
}
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
+
package org.opendaylight.restconf.handlers;
import com.google.common.base.Preconditions;
*/
public class TransactionChainHandler implements Handler<DOMTransactionChain> {
- private final DOMTransactionChain transactionChain;
+ private DOMTransactionChain transactionChain;
/**
* Prepare transaction chain service for Restconf services
this.transactionChain = transactionChain;
}
+ @Override
+ public void update(final DOMTransactionChain transactionChain) {
+ Preconditions.checkNotNull(transactionChain);
+ this.transactionChain = transactionChain;
+ }
+
@Override
public DOMTransactionChain get() {
return this.transactionChain;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
*/
public static Response deleteData(final TransactionVarsWrapper transactionNode) {
final CheckedFuture<Void, TransactionCommitFailedException> future = submitData(
- transactionNode.getTransactionChain().newReadWriteTransaction(),
+ transactionNode.getTransactionChain(), transactionNode.getTransactionChain().newReadWriteTransaction(),
transactionNode.getInstanceIdentifier().getInstanceIdentifier());
final ResponseFactory response = new ResponseFactory();
FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.DeleteData.DELETE_TX_TYPE, response);
/**
* Delete data via transaction. Return error if data to delete does not exist.
*
+ * @param transactionChain
+ * - transaction chain
* @param readWriteTx
* - read and write transaction
* @param path
* @return {@link CheckedFuture}
*/
private static CheckedFuture<Void, TransactionCommitFailedException> submitData(
- final DOMDataReadWriteTransaction readWriteTx, final YangInstanceIdentifier path) {
- TransactionUtil.checkItemExists(readWriteTx, LogicalDatastoreType.CONFIGURATION, path,
+ final DOMTransactionChain transactionChain, final DOMDataReadWriteTransaction readWriteTx,
+ final YangInstanceIdentifier path) {
+ TransactionUtil.checkItemExists(transactionChain, readWriteTx, LogicalDatastoreType.CONFIGURATION, path,
RestconfDataServiceConstant.DeleteData.DELETE_TX_TYPE);
readWriteTx.delete(LogicalDatastoreType.CONFIGURATION, path);
return readWriteTx.submit();
import java.util.ArrayList;
import java.util.List;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
final YangInstanceIdentifier path,
final DOMDataReadWriteTransaction readWriteTransaction) {
LOG.trace("Delete {} within Restconf PATCH: {}", dataStore.name(), path);
- TransactionUtil.checkItemExists(readWriteTransaction, dataStore, path, PatchData.PATCH_TX_TYPE);
+ checkItemExistsWithinTransaction(readWriteTransaction, dataStore, path);
readWriteTransaction.delete(dataStore, path);
}
final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
if (errorIfExists) {
- TransactionUtil.checkItemDoesNotExists(
- rWTransaction, dataStore, childPath, PatchData.PATCH_TX_TYPE);
+ checkItemDoesNotExistsWithinTransaction(rWTransaction, dataStore, childPath);
}
rWTransaction.put(dataStore, childPath, child);
}
} else {
if (errorIfExists) {
- TransactionUtil.checkItemDoesNotExists(
- rWTransaction, dataStore, path, PatchData.PATCH_TX_TYPE);
+ checkItemDoesNotExistsWithinTransaction(rWTransaction, dataStore, path);
}
TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
rWTransaction.put(dataStore, path, payload);
}
}
+
+ /**
+ * Check if items already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
+ * data does NOT already exists.
+ * @param rWTransaction Transaction
+ * @param store Datastore
+ * @param path Path to be checked
+ */
+ public static void checkItemExistsWithinTransaction(final DOMDataReadWriteTransaction rWTransaction,
+ final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ final CheckedFuture<Boolean, ReadFailedException> future = rWTransaction.exists(store, path);
+ final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
+
+ FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
+
+ if (!response.result) {
+ final String errMsg = "Operation via Restconf was not executed because data does not exist";
+ LOG.trace("{}:{}", errMsg, path);
+ throw new RestconfDocumentedException(
+ "Data does not exist", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, path);
+ }
+ }
+
+ /**
+ * Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
+ * data already exists.
+ * @param rWTransaction Transaction
+ * @param store Datastore
+ * @param path Path to be checked
+ */
+ public static void checkItemDoesNotExistsWithinTransaction(final DOMDataReadWriteTransaction rWTransaction,
+ final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ final CheckedFuture<Boolean, ReadFailedException> future = rWTransaction.exists(store, path);
+ final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
+
+ FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
+
+ if (response.result) {
+ final String errMsg = "Operation via Restconf was not executed because data already exists";
+ LOG.trace("{}:{}", errMsg, path);
+ throw new RestconfDocumentedException(
+ "Data already exists", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, path);
+ }
+ }
}
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
import org.opendaylight.restconf.common.references.SchemaContextRef;
import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
private static CheckedFuture<Void, TransactionCommitFailedException> submitData(final YangInstanceIdentifier path,
final NormalizedNode<?, ?> data, final TransactionVarsWrapper transactionNode,
final SchemaContext schemaContext) {
- final DOMDataReadWriteTransaction transaction = transactionNode.getTransactionChain().newReadWriteTransaction();
+ final DOMTransactionChain transactionChain = transactionNode.getTransactionChain();
+ final DOMDataReadWriteTransaction transaction = transactionChain.newReadWriteTransaction();
final NormalizedNode<?, ?> node = ImmutableNodes.fromInstanceId(schemaContext, path);
transaction.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(node.getIdentifier()), node);
TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
if (data instanceof MapNode) {
for (final MapEntryNode child : ((MapNode) data).getValue()) {
- putChild(child, transaction, path);
+ putChild(child, transactionChain, transaction, path);
}
} else if (data instanceof AugmentationNode) {
for (final DataContainerChild<? extends PathArgument, ?> child : ((AugmentationNode) data).getValue()) {
- putChild(child, transaction, path);
+ putChild(child, transactionChain, transaction, path);
}
} else if (data instanceof ChoiceNode) {
for (final DataContainerChild<? extends PathArgument, ?> child : ((ChoiceNode) data).getValue()) {
- putChild(child, transaction, path);
+ putChild(child, transactionChain, transaction, path);
}
} else if (data instanceof LeafSetNode<?>) {
for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) data).getValue()) {
- putChild(child, transaction, path);
+ putChild(child, transactionChain, transaction, path);
}
} else if (data instanceof ContainerNode) {
for (final DataContainerChild<? extends PathArgument, ?> child : ((ContainerNode) data).getValue()) {
- putChild(child, transaction, path);
+ putChild(child, transactionChain, transaction, path);
}
}
+
return transaction.submit();
}
*
* @param child
* - data
+ * @param transactionChain
+ * - transaction chain
* @param readWriteTx
* - transaction
* @param path
* - path to data
*/
- private static void putChild(final NormalizedNode<?, ?> child, final DOMDataReadWriteTransaction readWriteTx,
- final YangInstanceIdentifier path) {
+ private static void putChild(final NormalizedNode<?, ?> child, final DOMTransactionChain transactionChain,
+ final DOMDataReadWriteTransaction readWriteTx, final YangInstanceIdentifier path) {
final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
- TransactionUtil.checkItemDoesNotExists(readWriteTx, LogicalDatastoreType.CONFIGURATION, childPath,
+ TransactionUtil.checkItemDoesNotExists(
+ transactionChain, readWriteTx, LogicalDatastoreType.CONFIGURATION, childPath,
RestconfDataServiceConstant.PostData.POST_TX_TYPE);
readWriteTx.put(LogicalDatastoreType.CONFIGURATION, childPath, child);
}
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
+
package org.opendaylight.restconf.restful.utils;
import com.google.common.base.Preconditions;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.restconf.RestConnectorProvider;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
/**
* Check if items already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
* data does NOT already exists.
+ * @param transactionChain Transaction chain
* @param rWTransaction Transaction
* @param store Datastore
* @param path Path to be checked
* @param operationType Type of operation (READ, POST, PUT, DELETE...)
*/
- public static void checkItemExists(final DOMDataReadWriteTransaction rWTransaction,
+ public static void checkItemExists(final DOMTransactionChain transactionChain,
+ final DOMDataReadWriteTransaction rWTransaction,
final LogicalDatastoreType store, final YangInstanceIdentifier path,
final String operationType) {
final CheckedFuture<Boolean, ReadFailedException> future = rWTransaction.exists(store, path);
FutureCallbackTx.addCallback(future, operationType, response);
if (!response.result) {
+ // close transaction and reset transaction chain
+ rWTransaction.cancel();
+ RestConnectorProvider.resetTransactionChainForAdapaters(transactionChain);
+
+ // throw error
final String errMsg = "Operation via Restconf was not executed because data does not exist";
LOG.trace("{}:{}", errMsg, path);
throw new RestconfDocumentedException(
/**
* Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
* data already exists.
+ * @param transactionChain Transaction chain
* @param rWTransaction Transaction
* @param store Datastore
* @param path Path to be checked
* @param operationType Type of operation (READ, POST, PUT, DELETE...)
*/
- public static void checkItemDoesNotExists(final DOMDataReadWriteTransaction rWTransaction,
+ public static void checkItemDoesNotExists(final DOMTransactionChain transactionChain,
+ final DOMDataReadWriteTransaction rWTransaction,
final LogicalDatastoreType store, final YangInstanceIdentifier path,
final String operationType) {
final CheckedFuture<Boolean, ReadFailedException> future = rWTransaction.exists(store, path);
FutureCallbackTx.addCallback(future, operationType, response);
if (response.result) {
+ // close transaction and reset transaction chain
+ rWTransaction.cancel();
+ RestConnectorProvider.resetTransactionChainForAdapaters(transactionChain);
+
+ // throw error
final String errMsg = "Operation via Restconf was not executed because data already exists";
LOG.trace("{}:{}", errMsg, path);
throw new RestconfDocumentedException(
// verify interaction
verify(this.mockRegistration, times(1)).close();
+ verify(mockTransactionChain, times(1)).close();
}
}
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
import com.google.common.util.concurrent.Futures;
+import java.lang.reflect.Field;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.restconf.RestConnectorProvider;
+import org.opendaylight.restconf.handlers.TransactionChainHandler;
import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@Mock
private DOMDataReadWriteTransaction readWrite;
+ // Fields used when delete operation fails to reset transaction chain
+ private static Field handler;
+ private static Field broker;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ DeleteDataTransactionUtilTest.handler = RestConnectorProvider.class.getDeclaredField("transactionChainHandler");
+ DeleteDataTransactionUtilTest.broker = RestConnectorProvider.class.getDeclaredField("dataBroker");
+
+ DeleteDataTransactionUtilTest.handler.setAccessible(true);
+ DeleteDataTransactionUtilTest.handler.set(RestConnectorProvider.class, mock(TransactionChainHandler.class));
+
+ DeleteDataTransactionUtilTest.broker.setAccessible(true);
+ DeleteDataTransactionUtilTest.broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
+ }
+
+ @AfterClass
+ public static void clean() throws Exception {
+ DeleteDataTransactionUtilTest.handler.set(RestConnectorProvider.class, null);
+ DeleteDataTransactionUtilTest.handler.setAccessible(false);
+
+ DeleteDataTransactionUtilTest.broker.set(RestConnectorProvider.class, null);
+ DeleteDataTransactionUtilTest.broker.setAccessible(false);
+ }
+
@Before
public void init() throws Exception {
MockitoAnnotations.initMocks(this);