From: Ed Warnicke Date: Wed, 6 Aug 2014 19:21:31 +0000 (+0000) Subject: Merge "Remove old dependencies" X-Git-Tag: release/helium~338 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=a7730b0b1b7d86f270e7d2e1aff0e91e86cff125;hp=125644e6e71a7b47ebcfd85ee3d5671d69468742 Merge "Remove old dependencies" --- diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java index 2d81b6022d..e632e6336a 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java @@ -97,6 +97,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator, DataObject> toBinding( + InstanceIdentifier path, final Map> normalized) { Map, DataObject> newMap = new HashMap<>(); @@ -107,6 +108,11 @@ public abstract class AbstractForwardedDataBroker implements Delegator, DataObject> binding = potential.get(); newMap.put(binding.getKey(), binding.getValue()); + } else if (entry.getKey().getLastPathArgument() instanceof YangInstanceIdentifier.AugmentationIdentifier) { + DataObject bindingDataObject = getCodec().toBinding(path, entry.getValue()); + if (bindingDataObject != null) { + newMap.put(path, bindingDataObject); + } } } catch (DeserializationException e) { LOG.warn("Failed to transform {}, omitting it", entry, e); @@ -148,7 +154,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator> toBinding( + protected Set> toBinding(InstanceIdentifier path, final Set normalized) { Set> hashSet = new HashSet<>(); for (YangInstanceIdentifier normalizedPath : normalized) { @@ -157,6 +163,8 @@ public abstract class AbstractForwardedDataBroker implements Delegator binding = potential.get(); hashSet.add(binding); + } else if (normalizedPath.getLastPathArgument() instanceof YangInstanceIdentifier.AugmentationIdentifier) { + hashSet.add(path); } } catch (DeserializationException e) { LOG.warn("Failed to transform {}, omitting it", normalizedPath, e); @@ -219,7 +227,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator, DataObject> getCreatedData() { if (createdCache == null) { - createdCache = Collections.unmodifiableMap(toBinding(domEvent.getCreatedData())); + createdCache = Collections.unmodifiableMap(toBinding(path, domEvent.getCreatedData())); } return createdCache; } @@ -227,7 +235,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator, DataObject> getUpdatedData() { if (updatedCache == null) { - updatedCache = Collections.unmodifiableMap(toBinding(domEvent.getUpdatedData())); + updatedCache = Collections.unmodifiableMap(toBinding(path, domEvent.getUpdatedData())); } return updatedCache; @@ -236,7 +244,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator> getRemovedPaths() { if (removedCache == null) { - removedCache = Collections.unmodifiableSet(toBinding(domEvent.getRemovedPaths())); + removedCache = Collections.unmodifiableSet(toBinding(path, domEvent.getRemovedPaths())); } return removedCache; } @@ -244,7 +252,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator, DataObject> getOriginalData() { if (originalCache == null) { - originalCache = Collections.unmodifiableMap(toBinding(domEvent.getOriginalData())); + originalCache = Collections.unmodifiableMap(toBinding(path, domEvent.getOriginalData())); } return originalCache; diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1418AugmentationTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1418AugmentationTest.java new file mode 100644 index 0000000000..6b5c825b83 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1418AugmentationTest.java @@ -0,0 +1,135 @@ +package org.opendaylight.controller.md.sal.binding.impl.test; + +import org.junit.Test; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.binding.test.AbstractDataChangeListenerTest; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeLeafOnlyUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUses; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import static org.junit.Assert.assertTrue; +import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertContains; +import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertEmpty; +import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top; +import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY; +import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList; +import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.leafOnlyUsesAugment; +import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.complexUsesAugment; + +public class Bug1418AugmentationTest extends AbstractDataChangeListenerTest{ + private static final InstanceIdentifier TOP = InstanceIdentifier.create(Top.class); + private static final InstanceIdentifier TOP_FOO = TOP.child(TopLevelList.class, TOP_FOO_KEY); + private static final InstanceIdentifier SIMPLE_AUGMENT = + TOP.child(TopLevelList.class, TOP_FOO_KEY).augmentation(TreeLeafOnlyUsesAugment.class); + private static final InstanceIdentifier COMPLEX_AUGMENT = + TOP.child(TopLevelList.class, TOP_FOO_KEY).augmentation(TreeComplexUsesAugment.class); + private static final ListViaUsesKey LIST_VIA_USES_KEY = + new ListViaUsesKey("list key"); + private static final ListViaUsesKey LIST_VIA_USES_KEY_MOD = + new ListViaUsesKey("list key modified"); + + @Test + public void leafOnlyAugmentationCreatedTest() { + TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, + AsyncDataBroker.DataChangeScope.SUBTREE); + WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction(); + writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top()); + writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY))); + writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf")); + assertCommit(writeTx.submit()); + assertTrue(listener.hasEvent()); + AsyncDataChangeEvent, DataObject> event = listener.event(); + assertContains(event.getCreatedData(), SIMPLE_AUGMENT); + assertEmpty(event.getUpdatedData()); + assertEmpty(event.getOriginalData()); + assertEmpty(event.getRemovedPaths()); + } + + @Test + public void leafOnlyAugmentationUpdatedTest() { + WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction(); + writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top()); + writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY))); + writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf")); + assertCommit(writeTx.submit()); + TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, + AsyncDataBroker.DataChangeScope.SUBTREE); + writeTx = getDataBroker().newWriteOnlyTransaction(); + writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf changed")); + assertCommit(writeTx.submit()); + assertTrue(listener.hasEvent()); + AsyncDataChangeEvent, DataObject> event = listener.event(); + assertContains(event.getUpdatedData(), SIMPLE_AUGMENT); + assertContains(event.getOriginalData(), SIMPLE_AUGMENT); + assertEmpty(event.getCreatedData()); + assertEmpty(event.getRemovedPaths()); + } + + @Test + public void leafOnlyAugmentationDeletedTest() { + WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction(); + writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top()); + writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY))); + writeTx.put(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, leafOnlyUsesAugment("test leaf")); + assertCommit(writeTx.submit()); + TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT, + AsyncDataBroker.DataChangeScope.SUBTREE); + writeTx = getDataBroker().newWriteOnlyTransaction(); + writeTx.delete(LogicalDatastoreType.CONFIGURATION, SIMPLE_AUGMENT); + assertCommit(writeTx.submit()); + assertTrue(listener.hasEvent()); + AsyncDataChangeEvent, DataObject> event = listener.event(); + assertContains(event.getRemovedPaths(), SIMPLE_AUGMENT); + assertContains(event.getOriginalData(), SIMPLE_AUGMENT); + assertEmpty(event.getCreatedData()); + assertEmpty(event.getUpdatedData()); + } + + @Test + public void complexAugmentationCreatedTest() { + TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT, + AsyncDataBroker.DataChangeScope.SUBTREE); + WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction(); + writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top()); + writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY))); + writeTx.put(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugment(LIST_VIA_USES_KEY)); + assertCommit(writeTx.submit()); + assertTrue(listener.hasEvent()); + AsyncDataChangeEvent, DataObject> event = listener.event(); + assertContains(event.getCreatedData(), COMPLEX_AUGMENT); + assertContains(event.getCreatedData(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY)); + assertEmpty(event.getUpdatedData()); + assertEmpty(event.getOriginalData()); + assertEmpty(event.getRemovedPaths()); + } + + @Test + public void complexAugmentationUpdatedTest() { + WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction(); + writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP, top()); + writeTx.put(LogicalDatastoreType.CONFIGURATION, TOP_FOO, topLevelList(new TopLevelListKey(TOP_FOO_KEY))); + writeTx.put(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugment(LIST_VIA_USES_KEY)); + assertCommit(writeTx.submit()); + TestListener listener = createListener(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT, + AsyncDataBroker.DataChangeScope.SUBTREE); + writeTx = getDataBroker().newWriteOnlyTransaction(); + writeTx.put(LogicalDatastoreType.CONFIGURATION, COMPLEX_AUGMENT, complexUsesAugment(LIST_VIA_USES_KEY_MOD)); + assertCommit(writeTx.submit()); + assertTrue(listener.hasEvent()); + AsyncDataChangeEvent, DataObject> event = listener.event(); + assertContains(event.getUpdatedData(), COMPLEX_AUGMENT); + assertContains(event.getCreatedData(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY_MOD)); + assertContains(event.getRemovedPaths(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY)); + assertContains(event.getOriginalData(), COMPLEX_AUGMENT); + assertContains(event.getOriginalData(), COMPLEX_AUGMENT.child(ListViaUses.class, LIST_VIA_USES_KEY)); + } +} diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitDeadlockException.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitDeadlockException.java new file mode 100644 index 0000000000..60313bf109 --- /dev/null +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionCommitDeadlockException.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * 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.controller.md.sal.common.api.data; + +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; + +import com.google.common.base.Function; + +/** + * A type of TransactionCommitFailedException that indicates a situation that would result in a + * threading deadlock. This can occur if a caller that submits a write transaction tries to perform + * a blocking call via one of the get methods on the returned ListenableFuture. Callers + * should process the commit result asynchronously (via Futures#addCallback) to ensure deadlock + * won't occur. + * + * @author Thomas Pantelis + */ +public class TransactionCommitDeadlockException extends TransactionCommitFailedException { + + private static final long serialVersionUID = 1L; + + private static final String DEADLOCK_MESSAGE = + "An attempt to block on a ListenableFuture via a get method from a write " + + "transaction submit was detected that would result in deadlock. The commit " + + "result must be obtained asynchronously, e.g. via Futures#addCallback, to avoid deadlock."; + + public static Function DEADLOCK_EXECUTOR_FUNCTION = new Function() { + @Override + public Exception apply(Void notUsed) { + return new TransactionCommitDeadlockException( DEADLOCK_MESSAGE, + RpcResultBuilder.newError(ErrorType.APPLICATION, "lock-denied", DEADLOCK_MESSAGE)); + } + }; + + public TransactionCommitDeadlockException(String message, final RpcError... errors) { + super(message, errors); + } +} diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java index 667c0fc282..22dad6af23 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java @@ -10,9 +10,11 @@ package org.opendaylight.controller.config.yang.md.sal.dom.impl; import java.util.concurrent.Executors; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException; import org.opendaylight.controller.md.sal.dom.broker.impl.DOMDataBrokerImpl; import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; import org.opendaylight.controller.sal.core.spi.data.DOMStore; +import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ListeningExecutorService; @@ -64,7 +66,9 @@ public final class DomInmemoryDataBrokerModule extends . builder().put(LogicalDatastoreType.OPERATIONAL, operStore) .put(LogicalDatastoreType.CONFIGURATION, configStore).build(); - DOMDataBrokerImpl newDataBroker = new DOMDataBrokerImpl(datastores, MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor())); + DOMDataBrokerImpl newDataBroker = new DOMDataBrokerImpl(datastores, + new DeadlockDetectingListeningExecutorService(Executors.newSingleThreadExecutor(), + TransactionCommitDeadlockException.DEADLOCK_EXECUTOR_FUNCTION)); return newDataBroker; } diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java index b006ca94e5..0bb16a39b9 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java +++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/broker/impl/DOMBrokerTest.java @@ -3,26 +3,40 @@ package org.opendaylight.controller.md.sal.dom.broker.impl; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION; import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitDeadlockException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction; 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.store.impl.InMemoryDOMDataStore; import org.opendaylight.controller.md.sal.dom.store.impl.TestModel; import org.opendaylight.controller.sal.core.spi.data.DOMStore; +import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; @@ -31,6 +45,7 @@ public class DOMBrokerTest { private SchemaContext schemaContext; private DOMDataBrokerImpl domBroker; + private ListeningExecutorService executor; @Before public void setupStore() { @@ -46,11 +61,19 @@ public class DOMBrokerTest { .put(OPERATIONAL, operStore) // .build(); - ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + executor = new DeadlockDetectingListeningExecutorService(Executors.newSingleThreadExecutor(), + TransactionCommitDeadlockException.DEADLOCK_EXECUTOR_FUNCTION); domBroker = new DOMDataBrokerImpl(stores, executor); } - @Test + @After + public void tearDown() { + if( executor != null ) { + executor.shutdownNow(); + } + } + + @Test(timeout=10000) public void testTransactionIsolation() throws InterruptedException, ExecutionException { assertNotNull(domBroker); @@ -86,7 +109,7 @@ public class DOMBrokerTest { assertFalse(readTxContainer.get().isPresent()); } - @Test + @Test(timeout=10000) public void testTransactionCommit() throws InterruptedException, ExecutionException { DOMDataReadWriteTransaction writeTx = domBroker.newReadWriteTransaction(); @@ -114,6 +137,173 @@ public class DOMBrokerTest { assertTrue(afterCommitRead.isPresent()); } + /** + * Tests a simple DataChangeListener notification after a write. + */ + @Test + public void testDataChangeListener() throws Throwable { + + final NormalizedNode testNode = ImmutableNodes.containerNode( TestModel.TEST_QNAME ); + + TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener(); + + domBroker.registerDataChangeListener( OPERATIONAL, TestModel.TEST_PATH, + dcListener, DataChangeScope.BASE ); + + final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction(); + assertNotNull( writeTx ); + + writeTx.put( OPERATIONAL, TestModel.TEST_PATH, testNode ); + + AtomicReference caughtEx = submitTxAsync( writeTx ); + + dcListener.waitForChange(); + + if( caughtEx.get() != null ) { + throw caughtEx.get(); + } + + NormalizedNode actualNode = dcListener.change.getCreatedData().get( TestModel.TEST_PATH ); + assertEquals( "Created node", testNode, actualNode ); + } + + /** + * Tests a DataChangeListener that does an async submit of a write Tx in its onDataChanged method. + * This should succeed without deadlock. + */ + @Test + public void testDataChangeListenerDoingAsyncWriteTxSubmit() throws Throwable { + + final AtomicReference caughtCommitEx = new AtomicReference<>(); + final CountDownLatch commitCompletedLatch = new CountDownLatch( 1 ); + + TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener() { + @Override + public void onDataChanged( AsyncDataChangeEvent> change ) { + + DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction(); + writeTx.put( OPERATIONAL, TestModel.TEST2_PATH, + ImmutableNodes.containerNode( TestModel.TEST2_QNAME ) ); + Futures.addCallback( writeTx.submit(), new FutureCallback() { + @Override + public void onSuccess( Void result ) { + commitCompletedLatch.countDown(); + } + + @Override + public void onFailure( Throwable t ) { + caughtCommitEx.set( t ); + commitCompletedLatch.countDown(); + } + } ); + + super.onDataChanged( change ); + } + }; + + domBroker.registerDataChangeListener( OPERATIONAL, TestModel.TEST_PATH, + dcListener, DataChangeScope.BASE ); + + final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction(); + assertNotNull( writeTx ); + + writeTx.put( OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) ); + + AtomicReference caughtEx = submitTxAsync( writeTx ); + + dcListener.waitForChange(); + + if( caughtEx.get() != null ) { + throw caughtEx.get(); + } + + assertTrue( "Commit Future was not invoked", commitCompletedLatch.await( 5, TimeUnit.SECONDS ) ); + + if( caughtCommitEx.get() != null ) { + throw caughtCommitEx.get(); + } + } + + /** + * Tests a DataChangeListener that does a blocking submit of a write Tx in its onDataChanged method. + * This should throw an exception and not deadlock. + */ + @Test(expected=TransactionCommitDeadlockException.class) + public void testDataChangeListenerDoingBlockingWriteTxSubmit() throws Throwable { + + final AtomicReference caughtCommitEx = new AtomicReference<>(); + + TestDOMDataChangeListener dcListener = new TestDOMDataChangeListener() { + @Override + public void onDataChanged( AsyncDataChangeEvent> change ) { + DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction(); + writeTx.put( OPERATIONAL, TestModel.TEST2_PATH, + ImmutableNodes.containerNode( TestModel.TEST2_QNAME ) ); + try { + writeTx.submit().get(); + } catch( ExecutionException e ) { + caughtCommitEx.set( e.getCause() ); + } catch( Exception e ) { + caughtCommitEx.set( e ); + } + finally { + super.onDataChanged( change ); + } + } + }; + + domBroker.registerDataChangeListener( OPERATIONAL, TestModel.TEST_PATH, + dcListener, DataChangeScope.BASE ); + + final DOMDataWriteTransaction writeTx = domBroker.newWriteOnlyTransaction(); + assertNotNull( writeTx ); + + writeTx.put( OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) ); + + AtomicReference caughtEx = submitTxAsync( writeTx ); + dcListener.waitForChange(); + if( caughtEx.get() != null ) { + throw caughtEx.get(); + } + + if( caughtCommitEx.get() != null ) { + throw caughtCommitEx.get(); + } + } + + AtomicReference submitTxAsync( final DOMDataWriteTransaction writeTx ) { + final AtomicReference caughtEx = new AtomicReference<>(); + new Thread() { + @Override + public void run() { + + try { + writeTx.submit(); + } catch( Throwable e ) { + caughtEx.set( e ); + } + } + + }.start(); + + return caughtEx; + } + + static class TestDOMDataChangeListener implements DOMDataChangeListener { + + volatile AsyncDataChangeEvent> change; + private final CountDownLatch latch = new CountDownLatch( 1 ); + + @Override + public void onDataChanged( AsyncDataChangeEvent> change ) { + this.change = change; + latch.countDown(); + } + + void waitForChange() throws InterruptedException { + assertTrue( "onDataChanged was not called", latch.await( 5, TimeUnit.SECONDS ) ); + } + } } diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java index d5ba2a2b9a..09835ec5e3 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java +++ b/opendaylight/md-sal/sal-dom-broker/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java @@ -21,6 +21,8 @@ public class TestModel { public static final QName TEST_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13", "test"); + public static final QName TEST2_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13", + "test2"); public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list"); public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list"); public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice"); @@ -30,6 +32,7 @@ public class TestModel { private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang"; public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME); + public static final YangInstanceIdentifier TEST2_PATH = YangInstanceIdentifier.of(TEST2_QNAME); public static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build(); public static final QName TWO_QNAME = QName.create(TEST_QNAME,"two"); public static final QName THREE_QNAME = QName.create(TEST_QNAME,"three"); diff --git a/opendaylight/md-sal/sal-dom-broker/src/test/resources/odl-datastore-test.yang b/opendaylight/md-sal/sal-dom-broker/src/test/resources/odl-datastore-test.yang index 17541fecab..5fbf470f09 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/test/resources/odl-datastore-test.yang +++ b/opendaylight/md-sal/sal-dom-broker/src/test/resources/odl-datastore-test.yang @@ -39,4 +39,7 @@ module odl-datastore-test { } } } + + container test2 { + } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java index ae8e1b05af..93e6a2c0e9 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java @@ -14,8 +14,6 @@ import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Splitter; import com.google.common.base.Strings; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import java.io.UnsupportedEncodingException; @@ -86,10 +84,6 @@ public class ControllerContext implements SchemaContextListener { private static final Splitter SLASH_SPLITTER = Splitter.on('/'); - private final BiMap uriToModuleName = HashBiMap. create(); - - private final Map moduleNameToUri = uriToModuleName.inverse(); - private final AtomicReference> qnameToRpc = new AtomicReference<>(Collections.emptyMap()); @@ -279,16 +273,8 @@ public class ControllerContext implements SchemaContextListener { public String findModuleNameByNamespace(final URI namespace) { this.checkPreconditions(); - String moduleName = this.uriToModuleName.get(namespace); - if (moduleName == null) { - final Module module = this.findModuleByNamespace(namespace); - if (module != null) { - moduleName = module.getName(); - this.uriToModuleName.put(namespace, moduleName); - } - } - - return moduleName; + final Module module = this.findModuleByNamespace(namespace); + return module == null ? null : module.getName(); } public String findModuleNameByNamespace(final DOMMountPoint mountPoint, final URI namespace) { @@ -297,16 +283,8 @@ public class ControllerContext implements SchemaContextListener { } public URI findNamespaceByModuleName(final String moduleName) { - URI namespace = this.moduleNameToUri.get(moduleName); - if (namespace == null) { - Module module = this.findModuleByName(moduleName); - if (module != null) { - URI _namespace = module.getNamespace(); - namespace = _namespace; - this.uriToModuleName.put(namespace, moduleName); - } - } - return namespace; + final Module module = this.findModuleByName(moduleName); + return module == null ? null : module.getNamespace(); } public URI findNamespaceByModuleName(final DOMMountPoint mountPoint, final String moduleName) { @@ -326,26 +304,15 @@ public class ControllerContext implements SchemaContextListener { return globalSchema.getModules(); } + private static final CharSequence toRestconfIdentifier(final SchemaContext context, final QName qname) { + final Module schema = context.findModuleByNamespaceAndRevision(qname.getNamespace(), qname.getRevision()); + return schema == null ? null : schema.getName() + ':' + qname.getLocalName(); + } + public CharSequence toRestconfIdentifier(final QName qname) { this.checkPreconditions(); - String module = this.uriToModuleName.get(qname.getNamespace()); - if (module == null) { - final Module moduleSchema = globalSchema.findModuleByNamespaceAndRevision(qname.getNamespace(), - qname.getRevision()); - if (moduleSchema == null) { - return null; - } - - this.uriToModuleName.put(qname.getNamespace(), moduleSchema.getName()); - module = moduleSchema.getName(); - } - - StringBuilder builder = new StringBuilder(); - builder.append(module); - builder.append(":"); - builder.append(qname.getLocalName()); - return builder.toString(); + return toRestconfIdentifier(globalSchema, qname); } public CharSequence toRestconfIdentifier(final DOMMountPoint mountPoint, final QName qname) { @@ -353,19 +320,7 @@ public class ControllerContext implements SchemaContextListener { return null; } - SchemaContext schemaContext = mountPoint.getSchemaContext(); - - final Module moduleSchema = schemaContext.findModuleByNamespaceAndRevision(qname.getNamespace(), - qname.getRevision()); - if (moduleSchema == null) { - return null; - } - - StringBuilder builder = new StringBuilder(); - builder.append(moduleSchema.getName()); - builder.append(":"); - builder.append(qname.getLocalName()); - return builder.toString(); + return toRestconfIdentifier(mountPoint.getSchemaContext(), qname); } public Module getRestconfModule() { @@ -391,7 +346,7 @@ public class ControllerContext implements SchemaContextListener { final GroupingDefinition restconfGrouping = Iterables.getFirst(filteredGroups, null); - List instanceDataChildrenByName = this.findInstanceDataChildrenByName(restconfGrouping, + List instanceDataChildrenByName = findInstanceDataChildrenByName(restconfGrouping, Draft02.RestConfModule.ERRORS_CONTAINER_SCHEMA_NODE); return Iterables.getFirst(instanceDataChildrenByName, null); } @@ -417,38 +372,38 @@ public class ControllerContext implements SchemaContextListener { Iterable filteredGroups = Iterables.filter(groupings, GROUPING_FILTER); final GroupingDefinition restconfGrouping = Iterables.getFirst(filteredGroups, null); - List instanceDataChildrenByName = this.findInstanceDataChildrenByName(restconfGrouping, + List instanceDataChildrenByName = findInstanceDataChildrenByName(restconfGrouping, Draft02.RestConfModule.RESTCONF_CONTAINER_SCHEMA_NODE); final DataSchemaNode restconfContainer = Iterables.getFirst(instanceDataChildrenByName, null); if (Objects.equal(schemaNodeName, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE)) { - List instances = this.findInstanceDataChildrenByName( + List instances = findInstanceDataChildrenByName( ((DataNodeContainer) restconfContainer), Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE); return Iterables.getFirst(instances, null); } else if (Objects.equal(schemaNodeName, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE)) { - List instances = this.findInstanceDataChildrenByName( + List instances = findInstanceDataChildrenByName( ((DataNodeContainer) restconfContainer), Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); return Iterables.getFirst(instances, null); } else if (Objects.equal(schemaNodeName, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE)) { - List instances = this.findInstanceDataChildrenByName( + List instances = findInstanceDataChildrenByName( ((DataNodeContainer) restconfContainer), Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); final DataSchemaNode modules = Iterables.getFirst(instances, null); - instances = this.findInstanceDataChildrenByName(((DataNodeContainer) modules), + instances = findInstanceDataChildrenByName(((DataNodeContainer) modules), Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE); return Iterables.getFirst(instances, null); } else if (Objects.equal(schemaNodeName, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE)) { - List instances = this.findInstanceDataChildrenByName( + List instances = findInstanceDataChildrenByName( ((DataNodeContainer) restconfContainer), Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); return Iterables.getFirst(instances, null); } else if (Objects.equal(schemaNodeName, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE)) { - List instances = this.findInstanceDataChildrenByName( + List instances = findInstanceDataChildrenByName( ((DataNodeContainer) restconfContainer), Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE); final DataSchemaNode modules = Iterables.getFirst(instances, null); - instances = this.findInstanceDataChildrenByName(((DataNodeContainer) modules), + instances = findInstanceDataChildrenByName(((DataNodeContainer) modules), Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE); return Iterables.getFirst(instances, null); } else if (Objects.equal(schemaNodeName, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE)) { - List instances = this.findInstanceDataChildrenByName( + List instances = findInstanceDataChildrenByName( ((DataNodeContainer) restconfContainer), Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE); return Iterables.getFirst(instances, null); } @@ -602,7 +557,7 @@ public class ControllerContext implements SchemaContextListener { } } - targetNode = this.findInstanceDataChildByNameAndNamespace(parentNode, nodeName, module.getNamespace()); + targetNode = findInstanceDataChildByNameAndNamespace(parentNode, nodeName, module.getNamespace()); if (targetNode == null) { throw new RestconfDocumentedException("URI has bad format. Possible reasons:\n" + " 1. \"" + head + "\" was not found in parent data node.\n" + " 2. \"" + head @@ -610,7 +565,7 @@ public class ControllerContext implements SchemaContextListener { ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } } else { - final List potentialSchemaNodes = this.findInstanceDataChildrenByName(parentNode, nodeName); + final List potentialSchemaNodes = findInstanceDataChildrenByName(parentNode, nodeName); if (potentialSchemaNodes.size() > 1) { final StringBuilder strBuilder = new StringBuilder(); for (final DataSchemaNode potentialNodeSchema : potentialSchemaNodes) { @@ -680,11 +635,11 @@ public class ControllerContext implements SchemaContextListener { return new InstanceIdWithSchemaNode(builder.toInstance(), targetNode, mountPoint); } - public DataSchemaNode findInstanceDataChildByNameAndNamespace(final DataNodeContainer container, final String name, + public static DataSchemaNode findInstanceDataChildByNameAndNamespace(final DataNodeContainer container, final String name, final URI namespace) { Preconditions. checkNotNull(namespace); - final List potentialSchemaNodes = this.findInstanceDataChildrenByName(container, name); + final List potentialSchemaNodes = findInstanceDataChildrenByName(container, name); Predicate filter = new Predicate() { @Override @@ -697,12 +652,12 @@ public class ControllerContext implements SchemaContextListener { return Iterables.getFirst(result, null); } - public List findInstanceDataChildrenByName(final DataNodeContainer container, final String name) { + public static List findInstanceDataChildrenByName(final DataNodeContainer container, final String name) { Preconditions. checkNotNull(container); Preconditions. checkNotNull(name); List instantiatedDataNodeContainers = new ArrayList(); - this.collectInstanceDataNodeContainers(instantiatedDataNodeContainers, container, name); + collectInstanceDataNodeContainers(instantiatedDataNodeContainers, container, name); return instantiatedDataNodeContainers; } @@ -713,7 +668,7 @@ public class ControllerContext implements SchemaContextListener { } }; - private void collectInstanceDataNodeContainers(final List potentialSchemaNodes, + private static void collectInstanceDataNodeContainers(final List potentialSchemaNodes, final DataNodeContainer container, final String name) { Predicate filter = new Predicate() { @@ -728,7 +683,7 @@ public class ControllerContext implements SchemaContextListener { // Can't combine this loop with the filter above because the filter is // lazily-applied by Iterables.filter. for (final DataSchemaNode potentialNode : nodes) { - if (this.isInstantiatedDataSchema(potentialNode)) { + if (isInstantiatedDataSchema(potentialNode)) { potentialSchemaNodes.add(potentialNode); } } @@ -738,11 +693,11 @@ public class ControllerContext implements SchemaContextListener { final Iterable allCases = Iterables. concat(map); for (final ChoiceCaseNode caze : allCases) { - this.collectInstanceDataNodeContainers(potentialSchemaNodes, caze, name); + collectInstanceDataNodeContainers(potentialSchemaNodes, caze, name); } } - public boolean isInstantiatedDataSchema(final DataSchemaNode node) { + public static boolean isInstantiatedDataSchema(final DataSchemaNode node) { return node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode || node instanceof ContainerSchemaNode || node instanceof ListSchemaNode || node instanceof AnyXmlSchemaNode; @@ -876,11 +831,7 @@ public class ControllerContext implements SchemaContextListener { } private CharSequence convertToRestconfIdentifier(final NodeIdentifier argument, final ContainerSchemaNode node) { - StringBuilder builder = new StringBuilder(); - builder.append("/"); - QName nodeType = argument.getNodeType(); - builder.append(this.toRestconfIdentifier(nodeType)); - return builder.toString(); + return "/" + this.toRestconfIdentifier(argument.getNodeType()); } private CharSequence convertToRestconfIdentifier(final NodeIdentifierWithPredicates argument, @@ -890,9 +841,9 @@ public class ControllerContext implements SchemaContextListener { final Map keyValues = argument.getKeyValues(); StringBuilder builder = new StringBuilder(); - builder.append("/"); + builder.append('/'); builder.append(nodeIdentifier); - builder.append("/"); + builder.append('/'); List keyDefinition = node.getKeyDefinition(); boolean hasElements = false; @@ -900,7 +851,7 @@ public class ControllerContext implements SchemaContextListener { if (!hasElements) { hasElements = true; } else { - builder.append("/"); + builder.append('/'); } try { diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestCodec.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestCodec.java index d61ccfdacf..665fafacc8 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestCodec.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestCodec.java @@ -244,7 +244,7 @@ public class RestCodec { for (int i = 0; i < identities.size(); i++) { IdentityValue identityValue = identities.get(i); URI validNamespace = resolveValidNamespace(identityValue.getNamespace(), mountPoint); - DataSchemaNode node = ControllerContext.getInstance().findInstanceDataChildByNameAndNamespace( + DataSchemaNode node = ControllerContext.findInstanceDataChildByNameAndNamespace( parentContainer, identityValue.getValue(), validNamespace); if (node == null) { logger.info("'{}' node was not found in {}", identityValue, parentContainer.getChildNodes()); @@ -271,7 +271,7 @@ public class RestCodec { Map predicatesMap = new HashMap<>(); for (Predicate predicate : identityValue.getPredicates()) { validNamespace = resolveValidNamespace(predicate.getName().getNamespace(), mountPoint); - DataSchemaNode listKey = ControllerContext.getInstance() + DataSchemaNode listKey = ControllerContext .findInstanceDataChildByNameAndNamespace(listNode, predicate.getName().getValue(), validNamespace); predicatesMap.put(listKey.getQName(), predicate.getValue()); @@ -286,7 +286,7 @@ public class RestCodec { } result.add(pathArgument); if (i < identities.size() - 1) { // last element in instance-identifier can be other than - // DataNodeContainer + // DataNodeContainer if (node instanceof DataNodeContainer) { parentContainer = (DataNodeContainer) node; } else { diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java index 5cb3cc2bae..73ca02c505 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java @@ -15,6 +15,7 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; + import java.net.URI; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -32,6 +33,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; + import org.apache.commons.lang3.StringUtils; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -60,12 +62,8 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory; -import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer.CnSnFromNormalizedNodeSerializerFactory; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; @@ -358,31 +356,31 @@ public class RestconfImpl implements RestconfService { private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) { final List> streamNodeValues = new ArrayList>(); - List instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + List instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) streamSchemaNode), "name"); final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues - .add(NodeFactory. createImmutableSimpleNode(nameSchemaNode.getQName(), null, streamName)); + .add(NodeFactory. createImmutableSimpleNode(nameSchemaNode.getQName(), null, streamName)); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) streamSchemaNode), "description"); final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory. createImmutableSimpleNode(descriptionSchemaNode.getQName(), null, "DESCRIPTION_PLACEHOLDER")); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) streamSchemaNode), "replay-support"); final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory. createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null, Boolean.valueOf(true))); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time"); final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory. createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(), null, "")); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) streamSchemaNode), "events"); final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory. createImmutableSimpleNode(eventsSchemaNode.getQName(), null, "")); @@ -392,26 +390,26 @@ public class RestconfImpl implements RestconfService { private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) { final List> moduleNodeValues = new ArrayList>(); - List instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + List instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) moduleSchemaNode), "name"); final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); moduleNodeValues.add(NodeFactory. createImmutableSimpleNode(nameSchemaNode.getQName(), null, module.getName())); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) moduleSchemaNode), "revision"); final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Date _revision = module.getRevision(); moduleNodeValues.add(NodeFactory. createImmutableSimpleNode(revisionSchemaNode.getQName(), null, REVISION_FORMAT.format(_revision))); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) moduleSchemaNode), "namespace"); final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); moduleNodeValues.add(NodeFactory. createImmutableSimpleNode(namespaceSchemaNode.getQName(), null, module.getNamespace().toString())); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) moduleSchemaNode), "feature"); final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); for (final FeatureDefinition feature : module.getFeatures()) { @@ -830,7 +828,7 @@ public class RestconfImpl implements RestconfService { } String payloadName = this.getName(payload); - final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace( + final DataSchemaNode schemaNode = ControllerContext.findInstanceDataChildByNameAndNamespace( parentSchema, payloadName, module.getNamespace()); value = this.normalizeNode(payload, schemaNode, mountPoint); @@ -878,7 +876,7 @@ public class RestconfImpl implements RestconfService { } String payloadName = this.getName(payload); - final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(module, + final DataSchemaNode schemaNode = ControllerContext.findInstanceDataChildByNameAndNamespace(module, payloadName, module.getNamespace()); final CompositeNode value = this.normalizeNode(payload, schemaNode, null); final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode); @@ -1102,10 +1100,10 @@ public class RestconfImpl implements RestconfService { private boolean representsMountPointRootData(final Node data) { URI namespace = this.namespace(data); return (SchemaContext.NAME.getNamespace().equals(namespace) /* - * || MOUNT_POINT_MODULE_NAME .equals( namespace . - * toString( ) ) - */) - && SchemaContext.NAME.getLocalName().equals(this.localName(data)); + * || MOUNT_POINT_MODULE_NAME .equals( namespace . + * toString( ) ) + */) + && SchemaContext.NAME.getLocalName().equals(this.localName(data)); } private String addMountPointIdentifier(final String identifier) { @@ -1239,7 +1237,7 @@ public class RestconfImpl implements RestconfService { final List> children = compositeNodeBuilder.getValues(); checkNodeMultiplicityAccordingToSchema(schema, children); for (final NodeWrapper child : children) { - final List potentialSchemaNodes = this.controllerContext.findInstanceDataChildrenByName( + final List potentialSchemaNodes = ControllerContext.findInstanceDataChildrenByName( schema, child.getLocalName()); if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) { @@ -1334,15 +1332,16 @@ public class RestconfImpl implements RestconfService { } if (nodeBuilder.getNamespace() == null || Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) - || Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /* - * || Note : this check is wrong - - * can never be true as it compares - * a URI with a String not sure what - * the intention is so commented out - * ... Objects . equal ( nodeBuilder - * . getNamespace ( ) , - * MOUNT_POINT_MODULE_NAME ) - */) { + || Objects.equal(nodeBuilder.getNamespace().toString(), moduleName)) { + /* + * || Note : this check is wrong - + * can never be true as it compares + * a URI with a String not sure what + * the intention is so commented out + * ... Objects . equal ( nodeBuilder + * . getNamespace ( ) , + * MOUNT_POINT_MODULE_NAME ) + */ nodeBuilder.setQname(validQName); } @@ -1411,25 +1410,15 @@ public class RestconfImpl implements RestconfService { } private CompositeNode datastoreNormalizedNodeToCompositeNode(final NormalizedNode dataNode, final DataSchemaNode schema) { - Iterable> nodes = null; + Node nodes = null; if (dataNode == null) { throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.DATA_MISSING, "No data was found.")); } - if (schema instanceof ContainerSchemaNode && dataNode instanceof ContainerNode) { - nodes = CnSnFromNormalizedNodeSerializerFactory.getInstance().getContainerNodeSerializer() - .serialize((ContainerSchemaNode) schema, (ContainerNode) dataNode); - } else if (schema instanceof ListSchemaNode && dataNode instanceof MapNode) { - nodes = CnSnFromNormalizedNodeSerializerFactory.getInstance().getMapNodeSerializer() - .serialize((ListSchemaNode) schema, (MapNode) dataNode); - } else if (schema instanceof ListSchemaNode && dataNode instanceof MapEntryNode) { - nodes = CnSnFromNormalizedNodeSerializerFactory.getInstance().getMapEntryNodeSerializer() - .serialize((ListSchemaNode) schema, (MapEntryNode) dataNode); - } + nodes = DataNormalizer.toLegacy(dataNode); if (nodes != null) { - if (nodes.iterator().hasNext()) { - Node nodeOldStruct = nodes.iterator().next(); - return (CompositeNode) nodeOldStruct; + if (nodes instanceof CompositeNode) { + return (CompositeNode) nodes; } else { LOG.error("The node " + dataNode.getNodeType() + " couldn't be transformed to compositenode."); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java index d4f6909081..539248a147 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java @@ -61,17 +61,14 @@ import org.opendaylight.controller.sal.restconf.impl.RestconfImpl; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; @@ -674,9 +671,7 @@ public class RestGetOperationTest extends JerseyTest { } private NormalizedNode prepareCnDataForSlashesBehindMountPointTest() throws ParseException { - CollectionNodeBuilder lst1 = ImmutableMapNodeBuilder.create(); - lst1.withNodeIdentifier(TestUtils.getNodeIdentifier("lst1", "test:module", "2014-01-09")); - lst1.withChild(ImmutableMapEntryNodeBuilder + return ImmutableMapEntryNodeBuilder .create() .withNodeIdentifier( TestUtils.getNodeIdentifierPredicate("lst1", "test:module", "2014-01-09", "lf11", @@ -684,9 +679,8 @@ public class RestGetOperationTest extends JerseyTest { .withChild( ImmutableLeafNodeBuilder.create() .withNodeIdentifier(TestUtils.getNodeIdentifier("lf11", "test:module", "2014-01-09")) - .withValue("GigabitEthernet0/0/0/0").build()).build()); + .withValue("GigabitEthernet0/0/0/0").build()).build(); - return lst1.build(); } /** @@ -905,6 +899,9 @@ public class RestGetOperationTest extends JerseyTest { expectLeaf("depth3-leaf1", "depth3-leaf1-value"))); } + /** + * Tests behavior when invalid value of depth URI parameter + */ @Test public void getDataWithInvalidDepthParameterTest() { @@ -931,6 +928,10 @@ public class RestGetOperationTest extends JerseyTest { private void getDataWithInvalidDepthParameterTest(final UriInfo uriInfo) { try { + QName qNameDepth1Cont = QName.create("urn:nested:module", "2014-06-3", "depth1-cont"); + YangInstanceIdentifier ii = YangInstanceIdentifier.builder().node(qNameDepth1Cont).build(); + NormalizedNode value = (NormalizedNode)(Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(qNameDepth1Cont)).build()); + when(brokerFacade.readConfigurationData(eq(ii))).thenReturn(value); restconfImpl.readConfigurationData("nested-module:depth1-cont", uriInfo); fail("Expected RestconfDocumentedException"); } catch (RestconfDocumentedException e) { diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java index 562cac0bcf..c2b153f02b 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java @@ -62,15 +62,12 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder; import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; @@ -407,9 +404,7 @@ public final class TestUtils { static NormalizedNode prepareNormalizedNodeWithIetfInterfacesInterfacesData() throws ParseException { String ietfInterfacesDate = "2013-07-04"; - CollectionNodeBuilder intface = ImmutableMapNodeBuilder.create(); String namespace = "urn:ietf:params:xml:ns:yang:ietf-interfaces"; - intface.withNodeIdentifier(getNodeIdentifier("interface", namespace, ietfInterfacesDate)); DataContainerNodeAttrBuilder mapEntryNode = ImmutableMapEntryNodeBuilder.create(); Map predicates = new HashMap<>(); @@ -431,8 +426,6 @@ public final class TestUtils { .withNodeIdentifier(getNodeIdentifier("description", namespace, ietfInterfacesDate)) .withValue("some interface").build()); - intface.withChild(mapEntryNode.build()); - - return intface.build(); + return mapEntryNode.build(); } } diff --git a/opendaylight/md-sal/sal-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java b/opendaylight/md-sal/sal-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java index a0c23aecb4..af7a32924b 100644 --- a/opendaylight/md-sal/sal-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java +++ b/opendaylight/md-sal/sal-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java @@ -7,10 +7,11 @@ */ package org.opendaylight.controller.md.sal.test.model.util; -import java.util.Arrays; - +import com.google.common.collect.ImmutableList; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeLeafOnlyUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeLeafOnlyUsesAugmentBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUses; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ListViaUsesKey; @@ -25,7 +26,7 @@ import org.opendaylight.yangtools.yang.binding.Augmentation; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import com.google.common.collect.ImmutableList; +import java.util.Arrays; public class ListsBindingUtils { @@ -79,4 +80,9 @@ public class ListsBindingUtils { return new TreeComplexUsesAugmentBuilder().setListViaUses(listViaUses.build()).build(); } + public static TreeLeafOnlyUsesAugment leafOnlyUsesAugment(String leafFromGroupingValue) { + + return new TreeLeafOnlyUsesAugmentBuilder().setLeafFromGrouping(leafFromGroupingValue).build(); + } + } diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java index eddad8b4c7..fff8d611b7 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java @@ -65,9 +65,11 @@ public class ConfigPusher { LinkedHashMap result = new LinkedHashMap<>(); // start pushing snapshots: for (ConfigSnapshotHolder configSnapshotHolder : configs) { - EditAndCommitResponse editAndCommitResponseWithRetries = pushConfigWithConflictingVersionRetries(configSnapshotHolder); - logger.debug("Config snapshot pushed successfully: {}, result: {}", configSnapshotHolder, result); - result.put(configSnapshotHolder, editAndCommitResponseWithRetries); + if(configSnapshotHolder != null) { + EditAndCommitResponse editAndCommitResponseWithRetries = pushConfigWithConflictingVersionRetries(configSnapshotHolder); + logger.debug("Config snapshot pushed successfully: {}, result: {}", configSnapshotHolder, result); + result.put(configSnapshotHolder, editAndCommitResponseWithRetries); + } } logger.debug("All configuration snapshots have been pushed successfully."); return result;