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 com.google.common.util.concurrent.Futures;
23 import java.math.BigInteger;
25 import java.net.URISyntaxException;
26 import java.text.ParseException;
27 import java.text.SimpleDateFormat;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.Date;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
37 import java.util.concurrent.CancellationException;
38 import java.util.concurrent.ExecutionException;
39 import javax.ws.rs.core.Response;
40 import javax.ws.rs.core.Response.ResponseBuilder;
41 import javax.ws.rs.core.Response.Status;
42 import javax.ws.rs.core.UriBuilder;
43 import javax.ws.rs.core.UriInfo;
44 import org.apache.commons.lang3.StringUtils;
45 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
46 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
47 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
48 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
49 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
50 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
51 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
52 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
53 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
54 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
55 import org.opendaylight.controller.sal.rest.api.Draft02;
56 import org.opendaylight.controller.sal.rest.api.RestconfService;
57 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
58 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
59 import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
60 import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
61 import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
62 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
63 import org.opendaylight.controller.sal.streams.listeners.Notificator;
64 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
65 import org.opendaylight.yangtools.concepts.Codec;
66 import org.opendaylight.yangtools.yang.common.QName;
67 import org.opendaylight.yangtools.yang.common.QNameModule;
68 import org.opendaylight.yangtools.yang.common.RpcResult;
69 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
70 import org.opendaylight.yangtools.yang.data.api.Node;
71 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
75 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
76 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
77 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
79 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
80 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
81 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
82 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
83 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
84 import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
85 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
86 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
87 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
88 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
89 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
90 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
91 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
92 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
93 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
94 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
95 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
96 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
97 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
98 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
99 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
100 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
101 import org.opendaylight.yangtools.yang.model.api.Module;
102 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
103 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
104 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
105 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
106 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
107 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
108 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
109 import org.opendaylight.yangtools.yang.model.util.EmptyType;
110 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
111 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
112 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
113 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
114 import org.slf4j.Logger;
115 import org.slf4j.LoggerFactory;
117 public class RestconfImpl implements RestconfService {
119 private enum UriParameters {
120 PRETTY_PRINT("prettyPrint"),
123 private String uriParameterName;
125 UriParameters(final String uriParameterName) {
126 this.uriParameterName = uriParameterName;
130 public String toString() {
131 return uriParameterName;
135 private static class TypeDef {
136 public final TypeDefinition<? extends Object> typedef;
137 public final QName qName;
139 TypeDef(final TypeDefinition<? extends Object> typedef, final QName qName) {
140 this.typedef = typedef;
145 private final static RestconfImpl INSTANCE = new RestconfImpl();
147 private static final int NOTIFICATION_PORT = 8181;
149 private static final int CHAR_NOT_FOUND = -1;
151 private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
153 private final static SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
155 private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
157 private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
159 private BrokerFacade broker;
161 private ControllerContext controllerContext;
163 private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class);
165 private static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE;
167 private static final LogicalDatastoreType DEFAULT_DATASTORE = LogicalDatastoreType.CONFIGURATION;
169 private static final URI NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = URI.create("urn:sal:restconf:event:subscription");
171 private static final Date EVENT_SUBSCRIPTION_AUGMENT_REVISION;
173 private static final String DATASTORE_PARAM_NAME = "datastore";
175 private static final String SCOPE_PARAM_NAME = "scope";
177 private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
179 private static final String NETCONF_BASE_PAYLOAD_NAME = "data";
181 private static final QName NETCONF_BASE_QNAME;
185 EVENT_SUBSCRIPTION_AUGMENT_REVISION = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
186 NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME );
187 } catch (final ParseException e) {
188 throw new RestconfDocumentedException(
189 "It wasn't possible to convert revision date of sal-remote-augment to date", ErrorType.APPLICATION,
190 ErrorTag.OPERATION_FAILED);
191 } catch (final URISyntaxException e) {
192 throw new RestconfDocumentedException(
193 "It wasn't possible to create instance of URI class with "+NETCONF_BASE+" URI", ErrorType.APPLICATION,
194 ErrorTag.OPERATION_FAILED);
198 public void setBroker(final BrokerFacade broker) {
199 this.broker = broker;
202 public void setControllerContext(final ControllerContext controllerContext) {
203 this.controllerContext = controllerContext;
206 private RestconfImpl() {
209 public static RestconfImpl getInstance() {
214 public NormalizedNodeContext getModules(final UriInfo uriInfo) {
215 final Set<Module> allModules = controllerContext.getAllModules();
216 final MapNode allModuleMap = makeModuleMapNode(allModules);
218 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
220 final Module restconfModule = getRestconfModule();
221 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
222 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
223 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
225 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
226 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
227 moduleContainerBuilder.withChild(allModuleMap);
229 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
230 null, schemaContext), moduleContainerBuilder.build());
234 * Valid only for mount point
237 public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
238 Preconditions.checkNotNull(identifier);
239 if ( ! identifier.contains(ControllerContext.MOUNT)) {
240 final String errMsg = "URI has bad format. If modules behind mount point should be showed,"
241 + " URI has to end with " + ControllerContext.MOUNT;
242 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
245 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
246 final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
247 final Set<Module> modules = controllerContext.getAllModules(mountPoint);
248 final SchemaContext schemaContext = mountPoint.getSchemaContext();
249 final MapNode mountPointModulesMap = makeModuleMapNode(modules);
251 final Module restconfModule = getRestconfModule();
252 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
253 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
254 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
256 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
257 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
258 moduleContainerBuilder.withChild(mountPointModulesMap);
260 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
261 mountPoint, schemaContext), moduleContainerBuilder.build());
265 public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
266 Preconditions.checkNotNull(identifier);
267 final QName moduleNameAndRevision = getModuleNameAndRevision(identifier);
268 Module module = null;
269 DOMMountPoint mountPoint = null;
270 final SchemaContext schemaContext;
271 if (identifier.contains(ControllerContext.MOUNT)) {
272 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
273 mountPoint = mountPointIdentifier.getMountPoint();
274 module = controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
275 schemaContext = mountPoint.getSchemaContext();
277 module = controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
278 schemaContext = controllerContext.getGlobalSchema();
281 if (module == null) {
282 final String errMsg = "Module with name '" + moduleNameAndRevision.getLocalName()
283 + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found.";
284 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
287 final Module restconfModule = getRestconfModule();
288 final Set<Module> modules = Collections.singleton(module);
289 final MapNode moduleMap = makeModuleMapNode(modules);
291 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
292 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
293 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
295 return new NormalizedNodeContext(new InstanceIdentifierContext(null, moduleSchemaNode, mountPoint,
296 schemaContext), moduleMap);
300 public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
301 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
302 final Set<String> availableStreams = Notificator.getStreamNames();
303 final Module restconfModule = getRestconfModule();
304 final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
305 Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
306 Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
308 final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamsBuilder = Builders
309 .mapBuilder((ListSchemaNode) streamSchemaNode);
311 for (final String streamName : availableStreams) {
312 listStreamsBuilder.withChild(toStreamEntryNode(streamName, streamSchemaNode));
315 final DataSchemaNode streamsContainerSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
316 restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
317 Preconditions.checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode);
319 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder =
320 Builders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode);
321 streamsContainerBuilder.withChild(listStreamsBuilder.build());
324 return new NormalizedNodeContext(new InstanceIdentifierContext(null, streamsContainerSchemaNode, null,
325 schemaContext), streamsContainerBuilder.build());
329 public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
330 final Set<Module> allModules = controllerContext.getAllModules();
331 return operationsFromModulesToNormalizedContext(allModules, null);
335 public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
336 Set<Module> modules = null;
337 DOMMountPoint mountPoint = null;
338 if (identifier.contains(ControllerContext.MOUNT)) {
339 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
340 mountPoint = mountPointIdentifier.getMountPoint();
341 modules = controllerContext.getAllModules(mountPoint);
344 final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
345 throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
348 return operationsFromModulesToNormalizedContext(modules, mountPoint);
351 private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set<Module> modules,
352 final DOMMountPoint mountPoint) {
354 // FIXME find best way to change restconf-netconf yang schema for provide this functionality
355 final String errMsg = "We are not able support view operations functionality yet.";
356 throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
360 * @deprecated method will be removed in Lithium release
363 private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
364 final DOMMountPoint mountPoint, final boolean prettyPrint) {
365 final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
366 final Module restconfModule = getRestconfModule();
367 final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
368 restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
369 final QName qName = operationsSchemaNode.getQName();
370 final SchemaPath path = operationsSchemaNode.getPath();
371 final ContainerSchemaNodeBuilder containerSchemaNodeBuilder = new ContainerSchemaNodeBuilder(
372 Draft02.RestConfModule.NAME, 0, qName, path);
373 final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
374 for (final Module module : modules) {
375 final Set<RpcDefinition> rpcs = module.getRpcs();
376 for (final RpcDefinition rpc : rpcs) {
377 final QName rpcQName = rpc.getQName();
378 final SimpleNode<Object> immutableSimpleNode = NodeFactory.<Object> createImmutableSimpleNode(rpcQName, null,
380 operationsAsData.add(immutableSimpleNode);
382 final String name = module.getName();
383 final LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName,
384 SchemaPath.create(true, QName.create("dummy")));
385 final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
386 fakeRpcSchemaNode.setAugmenting(true);
388 final EmptyType instance = EmptyType.getInstance();
389 fakeRpcSchemaNode.setType(instance);
390 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build());
394 final CompositeNode operationsNode = NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
395 final ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
396 return new StructuredData(operationsNode, schemaNode, mountPoint, prettyPrint);
399 private Module getRestconfModule() {
400 final Module restconfModule = controllerContext.getRestconfModule();
401 if (restconfModule == null) {
402 throw new RestconfDocumentedException("ietf-restconf module was not found.", ErrorType.APPLICATION,
403 ErrorTag.OPERATION_NOT_SUPPORTED);
406 return restconfModule;
409 private QName getModuleNameAndRevision(final String identifier) {
410 final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
411 String moduleNameAndRevision = "";
412 if (mountIndex >= 0) {
413 moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
415 moduleNameAndRevision = identifier;
418 final Splitter splitter = Splitter.on("/").omitEmptyStrings();
419 final Iterable<String> split = splitter.split(moduleNameAndRevision);
420 final List<String> pathArgs = Lists.<String> newArrayList(split);
421 if (pathArgs.size() < 2) {
422 throw new RestconfDocumentedException(
423 "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL,
424 ErrorTag.INVALID_VALUE);
428 final String moduleName = pathArgs.get(0);
429 final String revision = pathArgs.get(1);
430 final Date moduleRevision = REVISION_FORMAT.parse(revision);
431 return QName.create(null, moduleRevision, moduleName);
432 } catch (final ParseException e) {
433 throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
434 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
439 * @deprecated method will be removed for Lithium release
440 * so, please use toStreamEntryNode method
443 * @param streamSchemaNode
447 private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
448 final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
449 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
450 ((DataNodeContainer) streamSchemaNode), "name");
451 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
453 .add(NodeFactory.<String> createImmutableSimpleNode(nameSchemaNode.getQName(), null, streamName));
455 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
456 ((DataNodeContainer) streamSchemaNode), "description");
457 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
458 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
459 "DESCRIPTION_PLACEHOLDER"));
461 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
462 ((DataNodeContainer) streamSchemaNode), "replay-support");
463 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
464 streamNodeValues.add(NodeFactory.<Boolean> createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
465 Boolean.valueOf(true)));
467 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
468 ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
469 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
470 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
473 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
474 ((DataNodeContainer) streamSchemaNode), "events");
475 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
476 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(eventsSchemaNode.getQName(), null, ""));
478 return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
482 * @deprecated method will be removed for Lithium release
483 * so, please use toModuleEntryNode method
486 * @param moduleSchemaNode
490 private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
491 final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
492 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
493 ((DataNodeContainer) moduleSchemaNode), "name");
494 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
495 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(nameSchemaNode.getQName(), null,
498 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
499 ((DataNodeContainer) moduleSchemaNode), "revision");
500 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
501 final Date _revision = module.getRevision();
502 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
503 REVISION_FORMAT.format(_revision)));
505 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
506 ((DataNodeContainer) moduleSchemaNode), "namespace");
507 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
508 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
509 module.getNamespace().toString()));
511 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
512 ((DataNodeContainer) moduleSchemaNode), "feature");
513 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
514 for (final FeatureDefinition feature : module.getFeatures()) {
515 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(featureSchemaNode.getQName(), null,
516 feature.getQName().getLocalName()));
519 return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
523 public Object getRoot() {
528 public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
529 final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
530 final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace();
531 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
532 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
533 final SchemaContext schemaContext;
534 if (identifier.contains(MOUNT_POINT_MODULE_NAME) && mountPoint != null) {
535 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
536 if ( ! mountRpcServices.isPresent()) {
537 throw new RestconfDocumentedException("Rpc service is missing.");
539 schemaContext = mountPoint.getSchemaContext();
540 response = mountRpcServices.get().invokeRpc(type, payload.getData());
542 if (namespace.toString().equals(SAL_REMOTE_NAMESPACE)) {
543 response = invokeSalRemoteRpcSubscribeRPC(payload);
545 response = broker.invokeRpc(type, payload.getData());
547 schemaContext = controllerContext.getGlobalSchema();
550 final DOMRpcResult result = checkRpcResponse(response);
552 DataSchemaNode resultNodeSchema = null;
553 NormalizedNode<?, ?> resultData = null;
554 if (result != null && result.getResult() != null) {
555 resultData = result.getResult();
556 final ContainerSchemaNode rpcDataSchemaNode = SchemaContextUtil.getRpcDataSchema(schemaContext, type);
557 resultNodeSchema = rpcDataSchemaNode.getDataChildByName(result.getResult().getNodeType());
560 return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
561 schemaContext), resultData);
564 private DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
565 if (response == null) {
569 final DOMRpcResult retValue = response.get();
570 if (retValue.getErrors() == null || retValue.getErrors().isEmpty()) {
573 throw new RestconfDocumentedException("RpcError message", null, retValue.getErrors());
575 catch (final InterruptedException e) {
576 throw new RestconfDocumentedException(
577 "The operation was interrupted while executing and did not complete.", ErrorType.RPC,
578 ErrorTag.PARTIAL_OPERATION);
580 catch (final ExecutionException e) {
581 Throwable cause = e.getCause();
582 if (cause instanceof CancellationException) {
583 throw new RestconfDocumentedException("The operation was cancelled while executing.", ErrorType.RPC,
584 ErrorTag.PARTIAL_OPERATION);
585 } else if (cause != null) {
586 while (cause.getCause() != null) {
587 cause = cause.getCause();
590 if (cause instanceof IllegalArgumentException) {
591 throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
592 ErrorTag.INVALID_VALUE);
595 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
598 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
604 private void validateInput(final DataSchemaNode inputSchema, final NormalizedNodeContext payload) {
605 if (inputSchema != null && payload.getData() == null) {
606 // expected a non null payload
607 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
608 } else if (inputSchema == null && payload.getData() != null) {
609 // did not expect any input
610 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
614 // TODO: Validate "mandatory" and "config" values here??? Or should those be
616 // validate in a more central location inside MD-SAL core.
621 * @deprecated method will be removed for Lithium release
627 private void validateInput(final DataSchemaNode inputSchema, final Node<?> payload) {
628 if (inputSchema != null && payload == null) {
629 // expected a non null payload
630 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
631 } else if (inputSchema == null && payload != null) {
632 // did not expect any input
633 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
637 // TODO: Validate "mandatory" and "config" values here??? Or should those be
639 // validate in a more central location inside MD-SAL core.
643 private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
644 final ContainerNode value = (ContainerNode) payload.getData();
645 final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
646 Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(new NodeIdentifier(
647 QName.create(payload.getInstanceIdentifierContext().getSchemaNode().getQName(), "path")));
648 final Object pathValue = path.isPresent() ? path.get().getValue() : null;
650 if (!(pathValue instanceof YangInstanceIdentifier)) {
651 throw new RestconfDocumentedException("Instance identifier was not normalized correctly.",
652 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
655 final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
656 String streamName = null;
657 if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
658 final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
660 LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class,
661 DATASTORE_PARAM_NAME);
662 datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
664 DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
665 scope = scope == null ? DEFAULT_SCOPE : scope;
667 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
668 + "/scope=" + scope);
671 if (Strings.isNullOrEmpty(streamName)) {
672 throw new RestconfDocumentedException(
673 "Path is empty or contains value node which is not Container or List build-in type.",
674 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
677 QName outputQname = QName.create(rpcQName, "output");
678 QName streamNameQname = QName.create(rpcQName, "stream-name");
680 ContainerNode output = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
681 .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
683 if (!Notificator.existListenerFor(streamName)) {
684 YangInstanceIdentifier normalizedPathIdentifier = controllerContext.toNormalized(pathIdentifier);
685 Notificator.createListener(normalizedPathIdentifier, streamName);
688 DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
690 return Futures.immediateCheckedFuture(defaultDOMRpcResult);
694 public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
695 if (StringUtils.isNotBlank(noPayload)) {
696 throw new RestconfDocumentedException("Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
699 String identifierEncoded = null;
700 DOMMountPoint mountPoint = null;
701 final SchemaContext schemaContext;
702 if (identifier.contains(ControllerContext.MOUNT)) {
703 // mounted RPC call - look up mount instance.
704 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
705 mountPoint = mountPointId.getMountPoint();
706 schemaContext = mountPoint.getSchemaContext();
707 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
708 + ControllerContext.MOUNT.length() + 1;
709 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
710 identifierEncoded = remoteRpcName;
712 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
713 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
714 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
715 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
717 identifierEncoded = identifier;
718 schemaContext = controllerContext.getGlobalSchema();
721 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
723 RpcDefinition rpc = null;
724 if (mountPoint == null) {
725 rpc = controllerContext.getRpcDefinition(identifierDecoded);
727 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
731 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
734 if (rpc.getInput() != null) {
735 // FIXME : find a correct Error from specification
736 throw new IllegalStateException("RPC " + rpc + " needs input value!");
739 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
740 if (mountPoint != null) {
741 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
742 if ( ! mountRpcServices.isPresent()) {
743 throw new RestconfDocumentedException("Rpc service is missing.");
745 response = mountRpcServices.get().invokeRpc(rpc.getPath(), null);
747 response = broker.invokeRpc(rpc.getPath(), null);
750 final DOMRpcResult result = checkRpcResponse(response);
752 DataSchemaNode resultNodeSchema = null;
753 NormalizedNode<?, ?> resultData = null;
754 if (result != null && result.getResult() != null) {
755 resultData = result.getResult();
756 final ContainerSchemaNode rpcDataSchemaNode =
757 SchemaContextUtil.getRpcDataSchema(schemaContext, rpc.getOutput().getPath());
758 resultNodeSchema = rpcDataSchemaNode.getDataChildByName(result.getResult().getNodeType());
761 return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
762 schemaContext), resultData);
765 private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
766 String identifierEncoded = null;
767 DOMMountPoint mountPoint = null;
768 if (identifier.contains(ControllerContext.MOUNT)) {
769 // mounted RPC call - look up mount instance.
770 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
771 mountPoint = mountPointId.getMountPoint();
773 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
774 + ControllerContext.MOUNT.length() + 1;
775 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
776 identifierEncoded = remoteRpcName;
778 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
779 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
780 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
781 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
783 identifierEncoded = identifier;
786 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
788 RpcDefinition rpc = null;
789 if (mountPoint == null) {
790 rpc = controllerContext.getRpcDefinition(identifierDecoded);
792 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
796 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
799 if (mountPoint == null) {
800 return new BrokerRpcExecutor(rpc, broker);
802 return new MountPointRpcExecutor(rpc, mountPoint);
807 private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
808 final String[] splittedIdentifier = identifierDecoded.split(":");
809 if (splittedIdentifier.length != 2) {
810 throw new RestconfDocumentedException(identifierDecoded
811 + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION,
812 ErrorTag.INVALID_VALUE);
814 for (final Module module : schemaContext.getModules()) {
815 if (module.getName().equals(splittedIdentifier[0])) {
816 for (final RpcDefinition rpcDefinition : module.getRpcs()) {
817 if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) {
818 return rpcDefinition;
827 * @deprecated method will be removed for Lithium release
830 private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload, final boolean prettyPrint) {
831 if (rpcExecutor == null) {
832 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
835 CompositeNode rpcRequest = null;
836 final RpcDefinition rpc = rpcExecutor.getRpcDefinition();
837 final QName rpcName = rpc.getQName();
839 if (payload == null) {
840 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
842 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
843 final List<Node<?>> input = Collections.<Node<?>> singletonList(value);
844 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
847 final RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
849 checkRpcSuccessAndThrowException(rpcResult);
851 if (rpcResult.getResult() == null) {
855 if (rpc.getOutput() == null) {
856 return null; // no output, nothing to send back.
859 return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null, prettyPrint);
862 private void checkRpcSuccessAndThrowException(final RpcResult<CompositeNode> rpcResult) {
863 if (rpcResult.isSuccessful() == false) {
865 throw new RestconfDocumentedException("The operation was not successful", null,
866 rpcResult.getErrors());
871 public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
872 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
873 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
874 NormalizedNode<?, ?> data = null;
875 YangInstanceIdentifier normalizedII;
876 if (mountPoint != null) {
877 normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData
878 .getInstanceIdentifier());
879 data = broker.readConfigurationData(mountPoint, normalizedII);
881 normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
882 data = broker.readConfigurationData(normalizedII);
884 return new NormalizedNodeContext(iiWithData, data);
887 @SuppressWarnings("unchecked")
888 private <T extends Node<?>> T pruneDataAtDepth(final T node, final Integer depth) {
893 if (node instanceof CompositeNode) {
894 final ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
896 for (final Node<?> childNode : ((CompositeNode) node).getValue()) {
897 newChildNodes.add(pruneDataAtDepth(childNode, depth - 1));
901 return (T) ImmutableCompositeNode.create(node.getNodeType(), newChildNodes.build());
902 } else { // SimpleNode
907 private Integer parseDepthParameter(final UriInfo info) {
908 final String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString());
909 if (Strings.isNullOrEmpty(param) || "unbounded".equals(param)) {
914 final Integer depth = Integer.valueOf(param);
916 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
917 "Invalid depth parameter: " + depth, null,
918 "The depth parameter must be an integer > 1 or \"unbounded\""));
922 } catch (final NumberFormatException e) {
923 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
924 "Invalid depth parameter: " + e.getMessage(), null,
925 "The depth parameter must be an integer > 1 or \"unbounded\""));
930 public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo info) {
931 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
932 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
933 NormalizedNode<?, ?> data = null;
934 YangInstanceIdentifier normalizedII;
935 if (mountPoint != null) {
936 normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData
937 .getInstanceIdentifier());
938 data = broker.readOperationalData(mountPoint, normalizedII);
940 normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
941 data = broker.readOperationalData(normalizedII);
944 return new NormalizedNodeContext(iiWithData, data);
947 private boolean parsePrettyPrintParameter(final UriInfo info) {
948 final String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
949 return Boolean.parseBoolean(param);
953 public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
954 Preconditions.checkNotNull(identifier);
955 final InstanceIdentifierContext<DataSchemaNode> iiWithData = controllerContext.toInstanceIdentifier(identifier);
957 validateInput(iiWithData.getSchemaNode(), payload);
958 validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier());
959 validateListKeysEqualityInPayloadAndUri(iiWithData, payload.getData());
961 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
962 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
965 * There is a small window where another write transaction could be updating the same data
966 * simultaneously and we get an OptimisticLockFailedException. This error is likely
967 * transient and The WriteTransaction#submit API docs state that a retry will likely
968 * succeed. So we'll try again if that scenario occurs. If it fails a third time then it
969 * probably will never succeed so we'll fail in that case.
971 * By retrying we're attempting to hide the internal implementation of the data store and
972 * how it handles concurrent updates from the restconf client. The client has instructed us
973 * to put the data and we should make every effort to do so without pushing optimistic lock
974 * failures back to the client and forcing them to handle it via retry (and having to
975 * document the behavior).
980 if (mountPoint != null) {
981 broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
983 broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet();
987 } catch (final TransactionCommitFailedException e) {
988 if(e instanceof OptimisticLockFailedException) {
990 LOG.debug("Got OptimisticLockFailedException on last try - failing");
991 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
994 LOG.debug("Got OptimisticLockFailedException - trying again");
996 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
1001 return Response.status(Status.OK).build();
1004 private void validateTopLevelNodeName(final NormalizedNodeContext node,
1005 final YangInstanceIdentifier identifier) {
1007 final String payloadName = node.getData().getNodeType().getLocalName();
1008 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1011 if ( ! pathArguments.hasNext()) {
1013 if ( ! node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) {
1014 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1015 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1019 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1020 if ( ! payloadName.equals(identifierName)) {
1021 throw new RestconfDocumentedException("Payload name (" + payloadName
1022 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1023 ErrorTag.MALFORMED_MESSAGE);
1029 * @deprecated method will be removed for Lithium release
1035 private void validateTopLevelNodeName(final Node<?> node,
1036 final YangInstanceIdentifier identifier) {
1037 final String payloadName = getName(node);
1038 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1041 if (!pathArguments.hasNext()) {
1043 if (!node.getNodeType().equals(NETCONF_BASE_QNAME)) {
1044 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1045 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1049 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1050 if (!payloadName.equals(identifierName)) {
1051 throw new RestconfDocumentedException("Payload name (" + payloadName
1052 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1053 ErrorTag.MALFORMED_MESSAGE);
1059 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1061 * @throws RestconfDocumentedException
1062 * if key values or key count in payload and URI isn't equal
1065 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
1066 final NormalizedNode<?, ?> payload) {
1067 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1068 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1069 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1070 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1071 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1073 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1079 * @deprecated method will be removed for Lithium release
1081 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1083 * @throws RestconfDocumentedException
1084 * if key values or key count in payload and URI isn't equal
1088 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
1089 final CompositeNode payload) {
1090 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1091 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1092 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1093 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1094 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1096 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1101 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final NormalizedNode<?, ?> payload,
1102 final List<QName> keyDefinitions) {
1103 for (final QName keyDefinition : keyDefinitions) {
1104 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1105 // should be caught during parsing URI to InstanceIdentifier
1106 if (uriKeyValue == null) {
1107 final String errMsg = "Missing key " + keyDefinition + " in URI.";
1108 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1110 // TODO thing about the possibility to fix
1111 // final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1112 // if (payloadKeyValues.isEmpty()) {
1113 // final String errMsg = "Missing key " + keyDefinition.getLocalName() + " in the message body.";
1114 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1117 // final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1118 // if (!uriKeyValue.equals(payloadKeyValue)) {
1119 // final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
1120 // "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. ";
1121 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1127 * @deprecated method will be removed for Lithium release
1129 * @param uriKeyValues
1131 * @param keyDefinitions
1134 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final CompositeNode payload,
1135 final List<QName> keyDefinitions) {
1136 for (final QName keyDefinition : keyDefinitions) {
1137 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1138 // should be caught during parsing URI to InstanceIdentifier
1139 if (uriKeyValue == null) {
1140 throw new RestconfDocumentedException("Missing key " + keyDefinition + " in URI.", ErrorType.PROTOCOL,
1141 ErrorTag.DATA_MISSING);
1143 final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1144 if (payloadKeyValues.isEmpty()) {
1145 throw new RestconfDocumentedException("Missing key " + keyDefinition.getLocalName()
1146 + " in the message body.", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1149 final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1150 if (!uriKeyValue.equals(payloadKeyValue)) {
1151 throw new RestconfDocumentedException("The value '" + uriKeyValue + "' for key '"
1152 + keyDefinition.getLocalName() + "' specified in the URI doesn't match the value '"
1153 + payloadKeyValue + "' specified in the message body. ", ErrorType.PROTOCOL,
1154 ErrorTag.INVALID_VALUE);
1160 public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
1161 if (payload == null) {
1162 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1165 final URI payloadNS = payload.getData().getNodeType().getNamespace();
1166 if (payloadNS == null) {
1167 throw new RestconfDocumentedException(
1168 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
1169 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1172 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
1174 final InstanceIdentifierContext iiWithData = mountPoint != null
1175 ? controllerContext.toMountPointIdentifier(identifier)
1176 : controllerContext.toInstanceIdentifier(identifier);
1177 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1180 if (mountPoint != null) {
1181 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
1183 broker.commitConfigurationDataPost(normalizedII, payload.getData());
1185 } catch(final RestconfDocumentedException e) {
1187 } catch (final Exception e) {
1188 throw new RestconfDocumentedException("Error creating data", e);
1192 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
1193 final URI location = resolveLocation(uriInfo, "config", mountPoint, normalizedII);
1194 if (location != null) {
1195 responseBuilder.location(location);
1197 return responseBuilder.build();
1200 // FIXME create RestconfIdetifierHelper and move this method there
1201 private YangInstanceIdentifier checkConsistencyOfNormalizedNodeContext(final NormalizedNodeContext payload) {
1202 Preconditions.checkArgument(payload != null);
1203 Preconditions.checkArgument(payload.getData() != null);
1204 Preconditions.checkArgument(payload.getData().getNodeType() != null);
1205 Preconditions.checkArgument(payload.getInstanceIdentifierContext() != null);
1206 Preconditions.checkArgument(payload.getInstanceIdentifierContext().getInstanceIdentifier() != null);
1208 final QName payloadNodeQname = payload.getData().getNodeType();
1209 final YangInstanceIdentifier yangIdent = payload.getInstanceIdentifierContext().getInstanceIdentifier();
1210 if (payloadNodeQname.compareTo(yangIdent.getLastPathArgument().getNodeType()) > 0) {
1213 final InstanceIdentifierContext parentContext = payload.getInstanceIdentifierContext();
1214 final SchemaNode parentSchemaNode = parentContext.getSchemaNode();
1215 if(parentSchemaNode instanceof DataNodeContainer) {
1216 final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode;
1217 for (final DataSchemaNode child : cast.getChildNodes()) {
1218 if (payloadNodeQname.compareTo(child.getQName()) == 0) {
1219 return YangInstanceIdentifier.builder(yangIdent).node(child.getQName()).build();
1223 if (parentSchemaNode instanceof RpcDefinition) {
1226 final String errMsg = "Error parsing input: DataSchemaNode has not children";
1227 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1231 public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
1232 if (payload == null) {
1233 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1236 final URI payloadNS = payload.getData().getNodeType().getNamespace();
1237 if (payloadNS == null) {
1238 throw new RestconfDocumentedException(
1239 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
1240 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1243 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
1244 final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext();
1245 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1248 if (mountPoint != null) {
1249 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
1252 broker.commitConfigurationDataPost(normalizedII, payload.getData());
1254 } catch(final RestconfDocumentedException e) {
1256 } catch (final Exception e) {
1257 throw new RestconfDocumentedException("Error creating data", e);
1260 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
1261 final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII);
1262 if (location != null) {
1263 responseBuilder.location(location);
1265 return responseBuilder.build();
1268 private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
1269 final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
1270 uriBuilder.path("config");
1272 uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
1273 } catch (final Exception e) {
1274 LOG.debug("Location for instance identifier"+normalizedII+"wasn't created", e);
1277 return uriBuilder.build();
1281 public Response deleteConfigurationData(final String identifier) {
1282 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
1283 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
1284 YangInstanceIdentifier normalizedII;
1287 if (mountPoint != null) {
1288 normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData
1289 .getInstanceIdentifier());
1290 broker.commitConfigurationDataDelete(mountPoint, normalizedII);
1292 normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
1293 broker.commitConfigurationDataDelete(normalizedII).get();
1295 } catch (final Exception e) {
1296 final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
1297 Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
1298 if (searchedException.isPresent()) {
1299 throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
1301 throw new RestconfDocumentedException("Error while deleting data", e);
1303 return Response.status(Status.OK).build();
1307 * Subscribes to some path in schema context (stream) to listen on changes on this stream.
1309 * Additional parameters for subscribing to stream are loaded via rpc input parameters:
1311 * <li>datastore</li> - default CONFIGURATION (other values of {@link LogicalDatastoreType} enum type)
1312 * <li>scope</li> - default BASE (other values of {@link DataChangeScope})
1316 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
1317 final String streamName = Notificator.createStreamNameFromUri(identifier);
1318 if (Strings.isNullOrEmpty(streamName)) {
1319 throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1322 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
1323 if (listener == null) {
1324 throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
1327 final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
1328 final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
1329 paramToValues.get(DATASTORE_PARAM_NAME));
1330 if (datastore == null) {
1331 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)",
1332 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1334 final DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME));
1335 if (scope == null) {
1336 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)",
1337 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1340 broker.registerToListenDataChanges(datastore, scope, listener);
1342 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
1343 int notificationPort = NOTIFICATION_PORT;
1345 final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
1346 notificationPort = webSocketServerInstance.getPort();
1347 } catch (final NullPointerException e) {
1348 WebSocketServer.createInstance(NOTIFICATION_PORT);
1350 final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
1351 final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
1353 return Response.status(Status.OK).location(uriToWebsocketServer).build();
1357 * Load parameter for subscribing to stream from input composite node
1361 * @return enum object if its string value is equal to {@code paramName}. In other cases null.
1363 private <T> T parseEnumTypeParameter(final ContainerNode value, final Class<T> classDescriptor,
1364 final String paramName) {
1365 final QNameModule salRemoteAugment = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
1366 EVENT_SUBSCRIPTION_AUGMENT_REVISION);
1367 Optional<DataContainerChild<? extends PathArgument, ?>> enumNode = value.getChild(new NodeIdentifier(
1368 QName.create(salRemoteAugment, paramName)));
1369 if (!enumNode.isPresent()) {
1372 final Object rawValue = enumNode.get().getValue();
1373 if (!(rawValue instanceof String)) {
1377 return resolveAsEnum(classDescriptor, (String) rawValue);
1381 * Checks whether {@code value} is one of the string representation of enumeration {@code classDescriptor}
1383 * @return enum object if string value of {@code classDescriptor} enumeration is equal to {@code value}. Other cases
1386 private <T> T parserURIEnumParameter(final Class<T> classDescriptor, final String value) {
1387 if (Strings.isNullOrEmpty(value)) {
1390 return resolveAsEnum(classDescriptor, value);
1393 private <T> T resolveAsEnum(final Class<T> classDescriptor, final String value) {
1394 final T[] enumConstants = classDescriptor.getEnumConstants();
1395 if (enumConstants != null) {
1396 for (final T enm : classDescriptor.getEnumConstants()) {
1397 if (((Enum<?>) enm).name().equals(value)) {
1405 private Map<String, String> resolveValuesFromUri(final String uri) {
1406 final Map<String, String> result = new HashMap<>();
1407 final String[] tokens = uri.split("/");
1408 for (int i = 1; i < tokens.length; i++) {
1409 final String[] parameterTokens = tokens[i].split("=");
1410 if (parameterTokens.length == 2) {
1411 result.put(parameterTokens[0], parameterTokens[1]);
1417 private Module findModule(final DOMMountPoint mountPoint, final Node<?> data) {
1418 Module module = null;
1419 if (data instanceof NodeWrapper) {
1420 module = findModule(mountPoint, (NodeWrapper<?>) data);
1421 } else if (data != null) {
1422 final URI namespace = data.getNodeType().getNamespace();
1423 if (mountPoint != null) {
1424 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1426 module = controllerContext.findModuleByNamespace(namespace);
1429 if (module != null) {
1432 throw new RestconfDocumentedException(
1433 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
1434 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1437 private Module findModule(final DOMMountPoint mountPoint, final NodeWrapper<?> data) {
1438 final URI namespace = data.getNamespace();
1439 Preconditions.<URI> checkNotNull(namespace);
1441 Module module = null;
1442 if (mountPoint != null) {
1443 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1444 if (module == null) {
1445 module = controllerContext.findModuleByName(mountPoint, namespace.toString());
1448 module = controllerContext.findModuleByNamespace(namespace);
1449 if (module == null) {
1450 module = controllerContext.findModuleByName(namespace.toString());
1457 private InstanceIdentifierContext addLastIdentifierFromData(final InstanceIdentifierContext identifierWithSchemaNode,
1458 final CompositeNode data, final DataSchemaNode schemaOfData, final SchemaContext schemaContext) {
1459 YangInstanceIdentifier instanceIdentifier = null;
1460 if (identifierWithSchemaNode != null) {
1461 instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
1464 final YangInstanceIdentifier iiOriginal = instanceIdentifier;
1465 InstanceIdentifierBuilder iiBuilder = null;
1466 if (iiOriginal == null) {
1467 iiBuilder = YangInstanceIdentifier.builder();
1469 iiBuilder = YangInstanceIdentifier.builder(iiOriginal);
1472 if ((schemaOfData instanceof ListSchemaNode)) {
1473 final HashMap<QName, Object> keys = resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
1474 iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
1476 iiBuilder.node(schemaOfData.getQName());
1479 final YangInstanceIdentifier instance = iiBuilder.toInstance();
1480 DOMMountPoint mountPoint = null;
1481 final SchemaContext schemaCtx = null;
1482 if (identifierWithSchemaNode != null) {
1483 mountPoint = identifierWithSchemaNode.getMountPoint();
1486 return new InstanceIdentifierContext(instance, schemaOfData, mountPoint,schemaContext);
1489 private HashMap<QName, Object> resolveKeysFromData(final ListSchemaNode listNode, final CompositeNode dataNode) {
1490 final HashMap<QName, Object> keyValues = new HashMap<QName, Object>();
1491 final List<QName> _keyDefinition = listNode.getKeyDefinition();
1492 for (final QName key : _keyDefinition) {
1493 SimpleNode<? extends Object> head = null;
1494 final String localName = key.getLocalName();
1495 final List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
1496 if (simpleNodesByName != null) {
1497 head = Iterables.getFirst(simpleNodesByName, null);
1500 Object dataNodeKeyValueObject = null;
1502 dataNodeKeyValueObject = head.getValue();
1505 if (dataNodeKeyValueObject == null) {
1506 throw new RestconfDocumentedException("Data contains list \"" + dataNode.getNodeType().getLocalName()
1507 + "\" which does not contain key: \"" + key.getLocalName() + "\"", ErrorType.PROTOCOL,
1508 ErrorTag.INVALID_VALUE);
1511 keyValues.put(key, dataNodeKeyValueObject);
1517 private boolean endsWithMountPoint(final String identifier) {
1518 return identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/");
1521 private boolean representsMountPointRootData(final Node<?> data) {
1522 final URI namespace = namespace(data);
1523 return (SchemaContext.NAME.getNamespace().equals(namespace) /*
1524 * || MOUNT_POINT_MODULE_NAME .equals( namespace .
1527 && SchemaContext.NAME.getLocalName().equals(localName(data));
1530 private String addMountPointIdentifier(final String identifier) {
1531 final boolean endsWith = identifier.endsWith("/");
1533 return (identifier + ControllerContext.MOUNT);
1536 return identifier + "/" + ControllerContext.MOUNT;
1540 * @deprecated method will be removed in Lithium release
1541 * we don't wish to use Node and CompositeNode anywhere
1544 public CompositeNode normalizeNode(final Node<?> node, final DataSchemaNode schema, final DOMMountPoint mountPoint) {
1545 if (schema == null) {
1546 final String localName = node == null ? null :
1547 node instanceof NodeWrapper ? ((NodeWrapper<?>)node).getLocalName() :
1548 node.getNodeType().getLocalName();
1550 throw new RestconfDocumentedException("Data schema node was not found for " + localName,
1551 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1554 if (!(schema instanceof DataNodeContainer)) {
1555 throw new RestconfDocumentedException("Root element has to be container or list yang datatype.",
1556 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1559 if ((node instanceof NodeWrapper<?>)) {
1560 NodeWrapper<?> nodeWrap = (NodeWrapper<?>) node;
1561 final boolean isChangeAllowed = ((NodeWrapper<?>) node).isChangeAllowed();
1562 if (isChangeAllowed) {
1563 nodeWrap = topLevelElementAsCompositeNodeWrapper((NodeWrapper<?>) node, schema);
1565 this.normalizeNode(nodeWrap, schema, null, mountPoint);
1566 } catch (final IllegalArgumentException e) {
1567 final RestconfDocumentedException restconfDocumentedException = new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1568 restconfDocumentedException.addSuppressed(e);
1569 throw restconfDocumentedException;
1571 if (nodeWrap instanceof CompositeNodeWrapper) {
1572 return ((CompositeNodeWrapper) nodeWrap).unwrap();
1577 if (node instanceof CompositeNode) {
1578 return (CompositeNode) node;
1581 throw new RestconfDocumentedException("Top level element is not interpreted as composite node.",
1582 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
1585 private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1586 final QName previousAugment, final DOMMountPoint mountPoint) {
1587 if (schema == null) {
1588 throw new RestconfDocumentedException("Data has bad format.\n\"" + nodeBuilder.getLocalName()
1589 + "\" does not exist in yang schema.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1592 QName currentAugment = null;
1593 if (nodeBuilder.getQname() != null) {
1594 currentAugment = previousAugment;
1596 currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
1597 if (nodeBuilder.getQname() == null) {
1598 throw new RestconfDocumentedException(
1599 "Data has bad format.\nIf data is in XML format then namespace for \""
1600 + nodeBuilder.getLocalName() + "\" should be \"" + schema.getQName().getNamespace()
1601 + "\".\n" + "If data is in JSON format then module name for \""
1602 + nodeBuilder.getLocalName() + "\" should be corresponding to namespace \""
1603 + schema.getQName().getNamespace() + "\".", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1607 if (nodeBuilder instanceof CompositeNodeWrapper) {
1608 if (schema instanceof DataNodeContainer) {
1609 normalizeCompositeNode((CompositeNodeWrapper) nodeBuilder, (DataNodeContainer) schema, mountPoint,
1611 } else if (schema instanceof AnyXmlSchemaNode) {
1612 normalizeAnyXmlNode((CompositeNodeWrapper) nodeBuilder, (AnyXmlSchemaNode) schema);
1614 } else if (nodeBuilder instanceof SimpleNodeWrapper) {
1615 normalizeSimpleNode((SimpleNodeWrapper) nodeBuilder, schema, mountPoint);
1616 } else if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1617 normalizeEmptyNode((EmptyNodeWrapper) nodeBuilder, schema);
1621 private void normalizeAnyXmlNode(final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema) {
1622 final List<NodeWrapper<?>> children = compositeNode.getValues();
1623 for (final NodeWrapper<? extends Object> child : children) {
1624 child.setNamespace(schema.getQName().getNamespace());
1625 if (child instanceof CompositeNodeWrapper) {
1626 normalizeAnyXmlNode((CompositeNodeWrapper) child, schema);
1631 private void normalizeEmptyNode(final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema) {
1632 if ((schema instanceof LeafSchemaNode)) {
1633 emptyNodeBuilder.setComposite(false);
1635 if ((schema instanceof ContainerSchemaNode)) {
1636 // FIXME: Add presence check
1637 emptyNodeBuilder.setComposite(true);
1642 private void normalizeSimpleNode(final SimpleNodeWrapper simpleNode, final DataSchemaNode schema,
1643 final DOMMountPoint mountPoint) {
1644 final Object value = simpleNode.getValue();
1645 Object inputValue = value;
1646 final TypeDef typeDef = this.typeDefinition(schema);
1647 TypeDefinition<? extends Object> typeDefinition = typeDef != null ? typeDef.typedef : null;
1649 // For leafrefs, extract the type it is pointing to
1650 if(typeDefinition instanceof LeafrefTypeDefinition) {
1651 if (schema.getQName().equals(typeDef.qName)) {
1652 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema);
1654 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), typeDef.qName);
1658 if (typeDefinition instanceof IdentityrefTypeDefinition) {
1659 inputValue = parseToIdentityValuesDTO(simpleNode, value, inputValue);
1662 Object outputValue = inputValue;
1664 if (typeDefinition != null) {
1665 final Codec<Object, Object> codec = RestCodec.from(typeDefinition, mountPoint);
1666 outputValue = codec == null ? null : codec.deserialize(inputValue);
1669 simpleNode.setValue(outputValue);
1672 private Object parseToIdentityValuesDTO(final SimpleNodeWrapper simpleNode, final Object value, Object inputValue) {
1673 if ((value instanceof String)) {
1674 inputValue = new IdentityValuesDTO(simpleNode.getNamespace().toString(), (String) value, null,
1676 } // else value is already instance of IdentityValuesDTO
1680 private void normalizeCompositeNode(final CompositeNodeWrapper compositeNodeBuilder,
1681 final DataNodeContainer schema, final DOMMountPoint mountPoint, final QName currentAugment) {
1682 final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
1683 checkNodeMultiplicityAccordingToSchema(schema, children);
1684 for (final NodeWrapper<? extends Object> child : children) {
1685 final List<DataSchemaNode> potentialSchemaNodes = ControllerContext.findInstanceDataChildrenByName(
1686 schema, child.getLocalName());
1688 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
1689 final StringBuilder builder = new StringBuilder();
1690 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1691 builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()).append("\n");
1694 throw new RestconfDocumentedException("Node \"" + child.getLocalName()
1695 + "\" is added as augment from more than one module. "
1696 + "Therefore node must have namespace (XML format) or module name (JSON format)."
1697 + "\nThe node is added as augment from modules with namespaces:\n" + builder,
1698 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1701 boolean rightNodeSchemaFound = false;
1702 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1703 if (!rightNodeSchemaFound) {
1704 final QName potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode,
1705 currentAugment, mountPoint);
1706 if (child.getQname() != null) {
1707 this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1708 rightNodeSchemaFound = true;
1713 if (!rightNodeSchemaFound) {
1714 throw new RestconfDocumentedException("Schema node \"" + child.getLocalName()
1715 + "\" was not found in module.", ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT);
1719 if ((schema instanceof ListSchemaNode)) {
1720 final ListSchemaNode listSchemaNode = (ListSchemaNode) schema;
1721 final List<QName> listKeys = listSchemaNode.getKeyDefinition();
1722 for (final QName listKey : listKeys) {
1723 boolean foundKey = false;
1724 for (final NodeWrapper<? extends Object> child : children) {
1725 if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1731 throw new RestconfDocumentedException("Missing key in URI \"" + listKey.getLocalName()
1732 + "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"", ErrorType.PROTOCOL,
1733 ErrorTag.DATA_MISSING);
1739 private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer,
1740 final List<NodeWrapper<?>> nodes) {
1741 final Map<String, Integer> equalNodeNamesToCounts = new HashMap<String, Integer>();
1742 for (final NodeWrapper<?> child : nodes) {
1743 Integer count = equalNodeNamesToCounts.get(child.getLocalName());
1744 equalNodeNamesToCounts.put(child.getLocalName(), count == null ? 1 : ++count);
1747 for (final DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) {
1748 if (childSchemaNode instanceof ContainerSchemaNode || childSchemaNode instanceof LeafSchemaNode) {
1749 final String localName = childSchemaNode.getQName().getLocalName();
1750 final Integer count = equalNodeNamesToCounts.get(localName);
1751 if (count != null && count > 1) {
1752 throw new RestconfDocumentedException("Multiple input data elements were specified for '"
1753 + childSchemaNode.getQName().getLocalName()
1754 + "'. The data for this element type can only be specified once.", ErrorType.APPLICATION,
1755 ErrorTag.BAD_ELEMENT);
1761 private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1762 final QName previousAugment, final DOMMountPoint mountPoint) {
1763 QName validQName = schema.getQName();
1764 QName currentAugment = previousAugment;
1765 if (schema.isAugmenting()) {
1766 currentAugment = schema.getQName();
1767 } else if (previousAugment != null
1768 && !Objects.equal(schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1769 validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1772 String moduleName = null;
1773 if (mountPoint == null) {
1774 moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1776 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1779 if (nodeBuilder.getNamespace() == null || Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace())
1780 || Objects.equal(nodeBuilder.getNamespace().toString(), moduleName)) {
1782 * || Note : this check is wrong -
1783 * can never be true as it compares
1784 * a URI with a String not sure what
1785 * the intention is so commented out
1786 * ... Objects . equal ( nodeBuilder
1787 * . getNamespace ( ) ,
1788 * MOUNT_POINT_MODULE_NAME )
1791 nodeBuilder.setQname(validQName);
1794 return currentAugment;
1797 private URI namespace(final Node<?> data) {
1798 if (data instanceof NodeWrapper) {
1799 return ((NodeWrapper<?>) data).getNamespace();
1800 } else if (data != null) {
1801 return data.getNodeType().getNamespace();
1803 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1807 private String localName(final Node<?> data) {
1808 if (data instanceof NodeWrapper) {
1809 return ((NodeWrapper<?>) data).getLocalName();
1810 } else if (data != null) {
1811 return data.getNodeType().getLocalName();
1813 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1818 * @deprecated method will be removed for Lithium release
1824 private String getName(final Node<?> data) {
1825 if (data instanceof NodeWrapper) {
1826 return ((NodeWrapper<?>) data).getLocalName();
1827 } else if (data != null) {
1828 return data.getNodeType().getLocalName();
1830 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1834 private TypeDef typeDefinition(final TypeDefinition<?> type, final QName nodeQName) {
1835 TypeDefinition<?> baseType = type;
1836 QName qName = nodeQName;
1837 while (baseType.getBaseType() != null) {
1838 if (baseType instanceof ExtendedType) {
1839 qName = baseType.getQName();
1841 baseType = baseType.getBaseType();
1844 return new TypeDef(baseType, qName);
1848 private TypeDef typeDefinition(final DataSchemaNode node) {
1849 if (node instanceof LeafListSchemaNode) {
1850 return typeDefinition(((LeafListSchemaNode)node).getType(), node.getQName());
1851 } else if (node instanceof LeafSchemaNode) {
1852 return typeDefinition(((LeafSchemaNode)node).getType(), node.getQName());
1853 } else if (node instanceof AnyXmlSchemaNode) {
1856 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
1860 private CompositeNode datastoreNormalizedNodeToCompositeNode(final NormalizedNode<?, ?> dataNode, final DataSchemaNode schema) {
1861 Node<?> nodes = null;
1862 if (dataNode == null) {
1863 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.DATA_MISSING,
1864 "No data was found."));
1866 nodes = DataNormalizer.toLegacy(dataNode);
1867 if (nodes != null) {
1868 if (nodes instanceof CompositeNode) {
1869 return (CompositeNode) nodes;
1871 LOG.error("The node " + dataNode.getNodeType() + " couldn't be transformed to compositenode.");
1874 LOG.error("Top level node isn't of type Container or List schema node but "
1875 + schema.getClass().getSimpleName());
1878 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1879 "It wasn't possible to correctly interpret data."));
1882 private NormalizedNode<?, ?> compositeNodeToDatastoreNormalizedNode(final CompositeNode compNode,
1883 final DataSchemaNode schema) {
1884 final List<Node<?>> lst = new ArrayList<Node<?>>();
1886 if (schema instanceof ContainerSchemaNode) {
1887 return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser()
1888 .parse(lst, (ContainerSchemaNode) schema);
1889 } else if (schema instanceof ListSchemaNode) {
1890 return CnSnToNormalizedNodeParserFactory.getInstance().getMapEntryNodeParser()
1891 .parse(lst, (ListSchemaNode) schema);
1894 LOG.error("Top level isn't of type container, list, leaf schema node but " + schema.getClass().getSimpleName());
1896 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1897 "It wasn't possible to translate specified data to datastore readable form."));
1900 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1901 final InstanceIdentifierContext iiWithSchemaNode) {
1902 return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false);
1905 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1906 final InstanceIdentifierContext iiWithSchemaNode, final boolean unwrapLastListNode) {
1907 return new InstanceIdentifierContext(instanceIdentifierToReadableFormForNormalizeNode(
1908 iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(),
1909 iiWithSchemaNode.getMountPoint(),iiWithSchemaNode.getSchemaContext());
1912 private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode(
1913 final YangInstanceIdentifier instIdentifier, final boolean unwrapLastListNode) {
1914 Preconditions.checkNotNull(instIdentifier, "Instance identifier can't be null");
1915 final List<PathArgument> result = new ArrayList<PathArgument>();
1916 final Iterator<PathArgument> iter = instIdentifier.getPathArguments().iterator();
1917 while (iter.hasNext()) {
1918 final PathArgument pathArgument = iter.next();
1919 if (pathArgument instanceof NodeIdentifierWithPredicates && (iter.hasNext() || unwrapLastListNode)) {
1920 result.add(new YangInstanceIdentifier.NodeIdentifier(pathArgument.getNodeType()));
1922 result.add(pathArgument);
1924 return YangInstanceIdentifier.create(result);
1927 private CompositeNodeWrapper topLevelElementAsCompositeNodeWrapper(final NodeWrapper<?> node,
1928 final DataSchemaNode schemaNode) {
1929 if (node instanceof CompositeNodeWrapper) {
1930 return (CompositeNodeWrapper) node;
1931 } else if (node instanceof SimpleNodeWrapper && isDataContainerNode(schemaNode)) {
1932 final SimpleNodeWrapper simpleNodeWrapper = (SimpleNodeWrapper) node;
1933 return new CompositeNodeWrapper(namespace(simpleNodeWrapper), localName(simpleNodeWrapper));
1936 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1937 "Top level element has to be composite node or has to represent data container node."));
1940 private boolean isDataContainerNode(final DataSchemaNode schemaNode) {
1941 if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode) {
1947 public BigInteger getOperationalReceived() {
1948 // TODO Auto-generated method stub
1952 private MapNode makeModuleMapNode(final Set<Module> modules) {
1953 Preconditions.checkNotNull(modules);
1954 final Module restconfModule = getRestconfModule();
1955 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
1956 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
1957 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
1959 final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
1960 .mapBuilder((ListSchemaNode) moduleSchemaNode);
1962 for (final Module module : modules) {
1963 listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode));
1965 return listModuleBuilder.build();
1968 protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) {
1969 Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode,
1970 "moduleSchemaNode has to be of type ListSchemaNode");
1971 final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode;
1972 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = Builders
1973 .mapEntryBuilder(listModuleSchemaNode);
1975 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1976 (listModuleSchemaNode), "name");
1977 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1978 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1979 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName())
1982 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1983 (listModuleSchemaNode), "revision");
1984 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1985 Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode);
1986 final String revision = REVISION_FORMAT.format(module.getRevision());
1987 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision)
1990 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1991 (listModuleSchemaNode), "namespace");
1992 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1993 Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode);
1994 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode)
1995 .withValue(module.getNamespace().toString()).build());
1997 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1998 (listModuleSchemaNode), "feature");
1999 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2000 Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode);
2001 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> featuresBuilder = Builders
2002 .leafSetBuilder((LeafListSchemaNode) featureSchemaNode);
2003 for (final FeatureDefinition feature : module.getFeatures()) {
2004 featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode))
2005 .withValue(feature.getQName().getLocalName()).build());
2007 moduleNodeValues.withChild(featuresBuilder.build());
2009 return moduleNodeValues.build();
2012 protected MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamSchemaNode) {
2013 Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode,
2014 "streamSchemaNode has to be of type ListSchemaNode");
2015 final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode;
2016 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
2017 .mapEntryBuilder(listStreamSchemaNode);
2019 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2020 (listStreamSchemaNode), "name");
2021 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2022 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
2023 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName)
2026 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2027 (listStreamSchemaNode), "description");
2028 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2029 Preconditions.checkState(descriptionSchemaNode instanceof LeafSchemaNode);
2030 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode)
2031 .withValue("DESCRIPTION_PLACEHOLDER").build());
2033 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2034 (listStreamSchemaNode), "replay-support");
2035 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2036 Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode);
2037 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode)
2038 .withValue(Boolean.valueOf(true)).build());
2040 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2041 (listStreamSchemaNode), "replay-log-creation-time");
2042 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2043 Preconditions.checkState(replayLogCreationTimeSchemaNode instanceof LeafSchemaNode);
2044 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode)
2045 .withValue("").build());
2047 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2048 (listStreamSchemaNode), "events");
2049 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2050 Preconditions.checkState(eventsSchemaNode instanceof LeafSchemaNode);
2051 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode)
2052 .withValue("").build());
2054 return streamNodeValues.build();