From: Robert Varga Date: Sun, 26 Mar 2017 14:09:40 +0000 (+0200) Subject: BUG-7983: extract codec caching from JSONCodecFactory X-Git-Tag: release/carbon~21 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F63%2F53863%2F2;p=yangtools.git BUG-7983: extract codec caching from JSONCodecFactory Caching aspect of CodecFactory is independent of the actual codec, factor it out for reuse with XML codecs. Change-Id: Ib6678a038482a2f50a8a55a0065158e3bc840cd8 Signed-off-by: Robert Varga --- diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/EagerJSONCodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/EagerJSONCodecFactory.java deleted file mode 100644 index c9d4b944a2..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/EagerJSONCodecFactory.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2017 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.yangtools.yang.data.codec.gson; - -import com.google.common.base.Preconditions; -import com.google.common.base.Stopwatch; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import java.util.Map; -import javax.annotation.concurrent.ThreadSafe; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.TypeDefinition; -import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Pre-computed JSONCodecFactory. All possible codecs are created upfront at instantiation time, after which they - * are available for the cost of a constant lookup. - * - * @author Robert Varga - */ -@ThreadSafe -final class EagerJSONCodecFactory extends JSONCodecFactory { - private static final Logger LOG = LoggerFactory.getLogger(EagerJSONCodecFactory.class); - - // Weak keys to retire the entry when SchemaContext goes away - private static final LoadingCache CACHE = CacheBuilder.newBuilder() - .weakKeys().build(new CacheLoader() { - @Override - public EagerJSONCodecFactory load(final SchemaContext key) { - final Stopwatch sw = Stopwatch.createStarted(); - final LazyJSONCodecFactory lazy = new LazyJSONCodecFactory(key); - final int visitedLeaves = requestCodecsForChildren(lazy, key); - sw.stop(); - - final Map, JSONCodec> simple = lazy.getSimpleCodecs(); - final Map> complex = lazy.getComplexCodecs(); - - LOG.debug("{} leaf nodes resulted in {} simple and {} complex codecs in {}", visitedLeaves, - simple.size(), complex.size(), sw); - return new EagerJSONCodecFactory(key, simple, complex); - } - }); - - private final Map, JSONCodec> simpleCodecs; - private final Map> complexCodecs; - - private EagerJSONCodecFactory(final SchemaContext context, final Map, JSONCodec> simpleCodecs, - final Map> complexCodecs) { - super(context); - this.simpleCodecs = Preconditions.checkNotNull(simpleCodecs); - this.complexCodecs = Preconditions.checkNotNull(complexCodecs); - } - - static EagerJSONCodecFactory get(final SchemaContext context) { - return CACHE.getUnchecked(context); - } - - static EagerJSONCodecFactory getIfPresent(final SchemaContext context) { - return CACHE.getIfPresent(context); - } - - @Override - JSONCodec lookupComplex(final TypedSchemaNode schema) { - final JSONCodec ret = complexCodecs.get(schema); - Preconditions.checkArgument(ret != null, "No codec available for schema %s", schema); - return ret; - } - - @Override - JSONCodec lookupSimple(final TypeDefinition type) { - return simpleCodecs.get(type); - } - - @Override - JSONCodec getComplex(final TypedSchemaNode schema, final JSONCodec codec) { - throw new IllegalStateException("Uncached codec for " + schema); - } - - @Override - JSONCodec getSimple(final TypeDefinition type, final JSONCodec codec) { - throw new IllegalStateException("Uncached codec for " + type); - } - - static int requestCodecsForChildren(final LazyJSONCodecFactory lazy, final DataNodeContainer parent) { - int ret = 0; - for (DataSchemaNode child : parent.getChildNodes()) { - if (child instanceof TypedSchemaNode) { - lazy.codecFor((TypedSchemaNode) child); - ++ret; - } else if (child instanceof DataNodeContainer) { - ret += requestCodecsForChildren(lazy, (DataNodeContainer) child); - } - } - - return ret; - } -} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java index 6ee91fd49b..60df4740fa 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONCodecFactory.java @@ -10,11 +10,13 @@ package org.opendaylight.yangtools.yang.data.codec.gson; import com.google.common.annotations.Beta; import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import com.google.common.base.Stopwatch; import com.google.common.base.Verify; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import org.opendaylight.yangtools.yang.data.impl.codec.AbstractIntegerStringCodec; import org.opendaylight.yangtools.yang.data.impl.codec.BinaryStringCodec; import org.opendaylight.yangtools.yang.data.impl.codec.BitsStringCodec; @@ -22,6 +24,13 @@ import org.opendaylight.yangtools.yang.data.impl.codec.BooleanStringCodec; import org.opendaylight.yangtools.yang.data.impl.codec.DecimalStringCodec; import org.opendaylight.yangtools.yang.data.impl.codec.EnumStringCodec; import org.opendaylight.yangtools.yang.data.impl.codec.StringStringCodec; +import org.opendaylight.yangtools.yang.data.util.codec.CodecCache; +import org.opendaylight.yangtools.yang.data.util.codec.LazyCodecCache; +import org.opendaylight.yangtools.yang.data.util.codec.NoopCodecCache; +import org.opendaylight.yangtools.yang.data.util.codec.PrecomputedCodecCache; +import org.opendaylight.yangtools.yang.data.util.codec.SharedCodecCache; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode; @@ -52,14 +61,60 @@ import org.slf4j.LoggerFactory; * {@link #createLazy(SchemaContext)} and {@link #createSimple(SchemaContext)} for details. */ @Beta -public abstract class JSONCodecFactory { +public final class JSONCodecFactory { + private static final class EagerCacheLoader extends CacheLoader { + @Override + public JSONCodecFactory load(final SchemaContext key) { + final Stopwatch sw = Stopwatch.createStarted(); + final LazyCodecCache> lazyCache = new LazyCodecCache<>(); + final JSONCodecFactory lazy = new JSONCodecFactory(key, lazyCache); + final int visitedLeaves = requestCodecsForChildren(lazy, key); + sw.stop(); + + final PrecomputedCodecCache> cache = lazyCache.toPrecomputed(); + LOG.debug("{} leaf nodes resulted in {} simple and {} complex codecs in {}", visitedLeaves, + cache.simpleSize(), cache.complexSize(), sw); + return new JSONCodecFactory(key, cache); + } + + private static int requestCodecsForChildren(final JSONCodecFactory lazy, final DataNodeContainer parent) { + int ret = 0; + for (DataSchemaNode child : parent.getChildNodes()) { + if (child instanceof TypedSchemaNode) { + lazy.codecFor((TypedSchemaNode) child); + ++ret; + } else if (child instanceof DataNodeContainer) { + ret += requestCodecsForChildren(lazy, (DataNodeContainer) child); + } + } + + return ret; + } + } + private static final Logger LOG = LoggerFactory.getLogger(JSONCodecFactory.class); + // Weak keys to retire the entry when SchemaContext goes away + private static final LoadingCache PRECOMPUTED = CacheBuilder.newBuilder() + .weakKeys().build(new EagerCacheLoader()); + + // Weak keys to retire the entry when SchemaContext goes away and to force identity-based lookup + private static final LoadingCache SHARED = CacheBuilder.newBuilder() + .weakKeys().build(new CacheLoader() { + @Override + public JSONCodecFactory load(final SchemaContext key) { + return new JSONCodecFactory(key, new SharedCodecCache<>()); + } + }); + + + private final CodecCache> cache; private final SchemaContext schemaContext; private final JSONCodec iidCodec; - JSONCodecFactory(final SchemaContext context) { + JSONCodecFactory(final SchemaContext context, final CodecCache> cache) { this.schemaContext = Preconditions.checkNotNull(context); + this.cache = Preconditions.checkNotNull(cache); iidCodec = new JSONStringInstanceIdentifierCodec(context, this); } @@ -96,7 +151,7 @@ public abstract class JSONCodecFactory { * @throws NullPointerException if context is null */ public static JSONCodecFactory getPrecomputed(final SchemaContext context) { - return EagerJSONCodecFactory.get(context); + return PRECOMPUTED.getUnchecked(context); } /** @@ -111,7 +166,7 @@ public abstract class JSONCodecFactory { * @throws NullPointerException if context is null */ public static Optional getPrecomputedIfAvailable(final SchemaContext context) { - return Optional.fromNullable(EagerJSONCodecFactory.getIfPresent(context)); + return Optional.fromNullable(PRECOMPUTED.getIfPresent(context)); } /** @@ -127,7 +182,7 @@ public abstract class JSONCodecFactory { * @throws NullPointerException if context is null */ public static JSONCodecFactory getShared(final SchemaContext context) { - return SharedJSONCodecFactory.get(context); + return SHARED.getUnchecked(context); } /** @@ -143,7 +198,7 @@ public abstract class JSONCodecFactory { * @throws NullPointerException if context is null */ public static JSONCodecFactory createLazy(final SchemaContext context) { - return new LazyJSONCodecFactory(context); + return new JSONCodecFactory(context, new LazyCodecCache<>()); } /** @@ -159,7 +214,7 @@ public abstract class JSONCodecFactory { * @throws NullPointerException if context is null. */ public static JSONCodecFactory createSimple(final SchemaContext context) { - return new SimpleJSONCodecFactory(context); + return new JSONCodecFactory(context, NoopCodecCache.getInstance()); } final JSONCodec codecFor(final TypedSchemaNode schema) { @@ -176,12 +231,12 @@ public abstract class JSONCodecFactory { * We assume prevalence is in above order and that caching is effective. We therefore */ final TypeDefinition type = schema.getType(); - JSONCodec ret = lookupSimple(type); + JSONCodec ret = cache.lookupSimple(type); if (ret != null) { LOG.trace("Type {} hit simple {}", type, ret); return ret; } - ret = lookupComplex(schema); + ret = cache.lookupComplex(schema); if (ret != null) { LOG.trace("Type {} hit complex {}", type, ret); return ret; @@ -197,47 +252,13 @@ public abstract class JSONCodecFactory { // ... and complex types afterwards ret = createComplexCodecFor(schema, type); LOG.trace("Type {} miss complex {}", type, ret); - return getComplex(schema, ret); + return cache.getComplex(schema, ret); } final SchemaContext getSchemaContext() { return schemaContext; } - /** - * Lookup a complex codec for schema node. - * - * @param schema Schema node - * @return Cached codec, or null if no codec is cached. - */ - @Nullable abstract JSONCodec lookupComplex(TypedSchemaNode schema); - - /** - * Lookup a simple codec for a type definition. - * - * @param type Type definitions - * @return Cached codec, or null if no codec is cached. - */ - @Nullable abstract JSONCodec lookupSimple(TypeDefinition type); - - /** - * Lookup-or-store a complex codec for a particular schema node. - * - * @param schema Schema node - * @param codec Codec to cache - * @return Codec instance, either already-cached, or the codec presented as argument. - */ - @Nonnull abstract JSONCodec getComplex(TypedSchemaNode schema, JSONCodec codec); - - /** - * Lookup-or-store a simple codec for a particular schema node. - * - * @param schema Schema node - * @param codec Codec to cache - * @return Codec instance, either already-cached, or the codec presented as argument. - */ - @Nonnull abstract JSONCodec getSimple(TypeDefinition type, JSONCodec codec); - private JSONCodec getSimpleCodecFor(final TypeDefinition type) { if (type instanceof InstanceIdentifierTypeDefinition) { // FIXME: there really are two favors, as 'require-instance true' needs to be validated. In order to deal @@ -278,7 +299,7 @@ public abstract class JSONCodecFactory { return null; } - return getSimple(type, Verify.verifyNotNull(ret)); + return cache.getSimple(type, Verify.verifyNotNull(ret)); } private JSONCodec createComplexCodecFor(final TypedSchemaNode schema, final TypeDefinition type) { @@ -317,7 +338,7 @@ public abstract class JSONCodecFactory { final List> codecs = new ArrayList<>(types.size()); for (TypeDefinition type : types) { - JSONCodec codec = lookupSimple(type); + JSONCodec codec = cache.lookupSimple(type); if (codec == null) { codec = Verify.verifyNotNull(getSimpleCodecFor(type), "Type %s did not resolve to a simple codec", type); @@ -334,7 +355,7 @@ public abstract class JSONCodecFactory { final List> codecs = new ArrayList<>(types.size()); for (TypeDefinition type : types) { - JSONCodec codec = lookupSimple(type); + JSONCodec codec = cache.lookupSimple(type); if (codec == null) { codec = getSimpleCodecFor(type); if (codec == null) { diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LazyJSONCodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LazyJSONCodecFactory.java deleted file mode 100644 index 424d50378f..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LazyJSONCodecFactory.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2017 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.yangtools.yang.data.codec.gson; - -import java.util.IdentityHashMap; -import java.util.Map; -import javax.annotation.concurrent.NotThreadSafe; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.TypeDefinition; -import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode; - -/** - * Lazily-computed JSONCodecFactory. This is a non-thread-safe factory, which performs caching of codecs. It is most - * appropriate for one-off encodings of repetitive data. - * - * @author Robert Varga - */ -@NotThreadSafe -final class LazyJSONCodecFactory extends JSONCodecFactory { - private final Map> complexCodecs = new IdentityHashMap<>(); - private final Map, JSONCodec> simpleCodecs = new IdentityHashMap<>(); - - LazyJSONCodecFactory(final SchemaContext context) { - super(context); - } - - @Override - JSONCodec getComplex(final TypedSchemaNode schema, final JSONCodec codec) { - return getComplexCodecs().computeIfAbsent(schema, any -> codec); - } - - @Override - JSONCodec lookupComplex(final TypedSchemaNode schema) { - return getComplexCodecs().get(schema); - } - - @Override - JSONCodec lookupSimple(final TypeDefinition type) { - return getSimpleCodecs().get(type); - } - - @Override - JSONCodec getSimple(final TypeDefinition type, final JSONCodec codec) { - return getSimpleCodecs().computeIfAbsent(type, any -> codec); - } - - Map, JSONCodec> getSimpleCodecs() { - return simpleCodecs; - } - - Map> getComplexCodecs() { - return complexCodecs; - } -} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SharedJSONCodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SharedJSONCodecFactory.java deleted file mode 100644 index 811ae3c2df..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SharedJSONCodecFactory.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2017 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.yangtools.yang.data.codec.gson; - -import com.google.common.base.Throwables; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import java.util.concurrent.ExecutionException; -import javax.annotation.concurrent.ThreadSafe; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.TypeDefinition; -import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode; - -/** - * A thread-safe lazily-populated codec factory. Instances are cached in an internal weak/soft cache. - * - * @author Robert Varga - */ -@ThreadSafe -final class SharedJSONCodecFactory extends JSONCodecFactory { - // Weak keys to retire the entry when SchemaContext goes away and to force identity-based lookup - private static final LoadingCache CACHE = CacheBuilder.newBuilder() - .weakKeys().build(new CacheLoader() { - @Override - public SharedJSONCodecFactory load(final SchemaContext key) { - return new SharedJSONCodecFactory(key); - } - }); - - // Weak keys to force identity lookup - // Soft values to keep unreferenced codecs around for a bit, but eventually we want them to go away - private final Cache, JSONCodec> simpleCodecs = CacheBuilder.newBuilder().weakKeys() - .softValues().build(); - private final Cache> complexCodecs = CacheBuilder.newBuilder().weakKeys().softValues() - .build(); - - SharedJSONCodecFactory(final SchemaContext context) { - super(context); - } - - static SharedJSONCodecFactory getIfPresent(final SchemaContext context) { - return CACHE.getIfPresent(context); - } - - static SharedJSONCodecFactory get(final SchemaContext context) { - return CACHE.getUnchecked(context); - } - - @Override - JSONCodec lookupComplex(final TypedSchemaNode schema) { - return complexCodecs.getIfPresent(schema); - } - - @Override - JSONCodec lookupSimple(final TypeDefinition type) { - return simpleCodecs.getIfPresent(type); - } - - @Override - JSONCodec getComplex(final TypedSchemaNode schema, final JSONCodec codec) { - try { - return complexCodecs.get(schema, () -> codec); - } catch (ExecutionException e) { - throw Throwables.propagate(e.getCause()); - } - } - - @Override - JSONCodec getSimple(final TypeDefinition type, final JSONCodec codec) { - try { - return simpleCodecs.get(type, () -> codec); - } catch (ExecutionException e) { - throw Throwables.propagate(e.getCause()); - } - } -} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SimpleJSONCodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SimpleJSONCodecFactory.java deleted file mode 100644 index 8a44a8629d..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/SimpleJSONCodecFactory.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2017 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.yangtools.yang.data.codec.gson; - -import javax.annotation.concurrent.ThreadSafe; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.TypeDefinition; -import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode; - -/** - * A simplistic factory, which does not perform any codec caching. Minimizes resident memory footprint at the expense - * of creating short-lived objects. - * - * @author Robert Varga - */ -@ThreadSafe -final class SimpleJSONCodecFactory extends JSONCodecFactory { - SimpleJSONCodecFactory(final SchemaContext context) { - super(context); - } - - @Override - JSONCodec lookupComplex(final TypedSchemaNode schema) { - return null; - } - - @Override - JSONCodec lookupSimple(final TypeDefinition type) { - return null; - } - - @Override - JSONCodec getSimple(final TypeDefinition type, final JSONCodec codec) { - return codec; - } - - @Override - JSONCodec getComplex(final TypedSchemaNode schema, final JSONCodec codec) { - return codec; - } -} diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/CodecCache.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/CodecCache.java new file mode 100644 index 0000000000..0d9925e1b1 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/CodecCache.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.yangtools.yang.data.util.codec; + +import com.google.common.annotations.Beta; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode; + +/** + * Abstract cache for codecs. + * + * @author Robert Varga + * + * @param Codec type + */ +@Beta +public abstract class CodecCache { + /** + * Lookup a complex codec for schema node. + * + * @param schema Schema node + * @return Cached codec, or null if no codec is cached. + */ + @Nullable public abstract T lookupComplex(TypedSchemaNode schema); + + /** + * Lookup a simple codec for a type definition. + * + * @param type Type definition + * @return Cached codec, or null if no codec is cached. + */ + @Nullable public abstract T lookupSimple(TypeDefinition type); + + /** + * Lookup-or-store a complex codec for a particular schema node. + * + * @param schema Schema node + * @param codec Codec to cache + * @return Codec instance, either already-cached, or the codec presented as argument. + */ + @Nonnull public abstract T getComplex(TypedSchemaNode schema, T codec); + + /** + * Lookup-or-store a simple codec for a particular schema node. + * + * @param type Type definition + * @param codec Codec to cache + * @return Codec instance, either already-cached, or the codec presented as argument. + */ + @Nonnull public abstract T getSimple(TypeDefinition type, T codec); +} diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/LazyCodecCache.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/LazyCodecCache.java new file mode 100644 index 0000000000..d1c7348124 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/LazyCodecCache.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.yangtools.yang.data.util.codec; + +import com.google.common.annotations.Beta; +import java.util.IdentityHashMap; +import java.util.Map; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode; + +/** + * Lazily-populated CodecCache. This is a non-thread-safe factory, which performs caching of codecs. It is most + * appropriate for one-off encodings of repetitive data. + * + * @author Robert Varga + */ +@Beta +@NotThreadSafe +public final class LazyCodecCache extends CodecCache { + private final Map complexCodecs = new IdentityHashMap<>(); + private final Map, T> simpleCodecs = new IdentityHashMap<>(); + + @Override + public T getComplex(final TypedSchemaNode schema, final T codec) { + return complexCodecs.computeIfAbsent(schema, any -> codec); + } + + @Override + public T lookupComplex(final TypedSchemaNode schema) { + return complexCodecs.get(schema); + } + + @Override + public T lookupSimple(final TypeDefinition type) { + return simpleCodecs.get(type); + } + + @Override + public T getSimple(final TypeDefinition type, final T codec) { + return simpleCodecs.computeIfAbsent(type, any -> codec); + } + + public PrecomputedCodecCache toPrecomputed() { + return new PrecomputedCodecCache<>(simpleCodecs, complexCodecs); + } +} diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/NoopCodecCache.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/NoopCodecCache.java new file mode 100644 index 0000000000..db1359d590 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/NoopCodecCache.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.yangtools.yang.data.util.codec; + +import com.google.common.annotations.Beta; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode; + +/** + * A no-operation codec cache. + * + * @author Robert Varga + * + * @param Codec type + */ +@Beta +@ThreadSafe +public final class NoopCodecCache extends CodecCache { + private static final NoopCodecCache INSTANCE = new NoopCodecCache<>(); + + private NoopCodecCache() { + // Hidden + } + + @SuppressWarnings("unchecked") + public static NoopCodecCache getInstance() { + return (NoopCodecCache) INSTANCE; + } + + @Override + public T lookupComplex(final TypedSchemaNode schema) { + return null; + } + + @Override + public T lookupSimple(final TypeDefinition type) { + return null; + } + + @Override + public T getSimple(final TypeDefinition type, final T codec) { + return codec; + } + + @Override + public T getComplex(final TypedSchemaNode schema, final T codec) { + return codec; + } +} diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/PrecomputedCodecCache.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/PrecomputedCodecCache.java new file mode 100644 index 0000000000..ec009a9c85 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/PrecomputedCodecCache.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.yangtools.yang.data.util.codec; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.util.Map; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode; + +/** + * Pre-computed CodecCache. All possible codecs are created upfront at instantiation time, after which they are + * available for the cost of a constant lookup. + * + * Instantiation needs to occur through {@link LazyCodecCache#toPrecomputed()} after the lazy cache has been fully + * populated. + * + * @author Robert Varga + */ +@Beta +@ThreadSafe +public final class PrecomputedCodecCache extends CodecCache { + private final Map, T> simpleCodecs; + private final Map complexCodecs; + + PrecomputedCodecCache(final Map, T> simpleCodecs, final Map complexCodecs) { + this.simpleCodecs = Preconditions.checkNotNull(simpleCodecs); + this.complexCodecs = Preconditions.checkNotNull(complexCodecs); + } + + @Override + public T lookupComplex(final TypedSchemaNode schema) { + final T ret = complexCodecs.get(schema); + Preconditions.checkArgument(ret != null, "No codec available for schema %s", schema); + return ret; + } + + @Override + public T lookupSimple(final TypeDefinition type) { + return simpleCodecs.get(type); + } + + @Override + public T getComplex(final TypedSchemaNode schema, final T codec) { + throw new IllegalStateException("Uncached codec for " + schema); + } + + @Override + public T getSimple(final TypeDefinition type, final T codec) { + throw new IllegalStateException("Uncached codec for " + type); + } + + public int complexSize() { + return complexCodecs.size(); + } + + public int simpleSize() { + return simpleCodecs.size(); + } +} diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/SharedCodecCache.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/SharedCodecCache.java new file mode 100644 index 0000000000..072f7aa3d1 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/codec/SharedCodecCache.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.yangtools.yang.data.util.codec; + +import com.google.common.annotations.Beta; +import com.google.common.base.Throwables; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import java.util.concurrent.ExecutionException; +import javax.annotation.concurrent.ThreadSafe; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode; + +/** + * A thread-safe lazily-populated codec cache. Instances are cached in an internal weak/soft cache. + * + * @author Robert Varga + */ +@Beta +@ThreadSafe +public final class SharedCodecCache extends CodecCache { + // Weak keys to force identity lookup + // Soft values to keep unreferenced codecs around for a bit, but eventually we want them to go away + private final Cache, T> simpleCodecs = CacheBuilder.newBuilder().weakKeys().softValues().build(); + private final Cache complexCodecs = CacheBuilder.newBuilder().weakKeys().softValues().build(); + + @Override + public T lookupComplex(final TypedSchemaNode schema) { + return complexCodecs.getIfPresent(schema); + } + + @Override + public T lookupSimple(final TypeDefinition type) { + return simpleCodecs.getIfPresent(type); + } + + @Override + public T getComplex(final TypedSchemaNode schema, final T codec) { + try { + return complexCodecs.get(schema, () -> codec); + } catch (ExecutionException e) { + throw Throwables.propagate(e.getCause()); + } + } + + @Override + public T getSimple(final TypeDefinition type, final T codec) { + try { + return simpleCodecs.get(type, () -> codec); + } catch (ExecutionException e) { + throw Throwables.propagate(e.getCause()); + } + } +}