2 * Copyright (c) 2014 Cisco Systems, Inc. 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.dom.codec.impl;
10 import com.google.common.collect.Iterables;
11 import java.lang.invoke.MethodHandles;
12 import java.lang.invoke.VarHandle;
13 import org.checkerframework.checker.lock.qual.Holding;
14 import org.eclipse.jdt.annotation.NonNull;
15 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode.ChildAddressabilitySummary;
16 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
17 import org.opendaylight.yangtools.yang.binding.DataObject;
18 import org.opendaylight.yangtools.yang.binding.DataRoot;
19 import org.opendaylight.yangtools.yang.binding.Identifiable;
20 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
21 import org.opendaylight.yangtools.yang.common.QNameModule;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
25 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
34 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
37 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 final class DataContainerCodecPrototype<T extends WithStatus> implements NodeContextSupplier {
42 private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
44 private static final VarHandle INSTANCE;
48 INSTANCE = MethodHandles.lookup().findVarHandle(DataContainerCodecPrototype.class,
49 "instance", DataContainerCodecContext.class);
50 } catch (NoSuchFieldException | IllegalAccessException e) {
51 throw new ExceptionInInitializerError(e);
55 private final T schema;
56 private final QNameModule namespace;
57 private final CodecContextFactory factory;
58 private final Item<?> bindingArg;
59 private final PathArgument yangArg;
60 private final ChildAddressabilitySummary childAddressabilitySummary;
63 * This field is now utterly in the hot path of CodecDataObject's instantiation of data. It is a volatile support
64 * double-checked locking, as detailed in https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
66 * We are opting for the document Java 9 equivalent, forsaking our usual CAS-based approach, where we'd re-assert
67 * concurrent loads. This improves safety a bit (by forcing a synchronized() block), as we expect prototypes to be
70 * In terms of safe publication, we have two volatile fields in DataObjectCodecContext to worry about, so given
71 * above access trade-off, we opt for the additional safety.
73 * TODO: all we need is safe publish semantics here, we can most probably tolerate concurrent value loads -- and
74 * everything except the the above volatiles seems to be ready. Those volatile should disappear once we have
75 * constant class loader visibility (and thus can discover all valid augmentations and aliases). That would
76 * mean dropping @Holding from createInstance().
78 @SuppressWarnings("unused")
79 private volatile DataContainerCodecContext<?, T> instance;
81 @SuppressWarnings("unchecked")
82 private DataContainerCodecPrototype(final Class<?> cls, final PathArgument arg, final T nodeSchema,
83 final CodecContextFactory factory) {
84 this(Item.of((Class<? extends DataObject>) cls), arg, nodeSchema, factory);
87 private DataContainerCodecPrototype(final Item<?> bindingArg, final PathArgument arg, final T nodeSchema,
88 final CodecContextFactory factory) {
89 this.bindingArg = bindingArg;
91 this.schema = nodeSchema;
92 this.factory = factory;
94 if (arg instanceof AugmentationIdentifier) {
95 this.namespace = Iterables.getFirst(((AugmentationIdentifier) arg).getPossibleChildNames(), null)
98 this.namespace = arg.getNodeType().getModule();
101 this.childAddressabilitySummary = computeChildAddressabilitySummary(nodeSchema);
104 private static ChildAddressabilitySummary computeChildAddressabilitySummary(final WithStatus nodeSchema) {
105 if (nodeSchema instanceof DataNodeContainer) {
106 boolean haveAddressable = false;
107 boolean haveUnaddressable = false;
108 for (DataSchemaNode child : ((DataNodeContainer) nodeSchema).getChildNodes()) {
109 if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
110 haveAddressable = true;
111 } else if (child instanceof ListSchemaNode) {
112 if (((ListSchemaNode) child).getKeyDefinition().isEmpty()) {
113 haveUnaddressable = true;
115 haveAddressable = true;
117 } else if (child instanceof AnydataSchemaNode || child instanceof AnyxmlSchemaNode
118 || child instanceof TypedDataSchemaNode) {
119 haveUnaddressable = true;
120 } else if (child instanceof ChoiceSchemaNode) {
121 switch (computeChildAddressabilitySummary(child)) {
123 haveAddressable = true;
126 haveAddressable = true;
127 haveUnaddressable = true;
130 haveUnaddressable = true;
133 throw new IllegalStateException("Unhandled accessibility summary for " + child);
136 LOG.warn("Unhandled child node {}", child);
140 if (!haveAddressable) {
141 // Empty or all are unaddressable
142 return ChildAddressabilitySummary.UNADDRESSABLE;
145 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
146 } else if (nodeSchema instanceof ChoiceSchemaNode) {
147 boolean haveAddressable = false;
148 boolean haveUnaddressable = false;
149 for (CaseSchemaNode child : ((ChoiceSchemaNode) nodeSchema).getCases()) {
150 switch (computeChildAddressabilitySummary(child)) {
152 haveAddressable = true;
155 haveUnaddressable = true;
158 // A child is mixed, which means we are mixed, too
159 return ChildAddressabilitySummary.MIXED;
161 throw new IllegalStateException("Unhandled accessibility summary for " + child);
165 if (!haveAddressable) {
166 // Empty or all are unaddressable
167 return ChildAddressabilitySummary.UNADDRESSABLE;
170 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
173 // No child nodes possible: return unaddressable
174 return ChildAddressabilitySummary.UNADDRESSABLE;
177 static DataContainerCodecPrototype<SchemaContext> rootPrototype(final CodecContextFactory factory) {
178 final SchemaContext schema = factory.getRuntimeContext().getEffectiveModelContext();
179 final NodeIdentifier arg = NodeIdentifier.create(schema.getQName());
180 return new DataContainerCodecPrototype<>(DataRoot.class, arg, schema, factory);
183 @SuppressWarnings({ "unchecked", "rawtypes" })
184 static <T extends DataSchemaNode> DataContainerCodecPrototype<T> from(final Class<?> cls, final T schema,
185 final CodecContextFactory factory) {
186 return new DataContainerCodecPrototype(cls, NodeIdentifier.create(schema.getQName()), schema, factory);
189 @SuppressWarnings({ "unchecked", "rawtypes" })
190 static <T extends DataSchemaNode> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T schema,
191 final CodecContextFactory factory) {
192 return new DataContainerCodecPrototype(bindingArg, NodeIdentifier.create(schema.getQName()), schema, factory);
195 @SuppressWarnings({ "rawtypes", "unchecked" })
196 static DataContainerCodecPrototype<?> from(final Class<?> augClass, final AugmentationIdentifier arg,
197 final AugmentationSchemaNode schema, final CodecContextFactory factory) {
198 return new DataContainerCodecPrototype(augClass, arg, schema, factory);
201 static DataContainerCodecPrototype<NotificationDefinition> from(final Class<?> augClass,
202 final NotificationDefinition schema, final CodecContextFactory factory) {
203 final PathArgument arg = NodeIdentifier.create(schema.getQName());
204 return new DataContainerCodecPrototype<>(augClass,arg, schema, factory);
207 protected T getSchema() {
211 ChildAddressabilitySummary getChildAddressabilitySummary() {
212 return childAddressabilitySummary;
215 protected QNameModule getNamespace() {
219 protected CodecContextFactory getFactory() {
223 protected Class<?> getBindingClass() {
224 return bindingArg.getType();
227 protected Item<?> getBindingArg() {
231 protected PathArgument getYangArg() {
236 public DataContainerCodecContext<?, T> get() {
237 final DataContainerCodecContext<?, T> existing = getInstance();
238 return existing != null ? existing : loadInstance();
241 private synchronized @NonNull DataContainerCodecContext<?, T> loadInstance() {
242 // re-acquire under lock
243 DataContainerCodecContext<?, T> tmp = getInstance();
245 tmp = createInstance();
246 INSTANCE.setRelease(this, tmp);
251 // Helper for getAcquire access to instance
252 private DataContainerCodecContext<?, T> getInstance() {
253 return (DataContainerCodecContext<?, T>) INSTANCE.getAcquire(this);
257 @SuppressWarnings({ "rawtypes", "unchecked" })
258 private @NonNull DataContainerCodecContext<?, T> createInstance() {
259 // FIXME: make protected abstract
260 if (schema instanceof ContainerSchemaNode) {
261 return new ContainerNodeCodecContext(this);
262 } else if (schema instanceof ListSchemaNode) {
263 return Identifiable.class.isAssignableFrom(getBindingClass())
264 ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListSchemaNode>) this)
265 : new ListNodeCodecContext(this);
266 } else if (schema instanceof ChoiceSchemaNode) {
267 return new ChoiceNodeCodecContext(this);
268 } else if (schema instanceof AugmentationSchemaNode) {
269 return new AugmentationNodeContext(this);
270 } else if (schema instanceof CaseSchemaNode) {
271 return new CaseNodeCodecContext(this);
273 throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + schema);
277 return schema instanceof ChoiceSchemaNode;