*/
package org.opendaylight.controller.config.manager.impl.osgi;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
/**
*
- * Extensible bundle tracker. Takes several BundleTrackerCustomizers and propagates bundle events to all of them.
- * Primary customizer
+ * Extensible bundle tracker. Takes several BundleTrackerCustomizers and
+ * propagates bundle events to all of them.
+ *
+ * Primary customizer may return tracking object,
+ * which will be passed to it during invocation of
+ * {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Future)}
+ *
+ *
+ * This extender modifies behaviour to not leak platform thread
+ * in {@link BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)}
+ * but deliver this event from its own single threaded executor.
+ *
+ * If bundle is removed before event for adding bundle was executed,
+ * that event is cancelled. If addingBundle event is currently in progress
+ * or was already executed, platform thread is block untill addingBundle
+ * finishes so bundle could be removed correctly in platform thread.
+ *
+ *
+ * Method {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)}
+ * is never invoked on registered trackers.
*
* @param <T>
*/
-public final class ExtensibleBundleTracker<T> extends BundleTracker<T> {
+public final class ExtensibleBundleTracker<T> extends BundleTracker<Future<T>> {
+ private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder()
+ .setNameFormat("config-bundle-tracker-%d")
+ .build();
+ private final ExecutorService eventExecutor;
private final BundleTrackerCustomizer<T> primaryTracker;
private final BundleTrackerCustomizer<?>[] additionalTrackers;
- private static final Logger logger = LoggerFactory.getLogger(ExtensibleBundleTracker.class);
+ private static final Logger LOG = LoggerFactory.getLogger(ExtensibleBundleTracker.class);
- public ExtensibleBundleTracker(BundleContext context, BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
- BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
+ public ExtensibleBundleTracker(final BundleContext context, final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
+ final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
this(context, Bundle.ACTIVE, primaryBundleTrackerCustomizer, additionalBundleTrackerCustomizers);
}
- public ExtensibleBundleTracker(BundleContext context, int bundleState,
- BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
- BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
+ public ExtensibleBundleTracker(final BundleContext context, final int bundleState,
+ final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
+ final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
super(context, bundleState, null);
this.primaryTracker = primaryBundleTrackerCustomizer;
this.additionalTrackers = additionalBundleTrackerCustomizers;
- logger.trace("Registered as extender with context {} and bundle state {}", context, bundleState);
+ eventExecutor = Executors.newSingleThreadExecutor(THREAD_FACTORY);
+ LOG.trace("Registered as extender with context {} and bundle state {}", context, bundleState);
}
@Override
- public T addingBundle(final Bundle bundle, final BundleEvent event) {
- T primaryTrackerRetVal = primaryTracker.addingBundle(bundle, event);
-
- forEachAdditionalBundle(new BundleStrategy() {
+ public Future<T> addingBundle(final Bundle bundle, final BundleEvent event) {
+ LOG.trace("Submiting AddingBundle for bundle {} and event {} to be processed asynchronously",bundle,event);
+ Future<T> future = eventExecutor.submit(new Callable<T>() {
@Override
- public void execute(BundleTrackerCustomizer<?> tracker) {
- tracker.addingBundle(bundle, event);
+ public T call() throws Exception {
+ try {
+ T primaryTrackerRetVal = primaryTracker.addingBundle(bundle, event);
+
+ forEachAdditionalBundle(new BundleStrategy() {
+ @Override
+ public void execute(final BundleTrackerCustomizer<?> tracker) {
+ tracker.addingBundle(bundle, event);
+ }
+ });
+ LOG.trace("AddingBundle for {} and event {} finished successfully",bundle,event);
+ return primaryTrackerRetVal;
+ } catch (Exception e) {
+ LOG.error("Failed to add bundle {}",e);
+ throw e;
+ }
}
});
-
- return primaryTrackerRetVal;
+ return future;
}
@Override
- public void modifiedBundle(final Bundle bundle, final BundleEvent event, final T object) {
- primaryTracker.modifiedBundle(bundle, event, object);
-
- forEachAdditionalBundle(new BundleStrategy() {
- @Override
- public void execute(BundleTrackerCustomizer<?> tracker) {
- tracker.modifiedBundle(bundle, event, null);
- }
- });
+ public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
+ // Intentionally NOOP
}
@Override
- public void removedBundle(final Bundle bundle, final BundleEvent event, final T object) {
- primaryTracker.removedBundle(bundle, event, object);
-
- forEachAdditionalBundle(new BundleStrategy() {
- @Override
- public void execute(BundleTrackerCustomizer<?> tracker) {
- tracker.removedBundle(bundle, event, null);
- }
- });
+ public void removedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
+ if(!object.isDone() && object.cancel(false)) {
+ // We canceled adding event before it was processed
+ // so it is safe to return
+ LOG.trace("Adding Bundle event for {} was cancelled. No additional work required.",bundle);
+ return;
+ }
+ try {
+ LOG.trace("Invoking removedBundle event for {}",bundle);
+ primaryTracker.removedBundle(bundle, event, object.get());
+ forEachAdditionalBundle(new BundleStrategy() {
+ @Override
+ public void execute(final BundleTrackerCustomizer<?> tracker) {
+ tracker.removedBundle(bundle, event, null);
+ }
+ });
+ LOG.trace("Removed bundle event for {} finished successfully.",bundle);
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Addition of bundle failed, ", e);
+ }
}
- private void forEachAdditionalBundle(BundleStrategy lambda) {
+ private void forEachAdditionalBundle(final BundleStrategy lambda) {
for (BundleTrackerCustomizer<?> trac : additionalTrackers) {
lambda.execute(trac);
}
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionChainFactory;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@Override
ListenerRegistration<DataChangeListener> registerDataChangeListener(LogicalDatastoreType store,
InstanceIdentifier<?> path, DataChangeListener listener, DataChangeScope triggeringScope);
+
+ @Override
+ BindingTransactionChain createTransactionChain(TransactionChainListener listener);
}
/**
* Stores a piece of data at the specified path. This acts as an add / replace
* operation, which is to say that whole subtree will be replaced by the specified data.
+ * * <p>
+ * This method does not automatically create missing parent nodes. It is equivalent to invoking
+ * {@link #put(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}
+ * with <code>createMissingParents</code> set to false.
* <p>
* For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
* <p>
*/
<T extends DataObject> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
+
+ /**
+ * Stores a piece of data at the specified path. This acts as an add /
+ * replace operation, which is to say that whole subtree will be replaced by
+ * the specified data.
+ * <p>
+ * For more information on usage and examples, please see the documentation
+ * in {@link AsyncWriteTransaction}.
+ * <p>
+ * If you need to make sure that a parent object exists but you do not want
+ * modify its pre-existing state by using put, consider using {@link #merge}
+ * instead.
+ *
+ * Note: Using <code>createMissingParents</code> with value true, may
+ * introduce garbage in data store, or recreate nodes, which were deleted by
+ * previous transaction.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be written to the specified path
+ * @param createMissingParents
+ * if true, any missing parent nodes will be automatically
+ * created using a merge operation.
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ <T extends DataObject> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data,
+ boolean createMissingParents);
+
/**
* Merges a piece of data with the existing data at a specified path. Any pre-existing data
* which is not explicitly overwritten will be preserved. This means that if you store a container,
* its child lists will be merged.
* <p>
+ * This method does not automatically create missing parent nodes. It is equivalent to invoking
+ * {@link #merge(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}
+ * with <code>createMissingParents</code> set to false.
+ * <p>
* For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
*<p>
* If you require an explicit replace operation, use {@link #put} instead.
- *
* @param store
* the logical data store which should be modified
* @param path
*/
<T extends DataObject> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
+ /**
+ * Merges a piece of data with the existing data at a specified path. Any
+ * pre-existing data which is not explicitly overwritten will be preserved.
+ * This means that if you store a container, its child lists will be merged.
+ * <p>
+ * For more information on usage and examples, please see the documentation
+ * in {@link AsyncWriteTransaction}.
+ * <p>
+ * If you require an explicit replace operation, use {@link #put} instead.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be merged to the specified path
+ * @param createMissingParents
+ * if true, any missing parent nodes will be automatically created
+ * using a merge operation.
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ <T extends DataObject> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data,
+ boolean createMissingParents);
+
@Override
void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
}
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
-import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
super(delegate, codec);
}
- protected final void doPutWithEnsureParents(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
- final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
- .toNormalizedNode(path, data);
-
- final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath = normalized.getKey();
- ensureParentsByMerge(store, normalizedPath, path);
- LOG.debug("Tx: {} : Putting data {}", getDelegate().getIdentifier(), normalizedPath);
- doPut(store, path, data);
- }
-
- protected final void doMergeWithEnsureParents(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
- final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
- .toNormalizedNode(path, data);
-
- final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath = normalized.getKey();
- ensureParentsByMerge(store, normalizedPath, path);
- LOG.debug("Tx: {} : Merge data {}", getDelegate().getIdentifier(), normalizedPath);
- doMerge(store, path, data);
- }
-
- private final void ensureParentsByMerge(final LogicalDatastoreType store,
+ @Override
+ protected final void ensureParentsByMerge(final LogicalDatastoreType store,
final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath,
final InstanceIdentifier<?> path) {
List<PathArgument> currentArguments = new ArrayList<>();
* Abstract Base Transaction for transactions which are backed by
* {@link DOMDataWriteTransaction}
*/
-public class AbstractWriteTransaction<T extends DOMDataWriteTransaction> extends
+public abstract class AbstractWriteTransaction<T extends DOMDataWriteTransaction> extends
AbstractForwardedTransaction<T> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractWriteTransaction.class);
super(delegate, codec);
}
- protected final void doPut(final LogicalDatastoreType store,
- final InstanceIdentifier<?> path, final DataObject data) {
+
+ public final <U extends DataObject> void put(final LogicalDatastoreType store,
+ final InstanceIdentifier<U> path, final U data, final boolean createParents) {
final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
.toNormalizedNode(path, data);
- ensureListParentIfNeeded(store,path,normalized);
+ if(createParents) {
+ ensureParentsByMerge(store, normalized.getKey(), path);
+ } else {
+ ensureListParentIfNeeded(store,path,normalized);
+ }
getDelegate().put(store, normalized.getKey(), normalized.getValue());
}
+ public final <U extends DataObject> void merge(final LogicalDatastoreType store,
+ final InstanceIdentifier<U> path, final U data,final boolean createParents) {
+
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
+ .toNormalizedNode(path, data);
+
+ if(createParents) {
+ ensureParentsByMerge(store, normalized.getKey(), path);
+ } else {
+ ensureListParentIfNeeded(store,path,normalized);
+ }
+
+ getDelegate().merge(store, normalized.getKey(), normalized.getValue());
+ }
+
+
/**
*
* Ensures list parent if item is list, otherwise noop.
}
}
- protected final void doMerge(final LogicalDatastoreType store,
- final InstanceIdentifier<?> path, final DataObject data) {
-
- final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
- .toNormalizedNode(path, data);
- ensureListParentIfNeeded(store,path,normalized);
- getDelegate().merge(store, normalized.getKey(), normalized.getValue());
- }
+ /**
+ * Subclasses of this class are required to implement creation of parent
+ * nodes based on behaviour of their underlying transaction.
+ *
+ * @param store
+ * @param key
+ * @param path
+ */
+ protected abstract void ensureParentsByMerge(LogicalDatastoreType store,
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier key, InstanceIdentifier<?> path);
protected final void doDelete(final LogicalDatastoreType store,
final InstanceIdentifier<?> path) {
*/
package org.opendaylight.controller.md.sal.binding.impl;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
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.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.ListenableFuture;
}
@Override
- public <T extends DataObject> void put(final LogicalDatastoreType store, final InstanceIdentifier<T> path,
- final T data) {
- doPut(store, path, data);
+ public <U extends DataObject> void put(final LogicalDatastoreType store, final InstanceIdentifier<U> path,
+ final U data) {
+ put(store, path, data,false);
}
@Override
public <T extends DataObject> void merge(final LogicalDatastoreType store, final InstanceIdentifier<T> path,
final T data) {
- doMerge(store, path, data);
+ merge(store, path, data,false);
+ }
+
+
+ @Override
+ protected void ensureParentsByMerge(final LogicalDatastoreType store,
+ final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath, final InstanceIdentifier<?> path) {
+ List<PathArgument> currentArguments = new ArrayList<>();
+ DataNormalizationOperation<?> currentOp = getCodec().getDataNormalizer().getRootOperation();
+ Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
+ while (iterator.hasNext()) {
+ PathArgument currentArg = iterator.next();
+ try {
+ currentOp = currentOp.getChild(currentArg);
+ } catch (DataNormalizationException e) {
+ throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
+ }
+ currentArguments.add(currentArg);
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier currentPath = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.create(
+ currentArguments);
+
+ getDelegate().merge(store, currentPath, currentOp.createDefault(currentArg));
+ }
}
@Override
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+@SuppressWarnings("deprecation")
public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDataBroker implements DataProviderService, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(ForwardedBackwardsCompatibleDataBroker.class);
@Override
public void putOperationalData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
boolean previouslyRemoved = posponedRemovedOperational.remove(path);
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ final InstanceIdentifier<DataObject> castedPath = (InstanceIdentifier) path;
if(previouslyRemoved) {
- doPutWithEnsureParents(LogicalDatastoreType.OPERATIONAL, path, data);
+ put(LogicalDatastoreType.OPERATIONAL, castedPath, data,true);
} else {
- doMergeWithEnsureParents(LogicalDatastoreType.OPERATIONAL, path, data);
+ merge(LogicalDatastoreType.OPERATIONAL, castedPath, data,true);
}
}
created.put(path, data);
}
updated.put(path, data);
+ @SuppressWarnings({"rawtypes","unchecked"})
+ final InstanceIdentifier<DataObject> castedPath = (InstanceIdentifier) path;
if(previouslyRemoved) {
- doPutWithEnsureParents(LogicalDatastoreType.CONFIGURATION, path, data);
+ put(LogicalDatastoreType.CONFIGURATION, castedPath, data,true);
} else {
- doMergeWithEnsureParents(LogicalDatastoreType.CONFIGURATION, path, data);
+ merge(LogicalDatastoreType.CONFIGURATION, castedPath, data,true);
}
}
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
*/
package org.opendaylight.controller.md.sal.binding.impl.test;
+import static org.junit.Assert.assertTrue;
+
import java.util.concurrent.ExecutionException;
import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
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.TopBuilder;
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.InstanceIdentifier;
+import com.google.common.base.Optional;
+
public class WriteTransactionTest extends AbstractDataBrokerTest {
private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
private static final TopLevelListKey TOP_LIST_KEY = new TopLevelListKey("foo");
private static final InstanceIdentifier<TopLevelList> NODE_PATH = TOP_PATH.child(TopLevelList.class, TOP_LIST_KEY);
-
+ private static final TopLevelList NODE = new TopLevelListBuilder().setKey(TOP_LIST_KEY).build();
@Test
public void test() throws InterruptedException, ExecutionException {
WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
writeTx.put(LogicalDatastoreType.OPERATIONAL, TOP_PATH, new TopBuilder().build());
- writeTx.put(LogicalDatastoreType.OPERATIONAL, NODE_PATH, new TopLevelListBuilder().setKey(TOP_LIST_KEY).build());
+ writeTx.put(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE);
writeTx.submit().get();
}
+ @Test
+ public void testPutCreateParentsSuccess() throws TransactionCommitFailedException, InterruptedException, ExecutionException {
+
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE,true);
+ writeTx.submit().checkedGet();
+
+ ReadOnlyTransaction readTx = getDataBroker().newReadOnlyTransaction();
+ Optional<Top> topNode = readTx.read(LogicalDatastoreType.OPERATIONAL, TOP_PATH).get();
+ assertTrue("Top node must exists after commit",topNode.isPresent());
+ Optional<TopLevelList> listNode = readTx.read(LogicalDatastoreType.OPERATIONAL, NODE_PATH).get();
+ assertTrue("List node must exists after commit",listNode.isPresent());
+ }
+
+ @Test
+ public void testMergeCreateParentsSuccess() throws TransactionCommitFailedException, InterruptedException, ExecutionException {
+
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.merge(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE,true);
+ writeTx.submit().checkedGet();
+
+ ReadOnlyTransaction readTx = getDataBroker().newReadOnlyTransaction();
+ Optional<Top> topNode = readTx.read(LogicalDatastoreType.OPERATIONAL, TOP_PATH).get();
+ assertTrue("Top node must exists after commit",topNode.isPresent());
+ Optional<TopLevelList> listNode = readTx.read(LogicalDatastoreType.OPERATIONAL, NODE_PATH).get();
+ assertTrue("List node must exists after commit",listNode.isPresent());
+ }
+
}
package org.opendaylight.controller.md.sal.dom.api;
import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
DOMMountPointBuilder createMountPoint(InstanceIdentifier path);
+ ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
public interface DOMMountPointBuilder {
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco 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.sal.core.api.mount;
+
+import java.util.EventListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+public interface MountProvisionListener extends EventListener {
+
+ void onMountPointCreated(InstanceIdentifier path);
+
+ void onMountPointRemoved(InstanceIdentifier path);
+
+}
*/
package org.opendaylight.controller.sal.core.api.mount;
-import java.util.EventListener;
-
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
- public interface MountProvisionListener extends EventListener {
-
- void onMountPointCreated(InstanceIdentifier path);
-
- void onMountPointRemoved(InstanceIdentifier path);
-
- }
}
import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
import org.opendaylight.controller.md.sal.dom.api.DOMService;
import org.opendaylight.controller.md.sal.dom.broker.spi.mount.SimpleDOMMountPoint;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
private final Map<InstanceIdentifier, SimpleDOMMountPoint> mountPoints = new HashMap<>();
+ private final ListenerRegistry<MountProvisionListener> listeners = ListenerRegistry.create();
+
@Override
public Optional<DOMMountPoint> getMountPoint(final InstanceIdentifier path) {
return Optional.<DOMMountPoint>fromNullable(mountPoints.get(path));
return new DOMMountPointBuilderImpl(path);
}
+ public void notifyMountCreated(final InstanceIdentifier identifier) {
+ for (final ListenerRegistration<MountProvisionListener> listener : listeners
+ .getListeners()) {
+ listener.getInstance().onMountPointCreated(identifier);
+ }
+ }
+
+ @Override
+ public ListenerRegistration<MountProvisionListener> registerProvisionListener(
+ final MountProvisionListener listener) {
+ return listeners.register(listener);
+ }
+
public ObjectRegistration<DOMMountPoint> registerMountPoint(final SimpleDOMMountPoint mountPoint) {
synchronized (mountPoints) {
Preconditions.checkState(!mountPoints.containsKey(mountPoint.getIdentifier()), "Mount point already exists");
mountPoints.put(mountPoint.getIdentifier(), mountPoint);
}
+ notifyMountCreated(mountPoint.getIdentifier());
+
+ // FIXME this shouldnt be null
return null;
}
import org.opendaylight.controller.sal.core.api.data.DataProviderService;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
package org.opendaylight.controller.sal.dom.broker.osgi;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
import org.opendaylight.controller.sal.rest.doc.impl.ApiDocGenerator;
import org.opendaylight.controller.sal.rest.doc.mountpoints.MountPointSwagger;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
import org.opendaylight.controller.sal.rest.doc.impl.BaseYangSwaggerGenerator;
import org.opendaylight.controller.sal.rest.doc.swagger.Api;
import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;