2 * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.javav2.dom.codec.impl;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
13 import com.google.common.cache.CacheBuilder;
14 import com.google.common.cache.CacheLoader;
15 import com.google.common.cache.LoadingCache;
16 import java.io.IOException;
17 import java.util.AbstractMap.SimpleEntry;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Map.Entry;
21 import java.util.Optional;
22 import java.util.function.Function;
23 import javax.annotation.Nonnull;
24 import javax.annotation.Nullable;
25 import org.opendaylight.mdsal.binding.javav2.dom.codec.api.BindingTreeCodec;
26 import org.opendaylight.mdsal.binding.javav2.dom.codec.api.factory.BindingNormalizedNodeWriterFactory;
27 import org.opendaylight.mdsal.binding.javav2.dom.codec.api.factory.BindingTreeCodecFactory;
28 import org.opendaylight.mdsal.binding.javav2.dom.codec.api.serializer.BindingNormalizedNodeSerializer;
29 import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.api.TreeNodeSerializerGenerator;
30 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.NotificationCodecContext;
31 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.BindingCodecContext;
32 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.NodeCodecContext;
33 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.TreeNodeCodecContext;
34 import org.opendaylight.mdsal.binding.javav2.runtime.context.BindingRuntimeContext;
35 import org.opendaylight.mdsal.binding.javav2.runtime.context.ModuleInfoBackedContext;
36 import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
37 import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
38 import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
39 import org.opendaylight.mdsal.binding.javav2.spec.base.Notification;
40 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
41 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
42 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingStreamEventWriter;
43 import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializer;
44 import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerImplementation;
45 import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerRegistry;
46 import org.opendaylight.yangtools.concepts.Delegator;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
55 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
57 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
58 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
59 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
60 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
65 * Serializing and deserializing Binding and DOM data.
68 public class BindingNormalizedNodeCodecRegistry implements TreeNodeSerializerRegistry, BindingTreeCodecFactory,
69 BindingNormalizedNodeWriterFactory, BindingNormalizedNodeSerializer {
71 private static final Logger LOG = LoggerFactory.getLogger(BindingNormalizedNodeCodecRegistry.class);
73 private final TreeNodeSerializerGenerator generator;
74 private final LoadingCache<Class<? extends TreeNode>, TreeNodeSerializer> serializers;
75 private volatile BindingCodecContext codecContext;
78 * Prepare generator for generating serializers and create loader for serializers.
81 * - serializer generator
83 public BindingNormalizedNodeCodecRegistry(final TreeNodeSerializerGenerator generator) {
84 this.generator = requireNonNull(generator);
85 this.serializers = CacheBuilder.newBuilder().weakKeys().build(new GeneratorLoader());
89 public TreeNodeSerializer getSerializer(final Class<? extends TreeNode> type) {
90 return serializers.getUnchecked(type);
94 * Get binding tree codec context.
96 * @return codec context
98 public BindingTreeCodec getCodecContext() {
103 * Create codec context based on runtime context and notify generator that runtime context has been
109 public void onBindingRuntimeContextUpdated(final BindingRuntimeContext context) {
110 codecContext = create(context);
111 generator.onBindingRuntimeContextUpdated(context);
116 public YangInstanceIdentifier toYangInstanceIdentifier(@Nonnull final InstanceIdentifier<?> binding) {
117 return codecContext.getInstanceIdentifierCodec().serialize(binding);
122 public InstanceIdentifier<?> fromYangInstanceIdentifier(@Nonnull final YangInstanceIdentifier dom) {
123 return codecContext.getInstanceIdentifierCodec().deserialize(dom);
128 public <T extends TreeNode> Entry<YangInstanceIdentifier, NormalizedNode<?, ?>>
129 toNormalizedNode(final InstanceIdentifier<T> path, final T data) {
130 final NormalizedNodeResult result = new NormalizedNodeResult();
131 // We create DOM stream writer which produces normalized nodes
132 final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
134 // We create Binding Stream Writer which translates from Binding to Normalized Nodes
135 final Entry<YangInstanceIdentifier, BindingStreamEventWriter> writeCtx =
136 codecContext.newWriter(path, domWriter);
138 // We get serializer which reads binding data and uses Binding To Normalized Node writer to write
141 getSerializer(path.getTargetType()).serialize(data, writeCtx.getValue());
142 } catch (final IOException e) {
143 LOG.error("Unexpected failure while serializing path {} data {}", path, data, e);
144 throw new IllegalStateException("Failed to create normalized node", e);
146 return new SimpleEntry<>(writeCtx.getKey(), result.getResult());
150 @SuppressWarnings("rawtypes")
152 public ContainerNode toNormalizedNodeNotification(@Nonnull final Notification data) {
153 final NormalizedNodeResult result = new NormalizedNodeResult();
154 // We create DOM stream writer which produces normalized nodes
155 final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
156 @SuppressWarnings("unchecked")
157 final Class<? extends TreeNode> type = (Class) ((Instantiable<?>) data).implementedInterface();
158 @SuppressWarnings("unchecked")
159 final BindingStreamEventWriter writer = newNotificationWriter((Class) type, domWriter);
161 getSerializer(type).serialize((TreeNode) data, writer);
162 } catch (final IOException e) {
163 LOG.error("Unexpected failure while serializing data {}", data, e);
164 throw new IllegalStateException("Failed to create normalized node", e);
166 return (ContainerNode) result.getResult();
171 @SuppressWarnings("unchecked")
173 public ContainerNode toNormalizedNodeOperationData(@Nonnull final TreeNode data) {
174 final NormalizedNodeResult result = new NormalizedNodeResult();
175 // We create DOM stream writer which produces normalized nodes
176 final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
177 @SuppressWarnings("rawtypes")
178 final Class<? extends TreeNode> type = data.getClass();
179 final Class<? extends Instantiable<?>> instData = (Class<? extends Instantiable<?>>) data.getClass();
180 final BindingStreamEventWriter writer = newOperationWriter(instData, domWriter);
182 getSerializer(type).serialize(data, writer);
183 } catch (final IOException e) {
184 LOG.error("Unexpected failure while serializing data {}", data, e);
185 throw new IllegalStateException("Failed to create normalized node", e);
187 return (ContainerNode) result.getResult();
190 private static boolean isBindingRepresentable(final NormalizedNode<?, ?> data) {
191 if (data instanceof ChoiceNode) {
194 if (data instanceof LeafNode<?>) {
197 if (data instanceof LeafSetNode) {
200 if (data instanceof LeafSetEntryNode<?>) {
203 if (data instanceof MapNode) {
206 if (data instanceof UnkeyedListNode) {
215 public Entry<InstanceIdentifier<?>, TreeNode> fromNormalizedNode(@Nonnull final YangInstanceIdentifier path,
216 final NormalizedNode<?, ?> data) {
217 if (!isBindingRepresentable(data)) {
221 final List<TreeArgument<?>> builder = new ArrayList<>();
222 final NodeCodecContext<?> codec = codecContext.getCodecContextNode(path, builder);
225 LOG.warn("Path {} does not have a binding equivalent, should have been caught earlier ({})", path,
231 final TreeNode lazyObj = codec.deserialize(data);
232 final InstanceIdentifier<?> bindingPath = InstanceIdentifier.create(builder);
233 return new SimpleEntry<>(bindingPath, lazyObj);
237 @SuppressWarnings({ "rawtypes", "unchecked" })
239 public Notification fromNormalizedNodeNotification(@Nonnull final SchemaPath path,
240 @Nonnull final ContainerNode data) {
241 final NotificationCodecContext<?> codec = codecContext.getNotificationContext(path);
242 return codec.deserialize(data);
247 public TreeNode fromNormalizedNodeOperationData(@Nonnull final SchemaPath path, @Nonnull final ContainerNode data) {
248 final OperationInputCodec<?> codec = codecContext.getOperationInputCodec(path);
249 return codec.deserialize(data);
254 public Entry<YangInstanceIdentifier, BindingStreamEventWriter>
255 newWriterAndIdentifier(@Nonnull final InstanceIdentifier<?> path,
256 @Nonnull final NormalizedNodeStreamWriter domWriter) {
257 return codecContext.newWriter(path, domWriter);
262 public BindingStreamEventWriter newWriter(@Nonnull final InstanceIdentifier<?> path,
263 @Nonnull final NormalizedNodeStreamWriter domWriter) {
264 return codecContext.newWriterWithoutIdentifier(path, domWriter);
269 public BindingStreamEventWriter newNotificationWriter(@Nonnull final Class<? extends Notification<?>> notification,
270 @Nonnull final NormalizedNodeStreamWriter domWriter) {
271 return codecContext.newNotificationWriter(notification, domWriter);
276 public BindingStreamEventWriter newOperationWriter(
277 @Nonnull final Class<? extends Instantiable<?>> operationInputOrOutput,
278 @Nonnull final NormalizedNodeStreamWriter domWriter) {
279 return codecContext.newOperationWriter(operationInputOrOutput, domWriter);
283 * Deserialize function based on tree node codec context resolved by binding path.
286 * - binding identifier
287 * @return function deserializer of codec context of binding path
289 public <T extends TreeNode> Function<Optional<NormalizedNode<?, ?>>, Optional<T>>
290 deserializeFunction(final InstanceIdentifier<T> path) {
291 final TreeNodeCodecContext<?, ?> ctx =
292 (TreeNodeCodecContext<?, ?>) codecContext.getCodecContextNode(path, null);
293 return new DeserializeFunction<>(ctx);
297 public BindingCodecContext create(final BindingRuntimeContext context) {
298 return new BindingCodecContext(context, this);
302 @SuppressWarnings("checkstyle:illegalCatch")
303 public BindingCodecContext create(final SchemaContext context, final Class<?>... bindingClasses) {
304 final ModuleInfoBackedContext strategy = ModuleInfoBackedContext.create();
305 for (final Class<?> bindingCls : bindingClasses) {
307 strategy.registerModuleInfo(BindingReflections.getModuleInfo(bindingCls));
308 } catch (final Exception e) {
309 throw new IllegalStateException(
310 "Could not create BindingRuntimeContext from class " + bindingCls.getName(), e);
313 final BindingRuntimeContext runtimeCtx = BindingRuntimeContext.create(strategy, context);
314 return create(runtimeCtx);
317 private static final class DeserializeFunction<T> implements Function<Optional<NormalizedNode<?, ?>>, Optional<T>> {
318 private final TreeNodeCodecContext<?, ?> ctx;
320 DeserializeFunction(final TreeNodeCodecContext<?, ?> ctx) {
325 @SuppressWarnings("unchecked")
327 public Optional<T> apply(@Nullable final Optional<NormalizedNode<?, ?>> input) {
328 if (input.isPresent()) {
329 return Optional.of((T) ctx.deserialize(input.get()));
331 return Optional.empty();
335 private final class GeneratorLoader extends CacheLoader<Class<? extends TreeNode>, TreeNodeSerializer> {
337 public TreeNodeSerializer load(final Class<? extends TreeNode> key) throws Exception {
338 final TreeNodeSerializerImplementation prototype = generator.getSerializer(key);
339 return new TreeNodeSerializerProxy(prototype);
343 private final class TreeNodeSerializerProxy
344 implements TreeNodeSerializer, Delegator<TreeNodeSerializerImplementation> {
345 private final TreeNodeSerializerImplementation delegate;
347 TreeNodeSerializerProxy(final TreeNodeSerializerImplementation delegate) {
348 this.delegate = delegate;
352 public TreeNodeSerializerImplementation getDelegate() {
357 public void serialize(final TreeNode obj, final BindingStreamEventWriter stream) throws IOException {
358 delegate.serialize(BindingNormalizedNodeCodecRegistry.this, obj, stream);