Split up BindingRuntimeContext
[mdsal.git] / binding / mdsal-binding-dom-adapter / src / main / java / org / opendaylight / mdsal / binding / dom / adapter / BindingToNormalizedNodeCodec.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.dom.adapter;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.annotations.Beta;
15 import com.google.common.cache.CacheBuilder;
16 import com.google.common.cache.CacheLoader;
17 import com.google.common.cache.LoadingCache;
18 import com.google.common.collect.ImmutableBiMap;
19 import com.google.common.collect.ImmutableSet;
20 import com.google.common.collect.Iterators;
21 import java.lang.reflect.Method;
22 import java.time.Instant;
23 import java.util.AbstractMap.SimpleEntry;
24 import java.util.Collection;
25 import java.util.HashSet;
26 import java.util.Map.Entry;
27 import java.util.Optional;
28 import java.util.Set;
29 import java.util.concurrent.TimeUnit;
30 import java.util.function.Function;
31 import java.util.stream.Collectors;
32 import javax.annotation.PreDestroy;
33 import javax.inject.Inject;
34 import javax.inject.Singleton;
35 import org.eclipse.jdt.annotation.NonNull;
36 import org.opendaylight.binding.runtime.api.BindingRuntimeContext;
37 import org.opendaylight.binding.runtime.api.BindingRuntimeGenerator;
38 import org.opendaylight.binding.runtime.api.ClassLoadingStrategy;
39 import org.opendaylight.binding.runtime.api.DefaultBindingRuntimeContext;
40 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
41 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
42 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeFactory;
43 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
44 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
45 import org.opendaylight.mdsal.binding.dom.codec.api.BindingLazyContainerNode;
46 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
47 import org.opendaylight.mdsal.binding.dom.codec.api.MissingSchemaException;
48 import org.opendaylight.mdsal.binding.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
49 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
50 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
51 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
52 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
53 import org.opendaylight.yangtools.concepts.ListenerRegistration;
54 import org.opendaylight.yangtools.yang.binding.Action;
55 import org.opendaylight.yangtools.yang.binding.DataContainer;
56 import org.opendaylight.yangtools.yang.binding.DataObject;
57 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
58 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
59 import org.opendaylight.yangtools.yang.binding.Notification;
60 import org.opendaylight.yangtools.yang.binding.RpcInput;
61 import org.opendaylight.yangtools.yang.binding.RpcOutput;
62 import org.opendaylight.yangtools.yang.binding.RpcService;
63 import org.opendaylight.yangtools.yang.common.QNameModule;
64 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
66 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
67 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
68 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
69 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
70 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
71 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
72 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
73 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
74 import org.opendaylight.yangtools.yang.model.api.Module;
75 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
76 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
77 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
78 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
79 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
80 import org.slf4j.Logger;
81 import org.slf4j.LoggerFactory;
82
83 /**
84  * A combinations of {@link BindingCodecTreeFactory} and {@link BindingNormalizedNodeSerializer}, with internal
85  * caching of instance identifiers.
86  *
87  * <p>
88  * NOTE: this class is non-final to allow controller adapter migration without duplicated code.
89  */
90 @Singleton
91 public class BindingToNormalizedNodeCodec implements BindingCodecTreeFactory,
92         BindingNormalizedNodeSerializer, SchemaContextListener, AutoCloseable {
93
94     private static final long WAIT_DURATION_SEC = 5;
95     private static final Logger LOG = LoggerFactory.getLogger(BindingToNormalizedNodeCodec.class);
96
97     private final LoadingCache<InstanceIdentifier<?>, YangInstanceIdentifier> iiCache = CacheBuilder.newBuilder()
98             .softValues().build(new CacheLoader<InstanceIdentifier<?>, YangInstanceIdentifier>() {
99                 @Override
100                 public YangInstanceIdentifier load(final InstanceIdentifier<?> key) {
101                     return toYangInstanceIdentifierBlocking(key);
102                 }
103             });
104     private final BindingNormalizedNodeCodecRegistry codecRegistry;
105     private final ClassLoadingStrategy classLoadingStrategy;
106     private final BindingRuntimeGenerator generator;
107     private final FutureSchema futureSchema;
108
109     private ListenerRegistration<?> listenerRegistration;
110
111     @Inject
112     public BindingToNormalizedNodeCodec(final BindingRuntimeGenerator generator,
113             final ClassLoadingStrategy classLoadingStrategy, final BindingNormalizedNodeCodecRegistry codecRegistry) {
114         this(generator, classLoadingStrategy, codecRegistry, false);
115     }
116
117     @Beta
118     public BindingToNormalizedNodeCodec(final BindingRuntimeContext runtimeContext) {
119         generator = (final SchemaContext context) -> {
120             throw new UnsupportedOperationException("Static context assigned");
121         };
122         classLoadingStrategy = runtimeContext.getStrategy();
123         codecRegistry = new BindingNormalizedNodeCodecRegistry(runtimeContext);
124         // TODO: this should have a specialized constructor or not be needed
125         futureSchema = FutureSchema.create(0, TimeUnit.SECONDS, false);
126         futureSchema.onRuntimeContextUpdated(runtimeContext);
127     }
128
129     public BindingToNormalizedNodeCodec(final BindingRuntimeGenerator generator,
130             final ClassLoadingStrategy classLoadingStrategy, final BindingNormalizedNodeCodecRegistry codecRegistry,
131             final boolean waitForSchema) {
132         this.generator = requireNonNull(generator, "generator");
133         this.classLoadingStrategy = requireNonNull(classLoadingStrategy, "classLoadingStrategy");
134         this.codecRegistry = requireNonNull(codecRegistry, "codecRegistry");
135         this.futureSchema = FutureSchema.create(WAIT_DURATION_SEC, TimeUnit.SECONDS, waitForSchema);
136     }
137
138     public static BindingToNormalizedNodeCodec newInstance(final BindingRuntimeGenerator generator,
139             final ClassLoadingStrategy classLoadingStrategy, final DOMSchemaService schemaService) {
140         final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry();
141         BindingToNormalizedNodeCodec instance = new BindingToNormalizedNodeCodec(generator, classLoadingStrategy,
142             codecRegistry, true);
143         instance.listenerRegistration = schemaService.registerSchemaContextListener(instance);
144         return instance;
145     }
146
147     protected YangInstanceIdentifier toYangInstanceIdentifierBlocking(
148             final InstanceIdentifier<? extends DataObject> binding) {
149         try {
150             return codecRegistry.toYangInstanceIdentifier(binding);
151         } catch (final MissingSchemaException e) {
152             waitForSchema(decompose(binding), e);
153             return codecRegistry.toYangInstanceIdentifier(binding);
154         }
155     }
156
157     /**
158      * Translates supplied Binding Instance Identifier into NormalizedNode
159      * instance identifier.
160      *
161      * @param binding
162      *            Binding Instance Identifier
163      * @return DOM Instance Identifier
164      * @throws IllegalArgumentException
165      *             If supplied Instance Identifier is not valid.
166      */
167     public final YangInstanceIdentifier toNormalized(final InstanceIdentifier<? extends DataObject> binding) {
168         return codecRegistry.toYangInstanceIdentifier(binding);
169     }
170
171     @Override
172     public final YangInstanceIdentifier toYangInstanceIdentifier(final InstanceIdentifier<?> binding) {
173         return codecRegistry.toYangInstanceIdentifier(binding);
174     }
175
176     protected YangInstanceIdentifier toYangInstanceIdentifierCached(final InstanceIdentifier<?> binding) {
177         return iiCache.getUnchecked(binding);
178     }
179
180     @Override
181     public final <T extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
182             final InstanceIdentifier<T> path, final T data) {
183         try {
184             return codecRegistry.toNormalizedNode(path, data);
185         } catch (final MissingSchemaException e) {
186             waitForSchema(decompose(path), e);
187             return codecRegistry.toNormalizedNode(path, data);
188         }
189     }
190
191     /**
192      * Converts Binding Map.Entry to DOM Map.Entry.
193      *
194      * <p>
195      * Same as {@link #toNormalizedNode(InstanceIdentifier, DataObject)}.
196      *
197      * @param binding Map Entry with InstanceIdentifier as key and DataObject as value.
198      * @return DOM Map Entry with {@link YangInstanceIdentifier} as key and {@link NormalizedNode}
199      *         as value.
200      */
201     @SuppressWarnings({"unchecked", "rawtypes"})
202     public final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
203             final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding) {
204         return toNormalizedNode((InstanceIdentifier) binding.getKey(), binding.getValue());
205     }
206
207     @Override
208     public final Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode(final YangInstanceIdentifier path,
209             final NormalizedNode<?, ?> data) {
210         return codecRegistry.fromNormalizedNode(path, data);
211     }
212
213     @Override
214     public final Notification fromNormalizedNodeNotification(final SchemaPath path, final ContainerNode data) {
215         return codecRegistry.fromNormalizedNodeNotification(path, data);
216     }
217
218     @Override
219     public final Notification fromNormalizedNodeNotification(final SchemaPath path, final ContainerNode data,
220             final Instant eventInstant) {
221         return codecRegistry.fromNormalizedNodeNotification(path, data, eventInstant);
222     }
223
224     @Override
225     public final DataObject fromNormalizedNodeRpcData(final SchemaPath path, final ContainerNode data) {
226         return codecRegistry.fromNormalizedNodeRpcData(path, data);
227     }
228
229     @Override
230     public <T extends RpcInput> T fromNormalizedNodeActionInput(final Class<? extends Action<?, ?, ?>> action,
231             final ContainerNode input) {
232         return codecRegistry.fromNormalizedNodeActionInput(action, input);
233     }
234
235     @Override
236     public <T extends RpcOutput> T fromNormalizedNodeActionOutput(final Class<? extends Action<?, ?, ?>> action,
237             final ContainerNode output) {
238         return codecRegistry.fromNormalizedNodeActionOutput(action, output);
239     }
240
241     @Override
242     public final InstanceIdentifier<?> fromYangInstanceIdentifier(final YangInstanceIdentifier dom) {
243         return codecRegistry.fromYangInstanceIdentifier(dom);
244     }
245
246     @Override
247     public final ContainerNode toNormalizedNodeNotification(final Notification data) {
248         return codecRegistry.toNormalizedNodeNotification(data);
249     }
250
251     @Override
252     public final ContainerNode toNormalizedNodeRpcData(final DataContainer data) {
253         return codecRegistry.toNormalizedNodeRpcData(data);
254     }
255
256     @Override
257     public ContainerNode toNormalizedNodeActionInput(final Class<? extends Action<?, ?, ?>> action,
258             final RpcInput input) {
259         return codecRegistry.toNormalizedNodeActionInput(action, input);
260     }
261
262     @Override
263     public ContainerNode toNormalizedNodeActionOutput(final Class<? extends Action<?, ?, ?>> action,
264             final RpcOutput output) {
265         return codecRegistry.toNormalizedNodeActionOutput(action, output);
266     }
267
268     @Override
269     public BindingLazyContainerNode<RpcInput> toLazyNormalizedNodeActionInput(
270             final Class<? extends Action<?, ?, ?>> action, final NodeIdentifier identifier, final RpcInput input) {
271         return codecRegistry.toLazyNormalizedNodeActionInput(action, identifier, input);
272     }
273
274     @Override
275     public BindingLazyContainerNode<RpcOutput> toLazyNormalizedNodeActionOutput(
276             final Class<? extends Action<?, ?, ?>> action, final NodeIdentifier identifier, final RpcOutput output) {
277         return codecRegistry.toLazyNormalizedNodeActionOutput(action, identifier, output);
278     }
279
280     /**
281      * Returns a Binding-Aware instance identifier from normalized
282      * instance-identifier if it is possible to create representation.
283      *
284      * <p>
285      * Returns Optional.empty for cases where target is mixin node except
286      * augmentation.
287      */
288     public final Optional<InstanceIdentifier<? extends DataObject>> toBinding(final YangInstanceIdentifier normalized)
289                     throws DeserializationException {
290         try {
291             return Optional.ofNullable(codecRegistry.fromYangInstanceIdentifier(normalized));
292         } catch (final IllegalArgumentException e) {
293             return Optional.empty();
294         }
295     }
296
297     public final Optional<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
298             final @NonNull Entry<YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
299                     throws DeserializationException {
300         try {
301             /*
302              * This cast is required, due to generics behaviour in openjdk / oracle javac.
303              *
304              * <p>
305              * InstanceIdentifier has definition InstanceIdentifier<T extends DataObject>,
306              * this means '?' is always Â <? extends DataObject>. Eclipse compiler
307              * is able to determine this relationship and treats
308              * Entry<InstanceIdentifier<?>, DataObject> and Entry<InstanceIdentifier<? extends DataObject, DataObject>
309              * as assignable. However openjdk / oracle javac treats this two types
310              * as incompatible and issues a compile error.
311              *
312              * <p>
313              * It is safe to lose generic information and cast it to other generic signature.
314              */
315             @SuppressWarnings("unchecked")
316             final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding = Entry.class.cast(
317                     codecRegistry.fromNormalizedNode(normalized.getKey(), normalized.getValue()));
318             return Optional.ofNullable(binding);
319         } catch (final IllegalArgumentException e) {
320             return Optional.empty();
321         }
322     }
323
324     @Override
325     public void onGlobalContextUpdated(final SchemaContext context) {
326         final BindingRuntimeContext runtimeContext = DefaultBindingRuntimeContext.create(
327             generator.generateTypeMapping(context), classLoadingStrategy);
328         codecRegistry.onBindingRuntimeContextUpdated(runtimeContext);
329         futureSchema.onRuntimeContextUpdated(runtimeContext);
330     }
331
332     /**
333      * Deprecated.
334      *
335      * @deprecated Use {@link BindingNormalizedNodeCodecRegistry#deserializeFunction} instead.
336      */
337     @Deprecated
338     public final <T extends DataObject> Function<Optional<NormalizedNode<?, ?>>, Optional<T>> deserializeFunction(
339             final InstanceIdentifier<T> path) {
340         return codecRegistry.deserializeFunction(path)::apply;
341     }
342
343     public final BindingNormalizedNodeCodecRegistry getCodecRegistry() {
344         return codecRegistry;
345     }
346
347     @Override
348     @PreDestroy
349     public void close() {
350         if (listenerRegistration != null) {
351             listenerRegistration.close();
352         }
353     }
354
355     public final BindingNormalizedNodeCodecRegistry getCodecFactory() {
356         return codecRegistry;
357     }
358
359     // FIXME: This should be probably part of Binding Runtime context
360     public final ImmutableBiMap<Method, SchemaPath> getRpcMethodToSchemaPath(final Class<? extends RpcService> key) {
361         final Module module = getModuleBlocking(key);
362         final ImmutableBiMap.Builder<Method, SchemaPath> ret = ImmutableBiMap.builder();
363         try {
364             for (final RpcDefinition rpcDef : module.getRpcs()) {
365                 final Method method = findRpcMethod(key, rpcDef);
366                 ret.put(method, rpcDef.getPath());
367             }
368         } catch (final NoSuchMethodException e) {
369             throw new IllegalStateException("Rpc defined in model does not have representation in generated class.", e);
370         }
371         return ret.build();
372     }
373
374     protected ImmutableBiMap<Method, RpcDefinition> getRpcMethodToSchema(final Class<? extends RpcService> key) {
375         final Module module = getModuleBlocking(key);
376         final ImmutableBiMap.Builder<Method, RpcDefinition> ret = ImmutableBiMap.builder();
377         try {
378             for (final RpcDefinition rpcDef : module.getRpcs()) {
379                 final Method method = findRpcMethod(key, rpcDef);
380                 ret.put(method, rpcDef);
381             }
382         } catch (final NoSuchMethodException e) {
383             throw new IllegalStateException("Rpc defined in model does not have representation in generated class.", e);
384         }
385         return ret.build();
386     }
387
388     private Module getModuleBlocking(final Class<?> modeledClass) {
389         final QNameModule moduleName = BindingReflections.getQNameModule(modeledClass);
390         BindingRuntimeContext localRuntimeContext = runtimeContext();
391         Module module = localRuntimeContext == null ? null :
392             localRuntimeContext.getSchemaContext().findModule(moduleName).orElse(null);
393         if (module == null && futureSchema.waitForSchema(moduleName)) {
394             localRuntimeContext = runtimeContext();
395             checkState(localRuntimeContext != null, "BindingRuntimeContext is not available.");
396             module = localRuntimeContext.getSchemaContext().findModule(moduleName).orElse(null);
397         }
398         if (module != null) {
399             return module;
400         }
401
402         LOG.debug("Schema for {} is not available; expected module name: {}; BindingRuntimeContext: {}",
403                 modeledClass, moduleName, localRuntimeContext);
404         throw new IllegalStateException(String.format("Schema for %s is not available; expected module name: %s; "
405                 + "full BindingRuntimeContext available in debug log", modeledClass, moduleName));
406     }
407
408     private void waitForSchema(final Collection<Class<?>> binding, final MissingSchemaException exception) {
409         LOG.warn("Blocking thread to wait for schema convergence updates for {} {}", futureSchema.getDuration(),
410             futureSchema.getUnit());
411         if (!futureSchema.waitForSchema(binding)) {
412             throw exception;
413         }
414     }
415
416     private Method findRpcMethod(final Class<? extends RpcService> key, final RpcDefinition rpcDef)
417             throws NoSuchMethodException {
418         final String methodName = BindingMapping.getRpcMethodName(rpcDef.getQName());
419         final Class<?> inputClz = runtimeContext().getClassForSchema(rpcDef.getInput());
420         return key.getMethod(methodName, inputClz);
421     }
422
423     @Override
424     public final BindingCodecTree create(final BindingRuntimeContext context) {
425         return codecRegistry.create(context);
426     }
427
428     protected @NonNull Entry<InstanceIdentifier<?>, BindingDataObjectCodecTreeNode<?>> getSubtreeCodec(
429             final YangInstanceIdentifier domIdentifier) {
430
431         final BindingCodecTree currentCodecTree = codecRegistry.getCodecContext();
432         final InstanceIdentifier<?> bindingPath = codecRegistry.fromYangInstanceIdentifier(domIdentifier);
433         checkArgument(bindingPath != null);
434         /**
435          * If we are able to deserialize YANG instance identifier, getSubtreeCodec must
436          * return non-null value.
437          */
438         final BindingDataObjectCodecTreeNode<?> codecContext = currentCodecTree.getSubtreeCodec(bindingPath);
439         return new SimpleEntry<>(bindingPath, codecContext);
440     }
441
442     @SuppressWarnings("unchecked")
443     public final Set<Class<? extends Notification>> getNotificationClasses(final Set<SchemaPath> interested) {
444         final Set<Class<? extends Notification>> result = new HashSet<>();
445         final BindingRuntimeContext runtimeContext = runtimeContext();
446         for (final NotificationDefinition notification : runtimeContext.getSchemaContext().getNotifications()) {
447             if (interested.contains(notification.getPath())) {
448                 try {
449                     result.add((Class<? extends Notification>) runtimeContext.getClassForSchema(notification));
450                 } catch (final IllegalStateException e) {
451                     // Ignore
452                     LOG.warn("Class for {} is currently not known.", notification.getPath(), e);
453                 }
454             }
455         }
456         return result;
457     }
458
459     SchemaPath getActionPath(final Class<? extends Action<?, ?, ?>> type) {
460         final ActionDefinition schema = runtimeContext().getActionDefinition(type);
461         checkArgument(schema != null, "Failed to find schema for %s", type);
462         return schema.getPath();
463     }
464
465     private BindingRuntimeContext runtimeContext() {
466         return futureSchema.runtimeContext();
467     }
468
469     private static Collection<Class<?>> decompose(final InstanceIdentifier<?> path) {
470         return ImmutableSet.copyOf(Iterators.transform(path.getPathArguments().iterator(), PathArgument::getType));
471     }
472
473     protected NormalizedNode<?, ?> instanceIdentifierToNode(final YangInstanceIdentifier parentPath) {
474         return ImmutableNodes.fromInstanceId(runtimeContext().getSchemaContext(), parentPath);
475     }
476
477     /**
478      * This method creates an empty list container of a particular type.
479      *
480      * @deprecated This method is not generally useful, as empty lists do not convey information in YANG (they are
481      *             equivalent to non-present lists). It also leaks implementation details to a broader scope and should
482      *             never have been public in the first place.
483      */
484     @Deprecated
485     public NormalizedNode<?, ?> getDefaultNodeFor(final YangInstanceIdentifier parentMapPath) {
486         final BindingCodecTreeNode mapCodec = requireNonNull(
487                 codecRegistry.getCodecContext().getSubtreeCodec(parentMapPath),
488                 "Codec not found for yang instance identifier: " + parentMapPath);
489         final WithStatus schema = mapCodec.getSchema();
490         if (schema instanceof ListSchemaNode) {
491             final ListSchemaNode castedSchema = (ListSchemaNode) schema;
492             return castedSchema.isUserOrdered() ? Builders.orderedMapBuilder(castedSchema).build()
493                     : Builders.mapBuilder(castedSchema).build();
494         }
495         throw new IllegalArgumentException("Path does not point to list schema node");
496     }
497
498     protected Collection<DOMDataTreeIdentifier> toDOMDataTreeIdentifiers(
499             final Collection<DataTreeIdentifier<?>> subtrees) {
500         return subtrees.stream().map(this::toDOMDataTreeIdentifier).collect(Collectors.toSet());
501     }
502
503     protected DOMDataTreeIdentifier toDOMDataTreeIdentifier(final DataTreeIdentifier<?> path) {
504         final YangInstanceIdentifier domPath = toYangInstanceIdentifierBlocking(path.getRootIdentifier());
505         return new DOMDataTreeIdentifier(path.getDatastoreType(), domPath);
506     }
507 }