From: Robert Varga Date: Thu, 11 Apr 2019 15:40:28 +0000 (+0200) Subject: Teach BindingNormalizedNodeCache to cache leaf type objects X-Git-Tag: v4.0.1~23 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=d8e14e7435ad76f4c1ccb1080a3b0206f62f66e5;p=mdsal.git Teach BindingNormalizedNodeCache to cache leaf type objects This adds the final bits to enable CachingNormalizedNodeSerializer to actually cache leaf objects. JIRA: MDSAL-407 Change-Id: I39bac36395f4e6809342d48a375d441b49cbc3b8 Signed-off-by: Jie Han Signed-off-by: Robert Varga --- diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractBindingNormalizedNodeCacheHolder.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractBindingNormalizedNodeCacheHolder.java index d578a559d4..d900d4e8c8 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractBindingNormalizedNodeCacheHolder.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/AbstractBindingNormalizedNodeCacheHolder.java @@ -13,6 +13,8 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableSet; +import org.opendaylight.mdsal.binding.dom.codec.api.BindingObjectCodecTreeNode; +import org.opendaylight.mdsal.binding.dom.codec.impl.LeafNodeCodecContext.OfTypeObject; import org.opendaylight.yangtools.yang.binding.BindingObject; /** @@ -20,24 +22,32 @@ import org.opendaylight.yangtools.yang.binding.BindingObject; * associated. */ abstract class AbstractBindingNormalizedNodeCacheHolder { - private final LoadingCache, DataObjectNormalizedNodeCache> caches = CacheBuilder - .newBuilder().build(new CacheLoader, DataObjectNormalizedNodeCache>() { + @SuppressWarnings("rawtypes") + private final LoadingCache caches = CacheBuilder + .newBuilder().build(new CacheLoader() { @Override - public DataObjectNormalizedNodeCache load(final DataContainerCodecContext key) { - return new DataObjectNormalizedNodeCache(AbstractBindingNormalizedNodeCacheHolder.this, key); + public AbstractBindingNormalizedNodeCache load(final NodeCodecContext key) { + if (key instanceof DataContainerCodecContext) { + return new DataObjectNormalizedNodeCache(AbstractBindingNormalizedNodeCacheHolder.this, + (DataContainerCodecContext) key); + } else if (key instanceof OfTypeObject) { + return new TypeObjectNormalizedNodeCache<>((OfTypeObject)key); + } else { + throw new IllegalStateException("Unhandled context " + key); + } } }); + private final ImmutableSet> cacheSpec; AbstractBindingNormalizedNodeCacheHolder(final ImmutableSet> cacheSpec) { this.cacheSpec = requireNonNull(cacheSpec); } - DataObjectNormalizedNodeCache getCachingSerializer(final DataContainerCodecContext childCtx) { - if (isCached(childCtx.getBindingClass())) { - return caches.getUnchecked(childCtx); - } - return null; + @SuppressWarnings("unchecked") + > + AbstractBindingNormalizedNodeCache getCachingSerializer(final C childCtx) { + return isCached(childCtx.getBindingClass()) ? caches.getUnchecked(childCtx) : null; } final boolean isCached(final Class type) { diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeCodec.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeCodec.java index 5c89dae130..c215141f9b 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeCodec.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeCodec.java @@ -33,7 +33,7 @@ class CachingNormalizedNodeCodec extends AbstractBindingNo @Override public NormalizedNode serialize(final D data) { // Serialize data using stream writer with child cache enable or using the cache if it is available - final DataObjectNormalizedNodeCache cache = getCachingSerializer(context); + final AbstractBindingNormalizedNodeCache cache = getCachingSerializer(context); return cache == null ? CachingNormalizedNodeSerializer.serializeUsingStreamWriter(this, context, data) : cache.get(data); } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeSerializer.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeSerializer.java index 658bc8939c..4012de2538 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeSerializer.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CachingNormalizedNodeSerializer.java @@ -8,11 +8,15 @@ package org.opendaylight.mdsal.binding.dom.codec.impl; import java.io.IOException; +import org.opendaylight.mdsal.binding.dom.codec.impl.LeafNodeCodecContext.OfTypeObject; import org.opendaylight.yangtools.yang.binding.BindingSerializer; import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter; import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.TypeObject; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Serializer of Binding objects to Normalized Node which uses {@link DataObjectNormalizedNodeCache} to @@ -26,8 +30,9 @@ import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; * {@link org.opendaylight.yangtools.yang.binding.DataObjectSerializer} to provide Binding object * for inspection and to prevent streaming of already serialized object. */ -final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEventWriter implements - BindingSerializer { +final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEventWriter + implements BindingSerializer { + private static final Logger LOG = LoggerFactory.getLogger(CachingNormalizedNodeSerializer.class); private final NormalizedNodeResult domResult; private final NormalizedNodeWriterWithAddChild domWriter; @@ -51,6 +56,34 @@ final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEvent return domResult.getResult(); } + @Override + public void leafNode(final String localName, final Object value) throws IOException { + if (value instanceof TypeObject) { + // TypeObject is a tagging interface used for generated classes which wrap derived and restricted types. + // They are immutable and hence we can safely wrap them in LeafNodes and reuse them, if directed to do so. + final TypeObject typed = (TypeObject) value; + final Class type = typed.getClass(); + if (cacheHolder.isCached(type)) { + final ValueNodeCodecContext context = ((DataObjectCodecContext) delegate.current()) + .getLeafChild(localName); + if (context instanceof OfTypeObject) { + final AbstractBindingNormalizedNodeCache cache = cacheHolder.getCachingSerializer( + (OfTypeObject)context); + if (cache != null) { + // We have a cache hit and are thus done + domWriter.addChild(cache.get(typed)); + return; + } + + LOG.debug("Unexpected failure to acquire cache for context {}, skipping caching", context); + } else { + LOG.debug("Context {} does not match expected TypeObject {}, skipping caching", context, typed); + } + } + } + super.leafNode(localName, value); + } + /** * Serializes input if it is cached, returns null otherwise. * @@ -66,7 +99,8 @@ final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEvent */ @Override public NormalizedNode serialize(final DataObject input) { - final DataObjectNormalizedNodeCache cachingSerializer = getCacheSerializer(input.implementedInterface()); + final AbstractBindingNormalizedNodeCache cachingSerializer = getCacheSerializer( + input.implementedInterface()); if (cachingSerializer != null) { final NormalizedNode domData = cachingSerializer.get(input); domWriter.addChild(domData); @@ -75,7 +109,8 @@ final class CachingNormalizedNodeSerializer extends ForwardingBindingStreamEvent return null; } - private DataObjectNormalizedNodeCache getCacheSerializer(final Class type) { + private AbstractBindingNormalizedNodeCache getCacheSerializer( + final Class type) { if (cacheHolder.isCached(type)) { final DataContainerCodecContext currentCtx = (DataContainerCodecContext) delegate.current(); if (type.equals(currentCtx.getBindingClass())) { diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/TypeObjectNormalizedNodeCache.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/TypeObjectNormalizedNodeCache.java index a9152a4351..7046cbf3cb 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/TypeObjectNormalizedNodeCache.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/TypeObjectNormalizedNodeCache.java @@ -14,15 +14,14 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; /** * A cache of NormalizedNodes corresponding to a particular TypeObject instantiation. */ -final class TypeObjectNormalizedNodeCache> - extends AbstractBindingNormalizedNodeCache { +final class TypeObjectNormalizedNodeCache> + extends AbstractBindingNormalizedNodeCache { TypeObjectNormalizedNodeCache(final C rootContext) { super(rootContext); } @Override - public NormalizedNode load(final T key) { + public NormalizedNode load(final TypeObject key) { return rootContext().serialize(key); } } diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/CachingCodecTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/CachingCodecTest.java index a5280ccde5..a70e46733d 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/CachingCodecTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/test/CachingCodecTest.java @@ -8,6 +8,7 @@ package org.opendaylight.mdsal.binding.dom.codec.test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; @@ -26,8 +27,13 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.te import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelList; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.test.binding.rev140701.two.level.list.TopLevelListKey; +import org.opendaylight.yang.gen.v1.urn.test.leaf.caching.codec.rev190201.Cont; +import org.opendaylight.yang.gen.v1.urn.test.leaf.caching.codec.rev190201.ContBuilder; +import org.opendaylight.yang.gen.v1.urn.test.leaf.caching.codec.rev190201.MyType; +import org.opendaylight.yangtools.yang.binding.BindingObject; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; @@ -44,13 +50,20 @@ public class CachingCodecTest extends AbstractBindingCodecTest { private static final Top TOP_TWO_LIST_DATA = new TopBuilder().setTopLevelList(TWO_LIST).build(); private static final Top TOP_THREE_LIST_DATA = new TopBuilder().setTopLevelList(THREE_LIST).build(); + private static final NodeIdentifier LEAF_ARG = new NodeIdentifier(QName.create(Cont.QNAME, "caching")); + private static final InstanceIdentifier CONT_PATH = InstanceIdentifier.create(Cont.class); + private static final Cont CONT_DATA = new ContBuilder().setCaching(new MyType(1)).setNonCaching("test").build(); + private static final Cont CONT2_DATA = new ContBuilder().setCaching(new MyType(1)).setNonCaching("test2").build(); + private BindingDataObjectCodecTreeNode topNode; + private BindingDataObjectCodecTreeNode contNode; @Override @Before public void before() { super.before(); topNode = registry.getCodecContext().getSubtreeCodec(TOP_PATH); + contNode = registry.getCodecContext().getSubtreeCodec(CONT_PATH); } private static List createList(final int num) { @@ -91,6 +104,16 @@ public class CachingCodecTest extends AbstractBindingCodecTest { verifyListItemSame(first, third); } + @Test + public void testLeafCache() { + final BindingNormalizedNodeCachingCodec cachingCodec = createContCachingCodec(Cont.class, MyType.class); + final NormalizedNode first = cachingCodec.serialize(CONT_DATA); + final NormalizedNode second = cachingCodec.serialize(CONT2_DATA); + + assertNotEquals(first, second); + verifyLeafItemSame(first, second); + } + @Test public void testDefaultInvocation() { final BindingNormalizedNodeCachingCodec cachingCodec = createCachingCodec(Top.class, TopLevelList.class); @@ -114,6 +137,12 @@ public class CachingCodecTest extends AbstractBindingCodecTest { return topNode.createCachingCodec(ImmutableSet.copyOf(classes)); } + @SafeVarargs + private final BindingNormalizedNodeCachingCodec createContCachingCodec( + final Class... classes) { + return contNode.createCachingCodec(ImmutableSet.copyOf(classes)); + } + private static void verifyListItemSame(final NormalizedNode firstTop, final NormalizedNode secondTop) { final Collection initialNodes = getListItems(firstTop).getValue(); final MapNode secondMap = getListItems(secondTop); @@ -128,4 +157,10 @@ public class CachingCodecTest extends AbstractBindingCodecTest { private static MapNode getListItems(final NormalizedNode top) { return (MapNode) ((DataContainerNode) top).getChild(TOP_LEVEL_LIST_ARG).get(); } + + private static void verifyLeafItemSame(final NormalizedNode firstCont, + final NormalizedNode secondCont) { + assertSame(((DataContainerNode) firstCont).getChild(LEAF_ARG).get(), + ((DataContainerNode) secondCont).getChild(LEAF_ARG).get()); + } } diff --git a/binding/mdsal-binding-test-model/src/main/yang/opendaylight-test-leafcachingcodec.yang b/binding/mdsal-binding-test-model/src/main/yang/opendaylight-test-leafcachingcodec.yang new file mode 100644 index 0000000000..748f1eb90b --- /dev/null +++ b/binding/mdsal-binding-test-model/src/main/yang/opendaylight-test-leafcachingcodec.yang @@ -0,0 +1,22 @@ +module test-leaf-caching-codec { + namespace "urn:test:leaf:caching:codec"; + prefix test; + + revision 2019-02-01; + + typedef my-type { + type uint16 { + range "1..4094"; + } + } + + container cont{ + leaf caching { + type my-type; + } + + leaf non-caching { + type string; + } + } +}