--- /dev/null
+/*
+ * 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.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.TypedSchemaNode;
+
+/**
+ * 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 {
+ // Weak keys to retire the entry when SchemaContext goes away
+ // Soft values to keep unreferenced factories around for a bit
+ private static final LoadingCache<SchemaContext, EagerJSONCodecFactory> CACHE = CacheBuilder.newBuilder()
+ .weakKeys().softValues().build(new CacheLoader<SchemaContext, EagerJSONCodecFactory>() {
+ @Override
+ public EagerJSONCodecFactory load(final SchemaContext key) {
+ return new EagerJSONCodecFactory(key);
+ }
+ });
+
+ private final Map<TypedSchemaNode, JSONCodec<?>> codecs;
+
+ EagerJSONCodecFactory(final SchemaContext context) {
+ super(context);
+ this.codecs = constructCodecs(context);
+ }
+
+ static EagerJSONCodecFactory getIfPresent(final SchemaContext context) {
+ return CACHE.getIfPresent(context);
+ }
+
+ static EagerJSONCodecFactory get(final SchemaContext context) {
+ return CACHE.getUnchecked(context);
+ }
+
+ @Override
+ JSONCodec<?> codecFor(final TypedSchemaNode schema) {
+ final JSONCodec<?> ret = codecs.get(schema);
+ Preconditions.checkArgument(ret != null, "No codec available for schema %s", schema);
+ return ret;
+ }
+
+ private static Map<TypedSchemaNode, JSONCodec<?>> constructCodecs(final SchemaContext context) {
+ final LazyJSONCodecFactory lazy = new LazyJSONCodecFactory(context);
+ requestCodecsForChildren(lazy, context);
+ return lazy.getCodecs();
+ }
+
+ private static void requestCodecsForChildren(final LazyJSONCodecFactory factory, final DataNodeContainer parent) {
+ for (DataSchemaNode child : parent.getChildNodes()) {
+ if (child instanceof TypedSchemaNode) {
+ factory.codecFor(child);
+ } else if (child instanceof DataNodeContainer) {
+ requestCodecsForChildren(factory, (DataNodeContainer)child);
+ }
+ }
+ }
+}
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
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 com.google.gson.stream.JsonWriter;
import java.io.IOException;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
* a particular {@link SchemaContext}, but can be reused by multiple {@link JSONNormalizedNodeStreamWriter}s.
*/
@Beta
-public final class JSONCodecFactory {
+public abstract class JSONCodecFactory {
private static final Logger LOG = LoggerFactory.getLogger(JSONCodecFactory.class);
private static final JSONCodec<Object> NULL_CODEC = new JSONCodec<Object>() {
@Override
}
};
- private final LoadingCache<TypedSchemaNode, JSONCodec<?>> codecs = CacheBuilder.newBuilder().softValues()
- .build(new CacheLoader<TypedSchemaNode, JSONCodec<?>>() {
- @Override
- public JSONCodec<?> load(@Nonnull final TypedSchemaNode key) throws Exception {
- final TypeDefinition<?> type = key.getType();
- return createCodec(key, type);
- }
- });
-
private final SchemaContext schemaContext;
private final JSONCodec<?> iidCodec;
- private JSONCodecFactory(final SchemaContext context) {
+ JSONCodecFactory(final SchemaContext context) {
this.schemaContext = Preconditions.checkNotNull(context);
iidCodec = new JSONStringInstanceIdentifierCodec(context, this);
}
* @return A codec factory instance.
*/
public static JSONCodecFactory create(final SchemaContext context) {
- return new JSONCodecFactory(context);
+ return SharedJSONCodecFactory.get(context);
}
- private JSONCodec<?> createCodec(final DataSchemaNode key, final TypeDefinition<?> type) {
+ final JSONCodec<?> createCodec(final DataSchemaNode key, final TypeDefinition<?> type) {
if (type instanceof LeafrefTypeDefinition) {
return createReferencedTypeCodec(key, (LeafrefTypeDefinition) type);
} else if (type instanceof IdentityrefTypeDefinition) {
- return createIdentityrefTypeCodec(key);
+ return new JSONStringIdentityrefCodec(schemaContext, key.getQName().getModule());
} else if (type instanceof UnionTypeDefinition) {
return createUnionTypeCodec(key, (UnionTypeDefinition) type);
- }
- return createFromSimpleType(key, type);
- }
-
- private JSONCodec<?> createReferencedTypeCodec(final DataSchemaNode schema,
- final LeafrefTypeDefinition type) {
- // FIXME: Verify if this does indeed support leafref of leafref
- final TypeDefinition<?> referencedType =
- SchemaContextUtil.getBaseTypeForLeafRef(type, getSchemaContext(), schema);
- Verify.verifyNotNull(referencedType, "Unable to find base type for leafref node '%s'.", schema.getPath());
- return createCodec(schema, referencedType);
- }
-
- private JSONCodec<QName> createIdentityrefTypeCodec(final DataSchemaNode schema) {
- final JSONCodec<QName> jsonStringIdentityrefCodec =
- new JSONStringIdentityrefCodec(schemaContext, schema.getQName().getModule());
- return jsonStringIdentityrefCodec;
- }
-
- private JSONCodec<Object> createUnionTypeCodec(final DataSchemaNode schema, final UnionTypeDefinition type) {
- final JSONCodec<Object> jsonStringUnionCodec = new JSONStringUnionCodec(schema, type, this);
- return jsonStringUnionCodec;
- }
-
- private JSONCodec<?> createFromSimpleType(final DataSchemaNode schema, final TypeDefinition<?> type) {
- if (type instanceof InstanceIdentifierTypeDefinition) {
+ } else if (type instanceof InstanceIdentifierTypeDefinition) {
return iidCodec;
- }
- if (type instanceof EmptyTypeDefinition) {
+ } else if (type instanceof EmptyTypeDefinition) {
return JSONEmptyCodec.INSTANCE;
}
final TypeDefinitionAwareCodec<Object, ?> codec = TypeDefinitionAwareCodec.from(type);
if (codec == null) {
- LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName()
- .getLocalName());
+ // catches anyxml
+ LOG.debug("Codec for {} is not implemented yet", type);
return NULL_CODEC;
}
return AbstractJSONCodec.create(codec);
}
- SchemaContext getSchemaContext() {
+ final SchemaContext getSchemaContext() {
return schemaContext;
}
JSONCodec<?> codecFor(final DataSchemaNode schema) {
Preconditions.checkArgument(schema instanceof TypedSchemaNode, "Unsupported node type %s", schema.getClass());
- return codecs.getUnchecked((TypedSchemaNode) schema);
+ return codecFor((TypedSchemaNode) schema);
}
- JSONCodec<?> codecFor(final DataSchemaNode schema, final TypeDefinition<?> unionSubType) {
+ abstract JSONCodec<?> codecFor(final TypedSchemaNode schema);
+
+ final JSONCodec<?> codecFor(final DataSchemaNode schema, final TypeDefinition<?> unionSubType) {
return createCodec(schema, unionSubType);
}
+
+ private JSONCodec<?> createReferencedTypeCodec(final DataSchemaNode schema, final LeafrefTypeDefinition type) {
+ // FIXME: Verify if this does indeed support leafref of leafref
+ final TypeDefinition<?> referencedType = SchemaContextUtil.getBaseTypeForLeafRef(type, schemaContext, schema);
+ Verify.verifyNotNull(referencedType, "Unable to find base type for leafref node '%s'.", schema.getPath());
+ return createCodec(schema, referencedType);
+ }
+
+ private JSONCodec<Object> createUnionTypeCodec(final DataSchemaNode schema, final UnionTypeDefinition type) {
+ final JSONCodec<Object> jsonStringUnionCodec = new JSONStringUnionCodec(schema, type, this);
+ return jsonStringUnionCodec;
+ }
}
--- /dev/null
+/*
+ * 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.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 repetetive data.
+ *
+ * @author Robert Varga
+ */
+@NotThreadSafe
+final class LazyJSONCodecFactory extends JSONCodecFactory {
+ private final Map<TypedSchemaNode, JSONCodec<?>> codecs = new IdentityHashMap<>();
+
+ LazyJSONCodecFactory(final SchemaContext context) {
+ super(context);
+ }
+
+ @Override
+ JSONCodec<?> codecFor(final TypedSchemaNode schema) {
+ return codecs.computeIfAbsent(schema, node -> createCodec(schema, schema.getType()));
+ }
+
+ // Used by EagerJSONCodecFactory. The map is leaked as-is, as we do not expect it to be touched again.
+ Map<TypedSchemaNode, JSONCodec<?>> getCodecs() {
+ return codecs;
+ }
+}
--- /dev/null
+/*
+ * 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.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+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<SchemaContext, SharedJSONCodecFactory> CACHE = CacheBuilder.newBuilder()
+ .weakKeys().build(new CacheLoader<SchemaContext, SharedJSONCodecFactory>() {
+ @Override
+ public SharedJSONCodecFactory load(final SchemaContext key) {
+ return new SharedJSONCodecFactory(key);
+ }
+ });
+
+ // Soft values to keep unreferenced codecs around for a bit, but eventually we want them to go away
+ private final LoadingCache<TypedSchemaNode, JSONCodec<?>> codecs = CacheBuilder.newBuilder().softValues()
+ .build(new CacheLoader<TypedSchemaNode, JSONCodec<?>>() {
+ @Override
+ public JSONCodec<?> load(@Nonnull final TypedSchemaNode key) {
+ return createCodec(key, key.getType());
+ }
+ });
+
+ 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<?> codecFor(final TypedSchemaNode schema) {
+ return codecs.getUnchecked(schema);
+ }
+}
--- /dev/null
+/*
+ * 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.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<?> codecFor(final TypedSchemaNode schema) {
+ return createCodec(schema, schema.getType());
+ }
+}