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 com.google.common.annotations.Beta;
11 import com.google.common.base.Function;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.cache.CacheBuilder;
15 import com.google.common.cache.CacheLoader;
16 import com.google.common.cache.LoadingCache;
17 import java.io.IOException;
18 import java.util.AbstractMap.SimpleEntry;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Map.Entry;
22 import javax.annotation.Nonnull;
23 import javax.annotation.Nullable;
24 import org.opendaylight.mdsal.binding.javav2.dom.codec.api.BindingTreeCodec;
25 import org.opendaylight.mdsal.binding.javav2.dom.codec.api.factory.BindingNormalizedNodeWriterFactory;
26 import org.opendaylight.mdsal.binding.javav2.dom.codec.api.factory.BindingTreeCodecFactory;
27 import org.opendaylight.mdsal.binding.javav2.dom.codec.api.serializer.BindingNormalizedNodeSerializer;
28 import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.api.TreeNodeSerializerGenerator;
29 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.NotificationCodecContext;
30 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.BindingCodecContext;
31 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.NodeCodecContext;
32 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.TreeNodeCodecContext;
33 import org.opendaylight.mdsal.binding.javav2.runtime.context.BindingRuntimeContext;
34 import org.opendaylight.mdsal.binding.javav2.runtime.context.ModuleInfoBackedContext;
35 import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
36 import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
37 import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
38 import org.opendaylight.mdsal.binding.javav2.spec.base.Notification;
39 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
40 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
41 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingStreamEventWriter;
42 import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializer;
43 import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerImplementation;
44 import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerRegistry;
45 import org.opendaylight.yangtools.concepts.Delegator;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
47 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
55 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
56 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
57 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
58 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
59 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
64 * Serializing and deserializing Binding and DOM data.
67 public class BindingNormalizedNodeCodecRegistry implements TreeNodeSerializerRegistry, BindingTreeCodecFactory,
68 BindingNormalizedNodeWriterFactory, BindingNormalizedNodeSerializer {
70 private static final Logger LOG = LoggerFactory.getLogger(BindingNormalizedNodeCodecRegistry.class);
72 private final TreeNodeSerializerGenerator generator;
73 private final LoadingCache<Class<? extends TreeNode>, TreeNodeSerializer> serializers;
74 private volatile BindingCodecContext codecContext;
77 * Prepare generator for generating serializers and create loader for serializers.
80 * - serializer generator
82 public BindingNormalizedNodeCodecRegistry(final TreeNodeSerializerGenerator generator) {
83 this.generator = Preconditions.checkNotNull(generator);
84 this.serializers = CacheBuilder.newBuilder().weakKeys().build(new GeneratorLoader());
88 public TreeNodeSerializer getSerializer(final Class<? extends TreeNode> type) {
89 return serializers.getUnchecked(type);
93 * Get binding tree codec context.
95 * @return codec context
97 public BindingTreeCodec getCodecContext() {
102 * Create codec context based on runtime context and notify generator that runtime context has been
108 public void onBindingRuntimeContextUpdated(final BindingRuntimeContext context) {
109 codecContext = create(context);
110 generator.onBindingRuntimeContextUpdated(context);
115 public YangInstanceIdentifier toYangInstanceIdentifier(@Nonnull final InstanceIdentifier<?> binding) {
116 return codecContext.getInstanceIdentifierCodec().serialize(binding);
121 public InstanceIdentifier<?> fromYangInstanceIdentifier(@Nonnull final YangInstanceIdentifier dom) {
122 return codecContext.getInstanceIdentifierCodec().deserialize(dom);
127 public <T extends TreeNode> Entry<YangInstanceIdentifier, NormalizedNode<?, ?>>
128 toNormalizedNode(final InstanceIdentifier<T> path, final T data) {
129 final NormalizedNodeResult result = new NormalizedNodeResult();
130 // We create DOM stream writer which produces normalized nodes
131 final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
133 // We create Binding Stream Writer which translates from Binding to Normalized Nodes
134 final Entry<YangInstanceIdentifier, BindingStreamEventWriter> writeCtx =
135 codecContext.newWriter(path, domWriter);
137 // We get serializer which reads binding data and uses Binding To Normalized Node writer to write
140 getSerializer(path.getTargetType()).serialize(data, writeCtx.getValue());
141 } catch (final IOException e) {
142 LOG.error("Unexpected failure while serializing path {} data {}", path, data, e);
143 throw new IllegalStateException("Failed to create normalized node", e);
145 return new SimpleEntry<>(writeCtx.getKey(), result.getResult());
149 @SuppressWarnings("rawtypes")
151 public ContainerNode toNormalizedNodeNotification(@Nonnull final Notification data) {
152 final NormalizedNodeResult result = new NormalizedNodeResult();
153 // We create DOM stream writer which produces normalized nodes
154 final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
155 @SuppressWarnings("unchecked")
156 final Class<? extends TreeNode> type = (Class) ((Instantiable<?>) data).implementedInterface();
157 @SuppressWarnings("unchecked")
158 final BindingStreamEventWriter writer = newNotificationWriter((Class) type, domWriter);
160 getSerializer(type).serialize((TreeNode) data, writer);
161 } catch (final IOException e) {
162 LOG.error("Unexpected failure while serializing data {}", data, e);
163 throw new IllegalStateException("Failed to create normalized node", e);
165 return (ContainerNode) result.getResult();
170 @SuppressWarnings("unchecked")
172 public ContainerNode toNormalizedNodeOperationData(@Nonnull final TreeNode data) {
173 final NormalizedNodeResult result = new NormalizedNodeResult();
174 // We create DOM stream writer which produces normalized nodes
175 final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
176 @SuppressWarnings("rawtypes")
177 final Class<? extends TreeNode> type = data.getClass();
178 final Class<? extends Instantiable<?>> instData = (Class<? extends Instantiable<?>>) data.getClass();
179 final BindingStreamEventWriter writer = newOperationWriter(instData, domWriter);
181 getSerializer(type).serialize(data, writer);
182 } catch (final IOException e) {
183 LOG.error("Unexpected failure while serializing data {}", data, e);
184 throw new IllegalStateException("Failed to create normalized node", e);
186 return (ContainerNode) result.getResult();
189 private static boolean isBindingRepresentable(final NormalizedNode<?, ?> data) {
190 if (data instanceof ChoiceNode) {
193 if (data instanceof LeafNode<?>) {
196 if (data instanceof LeafSetNode) {
199 if (data instanceof LeafSetEntryNode<?>) {
202 if (data instanceof MapNode) {
205 if (data instanceof UnkeyedListNode) {
214 public Entry<InstanceIdentifier<?>, TreeNode> fromNormalizedNode(@Nonnull final YangInstanceIdentifier path,
215 final NormalizedNode<?, ?> data) {
216 if (!isBindingRepresentable(data)) {
220 final List<TreeArgument<?>> builder = new ArrayList<>();
221 final NodeCodecContext<?> codec = codecContext.getCodecContextNode(path, builder);
224 LOG.warn("Path {} does not have a binding equivalent, should have been caught earlier ({})", path,
230 final TreeNode lazyObj = codec.deserialize(data);
231 final InstanceIdentifier<?> bindingPath = InstanceIdentifier.create(builder);
232 return new SimpleEntry<>(bindingPath, lazyObj);
236 @SuppressWarnings({ "rawtypes", "unchecked" })
238 public Notification fromNormalizedNodeNotification(@Nonnull final SchemaPath path,
239 @Nonnull final ContainerNode data) {
240 final NotificationCodecContext<?> codec = codecContext.getNotificationContext(path);
241 return codec.deserialize(data);
246 public TreeNode fromNormalizedNodeOperationData(@Nonnull final SchemaPath path, @Nonnull final ContainerNode data) {
247 final OperationInputCodec<?> codec = codecContext.getOperationInputCodec(path);
248 return codec.deserialize(data);
253 public Entry<YangInstanceIdentifier, BindingStreamEventWriter>
254 newWriterAndIdentifier(@Nonnull final InstanceIdentifier<?> path,
255 @Nonnull final NormalizedNodeStreamWriter domWriter) {
256 return codecContext.newWriter(path, domWriter);
261 public BindingStreamEventWriter newWriter(@Nonnull final InstanceIdentifier<?> path,
262 @Nonnull final NormalizedNodeStreamWriter domWriter) {
263 return codecContext.newWriterWithoutIdentifier(path, domWriter);
268 public BindingStreamEventWriter newNotificationWriter(@Nonnull final Class<? extends Notification<?>> notification,
269 @Nonnull final NormalizedNodeStreamWriter domWriter) {
270 return codecContext.newNotificationWriter(notification, domWriter);
275 public BindingStreamEventWriter newOperationWriter(
276 @Nonnull final Class<? extends Instantiable<?>> operationInputOrOutput,
277 @Nonnull final NormalizedNodeStreamWriter domWriter) {
278 return codecContext.newOperationWriter(operationInputOrOutput, domWriter);
282 * Deserialize function based on tree node codec context resolved by binding path.
285 * - binding identifier
286 * @return function deserializer of codec context of binding path
288 public <T extends TreeNode> Function<Optional<NormalizedNode<?, ?>>, Optional<T>>
289 deserializeFunction(final InstanceIdentifier<T> path) {
290 final TreeNodeCodecContext<?, ?> ctx =
291 (TreeNodeCodecContext<?, ?>) codecContext.getCodecContextNode(path, null);
292 return new DeserializeFunction<>(ctx);
296 public BindingCodecContext create(final BindingRuntimeContext context) {
297 return new BindingCodecContext(context, this);
301 @SuppressWarnings("checkstyle:illegalCatch")
302 public BindingCodecContext create(final SchemaContext context, final Class<?>... bindingClasses) {
303 final ModuleInfoBackedContext strategy = ModuleInfoBackedContext.create();
304 for (final Class<?> bindingCls : bindingClasses) {
306 strategy.registerModuleInfo(BindingReflections.getModuleInfo(bindingCls));
307 } catch (final Exception e) {
308 throw new IllegalStateException(
309 "Could not create BindingRuntimeContext from class " + bindingCls.getName(), e);
312 final BindingRuntimeContext runtimeCtx = BindingRuntimeContext.create(strategy, context);
313 return create(runtimeCtx);
316 private static final class DeserializeFunction<T> implements Function<Optional<NormalizedNode<?, ?>>, Optional<T>> {
317 private final TreeNodeCodecContext<?, ?> ctx;
319 DeserializeFunction(final TreeNodeCodecContext<?, ?> ctx) {
324 @SuppressWarnings("unchecked")
326 public Optional<T> apply(@Nullable final Optional<NormalizedNode<?, ?>> input) {
327 if (input.isPresent()) {
328 return Optional.of((T) ctx.deserialize(input.get()));
330 return Optional.absent();
334 private final class GeneratorLoader extends CacheLoader<Class<? extends TreeNode>, TreeNodeSerializer> {
336 public TreeNodeSerializer load(final Class<? extends TreeNode> key) throws Exception {
337 final TreeNodeSerializerImplementation prototype = generator.getSerializer(key);
338 return new TreeNodeSerializerProxy(prototype);
342 private final class TreeNodeSerializerProxy
343 implements TreeNodeSerializer, Delegator<TreeNodeSerializerImplementation> {
344 private final TreeNodeSerializerImplementation delegate;
346 TreeNodeSerializerProxy(final TreeNodeSerializerImplementation delegate) {
347 this.delegate = delegate;
351 public TreeNodeSerializerImplementation getDelegate() {
356 public void serialize(final TreeNode obj, final BindingStreamEventWriter stream) throws IOException {
357 delegate.serialize(BindingNormalizedNodeCodecRegistry.this, obj, stream);