2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3 * Copyright (c) 2014 Brocade Communication Systems, Inc.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.sal.restconf.impl;
11 import com.google.common.base.Objects;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Predicates;
15 import com.google.common.base.Splitter;
16 import com.google.common.base.Strings;
17 import com.google.common.base.Throwables;
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.Iterables;
20 import com.google.common.collect.Lists;
21 import com.google.common.util.concurrent.CheckedFuture;
22 import java.math.BigInteger;
24 import java.net.URISyntaxException;
25 import java.text.ParseException;
26 import java.text.SimpleDateFormat;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.List;
36 import java.util.concurrent.CancellationException;
37 import java.util.concurrent.ExecutionException;
38 import javax.ws.rs.core.Response;
39 import javax.ws.rs.core.Response.ResponseBuilder;
40 import javax.ws.rs.core.Response.Status;
41 import javax.ws.rs.core.UriBuilder;
42 import javax.ws.rs.core.UriInfo;
43 import org.apache.commons.lang3.StringUtils;
44 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
45 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
46 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
47 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
48 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
49 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
50 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
51 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
52 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
53 import org.opendaylight.controller.sal.rest.api.Draft02;
54 import org.opendaylight.controller.sal.rest.api.RestconfService;
55 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
56 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
57 import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
58 import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
59 import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
60 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
61 import org.opendaylight.controller.sal.streams.listeners.Notificator;
62 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
63 import org.opendaylight.yangtools.concepts.Codec;
64 import org.opendaylight.yangtools.yang.common.QName;
65 import org.opendaylight.yangtools.yang.common.QNameModule;
66 import org.opendaylight.yangtools.yang.common.RpcResult;
67 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
68 import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
69 import org.opendaylight.yangtools.yang.data.api.Node;
70 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
75 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
76 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
80 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
81 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
82 import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
83 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
84 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
85 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
86 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
87 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
88 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
89 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
90 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
91 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
92 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
93 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
94 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
95 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
96 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
97 import org.opendaylight.yangtools.yang.model.api.Module;
98 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
99 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
100 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
101 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
102 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
103 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
104 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
105 import org.opendaylight.yangtools.yang.model.util.EmptyType;
106 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
107 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
108 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
109 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
110 import org.slf4j.Logger;
111 import org.slf4j.LoggerFactory;
113 public class RestconfImpl implements RestconfService {
115 private enum UriParameters {
116 PRETTY_PRINT("prettyPrint"),
119 private String uriParameterName;
121 UriParameters(final String uriParameterName) {
122 this.uriParameterName = uriParameterName;
126 public String toString() {
127 return uriParameterName;
131 private static class TypeDef {
132 public final TypeDefinition<? extends Object> typedef;
133 public final QName qName;
135 TypeDef(final TypeDefinition<? extends Object> typedef, final QName qName) {
136 this.typedef = typedef;
141 private final static RestconfImpl INSTANCE = new RestconfImpl();
143 private static final int NOTIFICATION_PORT = 8181;
145 private static final int CHAR_NOT_FOUND = -1;
147 private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
149 private final static SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
151 private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
153 private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
155 private BrokerFacade broker;
157 private ControllerContext controllerContext;
159 private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class);
161 private static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE;
163 private static final LogicalDatastoreType DEFAULT_DATASTORE = LogicalDatastoreType.CONFIGURATION;
165 private static final URI NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = URI.create("urn:sal:restconf:event:subscription");
167 private static final Date EVENT_SUBSCRIPTION_AUGMENT_REVISION;
169 private static final String DATASTORE_PARAM_NAME = "datastore";
171 private static final String SCOPE_PARAM_NAME = "scope";
173 private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
175 private static final String NETCONF_BASE_PAYLOAD_NAME = "data";
177 private static final QName NETCONF_BASE_QNAME;
181 EVENT_SUBSCRIPTION_AUGMENT_REVISION = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
182 NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME );
183 } catch (final ParseException e) {
184 throw new RestconfDocumentedException(
185 "It wasn't possible to convert revision date of sal-remote-augment to date", ErrorType.APPLICATION,
186 ErrorTag.OPERATION_FAILED);
187 } catch (final URISyntaxException e) {
188 throw new RestconfDocumentedException(
189 "It wasn't possible to create instance of URI class with "+NETCONF_BASE+" URI", ErrorType.APPLICATION,
190 ErrorTag.OPERATION_FAILED);
194 public void setBroker(final BrokerFacade broker) {
195 this.broker = broker;
198 public void setControllerContext(final ControllerContext controllerContext) {
199 this.controllerContext = controllerContext;
202 private RestconfImpl() {
205 public static RestconfImpl getInstance() {
210 public NormalizedNodeContext getModules(final UriInfo uriInfo) {
211 final Set<Module> allModules = controllerContext.getAllModules();
212 final MapNode allModuleMap = makeModuleMapNode(allModules);
214 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
216 final Module restconfModule = getRestconfModule();
217 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
218 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
219 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
221 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
222 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
223 moduleContainerBuilder.withChild(allModuleMap);
225 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
226 null, schemaContext), moduleContainerBuilder.build());
230 * Valid only for mount point
233 public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
234 Preconditions.checkNotNull(identifier);
235 if ( ! identifier.contains(ControllerContext.MOUNT)) {
236 final String errMsg = "URI has bad format. If modules behind mount point should be showed,"
237 + " URI has to end with " + ControllerContext.MOUNT;
238 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
241 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
242 final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
243 final Set<Module> modules = controllerContext.getAllModules(mountPoint);
244 final SchemaContext schemaContext = mountPoint.getSchemaContext();
245 final MapNode mountPointModulesMap = makeModuleMapNode(modules);
247 final Module restconfModule = getRestconfModule();
248 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
249 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
250 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
252 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
253 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
254 moduleContainerBuilder.withChild(mountPointModulesMap);
256 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
257 mountPoint, schemaContext), moduleContainerBuilder.build());
261 public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
262 Preconditions.checkNotNull(identifier);
263 final QName moduleNameAndRevision = getModuleNameAndRevision(identifier);
264 Module module = null;
265 DOMMountPoint mountPoint = null;
266 final SchemaContext schemaContext;
267 if (identifier.contains(ControllerContext.MOUNT)) {
268 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
269 mountPoint = mountPointIdentifier.getMountPoint();
270 module = controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
271 schemaContext = mountPoint.getSchemaContext();
273 module = controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
274 schemaContext = controllerContext.getGlobalSchema();
277 if (module == null) {
278 final String errMsg = "Module with name '" + moduleNameAndRevision.getLocalName()
279 + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found.";
280 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
283 final Module restconfModule = getRestconfModule();
284 final Set<Module> modules = Collections.singleton(module);
285 final MapNode moduleMap = makeModuleMapNode(modules);
287 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
288 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
289 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
291 return new NormalizedNodeContext(new InstanceIdentifierContext(null, moduleSchemaNode, mountPoint,
292 schemaContext), moduleMap);
296 public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
297 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
298 final Set<String> availableStreams = Notificator.getStreamNames();
299 final Module restconfModule = getRestconfModule();
300 final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
301 Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
302 Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
304 final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamsBuilder = Builders
305 .mapBuilder((ListSchemaNode) streamSchemaNode);
307 for (final String streamName : availableStreams) {
308 listStreamsBuilder.withChild(toStreamEntryNode(streamName, streamSchemaNode));
311 final DataSchemaNode streamsContainerSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
312 restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
313 Preconditions.checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode);
315 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder =
316 Builders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode);
317 streamsContainerBuilder.withChild(listStreamsBuilder.build());
320 return new NormalizedNodeContext(new InstanceIdentifierContext(null, streamsContainerSchemaNode, null,
321 schemaContext), streamsContainerBuilder.build());
325 public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
326 final Set<Module> allModules = controllerContext.getAllModules();
327 return operationsFromModulesToNormalizedContext(allModules, null);
331 public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
332 Set<Module> modules = null;
333 DOMMountPoint mountPoint = null;
334 if (identifier.contains(ControllerContext.MOUNT)) {
335 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
336 mountPoint = mountPointIdentifier.getMountPoint();
337 modules = controllerContext.getAllModules(mountPoint);
340 final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
341 throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
344 return operationsFromModulesToNormalizedContext(modules, mountPoint);
347 private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set<Module> modules,
348 final DOMMountPoint mountPoint) {
350 // FIXME find best way to change restconf-netconf yang schema for provide this functionality
351 final String errMsg = "We are not able support view operations functionality yet.";
352 throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
356 * @deprecated method will be removed in Lithium release
359 private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
360 final DOMMountPoint mountPoint, final boolean prettyPrint) {
361 final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
362 final Module restconfModule = getRestconfModule();
363 final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
364 restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
365 final QName qName = operationsSchemaNode.getQName();
366 final SchemaPath path = operationsSchemaNode.getPath();
367 final ContainerSchemaNodeBuilder containerSchemaNodeBuilder = new ContainerSchemaNodeBuilder(
368 Draft02.RestConfModule.NAME, 0, qName, path);
369 final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
370 for (final Module module : modules) {
371 final Set<RpcDefinition> rpcs = module.getRpcs();
372 for (final RpcDefinition rpc : rpcs) {
373 final QName rpcQName = rpc.getQName();
374 final SimpleNode<Object> immutableSimpleNode = NodeFactory.<Object> createImmutableSimpleNode(rpcQName, null,
376 operationsAsData.add(immutableSimpleNode);
378 final String name = module.getName();
379 final LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName,
380 SchemaPath.create(true, QName.create("dummy")));
381 final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
382 fakeRpcSchemaNode.setAugmenting(true);
384 final EmptyType instance = EmptyType.getInstance();
385 fakeRpcSchemaNode.setType(instance);
386 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build());
390 final CompositeNode operationsNode = NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
391 final ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
392 return new StructuredData(operationsNode, schemaNode, mountPoint, prettyPrint);
395 private Module getRestconfModule() {
396 final Module restconfModule = controllerContext.getRestconfModule();
397 if (restconfModule == null) {
398 throw new RestconfDocumentedException("ietf-restconf module was not found.", ErrorType.APPLICATION,
399 ErrorTag.OPERATION_NOT_SUPPORTED);
402 return restconfModule;
405 private QName getModuleNameAndRevision(final String identifier) {
406 final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
407 String moduleNameAndRevision = "";
408 if (mountIndex >= 0) {
409 moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
411 moduleNameAndRevision = identifier;
414 final Splitter splitter = Splitter.on("/").omitEmptyStrings();
415 final Iterable<String> split = splitter.split(moduleNameAndRevision);
416 final List<String> pathArgs = Lists.<String> newArrayList(split);
417 if (pathArgs.size() < 2) {
418 throw new RestconfDocumentedException(
419 "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL,
420 ErrorTag.INVALID_VALUE);
424 final String moduleName = pathArgs.get(0);
425 final String revision = pathArgs.get(1);
426 final Date moduleRevision = REVISION_FORMAT.parse(revision);
427 return QName.create(null, moduleRevision, moduleName);
428 } catch (final ParseException e) {
429 throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
430 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
435 * @deprecated method will be removed for Lithium release
436 * so, please use toStreamEntryNode method
439 * @param streamSchemaNode
443 private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
444 final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
445 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
446 ((DataNodeContainer) streamSchemaNode), "name");
447 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
449 .add(NodeFactory.<String> createImmutableSimpleNode(nameSchemaNode.getQName(), null, streamName));
451 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
452 ((DataNodeContainer) streamSchemaNode), "description");
453 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
454 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
455 "DESCRIPTION_PLACEHOLDER"));
457 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
458 ((DataNodeContainer) streamSchemaNode), "replay-support");
459 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
460 streamNodeValues.add(NodeFactory.<Boolean> createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
461 Boolean.valueOf(true)));
463 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
464 ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
465 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
466 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
469 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
470 ((DataNodeContainer) streamSchemaNode), "events");
471 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
472 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(eventsSchemaNode.getQName(), null, ""));
474 return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
478 * @deprecated method will be removed for Lithium release
479 * so, please use toModuleEntryNode method
482 * @param moduleSchemaNode
486 private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
487 final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
488 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
489 ((DataNodeContainer) moduleSchemaNode), "name");
490 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
491 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(nameSchemaNode.getQName(), null,
494 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
495 ((DataNodeContainer) moduleSchemaNode), "revision");
496 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
497 final Date _revision = module.getRevision();
498 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
499 REVISION_FORMAT.format(_revision)));
501 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
502 ((DataNodeContainer) moduleSchemaNode), "namespace");
503 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
504 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
505 module.getNamespace().toString()));
507 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
508 ((DataNodeContainer) moduleSchemaNode), "feature");
509 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
510 for (final FeatureDefinition feature : module.getFeatures()) {
511 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(featureSchemaNode.getQName(), null,
512 feature.getQName().getLocalName()));
515 return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
519 public Object getRoot() {
524 public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
525 final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
526 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
527 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
528 final SchemaContext schemaContext;
529 if (identifier.contains(MOUNT_POINT_MODULE_NAME) && mountPoint != null) {
530 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
531 if ( ! mountRpcServices.isPresent()) {
532 throw new RestconfDocumentedException("Rpc service is missing.");
534 schemaContext = mountPoint.getSchemaContext();
535 response = mountRpcServices.get().invokeRpc(type, payload.getData());
537 response = broker.invokeRpc(type, payload.getData());
538 schemaContext = controllerContext.getGlobalSchema();
541 final DOMRpcResult result = checkRpcResponse(response);
543 DataSchemaNode resultNodeSchema = null;
544 NormalizedNode<?, ?> resultData = null;
545 if (result != null && result.getResult() != null) {
546 resultData = result.getResult();
547 final ContainerSchemaNode rpcDataSchemaNode = SchemaContextUtil.getRpcDataSchema(schemaContext, type);
548 resultNodeSchema = rpcDataSchemaNode.getDataChildByName(result.getResult().getNodeType());
551 return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
552 schemaContext), resultData);
555 private DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
556 if (response == null) {
560 final DOMRpcResult retValue = response.get();
561 if (retValue.getErrors() == null || retValue.getErrors().isEmpty()) {
564 throw new RestconfDocumentedException("RpcError message", null, retValue.getErrors());
566 catch (final InterruptedException e) {
567 throw new RestconfDocumentedException(
568 "The operation was interrupted while executing and did not complete.", ErrorType.RPC,
569 ErrorTag.PARTIAL_OPERATION);
571 catch (final ExecutionException e) {
572 Throwable cause = e.getCause();
573 if (cause instanceof CancellationException) {
574 throw new RestconfDocumentedException("The operation was cancelled while executing.", ErrorType.RPC,
575 ErrorTag.PARTIAL_OPERATION);
576 } else if (cause != null) {
577 while (cause.getCause() != null) {
578 cause = cause.getCause();
581 if (cause instanceof IllegalArgumentException) {
582 throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
583 ErrorTag.INVALID_VALUE);
586 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
589 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
595 private void validateInput(final DataSchemaNode inputSchema, final NormalizedNodeContext payload) {
596 if (inputSchema != null && payload.getData() == null) {
597 // expected a non null payload
598 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
599 } else if (inputSchema == null && payload.getData() != null) {
600 // did not expect any input
601 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
605 // TODO: Validate "mandatory" and "config" values here??? Or should those be
607 // validate in a more central location inside MD-SAL core.
612 * @deprecated method will be removed for Lithium release
618 private void validateInput(final DataSchemaNode inputSchema, final Node<?> payload) {
619 if (inputSchema != null && payload == null) {
620 // expected a non null payload
621 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
622 } else if (inputSchema == null && payload != null) {
623 // did not expect any input
624 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
628 // TODO: Validate "mandatory" and "config" values here??? Or should those be
630 // validate in a more central location inside MD-SAL core.
635 * @deprecated method wil be removed for Lithium release
638 private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload, final RpcDefinition rpc,
639 final boolean prettyPrint) {
640 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
641 final SimpleNode<? extends Object> pathNode = value == null ? null : value.getFirstSimpleByName(QName.create(
642 rpc.getQName(), "path"));
643 final Object pathValue = pathNode == null ? null : pathNode.getValue();
645 if (!(pathValue instanceof YangInstanceIdentifier)) {
646 throw new RestconfDocumentedException("Instance identifier was not normalized correctly.",
647 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
650 final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
651 String streamName = null;
652 if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
653 final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
655 LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class,
656 DATASTORE_PARAM_NAME);
657 datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
659 DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
660 scope = scope == null ? DEFAULT_SCOPE : scope;
662 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
663 + "/scope=" + scope);
666 if (Strings.isNullOrEmpty(streamName)) {
667 throw new RestconfDocumentedException(
668 "Path is empty or contains data node which is not Container or List build-in type.",
669 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
672 final SimpleNode<String> streamNameNode = NodeFactory.<String> createImmutableSimpleNode(
673 QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
674 final List<Node<?>> output = new ArrayList<Node<?>>();
675 output.add(streamNameNode);
677 final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(rpc.getOutput().getQName(),
678 null, output, null, null);
680 if (!Notificator.existListenerFor(streamName)) {
681 Notificator.createListener(pathIdentifier, streamName);
684 return new StructuredData(responseData, rpc.getOutput(), null, prettyPrint);
688 public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
689 if (StringUtils.isNotBlank(noPayload)) {
690 throw new RestconfDocumentedException("Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
693 String identifierEncoded = null;
694 DOMMountPoint mountPoint = null;
695 final SchemaContext schemaContext;
696 if (identifier.contains(ControllerContext.MOUNT)) {
697 // mounted RPC call - look up mount instance.
698 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
699 mountPoint = mountPointId.getMountPoint();
700 schemaContext = mountPoint.getSchemaContext();
701 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
702 + ControllerContext.MOUNT.length() + 1;
703 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
704 identifierEncoded = remoteRpcName;
706 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
707 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
708 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
709 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
711 identifierEncoded = identifier;
712 schemaContext = controllerContext.getGlobalSchema();
715 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
717 RpcDefinition rpc = null;
718 if (mountPoint == null) {
719 rpc = controllerContext.getRpcDefinition(identifierDecoded);
721 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
725 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
728 if (rpc.getInput() != null) {
729 // FIXME : find a correct Error from specification
730 throw new IllegalStateException("RPC " + rpc + " needs input value!");
733 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
734 if (mountPoint != null) {
735 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
736 if ( ! mountRpcServices.isPresent()) {
737 throw new RestconfDocumentedException("Rpc service is missing.");
739 response = mountRpcServices.get().invokeRpc(rpc.getPath(), null);
741 response = broker.invokeRpc(rpc.getPath(), null);
744 final DOMRpcResult result = checkRpcResponse(response);
746 DataSchemaNode resultNodeSchema = null;
747 NormalizedNode<?, ?> resultData = null;
748 if (result != null && result.getResult() != null) {
749 resultData = result.getResult();
750 final ContainerSchemaNode rpcDataSchemaNode =
751 SchemaContextUtil.getRpcDataSchema(schemaContext, rpc.getOutput().getPath());
752 resultNodeSchema = rpcDataSchemaNode.getDataChildByName(result.getResult().getNodeType());
755 return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
756 schemaContext), resultData);
759 private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
760 String identifierEncoded = null;
761 DOMMountPoint mountPoint = null;
762 if (identifier.contains(ControllerContext.MOUNT)) {
763 // mounted RPC call - look up mount instance.
764 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
765 mountPoint = mountPointId.getMountPoint();
767 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
768 + ControllerContext.MOUNT.length() + 1;
769 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
770 identifierEncoded = remoteRpcName;
772 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
773 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
774 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
775 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
777 identifierEncoded = identifier;
780 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
782 RpcDefinition rpc = null;
783 if (mountPoint == null) {
784 rpc = controllerContext.getRpcDefinition(identifierDecoded);
786 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
790 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
793 if (mountPoint == null) {
794 return new BrokerRpcExecutor(rpc, broker);
796 return new MountPointRpcExecutor(rpc, mountPoint);
801 private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
802 final String[] splittedIdentifier = identifierDecoded.split(":");
803 if (splittedIdentifier.length != 2) {
804 throw new RestconfDocumentedException(identifierDecoded
805 + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION,
806 ErrorTag.INVALID_VALUE);
808 for (final Module module : schemaContext.getModules()) {
809 if (module.getName().equals(splittedIdentifier[0])) {
810 for (final RpcDefinition rpcDefinition : module.getRpcs()) {
811 if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) {
812 return rpcDefinition;
821 * @deprecated method will be removed for Lithium release
824 private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload, final boolean prettyPrint) {
825 if (rpcExecutor == null) {
826 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
829 CompositeNode rpcRequest = null;
830 final RpcDefinition rpc = rpcExecutor.getRpcDefinition();
831 final QName rpcName = rpc.getQName();
833 if (payload == null) {
834 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
836 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
837 final List<Node<?>> input = Collections.<Node<?>> singletonList(value);
838 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
841 final RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
843 checkRpcSuccessAndThrowException(rpcResult);
845 if (rpcResult.getResult() == null) {
849 if (rpc.getOutput() == null) {
850 return null; // no output, nothing to send back.
853 return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null, prettyPrint);
856 private void checkRpcSuccessAndThrowException(final RpcResult<CompositeNode> rpcResult) {
857 if (rpcResult.isSuccessful() == false) {
859 throw new RestconfDocumentedException("The operation was not successful", null,
860 rpcResult.getErrors());
865 public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
866 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
867 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
868 NormalizedNode<?, ?> data = null;
869 YangInstanceIdentifier normalizedII;
870 if (mountPoint != null) {
871 normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData
872 .getInstanceIdentifier());
873 data = broker.readConfigurationData(mountPoint, normalizedII);
875 normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
876 data = broker.readConfigurationData(normalizedII);
878 return new NormalizedNodeContext(iiWithData, data);
881 @SuppressWarnings("unchecked")
882 private <T extends Node<?>> T pruneDataAtDepth(final T node, final Integer depth) {
887 if (node instanceof CompositeNode) {
888 final ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
890 for (final Node<?> childNode : ((CompositeNode) node).getValue()) {
891 newChildNodes.add(pruneDataAtDepth(childNode, depth - 1));
895 return (T) ImmutableCompositeNode.create(node.getNodeType(), newChildNodes.build());
896 } else { // SimpleNode
901 private Integer parseDepthParameter(final UriInfo info) {
902 final String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString());
903 if (Strings.isNullOrEmpty(param) || "unbounded".equals(param)) {
908 final Integer depth = Integer.valueOf(param);
910 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
911 "Invalid depth parameter: " + depth, null,
912 "The depth parameter must be an integer > 1 or \"unbounded\""));
916 } catch (final NumberFormatException e) {
917 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
918 "Invalid depth parameter: " + e.getMessage(), null,
919 "The depth parameter must be an integer > 1 or \"unbounded\""));
924 public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo info) {
925 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
926 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
927 NormalizedNode<?, ?> data = null;
928 YangInstanceIdentifier normalizedII;
929 if (mountPoint != null) {
930 normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData
931 .getInstanceIdentifier());
932 data = broker.readOperationalData(mountPoint, normalizedII);
934 normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
935 data = broker.readOperationalData(normalizedII);
938 return new NormalizedNodeContext(iiWithData, data);
941 private boolean parsePrettyPrintParameter(final UriInfo info) {
942 final String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
943 return Boolean.parseBoolean(param);
947 public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
948 Preconditions.checkNotNull(identifier);
949 final InstanceIdentifierContext<DataSchemaNode> iiWithData = controllerContext.toInstanceIdentifier(identifier);
951 validateInput(iiWithData.getSchemaNode(), payload);
952 validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier());
953 validateListKeysEqualityInPayloadAndUri(iiWithData, payload.getData());
955 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
956 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
959 * There is a small window where another write transaction could be updating the same data
960 * simultaneously and we get an OptimisticLockFailedException. This error is likely
961 * transient and The WriteTransaction#submit API docs state that a retry will likely
962 * succeed. So we'll try again if that scenario occurs. If it fails a third time then it
963 * probably will never succeed so we'll fail in that case.
965 * By retrying we're attempting to hide the internal implementation of the data store and
966 * how it handles concurrent updates from the restconf client. The client has instructed us
967 * to put the data and we should make every effort to do so without pushing optimistic lock
968 * failures back to the client and forcing them to handle it via retry (and having to
969 * document the behavior).
974 if (mountPoint != null) {
975 broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
977 broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet();
981 } catch (final TransactionCommitFailedException e) {
982 if(e instanceof OptimisticLockFailedException) {
984 LOG.debug("Got OptimisticLockFailedException on last try - failing");
985 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
988 LOG.debug("Got OptimisticLockFailedException - trying again");
990 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
995 return Response.status(Status.OK).build();
998 private void validateTopLevelNodeName(final NormalizedNodeContext node,
999 final YangInstanceIdentifier identifier) {
1001 final String payloadName = node.getData().getNodeType().getLocalName();
1002 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1005 if ( ! pathArguments.hasNext()) {
1007 if ( ! node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) {
1008 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1009 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1013 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1014 if ( ! payloadName.equals(identifierName)) {
1015 throw new RestconfDocumentedException("Payload name (" + payloadName
1016 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1017 ErrorTag.MALFORMED_MESSAGE);
1023 * @deprecated method will be removed for Lithium release
1029 private void validateTopLevelNodeName(final Node<?> node,
1030 final YangInstanceIdentifier identifier) {
1031 final String payloadName = getName(node);
1032 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1035 if (!pathArguments.hasNext()) {
1037 if (!node.getNodeType().equals(NETCONF_BASE_QNAME)) {
1038 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1039 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1043 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1044 if (!payloadName.equals(identifierName)) {
1045 throw new RestconfDocumentedException("Payload name (" + payloadName
1046 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1047 ErrorTag.MALFORMED_MESSAGE);
1053 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1055 * @throws RestconfDocumentedException
1056 * if key values or key count in payload and URI isn't equal
1059 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
1060 final NormalizedNode<?, ?> payload) {
1061 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1062 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1063 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1064 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1065 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1067 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1073 * @deprecated method will be removed for Lithium release
1075 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1077 * @throws RestconfDocumentedException
1078 * if key values or key count in payload and URI isn't equal
1082 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
1083 final CompositeNode payload) {
1084 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1085 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1086 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1087 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1088 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1090 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1095 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final NormalizedNode<?, ?> payload,
1096 final List<QName> keyDefinitions) {
1097 for (final QName keyDefinition : keyDefinitions) {
1098 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1099 // should be caught during parsing URI to InstanceIdentifier
1100 if (uriKeyValue == null) {
1101 final String errMsg = "Missing key " + keyDefinition + " in URI.";
1102 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1104 // TODO thing about the possibility to fix
1105 // final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1106 // if (payloadKeyValues.isEmpty()) {
1107 // final String errMsg = "Missing key " + keyDefinition.getLocalName() + " in the message body.";
1108 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1111 // final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1112 // if (!uriKeyValue.equals(payloadKeyValue)) {
1113 // final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
1114 // "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. ";
1115 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1121 * @deprecated method will be removed for Lithium release
1123 * @param uriKeyValues
1125 * @param keyDefinitions
1128 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final CompositeNode payload,
1129 final List<QName> keyDefinitions) {
1130 for (final QName keyDefinition : keyDefinitions) {
1131 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1132 // should be caught during parsing URI to InstanceIdentifier
1133 if (uriKeyValue == null) {
1134 throw new RestconfDocumentedException("Missing key " + keyDefinition + " in URI.", ErrorType.PROTOCOL,
1135 ErrorTag.DATA_MISSING);
1137 final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1138 if (payloadKeyValues.isEmpty()) {
1139 throw new RestconfDocumentedException("Missing key " + keyDefinition.getLocalName()
1140 + " in the message body.", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1143 final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1144 if (!uriKeyValue.equals(payloadKeyValue)) {
1145 throw new RestconfDocumentedException("The value '" + uriKeyValue + "' for key '"
1146 + keyDefinition.getLocalName() + "' specified in the URI doesn't match the value '"
1147 + payloadKeyValue + "' specified in the message body. ", ErrorType.PROTOCOL,
1148 ErrorTag.INVALID_VALUE);
1154 public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
1155 if (payload == null) {
1156 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1159 final URI payloadNS = payload.getData().getNodeType().getNamespace();
1160 if (payloadNS == null) {
1161 throw new RestconfDocumentedException(
1162 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
1163 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1166 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
1168 final InstanceIdentifierContext iiWithData = mountPoint != null
1169 ? controllerContext.toMountPointIdentifier(identifier)
1170 : controllerContext.toInstanceIdentifier(identifier);
1171 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1174 if (mountPoint != null) {
1175 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
1177 broker.commitConfigurationDataPost(normalizedII, payload.getData());
1179 } catch(final RestconfDocumentedException e) {
1181 } catch (final Exception e) {
1182 throw new RestconfDocumentedException("Error creating data", e);
1186 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
1187 final URI location = resolveLocation(uriInfo, "config", mountPoint, normalizedII);
1188 if (location != null) {
1189 responseBuilder.location(location);
1191 return responseBuilder.build();
1194 // FIXME create RestconfIdetifierHelper and move this method there
1195 private YangInstanceIdentifier checkConsistencyOfNormalizedNodeContext(final NormalizedNodeContext payload) {
1196 Preconditions.checkArgument(payload != null);
1197 Preconditions.checkArgument(payload.getData() != null);
1198 Preconditions.checkArgument(payload.getData().getNodeType() != null);
1199 Preconditions.checkArgument(payload.getInstanceIdentifierContext() != null);
1200 Preconditions.checkArgument(payload.getInstanceIdentifierContext().getInstanceIdentifier() != null);
1202 final QName payloadNodeQname = payload.getData().getNodeType();
1203 final YangInstanceIdentifier yangIdent = payload.getInstanceIdentifierContext().getInstanceIdentifier();
1204 if (payloadNodeQname.compareTo(yangIdent.getLastPathArgument().getNodeType()) > 0) {
1207 final InstanceIdentifierContext parentContext = payload.getInstanceIdentifierContext();
1208 final SchemaNode parentSchemaNode = parentContext.getSchemaNode();
1209 if(parentSchemaNode instanceof DataNodeContainer) {
1210 final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode;
1211 for (final DataSchemaNode child : cast.getChildNodes()) {
1212 if (payloadNodeQname.compareTo(child.getQName()) == 0) {
1213 return YangInstanceIdentifier.builder(yangIdent).node(child.getQName()).build();
1217 if (parentSchemaNode instanceof RpcDefinition) {
1220 final String errMsg = "Error parsing input: DataSchemaNode has not children";
1221 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1225 public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
1226 if (payload == null) {
1227 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1230 final URI payloadNS = payload.getData().getNodeType().getNamespace();
1231 if (payloadNS == null) {
1232 throw new RestconfDocumentedException(
1233 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
1234 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1237 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
1238 final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext();
1239 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1242 if (mountPoint != null) {
1243 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
1246 broker.commitConfigurationDataPost(normalizedII, payload.getData());
1248 } catch(final RestconfDocumentedException e) {
1250 } catch (final Exception e) {
1251 throw new RestconfDocumentedException("Error creating data", e);
1254 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
1255 final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII);
1256 if (location != null) {
1257 responseBuilder.location(location);
1259 return responseBuilder.build();
1262 private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
1263 final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
1264 uriBuilder.path("config");
1266 uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
1267 } catch (final Exception e) {
1268 LOG.debug("Location for instance identifier"+normalizedII+"wasn't created", e);
1271 return uriBuilder.build();
1275 public Response deleteConfigurationData(final String identifier) {
1276 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
1277 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
1278 YangInstanceIdentifier normalizedII;
1281 if (mountPoint != null) {
1282 normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData
1283 .getInstanceIdentifier());
1284 broker.commitConfigurationDataDelete(mountPoint, normalizedII);
1286 normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
1287 broker.commitConfigurationDataDelete(normalizedII).get();
1289 } catch (final Exception e) {
1290 final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
1291 Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
1292 if (searchedException.isPresent()) {
1293 throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
1295 throw new RestconfDocumentedException("Error while deleting data", e);
1297 return Response.status(Status.OK).build();
1301 * Subscribes to some path in schema context (stream) to listen on changes on this stream.
1303 * Additional parameters for subscribing to stream are loaded via rpc input parameters:
1305 * <li>datastore</li> - default CONFIGURATION (other values of {@link LogicalDatastoreType} enum type)
1306 * <li>scope</li> - default BASE (other values of {@link DataChangeScope})
1310 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
1311 final String streamName = Notificator.createStreamNameFromUri(identifier);
1312 if (Strings.isNullOrEmpty(streamName)) {
1313 throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1316 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
1317 if (listener == null) {
1318 throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
1321 final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
1322 final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
1323 paramToValues.get(DATASTORE_PARAM_NAME));
1324 if (datastore == null) {
1325 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)",
1326 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1328 final DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME));
1329 if (scope == null) {
1330 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)",
1331 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1334 broker.registerToListenDataChanges(datastore, scope, listener);
1336 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
1337 int notificationPort = NOTIFICATION_PORT;
1339 final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
1340 notificationPort = webSocketServerInstance.getPort();
1341 } catch (final NullPointerException e) {
1342 WebSocketServer.createInstance(NOTIFICATION_PORT);
1344 final UriBuilder port = uriBuilder.port(notificationPort);
1345 final URI uriToWebsocketServer = port.replacePath(streamName).build();
1347 return Response.status(Status.OK).location(uriToWebsocketServer).build();
1351 * Load parameter for subscribing to stream from input composite node
1355 * @return enum object if its string value is equal to {@code paramName}. In other cases null.
1357 private <T> T parseEnumTypeParameter(final CompositeNode compNode, final Class<T> classDescriptor,
1358 final String paramName) {
1359 final QNameModule salRemoteAugment = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
1360 EVENT_SUBSCRIPTION_AUGMENT_REVISION);
1361 final SimpleNode<?> simpleNode = compNode.getFirstSimpleByName(QName.create(salRemoteAugment, paramName));
1362 if (simpleNode == null) {
1365 final Object rawValue = simpleNode.getValue();
1366 if (!(rawValue instanceof String)) {
1370 return resolveAsEnum(classDescriptor, (String) rawValue);
1374 * Checks whether {@code value} is one of the string representation of enumeration {@code classDescriptor}
1376 * @return enum object if string value of {@code classDescriptor} enumeration is equal to {@code value}. Other cases
1379 private <T> T parserURIEnumParameter(final Class<T> classDescriptor, final String value) {
1380 if (Strings.isNullOrEmpty(value)) {
1383 return resolveAsEnum(classDescriptor, value);
1386 private <T> T resolveAsEnum(final Class<T> classDescriptor, final String value) {
1387 final T[] enumConstants = classDescriptor.getEnumConstants();
1388 if (enumConstants != null) {
1389 for (final T enm : classDescriptor.getEnumConstants()) {
1390 if (((Enum<?>) enm).name().equals(value)) {
1398 private Map<String, String> resolveValuesFromUri(final String uri) {
1399 final Map<String, String> result = new HashMap<>();
1400 final String[] tokens = uri.split("/");
1401 for (int i = 1; i < tokens.length; i++) {
1402 final String[] parameterTokens = tokens[i].split("=");
1403 if (parameterTokens.length == 2) {
1404 result.put(parameterTokens[0], parameterTokens[1]);
1410 private Module findModule(final DOMMountPoint mountPoint, final Node<?> data) {
1411 Module module = null;
1412 if (data instanceof NodeWrapper) {
1413 module = findModule(mountPoint, (NodeWrapper<?>) data);
1414 } else if (data != null) {
1415 final URI namespace = data.getNodeType().getNamespace();
1416 if (mountPoint != null) {
1417 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1419 module = controllerContext.findModuleByNamespace(namespace);
1422 if (module != null) {
1425 throw new RestconfDocumentedException(
1426 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
1427 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1430 private Module findModule(final DOMMountPoint mountPoint, final NodeWrapper<?> data) {
1431 final URI namespace = data.getNamespace();
1432 Preconditions.<URI> checkNotNull(namespace);
1434 Module module = null;
1435 if (mountPoint != null) {
1436 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1437 if (module == null) {
1438 module = controllerContext.findModuleByName(mountPoint, namespace.toString());
1441 module = controllerContext.findModuleByNamespace(namespace);
1442 if (module == null) {
1443 module = controllerContext.findModuleByName(namespace.toString());
1450 private InstanceIdentifierContext addLastIdentifierFromData(final InstanceIdentifierContext identifierWithSchemaNode,
1451 final CompositeNode data, final DataSchemaNode schemaOfData, final SchemaContext schemaContext) {
1452 YangInstanceIdentifier instanceIdentifier = null;
1453 if (identifierWithSchemaNode != null) {
1454 instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
1457 final YangInstanceIdentifier iiOriginal = instanceIdentifier;
1458 InstanceIdentifierBuilder iiBuilder = null;
1459 if (iiOriginal == null) {
1460 iiBuilder = YangInstanceIdentifier.builder();
1462 iiBuilder = YangInstanceIdentifier.builder(iiOriginal);
1465 if ((schemaOfData instanceof ListSchemaNode)) {
1466 final HashMap<QName, Object> keys = resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
1467 iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
1469 iiBuilder.node(schemaOfData.getQName());
1472 final YangInstanceIdentifier instance = iiBuilder.toInstance();
1473 DOMMountPoint mountPoint = null;
1474 final SchemaContext schemaCtx = null;
1475 if (identifierWithSchemaNode != null) {
1476 mountPoint = identifierWithSchemaNode.getMountPoint();
1479 return new InstanceIdentifierContext(instance, schemaOfData, mountPoint,schemaContext);
1482 private HashMap<QName, Object> resolveKeysFromData(final ListSchemaNode listNode, final CompositeNode dataNode) {
1483 final HashMap<QName, Object> keyValues = new HashMap<QName, Object>();
1484 final List<QName> _keyDefinition = listNode.getKeyDefinition();
1485 for (final QName key : _keyDefinition) {
1486 SimpleNode<? extends Object> head = null;
1487 final String localName = key.getLocalName();
1488 final List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
1489 if (simpleNodesByName != null) {
1490 head = Iterables.getFirst(simpleNodesByName, null);
1493 Object dataNodeKeyValueObject = null;
1495 dataNodeKeyValueObject = head.getValue();
1498 if (dataNodeKeyValueObject == null) {
1499 throw new RestconfDocumentedException("Data contains list \"" + dataNode.getNodeType().getLocalName()
1500 + "\" which does not contain key: \"" + key.getLocalName() + "\"", ErrorType.PROTOCOL,
1501 ErrorTag.INVALID_VALUE);
1504 keyValues.put(key, dataNodeKeyValueObject);
1510 private boolean endsWithMountPoint(final String identifier) {
1511 return identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/");
1514 private boolean representsMountPointRootData(final Node<?> data) {
1515 final URI namespace = namespace(data);
1516 return (SchemaContext.NAME.getNamespace().equals(namespace) /*
1517 * || MOUNT_POINT_MODULE_NAME .equals( namespace .
1520 && SchemaContext.NAME.getLocalName().equals(localName(data));
1523 private String addMountPointIdentifier(final String identifier) {
1524 final boolean endsWith = identifier.endsWith("/");
1526 return (identifier + ControllerContext.MOUNT);
1529 return identifier + "/" + ControllerContext.MOUNT;
1533 * @deprecated method will be removed in Lithium release
1534 * we don't wish to use Node and CompositeNode anywhere
1537 public CompositeNode normalizeNode(final Node<?> node, final DataSchemaNode schema, final DOMMountPoint mountPoint) {
1538 if (schema == null) {
1539 final String localName = node == null ? null :
1540 node instanceof NodeWrapper ? ((NodeWrapper<?>)node).getLocalName() :
1541 node.getNodeType().getLocalName();
1543 throw new RestconfDocumentedException("Data schema node was not found for " + localName,
1544 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1547 if (!(schema instanceof DataNodeContainer)) {
1548 throw new RestconfDocumentedException("Root element has to be container or list yang datatype.",
1549 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1552 if ((node instanceof NodeWrapper<?>)) {
1553 NodeWrapper<?> nodeWrap = (NodeWrapper<?>) node;
1554 final boolean isChangeAllowed = ((NodeWrapper<?>) node).isChangeAllowed();
1555 if (isChangeAllowed) {
1556 nodeWrap = topLevelElementAsCompositeNodeWrapper((NodeWrapper<?>) node, schema);
1558 this.normalizeNode(nodeWrap, schema, null, mountPoint);
1559 } catch (final IllegalArgumentException e) {
1560 final RestconfDocumentedException restconfDocumentedException = new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1561 restconfDocumentedException.addSuppressed(e);
1562 throw restconfDocumentedException;
1564 if (nodeWrap instanceof CompositeNodeWrapper) {
1565 return ((CompositeNodeWrapper) nodeWrap).unwrap();
1570 if (node instanceof CompositeNode) {
1571 return (CompositeNode) node;
1574 throw new RestconfDocumentedException("Top level element is not interpreted as composite node.",
1575 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
1578 private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1579 final QName previousAugment, final DOMMountPoint mountPoint) {
1580 if (schema == null) {
1581 throw new RestconfDocumentedException("Data has bad format.\n\"" + nodeBuilder.getLocalName()
1582 + "\" does not exist in yang schema.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1585 QName currentAugment = null;
1586 if (nodeBuilder.getQname() != null) {
1587 currentAugment = previousAugment;
1589 currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
1590 if (nodeBuilder.getQname() == null) {
1591 throw new RestconfDocumentedException(
1592 "Data has bad format.\nIf data is in XML format then namespace for \""
1593 + nodeBuilder.getLocalName() + "\" should be \"" + schema.getQName().getNamespace()
1594 + "\".\n" + "If data is in JSON format then module name for \""
1595 + nodeBuilder.getLocalName() + "\" should be corresponding to namespace \""
1596 + schema.getQName().getNamespace() + "\".", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1600 if (nodeBuilder instanceof CompositeNodeWrapper) {
1601 if (schema instanceof DataNodeContainer) {
1602 normalizeCompositeNode((CompositeNodeWrapper) nodeBuilder, (DataNodeContainer) schema, mountPoint,
1604 } else if (schema instanceof AnyXmlSchemaNode) {
1605 normalizeAnyXmlNode((CompositeNodeWrapper) nodeBuilder, (AnyXmlSchemaNode) schema);
1607 } else if (nodeBuilder instanceof SimpleNodeWrapper) {
1608 normalizeSimpleNode((SimpleNodeWrapper) nodeBuilder, schema, mountPoint);
1609 } else if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1610 normalizeEmptyNode((EmptyNodeWrapper) nodeBuilder, schema);
1614 private void normalizeAnyXmlNode(final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema) {
1615 final List<NodeWrapper<?>> children = compositeNode.getValues();
1616 for (final NodeWrapper<? extends Object> child : children) {
1617 child.setNamespace(schema.getQName().getNamespace());
1618 if (child instanceof CompositeNodeWrapper) {
1619 normalizeAnyXmlNode((CompositeNodeWrapper) child, schema);
1624 private void normalizeEmptyNode(final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema) {
1625 if ((schema instanceof LeafSchemaNode)) {
1626 emptyNodeBuilder.setComposite(false);
1628 if ((schema instanceof ContainerSchemaNode)) {
1629 // FIXME: Add presence check
1630 emptyNodeBuilder.setComposite(true);
1635 private void normalizeSimpleNode(final SimpleNodeWrapper simpleNode, final DataSchemaNode schema,
1636 final DOMMountPoint mountPoint) {
1637 final Object value = simpleNode.getValue();
1638 Object inputValue = value;
1639 final TypeDef typeDef = this.typeDefinition(schema);
1640 TypeDefinition<? extends Object> typeDefinition = typeDef != null ? typeDef.typedef : null;
1642 // For leafrefs, extract the type it is pointing to
1643 if(typeDefinition instanceof LeafrefTypeDefinition) {
1644 if (schema.getQName().equals(typeDef.qName)) {
1645 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema);
1647 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), typeDef.qName);
1651 if (typeDefinition instanceof IdentityrefTypeDefinition) {
1652 inputValue = parseToIdentityValuesDTO(simpleNode, value, inputValue);
1655 Object outputValue = inputValue;
1657 if (typeDefinition != null) {
1658 final Codec<Object, Object> codec = RestCodec.from(typeDefinition, mountPoint);
1659 outputValue = codec == null ? null : codec.deserialize(inputValue);
1662 simpleNode.setValue(outputValue);
1665 private Object parseToIdentityValuesDTO(final SimpleNodeWrapper simpleNode, final Object value, Object inputValue) {
1666 if ((value instanceof String)) {
1667 inputValue = new IdentityValuesDTO(simpleNode.getNamespace().toString(), (String) value, null,
1669 } // else value is already instance of IdentityValuesDTO
1673 private void normalizeCompositeNode(final CompositeNodeWrapper compositeNodeBuilder,
1674 final DataNodeContainer schema, final DOMMountPoint mountPoint, final QName currentAugment) {
1675 final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
1676 checkNodeMultiplicityAccordingToSchema(schema, children);
1677 for (final NodeWrapper<? extends Object> child : children) {
1678 final List<DataSchemaNode> potentialSchemaNodes = ControllerContext.findInstanceDataChildrenByName(
1679 schema, child.getLocalName());
1681 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
1682 final StringBuilder builder = new StringBuilder();
1683 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1684 builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()).append("\n");
1687 throw new RestconfDocumentedException("Node \"" + child.getLocalName()
1688 + "\" is added as augment from more than one module. "
1689 + "Therefore node must have namespace (XML format) or module name (JSON format)."
1690 + "\nThe node is added as augment from modules with namespaces:\n" + builder,
1691 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1694 boolean rightNodeSchemaFound = false;
1695 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1696 if (!rightNodeSchemaFound) {
1697 final QName potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode,
1698 currentAugment, mountPoint);
1699 if (child.getQname() != null) {
1700 this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1701 rightNodeSchemaFound = true;
1706 if (!rightNodeSchemaFound) {
1707 throw new RestconfDocumentedException("Schema node \"" + child.getLocalName()
1708 + "\" was not found in module.", ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT);
1712 if ((schema instanceof ListSchemaNode)) {
1713 final ListSchemaNode listSchemaNode = (ListSchemaNode) schema;
1714 final List<QName> listKeys = listSchemaNode.getKeyDefinition();
1715 for (final QName listKey : listKeys) {
1716 boolean foundKey = false;
1717 for (final NodeWrapper<? extends Object> child : children) {
1718 if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1724 throw new RestconfDocumentedException("Missing key in URI \"" + listKey.getLocalName()
1725 + "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"", ErrorType.PROTOCOL,
1726 ErrorTag.DATA_MISSING);
1732 private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer,
1733 final List<NodeWrapper<?>> nodes) {
1734 final Map<String, Integer> equalNodeNamesToCounts = new HashMap<String, Integer>();
1735 for (final NodeWrapper<?> child : nodes) {
1736 Integer count = equalNodeNamesToCounts.get(child.getLocalName());
1737 equalNodeNamesToCounts.put(child.getLocalName(), count == null ? 1 : ++count);
1740 for (final DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) {
1741 if (childSchemaNode instanceof ContainerSchemaNode || childSchemaNode instanceof LeafSchemaNode) {
1742 final String localName = childSchemaNode.getQName().getLocalName();
1743 final Integer count = equalNodeNamesToCounts.get(localName);
1744 if (count != null && count > 1) {
1745 throw new RestconfDocumentedException("Multiple input data elements were specified for '"
1746 + childSchemaNode.getQName().getLocalName()
1747 + "'. The data for this element type can only be specified once.", ErrorType.APPLICATION,
1748 ErrorTag.BAD_ELEMENT);
1754 private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1755 final QName previousAugment, final DOMMountPoint mountPoint) {
1756 QName validQName = schema.getQName();
1757 QName currentAugment = previousAugment;
1758 if (schema.isAugmenting()) {
1759 currentAugment = schema.getQName();
1760 } else if (previousAugment != null
1761 && !Objects.equal(schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1762 validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1765 String moduleName = null;
1766 if (mountPoint == null) {
1767 moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1769 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1772 if (nodeBuilder.getNamespace() == null || Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace())
1773 || Objects.equal(nodeBuilder.getNamespace().toString(), moduleName)) {
1775 * || Note : this check is wrong -
1776 * can never be true as it compares
1777 * a URI with a String not sure what
1778 * the intention is so commented out
1779 * ... Objects . equal ( nodeBuilder
1780 * . getNamespace ( ) ,
1781 * MOUNT_POINT_MODULE_NAME )
1784 nodeBuilder.setQname(validQName);
1787 return currentAugment;
1790 private URI namespace(final Node<?> data) {
1791 if (data instanceof NodeWrapper) {
1792 return ((NodeWrapper<?>) data).getNamespace();
1793 } else if (data != null) {
1794 return data.getNodeType().getNamespace();
1796 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1800 private String localName(final Node<?> data) {
1801 if (data instanceof NodeWrapper) {
1802 return ((NodeWrapper<?>) data).getLocalName();
1803 } else if (data != null) {
1804 return data.getNodeType().getLocalName();
1806 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1811 * @deprecated method will be removed for Lithium release
1817 private String getName(final Node<?> data) {
1818 if (data instanceof NodeWrapper) {
1819 return ((NodeWrapper<?>) data).getLocalName();
1820 } else if (data != null) {
1821 return data.getNodeType().getLocalName();
1823 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1827 private TypeDef typeDefinition(final TypeDefinition<?> type, final QName nodeQName) {
1828 TypeDefinition<?> baseType = type;
1829 QName qName = nodeQName;
1830 while (baseType.getBaseType() != null) {
1831 if (baseType instanceof ExtendedType) {
1832 qName = baseType.getQName();
1834 baseType = baseType.getBaseType();
1837 return new TypeDef(baseType, qName);
1841 private TypeDef typeDefinition(final DataSchemaNode node) {
1842 if (node instanceof LeafListSchemaNode) {
1843 return typeDefinition(((LeafListSchemaNode)node).getType(), node.getQName());
1844 } else if (node instanceof LeafSchemaNode) {
1845 return typeDefinition(((LeafSchemaNode)node).getType(), node.getQName());
1846 } else if (node instanceof AnyXmlSchemaNode) {
1849 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
1853 private CompositeNode datastoreNormalizedNodeToCompositeNode(final NormalizedNode<?, ?> dataNode, final DataSchemaNode schema) {
1854 Node<?> nodes = null;
1855 if (dataNode == null) {
1856 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.DATA_MISSING,
1857 "No data was found."));
1859 nodes = DataNormalizer.toLegacy(dataNode);
1860 if (nodes != null) {
1861 if (nodes instanceof CompositeNode) {
1862 return (CompositeNode) nodes;
1864 LOG.error("The node " + dataNode.getNodeType() + " couldn't be transformed to compositenode.");
1867 LOG.error("Top level node isn't of type Container or List schema node but "
1868 + schema.getClass().getSimpleName());
1871 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1872 "It wasn't possible to correctly interpret data."));
1875 private NormalizedNode<?, ?> compositeNodeToDatastoreNormalizedNode(final CompositeNode compNode,
1876 final DataSchemaNode schema) {
1877 final List<Node<?>> lst = new ArrayList<Node<?>>();
1879 if (schema instanceof ContainerSchemaNode) {
1880 return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser()
1881 .parse(lst, (ContainerSchemaNode) schema);
1882 } else if (schema instanceof ListSchemaNode) {
1883 return CnSnToNormalizedNodeParserFactory.getInstance().getMapEntryNodeParser()
1884 .parse(lst, (ListSchemaNode) schema);
1887 LOG.error("Top level isn't of type container, list, leaf schema node but " + schema.getClass().getSimpleName());
1889 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1890 "It wasn't possible to translate specified data to datastore readable form."));
1893 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1894 final InstanceIdentifierContext iiWithSchemaNode) {
1895 return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false);
1898 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1899 final InstanceIdentifierContext iiWithSchemaNode, final boolean unwrapLastListNode) {
1900 return new InstanceIdentifierContext(instanceIdentifierToReadableFormForNormalizeNode(
1901 iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(),
1902 iiWithSchemaNode.getMountPoint(),iiWithSchemaNode.getSchemaContext());
1905 private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode(
1906 final YangInstanceIdentifier instIdentifier, final boolean unwrapLastListNode) {
1907 Preconditions.checkNotNull(instIdentifier, "Instance identifier can't be null");
1908 final List<PathArgument> result = new ArrayList<PathArgument>();
1909 final Iterator<PathArgument> iter = instIdentifier.getPathArguments().iterator();
1910 while (iter.hasNext()) {
1911 final PathArgument pathArgument = iter.next();
1912 if (pathArgument instanceof NodeIdentifierWithPredicates && (iter.hasNext() || unwrapLastListNode)) {
1913 result.add(new YangInstanceIdentifier.NodeIdentifier(pathArgument.getNodeType()));
1915 result.add(pathArgument);
1917 return YangInstanceIdentifier.create(result);
1920 private CompositeNodeWrapper topLevelElementAsCompositeNodeWrapper(final NodeWrapper<?> node,
1921 final DataSchemaNode schemaNode) {
1922 if (node instanceof CompositeNodeWrapper) {
1923 return (CompositeNodeWrapper) node;
1924 } else if (node instanceof SimpleNodeWrapper && isDataContainerNode(schemaNode)) {
1925 final SimpleNodeWrapper simpleNodeWrapper = (SimpleNodeWrapper) node;
1926 return new CompositeNodeWrapper(namespace(simpleNodeWrapper), localName(simpleNodeWrapper));
1929 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1930 "Top level element has to be composite node or has to represent data container node."));
1933 private boolean isDataContainerNode(final DataSchemaNode schemaNode) {
1934 if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode) {
1940 public BigInteger getOperationalReceived() {
1941 // TODO Auto-generated method stub
1945 private MapNode makeModuleMapNode(final Set<Module> modules) {
1946 Preconditions.checkNotNull(modules);
1947 final Module restconfModule = getRestconfModule();
1948 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
1949 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
1950 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
1952 final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
1953 .mapBuilder((ListSchemaNode) moduleSchemaNode);
1955 for (final Module module : modules) {
1956 listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode));
1958 return listModuleBuilder.build();
1961 protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) {
1962 Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode,
1963 "moduleSchemaNode has to be of type ListSchemaNode");
\r final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode;
\r final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = Builders
\r .mapEntryBuilder(listModuleSchemaNode);
\r\r List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
\r (listModuleSchemaNode), "name");
\r final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
\r Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
\r moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName())
\r .build());
\r\r instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
\r (listModuleSchemaNode), "revision");
\r final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
\r Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode);
\r final String revision = REVISION_FORMAT.format(module.getRevision());
\r moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision)
\r .build());
\r\r instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
\r (listModuleSchemaNode), "namespace");
\r final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
\r Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode);
\r moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode)
\r .withValue(module.getNamespace().toString()).build());
\r\r instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
\r (listModuleSchemaNode), "feature");
\r final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
\r Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode);
\r final ListNodeBuilder<Object, LeafSetEntryNode<Object>> featuresBuilder = Builders
\r .leafSetBuilder((LeafListSchemaNode) featureSchemaNode);
\r for (final FeatureDefinition feature : module.getFeatures()) {
\r featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode))
\r .withValue(feature.getQName().getLocalName()).build());
\r }
\r moduleNodeValues.withChild(featuresBuilder.build());
1964 \r return moduleNodeValues.build();
\r }
1966 protected MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamSchemaNode) {
1967 Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode,
1968 "streamSchemaNode has to be of type ListSchemaNode");
1969 final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode;
1970 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
1971 .mapEntryBuilder(listStreamSchemaNode);
1973 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1974 (listStreamSchemaNode), "name");
1975 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1976 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1977 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName)
1980 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1981 (listStreamSchemaNode), "description");
1982 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1983 Preconditions.checkState(descriptionSchemaNode instanceof LeafSchemaNode);
1984 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode)
1985 .withValue("DESCRIPTION_PLACEHOLDER").build());
1987 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1988 (listStreamSchemaNode), "replay-support");
1989 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1990 Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode);
1991 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode)
1992 .withValue(Boolean.valueOf(true)).build());
1994 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1995 (listStreamSchemaNode), "replay-log-creation-time");
1996 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1997 Preconditions.checkState(replayLogCreationTimeSchemaNode instanceof LeafSchemaNode);
1998 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode)
1999 .withValue("").build());
2001 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2002 (listStreamSchemaNode), "events");
2003 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2004 Preconditions.checkState(eventsSchemaNode instanceof LeafSchemaNode);
2005 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode)
2006 .withValue("").build());
2008 return streamNodeValues.build();