From 735503bdc3868c9cddd6eea62551d4c395da822a Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Mon, 26 Oct 2020 02:28:00 +0100 Subject: [PATCH] Cache leaf lookups When we are binding a method name to its schema, we are performing the inverse lookup. Instantiate a cache for these, so we end up searching only once in DefaultQueryFactory's life. JIRA: MDSAL-611 Change-Id: Idf9f3de22d907036478dc289387746eeb5a719a7 Signed-off-by: Robert Varga --- .../query/DefaultDescendantQueryBuilder.java | 5 +- .../adapter/query/DefaultQueryFactory.java | 73 ++++++++++++++++++- .../dom/adapter/query/QueryBuilderState.java | 26 ++----- 3 files changed, 81 insertions(+), 23 deletions(-) diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/DefaultDescendantQueryBuilder.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/DefaultDescendantQueryBuilder.java index c99ab47ee5..9d8929bfda 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/DefaultDescendantQueryBuilder.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/DefaultDescendantQueryBuilder.java @@ -11,7 +11,6 @@ import org.opendaylight.mdsal.binding.api.query.DescendantQueryBuilder; import org.opendaylight.mdsal.binding.api.query.MatchBuilderPath; import org.opendaylight.mdsal.binding.api.query.QueryExpression; import org.opendaylight.mdsal.binding.api.query.QueryStructureException; -import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree; import org.opendaylight.yangtools.yang.binding.ChildOf; import org.opendaylight.yangtools.yang.binding.ChoiceIn; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -23,8 +22,8 @@ final class DefaultDescendantQueryBuilder childPath; private final QueryBuilderState builder; - DefaultDescendantQueryBuilder(final BindingCodecTree codec, final InstanceIdentifier rootPath) { - this.builder = new QueryBuilderState(codec, rootPath); + DefaultDescendantQueryBuilder(final DefaultQueryFactory factory, final InstanceIdentifier rootPath) { + this.builder = new QueryBuilderState(factory, rootPath); this.childPath = rootPath.builder(); } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/DefaultQueryFactory.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/DefaultQueryFactory.java index 29e0ad62f3..c254625711 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/DefaultQueryFactory.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/DefaultQueryFactory.java @@ -10,21 +10,75 @@ package org.opendaylight.mdsal.binding.dom.adapter.query; import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; +import com.google.common.base.Throwables; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import java.util.ServiceLoader; +import java.util.concurrent.ExecutionException; import javax.inject.Inject; import javax.inject.Singleton; +import org.eclipse.jdt.annotation.NonNull; import org.kohsuke.MetaInfServices; import org.opendaylight.mdsal.binding.api.query.DescendantQueryBuilder; import org.opendaylight.mdsal.binding.api.query.QueryFactory; +import org.opendaylight.mdsal.binding.api.query.QueryStructureException; import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree; +import org.opendaylight.mdsal.binding.dom.codec.spi.BindingSchemaMapping; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Beta @MetaInfServices @Singleton public final class DefaultQueryFactory implements QueryFactory { - private final BindingCodecTree codec; + private static final class MethodId { + final DataNodeContainer parent; + final String methodName; + + MethodId(final DataNodeContainer parent, final String methodName) { + this.parent = requireNonNull(parent); + this.methodName = requireNonNull(methodName); + } + + @Override + public int hashCode() { + return parent.hashCode() * 31 + methodName.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof MethodId)) { + return false; + } + final MethodId other = (MethodId) obj; + return methodName.equals(other.methodName) && parent.equals(other.parent); + } + } + + private static final Logger LOG = LoggerFactory.getLogger(DefaultQueryFactory.class); + + private final LoadingCache knownMethods = CacheBuilder.newBuilder().build( + new CacheLoader() { + @Override + public NodeIdentifier load(final MethodId key) { + final DataNodeContainer parent = key.parent; + final String methodName = key.methodName; + + for (DataSchemaNode child : parent.getChildNodes()) { + if (methodName.equals(BindingSchemaMapping.getGetterMethodName(child))) { + return NodeIdentifier.create(child.getQName()); + } + } + throw new QueryStructureException("Failed to find schema matching " + methodName + " in " + parent); + } + }); + private final @NonNull BindingCodecTree codec; public DefaultQueryFactory() { this(ServiceLoader.load(BindingCodecTree.class).findFirst().orElseThrow()); @@ -37,6 +91,21 @@ public final class DefaultQueryFactory implements QueryFactory { @Override public DescendantQueryBuilder querySubtree(final InstanceIdentifier rootPath) { - return new DefaultDescendantQueryBuilder<>(codec, rootPath); + return new DefaultDescendantQueryBuilder<>(this, rootPath); + } + + @NonNull BindingCodecTree codec() { + return codec; + } + + @NonNull NodeIdentifier findChild(final DataNodeContainer parent, final String methodName) { + try { + return knownMethods.get(new MethodId(parent, methodName)); + } catch (ExecutionException e) { + LOG.debug("Failed to find method for {}", methodName, e); + final Throwable cause = e.getCause(); + Throwables.throwIfUnchecked(cause); + throw new IllegalStateException("Failed to load cache", e); + } } } diff --git a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryBuilderState.java b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryBuilderState.java index e6cb12181d..c57497c0de 100644 --- a/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryBuilderState.java +++ b/binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryBuilderState.java @@ -16,12 +16,10 @@ import java.util.List; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.mdsal.binding.api.query.MatchBuilderPath.LeafReference; import org.opendaylight.mdsal.binding.api.query.QueryExpression; -import org.opendaylight.mdsal.binding.api.query.QueryStructureException; import org.opendaylight.mdsal.binding.dom.adapter.query.LambdaDecoder.LambdaTarget; import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree; import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode; import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode; -import org.opendaylight.mdsal.binding.dom.codec.spi.BindingSchemaMapping; import org.opendaylight.mdsal.dom.api.query.DOMQuery; import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate; import org.opendaylight.yangtools.concepts.Immutable; @@ -30,7 +28,6 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus; final class QueryBuilderState { @@ -44,15 +41,17 @@ final class QueryBuilderState { } } - private final BindingCodecTree codec; - private final List predicates = new ArrayList<>(); + private final DefaultQueryFactory factory; private final YangInstanceIdentifier root; + private final BindingCodecTree codec; + private YangInstanceIdentifier absoluteSelect; private YangInstanceIdentifier relativeSelect; - QueryBuilderState(final BindingCodecTree codec, final InstanceIdentifier root) { - this.codec = requireNonNull(codec); + QueryBuilderState(final DefaultQueryFactory factory, final InstanceIdentifier root) { + this.codec = factory.codec(); + this.factory = factory; this.root = fromBinding(root); } @@ -77,12 +76,12 @@ final class QueryBuilderState { final LambdaTarget targetLeaf = LambdaDecoder.resolveLambda(ref); verify(targetLeaf.targetClass.equals(bindingPath.getTargetType().getName()), "Mismatched target %s and path %s", targetLeaf, bindingPath); - final DataSchemaNode child = findChild((DataNodeContainer) targetSchema, targetLeaf.targetMethod); + final NodeIdentifier childId = factory.findChild((DataNodeContainer) targetSchema, targetLeaf.targetMethod); final YangInstanceIdentifier absTarget = fromBinding(bindingPath); final YangInstanceIdentifier relTarget = absTarget.relativeTo(absoluteSelect) .orElseThrow(() -> new IllegalStateException(absoluteSelect + " is not an ancestor of " + absTarget)); - return new BoundMethod(relTarget, targetCodec.yangPathArgumentChild(NodeIdentifier.create(child.getQName()))); + return new BoundMethod(relTarget, targetCodec.yangPathArgumentChild(childId)); } void addPredicate(final DOMQueryPredicate predicate) { @@ -96,13 +95,4 @@ final class QueryBuilderState { private @NonNull YangInstanceIdentifier fromBinding(final InstanceIdentifier bindingId) { return codec.getInstanceIdentifierCodec().fromBinding(bindingId); } - - private static DataSchemaNode findChild(final DataNodeContainer parent, final String methodName) { - for (DataSchemaNode child : parent.getChildNodes()) { - if (methodName.equals(BindingSchemaMapping.getGetterMethodName(child))) { - return child; - } - } - throw new QueryStructureException("Failed to find schema matching " + methodName + " in " + parent); - } } -- 2.36.6