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 org.checkerframework.checker.lock.qual.Holding;
12 import org.eclipse.jdt.annotation.NonNull;
13 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode.ChildAddressabilitySummary;
14 import org.opendaylight.mdsal.binding.dom.codec.impl.NodeCodecContext.CodecContextFactory;
15 import org.opendaylight.yangtools.yang.binding.DataObject;
16 import org.opendaylight.yangtools.yang.binding.DataRoot;
17 import org.opendaylight.yangtools.yang.binding.Identifiable;
18 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
19 import org.opendaylight.yangtools.yang.common.QNameModule;
20 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
21 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
22 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
23 import org.opendaylight.yangtools.yang.model.api.AnyDataSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
30 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
32 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
34 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
35 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 final class DataContainerCodecPrototype<T extends WithStatus> implements NodeContextSupplier {
40 private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecPrototype.class);
42 private final T schema;
43 private final QNameModule namespace;
44 private final CodecContextFactory factory;
45 private final Item<?> bindingArg;
46 private final PathArgument yangArg;
47 private final ChildAddressabilitySummary childAddressabilitySummary;
50 * FIXME: This field is now utterly in the hot path of CodecDataObject's instantiation of data. It is volatile
51 * support double-checked locking, as detailed in
52 * https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java.
54 * Removing volatile here would seem to be worthwhile, as it would mean that in the initialized case we end
55 * up with no happens-before synchronization between threads, allowing JIT to perform better.
57 * Since we are on Java 5+, DataContainerCodecContext and its subclasses play a role here. All of their
58 * fields are either final or volatile, so intuition would indicate they themselves could (already?) be
59 * acting in the same capacity as "FinalWrapper" in the above example does -- in which case we can safely
60 * remove the volatile access altogether, after documenting this fact there (and potentially placing some
63 * The presence of volatile fields seems to violate this, as
64 * http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html mentions "such that all of the
65 * fields of Helper are final".
67 * There are only two violations, both relating to dynamic discovery of augmentations, which should be
68 * eliminated once we have constant class loader visibility (and thus can discover all valid augmentations
69 * and aliases). Alternatively, adding an indirection could be considered after the effects are fully
72 * Alternatively we can consider the use of VarHandle acquire/release mechanics to remove this instance
73 * from global synchronization order -- that would be a Java 9+ concern, though.
75 * Finally, the benefits of addressing this are masked by CodecDataObject operating on volatiles, hence
76 * already doing a barrier very close to us -- thus reducing the scope in which code can be reordered.
77 * If CodecDataObject moves to VarHandles and uses acquire/release there, we will be put on the spot here,
80 private volatile DataContainerCodecContext<?, T> instance;
82 @SuppressWarnings("unchecked")
83 private DataContainerCodecPrototype(final Class<?> cls, final PathArgument arg, final T nodeSchema,
84 final CodecContextFactory factory) {
85 this(Item.of((Class<? extends DataObject>) cls), arg, nodeSchema, factory);
88 private DataContainerCodecPrototype(final Item<?> bindingArg, final PathArgument arg, final T nodeSchema,
89 final CodecContextFactory factory) {
90 this.bindingArg = bindingArg;
92 this.schema = nodeSchema;
93 this.factory = factory;
95 if (arg instanceof AugmentationIdentifier) {
96 this.namespace = Iterables.getFirst(((AugmentationIdentifier) arg).getPossibleChildNames(), null)
99 this.namespace = arg.getNodeType().getModule();
102 this.childAddressabilitySummary = computeChildAddressabilitySummary(nodeSchema);
105 private static ChildAddressabilitySummary computeChildAddressabilitySummary(final WithStatus nodeSchema) {
106 if (nodeSchema instanceof DataNodeContainer) {
107 boolean haveAddressable = false;
108 boolean haveUnaddressable = false;
109 for (DataSchemaNode child : ((DataNodeContainer) nodeSchema).getChildNodes()) {
110 if (child instanceof ContainerSchemaNode || child instanceof AugmentationSchemaNode) {
111 haveAddressable = true;
112 } else if (child instanceof ListSchemaNode) {
113 if (((ListSchemaNode) child).getKeyDefinition().isEmpty()) {
114 haveUnaddressable = true;
116 haveAddressable = true;
118 } else if (child instanceof AnyDataSchemaNode || child instanceof AnyXmlSchemaNode
119 || child instanceof TypedDataSchemaNode) {
120 haveUnaddressable = true;
121 } else if (child instanceof ChoiceSchemaNode) {
122 switch (computeChildAddressabilitySummary(child)) {
124 haveAddressable = true;
127 haveAddressable = true;
128 haveUnaddressable = true;
131 haveUnaddressable = true;
134 throw new IllegalStateException("Unhandled accessibility summary for " + child);
137 LOG.warn("Unhandled child node {}", child);
141 if (!haveAddressable) {
142 // Empty or all are unaddressable
143 return ChildAddressabilitySummary.UNADDRESSABLE;
146 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
147 } else if (nodeSchema instanceof ChoiceSchemaNode) {
148 boolean haveAddressable = false;
149 boolean haveUnaddressable = false;
150 for (CaseSchemaNode child : ((ChoiceSchemaNode) nodeSchema).getCases().values()) {
151 switch (computeChildAddressabilitySummary(child)) {
153 haveAddressable = true;
156 haveUnaddressable = true;
159 // A child is mixed, which means we are mixed, too
160 return ChildAddressabilitySummary.MIXED;
162 throw new IllegalStateException("Unhandled accessibility summary for " + child);
166 if (!haveAddressable) {
167 // Empty or all are unaddressable
168 return ChildAddressabilitySummary.UNADDRESSABLE;
171 return haveUnaddressable ? ChildAddressabilitySummary.MIXED : ChildAddressabilitySummary.ADDRESSABLE;
174 // No child nodes possible: return unaddressable
175 return ChildAddressabilitySummary.UNADDRESSABLE;
178 static DataContainerCodecPrototype<SchemaContext> rootPrototype(final CodecContextFactory factory) {
179 final SchemaContext schema = factory.getRuntimeContext().getSchemaContext();
180 final NodeIdentifier arg = NodeIdentifier.create(schema.getQName());
181 return new DataContainerCodecPrototype<>(DataRoot.class, arg, schema, factory);
184 @SuppressWarnings({ "unchecked", "rawtypes" })
185 static <T extends DataSchemaNode> DataContainerCodecPrototype<T> from(final Class<?> cls, final T schema,
186 final CodecContextFactory factory) {
187 return new DataContainerCodecPrototype(cls, NodeIdentifier.create(schema.getQName()), schema, factory);
190 @SuppressWarnings({ "unchecked", "rawtypes" })
191 static <T extends DataSchemaNode> DataContainerCodecPrototype<T> from(final Item<?> bindingArg, final T schema,
192 final CodecContextFactory factory) {
193 return new DataContainerCodecPrototype(bindingArg, NodeIdentifier.create(schema.getQName()), schema, factory);
196 @SuppressWarnings({ "rawtypes", "unchecked" })
197 static DataContainerCodecPrototype<?> from(final Class<?> augClass, final AugmentationIdentifier arg,
198 final AugmentationSchemaNode schema, final CodecContextFactory factory) {
199 return new DataContainerCodecPrototype(augClass, arg, schema, factory);
202 static DataContainerCodecPrototype<NotificationDefinition> from(final Class<?> augClass,
203 final NotificationDefinition schema, final CodecContextFactory factory) {
204 final PathArgument arg = NodeIdentifier.create(schema.getQName());
205 return new DataContainerCodecPrototype<>(augClass,arg, schema, factory);
208 protected T getSchema() {
212 ChildAddressabilitySummary getChildAddressabilitySummary() {
213 return childAddressabilitySummary;
216 protected QNameModule getNamespace() {
220 protected CodecContextFactory getFactory() {
224 protected Class<?> getBindingClass() {
225 return bindingArg.getType();
228 protected Item<?> getBindingArg() {
232 protected PathArgument getYangArg() {
237 public DataContainerCodecContext<?, T> get() {
238 DataContainerCodecContext<?, T> tmp = instance;
240 synchronized (this) {
243 instance = tmp = createInstance();
252 @SuppressWarnings({ "rawtypes", "unchecked" })
253 private @NonNull DataContainerCodecContext<?, T> createInstance() {
254 // FIXME: make protected abstract
255 if (schema instanceof ContainerSchemaNode) {
256 return new ContainerNodeCodecContext(this);
257 } else if (schema instanceof ListSchemaNode) {
258 return Identifiable.class.isAssignableFrom(getBindingClass())
259 ? KeyedListNodeCodecContext.create((DataContainerCodecPrototype<ListSchemaNode>) this)
260 : new ListNodeCodecContext(this);
261 } else if (schema instanceof ChoiceSchemaNode) {
262 return new ChoiceNodeCodecContext(this);
263 } else if (schema instanceof AugmentationSchemaNode) {
264 return new AugmentationNodeContext(this);
265 } else if (schema instanceof CaseSchemaNode) {
266 return new CaseNodeCodecContext(this);
268 throw new IllegalArgumentException("Unsupported type " + getBindingClass() + " " + schema);
272 return schema instanceof ChoiceSchemaNode;