X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-data-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fdata%2Fimpl%2Fschema%2Ftree%2FInMemoryDataTree.java;h=1078f74b17187f5b71e2ab6737091f1d2ffa84fc;hb=37380a5c65e213bc5f34b521d8f8e7d315df7465;hp=0ed17c685a8aef53900a0b6a9fb71448f967a102;hpb=27123cb0892d868e4c3ea664d6632a3ec0431d17;p=yangtools.git diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTree.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTree.java index 0ed17c685a..1078f74b17 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTree.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTree.java @@ -7,94 +7,133 @@ */ package org.opendaylight.yangtools.yang.data.impl.schema.tree; +import static java.util.Objects.requireNonNull; + import com.google.common.base.MoreObjects; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import java.util.Collections; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.Optional; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +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.NormalizedNodes; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; -import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; -import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeConfiguration; import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; +import org.opendaylight.yangtools.yang.model.api.ContainerLike; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Read-only snapshot of the data tree. */ -final class InMemoryDataTree implements DataTree { - private static final YangInstanceIdentifier PUBLIC_ROOT_PATH = YangInstanceIdentifier.create(Collections.emptyList()); - private static final AtomicReferenceFieldUpdater STATE_UPDATER = - AtomicReferenceFieldUpdater.newUpdater(InMemoryDataTree.class, DataTreeState.class, "state"); +final class InMemoryDataTree extends AbstractDataTreeTip implements DataTree { + private static final VarHandle STATE; + + static { + try { + STATE = MethodHandles.lookup().findVarHandle(InMemoryDataTree.class, "state", DataTreeState.class); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + private static final Logger LOG = LoggerFactory.getLogger(InMemoryDataTree.class); + private final DataTreeConfiguration treeConfig; + private final boolean maskMandatory; + /** - * Current data store state generation. + * Current data store state generation. All accesses need to go through {@link #STATE} */ + @SuppressWarnings("unused") private volatile DataTreeState state; - public InMemoryDataTree(final TreeNode rootNode, final SchemaContext schemaContext) { + InMemoryDataTree(final TreeNode rootNode, final DataTreeConfiguration treeConfig, + final EffectiveModelContext schemaContext) { + this.treeConfig = requireNonNull(treeConfig, "treeConfig"); + maskMandatory = true; state = DataTreeState.createInitial(rootNode); if (schemaContext != null) { - setSchemaContext(schemaContext); + setEffectiveModelContext(schemaContext); } } - /* - * This method is synchronized to guard against user attempting to install - * multiple contexts. Otherwise it runs in a lock-free manner. - */ - @Override - public synchronized void setSchemaContext(final SchemaContext newSchemaContext) { - Preconditions.checkNotNull(newSchemaContext); + InMemoryDataTree(final TreeNode rootNode, final DataTreeConfiguration treeConfig, + final EffectiveModelContext schemaContext, final DataSchemaNode rootSchemaNode, + final boolean maskMandatory) { + this.treeConfig = requireNonNull(treeConfig, "treeConfig"); + this.maskMandatory = maskMandatory; - LOG.info("Attempting to install schema contexts"); - LOG.debug("Following schema contexts will be attempted {}", newSchemaContext); + state = DataTreeState.createInitial(rootNode).withSchemaContext(schemaContext, getOperation(rootSchemaNode)); + } - final SchemaAwareApplyOperation operation = SchemaAwareApplyOperation.from(newSchemaContext); + private ModificationApplyOperation getOperation(final DataSchemaNode rootSchemaNode) { + if (rootSchemaNode instanceof ContainerLike && maskMandatory) { + return new ContainerModificationStrategy((ContainerLike) rootSchemaNode, treeConfig); + } + if (rootSchemaNode instanceof ListSchemaNode) { + final PathArgument arg = treeConfig.getRootPath().getLastPathArgument(); + if (arg instanceof NodeIdentifierWithPredicates) { + return maskMandatory ? new MapEntryModificationStrategy((ListSchemaNode) rootSchemaNode, treeConfig) + : MapEntryModificationStrategy.of((ListSchemaNode) rootSchemaNode, treeConfig); + } + } - DataTreeState currentState, newState; - do { - currentState = state; - newState = currentState.withSchemaContext(newSchemaContext, operation); - } while (!STATE_UPDATER.compareAndSet(this, currentState, newState)); + try { + return SchemaAwareApplyOperation.from(rootSchemaNode, treeConfig); + } catch (ExcludedDataSchemaNodeException e) { + throw new IllegalArgumentException("Root node does not belong current data tree", e); + } } @Override - public InMemoryDataTreeSnapshot takeSnapshot() { - return state.newSnapshot(); + public void setEffectiveModelContext(final EffectiveModelContext newModelContext) { + internalSetSchemaContext(newModelContext); } - @Override - public void validate(final DataTreeModification modification) throws DataValidationFailedException { - Preconditions.checkArgument(modification instanceof InMemoryDataTreeModification, "Invalid modification class %s", modification.getClass()); - final InMemoryDataTreeModification m = (InMemoryDataTreeModification)modification; - - m.getStrategy().checkApplicable(PUBLIC_ROOT_PATH, m.getRootModification(), Optional.of(state.getRoot())); - } + /* + * This method is synchronized to guard against user attempting to install + * multiple contexts. Otherwise it runs in a lock-free manner. + */ + private synchronized void internalSetSchemaContext(final EffectiveModelContext newSchemaContext) { + requireNonNull(newSchemaContext); - @Override - public DataTreeCandidate prepare(final DataTreeModification modification) { - Preconditions.checkArgument(modification instanceof InMemoryDataTreeModification, "Invalid modification class %s", modification.getClass()); + LOG.debug("Following schema contexts will be attempted {}", newSchemaContext); - final InMemoryDataTreeModification m = (InMemoryDataTreeModification)modification; - final ModifiedNode root = m.getRootModification(); + final DataSchemaContextTree contextTree = DataSchemaContextTree.from(newSchemaContext); + final Optional> rootContextNode = contextTree.findChild(getRootPath()); + if (!rootContextNode.isPresent()) { + LOG.warn("Could not find root {} in new schema context, not upgrading", getRootPath()); + return; + } - if (root.getType() == ModificationType.UNMODIFIED) { - return new NoopDataTreeCandidate(PUBLIC_ROOT_PATH, root); + final DataSchemaNode rootSchemaNode = rootContextNode.get().getDataSchemaNode(); + if (!(rootSchemaNode instanceof DataNodeContainer)) { + LOG.warn("Root {} resolves to non-container type {}, not upgrading", getRootPath(), rootSchemaNode); + return; } - final TreeNode currentRoot = state.getRoot(); - final Optional newRoot = m.getStrategy().apply(m.getRootModification(), - Optional.of(currentRoot), m.getVersion()); - Preconditions.checkState(newRoot.isPresent(), "Apply strategy failed to produce root node"); - return new InMemoryDataTreeCandidate(PUBLIC_ROOT_PATH, root, currentRoot, newRoot.get()); + final ModificationApplyOperation rootNode = getOperation(rootSchemaNode); + DataTreeState currentState; + DataTreeState newState; + do { + currentState = currentState(); + newState = currentState.withSchemaContext(newSchemaContext, rootNode); + // TODO: can we lower this to compareAndSwapRelease? + } while (!STATE.compareAndSet(this, currentState, newState)); + } + + @Override + public InMemoryDataTreeSnapshot takeSnapshot() { + return currentState().newSnapshot(); } @Override @@ -102,31 +141,61 @@ final class InMemoryDataTree implements DataTree { if (candidate instanceof NoopDataTreeCandidate) { return; } + if (!(candidate instanceof InMemoryDataTreeCandidate)) { + throw new IllegalArgumentException("Invalid candidate class " + candidate.getClass()); + } - Preconditions.checkArgument(candidate instanceof InMemoryDataTreeCandidate, "Invalid candidate class %s", candidate.getClass()); final InMemoryDataTreeCandidate c = (InMemoryDataTreeCandidate)candidate; - if (LOG.isTraceEnabled()) { - LOG.trace("Data Tree is {}", NormalizedNodes.toStringTree(c.getAfterRoot().getData())); + LOG.trace("Data Tree is {}", NormalizedNodes.toStringTree(c.getTipRoot().getData())); } - final TreeNode newRoot = c.getAfterRoot(); - DataTreeState currentState, newState; + final TreeNode newRoot = c.getTipRoot(); + DataTreeState currentState; + DataTreeState newState; do { - currentState = state; + currentState = currentState(); final TreeNode currentRoot = currentState.getRoot(); LOG.debug("Updating datastore from {} to {}", currentRoot, newRoot); final TreeNode oldRoot = c.getBeforeRoot(); - Preconditions.checkState(oldRoot == currentRoot, "Store tree %s and candidate base %s differ.", currentRoot, oldRoot); + if (oldRoot != currentRoot) { + final String oldStr = simpleToString(oldRoot); + final String currentStr = simpleToString(currentRoot); + throw new IllegalStateException("Store tree " + currentStr + " and candidate base " + oldStr + + " differ."); + } newState = currentState.withRoot(newRoot); LOG.trace("Updated state from {} to {}", currentState, newState); - } while (!STATE_UPDATER.compareAndSet(this, currentState, newState)); + // TODO: can we lower this to compareAndSwapRelease? + } while (!STATE.compareAndSet(this, currentState, newState)); + } + + private static String simpleToString(final Object obj) { + return obj.getClass().getName() + "@" + Integer.toHexString(obj.hashCode()); + } + + private DataTreeState currentState() { + return (DataTreeState) STATE.getAcquire(this); + } + + @Override + public YangInstanceIdentifier getRootPath() { + return treeConfig.getRootPath(); } @Override public String toString() { - return MoreObjects.toStringHelper(this).add("object", super.toString()).add("state", state).toString(); + return MoreObjects.toStringHelper(this) + .add("object", super.toString()) + .add("config", treeConfig) + .add("state", currentState()) + .toString(); + } + + @Override + protected TreeNode getTipRoot() { + return currentState().getRoot(); } }