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 final NormalizedNode<?, ?> resultData = result.getResult();
554 if (result != null && result.getResult() != null) {
555 final RpcDefinition rpcDef = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode();
556 resultNodeSchema = rpcDef.getOutput();
559 return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
560 schemaContext), resultData);
563 private DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
564 if (response == null) {
568 final DOMRpcResult retValue = response.get();
569 if (retValue.getErrors() == null || retValue.getErrors().isEmpty()) {
572 throw new RestconfDocumentedException("RpcError message", null, retValue.getErrors());
574 catch (final InterruptedException e) {
575 throw new RestconfDocumentedException(
576 "The operation was interrupted while executing and did not complete.", ErrorType.RPC,
577 ErrorTag.PARTIAL_OPERATION);
579 catch (final ExecutionException e) {
580 Throwable cause = e.getCause();
581 if (cause instanceof CancellationException) {
582 throw new RestconfDocumentedException("The operation was cancelled while executing.", ErrorType.RPC,
583 ErrorTag.PARTIAL_OPERATION);
584 } else if (cause != null) {
585 while (cause.getCause() != null) {
586 cause = cause.getCause();
589 if (cause instanceof IllegalArgumentException) {
590 throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
591 ErrorTag.INVALID_VALUE);
594 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
597 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
603 private void validateInput(final SchemaNode inputSchema, final NormalizedNodeContext payload) {
604 if (inputSchema != null && payload.getData() == null) {
605 // expected a non null payload
606 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
607 } else if (inputSchema == null && payload.getData() != null) {
608 // did not expect any input
609 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
613 // TODO: Validate "mandatory" and "config" values here??? Or should those be
615 // validate in a more central location inside MD-SAL core.
620 * @deprecated method will be removed for Lithium release
626 private void validateInput(final DataSchemaNode inputSchema, final Node<?> payload) {
627 if (inputSchema != null && payload == null) {
628 // expected a non null payload
629 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
630 } else if (inputSchema == null && payload != null) {
631 // did not expect any input
632 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
636 // TODO: Validate "mandatory" and "config" values here??? Or should those be
638 // validate in a more central location inside MD-SAL core.
642 private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
643 final ContainerNode value = (ContainerNode) payload.getData();
644 final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
645 final Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(new NodeIdentifier(
646 QName.create(payload.getInstanceIdentifierContext().getSchemaNode().getQName(), "path")));
647 final Object pathValue = path.isPresent() ? path.get().getValue() : null;
649 if (!(pathValue instanceof YangInstanceIdentifier)) {
650 throw new RestconfDocumentedException("Instance identifier was not normalized correctly.",
651 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
654 final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
655 String streamName = null;
656 if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
657 final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
659 LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class,
660 DATASTORE_PARAM_NAME);
661 datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
663 DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
664 scope = scope == null ? DEFAULT_SCOPE : scope;
666 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
667 + "/scope=" + scope);
670 if (Strings.isNullOrEmpty(streamName)) {
671 throw new RestconfDocumentedException(
672 "Path is empty or contains value node which is not Container or List build-in type.",
673 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
676 final QName outputQname = QName.create(rpcQName, "output");
677 final QName streamNameQname = QName.create(rpcQName, "stream-name");
679 final ContainerNode output = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
680 .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
682 if (!Notificator.existListenerFor(streamName)) {
683 final YangInstanceIdentifier normalizedPathIdentifier = controllerContext.toNormalized(pathIdentifier);
684 Notificator.createListener(normalizedPathIdentifier, streamName);
687 final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
689 return Futures.immediateCheckedFuture(defaultDOMRpcResult);
693 public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
694 if (StringUtils.isNotBlank(noPayload)) {
695 throw new RestconfDocumentedException("Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
698 String identifierEncoded = null;
699 DOMMountPoint mountPoint = null;
700 final SchemaContext schemaContext;
701 if (identifier.contains(ControllerContext.MOUNT)) {
702 // mounted RPC call - look up mount instance.
703 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
704 mountPoint = mountPointId.getMountPoint();
705 schemaContext = mountPoint.getSchemaContext();
706 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
707 + ControllerContext.MOUNT.length() + 1;
708 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
709 identifierEncoded = remoteRpcName;
711 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
712 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
713 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
714 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
716 identifierEncoded = identifier;
717 schemaContext = controllerContext.getGlobalSchema();
720 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
722 RpcDefinition rpc = null;
723 if (mountPoint == null) {
724 rpc = controllerContext.getRpcDefinition(identifierDecoded);
726 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
730 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
733 if (rpc.getInput() != null) {
734 // FIXME : find a correct Error from specification
735 throw new IllegalStateException("RPC " + rpc + " needs input value!");
738 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
739 if (mountPoint != null) {
740 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
741 if ( ! mountRpcServices.isPresent()) {
742 throw new RestconfDocumentedException("Rpc service is missing.");
744 response = mountRpcServices.get().invokeRpc(rpc.getPath(), null);
746 response = broker.invokeRpc(rpc.getPath(), null);
749 final DOMRpcResult result = checkRpcResponse(response);
751 DataSchemaNode resultNodeSchema = null;
752 NormalizedNode<?, ?> resultData = null;
753 if (result != null && result.getResult() != null) {
754 resultData = result.getResult();
755 final ContainerSchemaNode rpcDataSchemaNode =
756 SchemaContextUtil.getRpcDataSchema(schemaContext, rpc.getOutput().getPath());
757 resultNodeSchema = rpcDataSchemaNode.getDataChildByName(result.getResult().getNodeType());
760 return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
761 schemaContext), resultData);
764 private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
765 String identifierEncoded = null;
766 DOMMountPoint mountPoint = null;
767 if (identifier.contains(ControllerContext.MOUNT)) {
768 // mounted RPC call - look up mount instance.
769 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
770 mountPoint = mountPointId.getMountPoint();
772 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
773 + ControllerContext.MOUNT.length() + 1;
774 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
775 identifierEncoded = remoteRpcName;
777 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
778 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
779 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
780 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
782 identifierEncoded = identifier;
785 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
787 RpcDefinition rpc = null;
788 if (mountPoint == null) {
789 rpc = controllerContext.getRpcDefinition(identifierDecoded);
791 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
795 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
798 if (mountPoint == null) {
799 return new BrokerRpcExecutor(rpc, broker);
801 return new MountPointRpcExecutor(rpc, mountPoint);
806 private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
807 final String[] splittedIdentifier = identifierDecoded.split(":");
808 if (splittedIdentifier.length != 2) {
809 throw new RestconfDocumentedException(identifierDecoded
810 + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION,
811 ErrorTag.INVALID_VALUE);
813 for (final Module module : schemaContext.getModules()) {
814 if (module.getName().equals(splittedIdentifier[0])) {
815 for (final RpcDefinition rpcDefinition : module.getRpcs()) {
816 if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) {
817 return rpcDefinition;
826 * @deprecated method will be removed for Lithium release
829 private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload, final boolean prettyPrint) {
830 if (rpcExecutor == null) {
831 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
834 CompositeNode rpcRequest = null;
835 final RpcDefinition rpc = rpcExecutor.getRpcDefinition();
836 final QName rpcName = rpc.getQName();
838 if (payload == null) {
839 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
841 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
842 final List<Node<?>> input = Collections.<Node<?>> singletonList(value);
843 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
846 final RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
848 checkRpcSuccessAndThrowException(rpcResult);
850 if (rpcResult.getResult() == null) {
854 if (rpc.getOutput() == null) {
855 return null; // no output, nothing to send back.
858 return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null, prettyPrint);
861 private void checkRpcSuccessAndThrowException(final RpcResult<CompositeNode> rpcResult) {
862 if (rpcResult.isSuccessful() == false) {
864 throw new RestconfDocumentedException("The operation was not successful", null,
865 rpcResult.getErrors());
870 public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
871 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
872 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
873 NormalizedNode<?, ?> data = null;
874 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
875 if (mountPoint != null) {
876 data = broker.readConfigurationData(mountPoint, normalizedII);
878 data = broker.readConfigurationData(normalizedII);
881 throw new RestconfDocumentedException(
882 "Request could not be completed because the relevant data model content does not exist.",
883 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
885 return new NormalizedNodeContext(iiWithData, data);
888 @SuppressWarnings("unchecked")
889 private <T extends Node<?>> T pruneDataAtDepth(final T node, final Integer depth) {
894 if (node instanceof CompositeNode) {
895 final ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
897 for (final Node<?> childNode : ((CompositeNode) node).getValue()) {
898 newChildNodes.add(pruneDataAtDepth(childNode, depth - 1));
902 return (T) ImmutableCompositeNode.create(node.getNodeType(), newChildNodes.build());
903 } else { // SimpleNode
908 private Integer parseDepthParameter(final UriInfo info) {
909 final String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString());
910 if (Strings.isNullOrEmpty(param) || "unbounded".equals(param)) {
915 final Integer depth = Integer.valueOf(param);
917 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
918 "Invalid depth parameter: " + depth, null,
919 "The depth parameter must be an integer > 1 or \"unbounded\""));
923 } catch (final NumberFormatException e) {
924 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
925 "Invalid depth parameter: " + e.getMessage(), null,
926 "The depth parameter must be an integer > 1 or \"unbounded\""));
931 public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo info) {
932 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
933 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
934 NormalizedNode<?, ?> data = null;
935 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
936 if (mountPoint != null) {
937 data = broker.readOperationalData(mountPoint, normalizedII);
939 data = broker.readOperationalData(normalizedII);
942 throw new RestconfDocumentedException(
943 "Request could not be completed because the relevant data model content does not exist.",
944 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
946 return new NormalizedNodeContext(iiWithData, data);
949 private boolean parsePrettyPrintParameter(final UriInfo info) {
950 final String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
951 return Boolean.parseBoolean(param);
955 public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
956 Preconditions.checkNotNull(identifier);
957 final InstanceIdentifierContext<DataSchemaNode> iiWithData =
958 (InstanceIdentifierContext<DataSchemaNode>) payload.getInstanceIdentifierContext();
960 validateInput(iiWithData.getSchemaNode(), payload);
961 validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier());
962 validateListKeysEqualityInPayloadAndUri(iiWithData, payload.getData());
964 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
965 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
968 * There is a small window where another write transaction could be updating the same data
969 * simultaneously and we get an OptimisticLockFailedException. This error is likely
970 * transient and The WriteTransaction#submit API docs state that a retry will likely
971 * succeed. So we'll try again if that scenario occurs. If it fails a third time then it
972 * probably will never succeed so we'll fail in that case.
974 * By retrying we're attempting to hide the internal implementation of the data store and
975 * how it handles concurrent updates from the restconf client. The client has instructed us
976 * to put the data and we should make every effort to do so without pushing optimistic lock
977 * failures back to the client and forcing them to handle it via retry (and having to
978 * document the behavior).
983 if (mountPoint != null) {
984 broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
986 broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet();
990 } catch (final TransactionCommitFailedException e) {
991 if(e instanceof OptimisticLockFailedException) {
993 LOG.debug("Got OptimisticLockFailedException on last try - failing");
994 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
997 LOG.debug("Got OptimisticLockFailedException - trying again");
999 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
1004 return Response.status(Status.OK).build();
1007 private void validateTopLevelNodeName(final NormalizedNodeContext node,
1008 final YangInstanceIdentifier identifier) {
1010 final String payloadName = node.getData().getNodeType().getLocalName();
1011 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1014 if ( ! pathArguments.hasNext()) {
1016 if ( ! node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) {
1017 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1018 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1022 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1023 if ( ! payloadName.equals(identifierName)) {
1024 throw new RestconfDocumentedException("Payload name (" + payloadName
1025 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1026 ErrorTag.MALFORMED_MESSAGE);
1032 * @deprecated method will be removed for Lithium release
1038 private void validateTopLevelNodeName(final Node<?> node,
1039 final YangInstanceIdentifier identifier) {
1040 final String payloadName = getName(node);
1041 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1044 if (!pathArguments.hasNext()) {
1046 if (!node.getNodeType().equals(NETCONF_BASE_QNAME)) {
1047 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1048 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1052 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1053 if (!payloadName.equals(identifierName)) {
1054 throw new RestconfDocumentedException("Payload name (" + payloadName
1055 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1056 ErrorTag.MALFORMED_MESSAGE);
1062 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1064 * @throws RestconfDocumentedException
1065 * if key values or key count in payload and URI isn't equal
1068 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext<DataSchemaNode> iiWithData,
1069 final NormalizedNode<?, ?> payload) {
1070 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1071 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1072 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1073 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1074 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1076 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1082 * @deprecated method will be removed for Lithium release
1084 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1086 * @throws RestconfDocumentedException
1087 * if key values or key count in payload and URI isn't equal
1091 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
1092 final CompositeNode payload) {
1093 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1094 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1095 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1096 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1097 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1099 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1104 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final NormalizedNode<?, ?> payload,
1105 final List<QName> keyDefinitions) {
1106 for (final QName keyDefinition : keyDefinitions) {
1107 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1108 // should be caught during parsing URI to InstanceIdentifier
1109 if (uriKeyValue == null) {
1110 final String errMsg = "Missing key " + keyDefinition + " in URI.";
1111 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1113 // TODO thing about the possibility to fix
1114 // final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1115 // if (payloadKeyValues.isEmpty()) {
1116 // final String errMsg = "Missing key " + keyDefinition.getLocalName() + " in the message body.";
1117 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1120 // final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1121 // if (!uriKeyValue.equals(payloadKeyValue)) {
1122 // final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
1123 // "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. ";
1124 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1130 * @deprecated method will be removed for Lithium release
1132 * @param uriKeyValues
1134 * @param keyDefinitions
1137 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final CompositeNode payload,
1138 final List<QName> keyDefinitions) {
1139 for (final QName keyDefinition : keyDefinitions) {
1140 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1141 // should be caught during parsing URI to InstanceIdentifier
1142 if (uriKeyValue == null) {
1143 throw new RestconfDocumentedException("Missing key " + keyDefinition + " in URI.", ErrorType.PROTOCOL,
1144 ErrorTag.DATA_MISSING);
1146 final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1147 if (payloadKeyValues.isEmpty()) {
1148 throw new RestconfDocumentedException("Missing key " + keyDefinition.getLocalName()
1149 + " in the message body.", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1152 final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1153 if (!uriKeyValue.equals(payloadKeyValue)) {
1154 throw new RestconfDocumentedException("The value '" + uriKeyValue + "' for key '"
1155 + keyDefinition.getLocalName() + "' specified in the URI doesn't match the value '"
1156 + payloadKeyValue + "' specified in the message body. ", ErrorType.PROTOCOL,
1157 ErrorTag.INVALID_VALUE);
1163 public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
1164 return createConfigurationData(payload, uriInfo);
1167 // FIXME create RestconfIdetifierHelper and move this method there
1168 private YangInstanceIdentifier checkConsistencyOfNormalizedNodeContext(final NormalizedNodeContext payload) {
1169 Preconditions.checkArgument(payload != null);
1170 Preconditions.checkArgument(payload.getData() != null);
1171 Preconditions.checkArgument(payload.getData().getNodeType() != null);
1172 Preconditions.checkArgument(payload.getInstanceIdentifierContext() != null);
1173 Preconditions.checkArgument(payload.getInstanceIdentifierContext().getInstanceIdentifier() != null);
1175 final QName payloadNodeQname = payload.getData().getNodeType();
1176 final YangInstanceIdentifier yangIdent = payload.getInstanceIdentifierContext().getInstanceIdentifier();
1177 if (payloadNodeQname.compareTo(yangIdent.getLastPathArgument().getNodeType()) > 0) {
1180 final InstanceIdentifierContext parentContext = payload.getInstanceIdentifierContext();
1181 final SchemaNode parentSchemaNode = parentContext.getSchemaNode();
1182 if(parentSchemaNode instanceof DataNodeContainer) {
1183 final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode;
1184 for (final DataSchemaNode child : cast.getChildNodes()) {
1185 if (payloadNodeQname.compareTo(child.getQName()) == 0) {
1186 return YangInstanceIdentifier.builder(yangIdent).node(child.getQName()).build();
1190 if (parentSchemaNode instanceof RpcDefinition) {
1193 final String errMsg = "Error parsing input: DataSchemaNode has not children";
1194 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1198 public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
1199 if (payload == null) {
1200 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1203 final URI payloadNS = payload.getData().getNodeType().getNamespace();
1204 if (payloadNS == null) {
1205 throw new RestconfDocumentedException(
1206 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
1207 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1210 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
1211 final InstanceIdentifierContext<DataSchemaNode> iiWithData = (InstanceIdentifierContext<DataSchemaNode>) payload.getInstanceIdentifierContext();
1212 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1213 final YangInstanceIdentifier resultII;
1215 if (mountPoint != null) {
1216 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
1219 broker.commitConfigurationDataPost(normalizedII, payload.getData());
1221 } catch(final RestconfDocumentedException e) {
1223 } catch (final Exception e) {
1224 throw new RestconfDocumentedException("Error creating data", e);
1227 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
1228 // FIXME: Provide path to result.
1229 final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII);
1230 if (location != null) {
1231 responseBuilder.location(location);
1233 return responseBuilder.build();
1236 private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
1237 final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
1238 uriBuilder.path("config");
1240 uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
1241 } catch (final Exception e) {
1242 LOG.debug("Location for instance identifier"+normalizedII+"wasn't created", e);
1245 return uriBuilder.build();
1249 public Response deleteConfigurationData(final String identifier) {
1250 final InstanceIdentifierContext<DataSchemaNode> iiWithData = controllerContext.toInstanceIdentifier(identifier);
1251 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
1252 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1255 if (mountPoint != null) {
1256 broker.commitConfigurationDataDelete(mountPoint, normalizedII);
1258 broker.commitConfigurationDataDelete(normalizedII).get();
1260 } catch (final Exception e) {
1261 final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
1262 Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
1263 if (searchedException.isPresent()) {
1264 throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
1266 throw new RestconfDocumentedException("Error while deleting data", e);
1268 return Response.status(Status.OK).build();
1272 * Subscribes to some path in schema context (stream) to listen on changes on this stream.
1274 * Additional parameters for subscribing to stream are loaded via rpc input parameters:
1276 * <li>datastore</li> - default CONFIGURATION (other values of {@link LogicalDatastoreType} enum type)
1277 * <li>scope</li> - default BASE (other values of {@link DataChangeScope})
1281 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
1282 final String streamName = Notificator.createStreamNameFromUri(identifier);
1283 if (Strings.isNullOrEmpty(streamName)) {
1284 throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1287 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
1288 if (listener == null) {
1289 throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
1292 final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
1293 final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
1294 paramToValues.get(DATASTORE_PARAM_NAME));
1295 if (datastore == null) {
1296 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)",
1297 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1299 final DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME));
1300 if (scope == null) {
1301 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)",
1302 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1305 broker.registerToListenDataChanges(datastore, scope, listener);
1307 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
1308 int notificationPort = NOTIFICATION_PORT;
1310 final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
1311 notificationPort = webSocketServerInstance.getPort();
1312 } catch (final NullPointerException e) {
1313 WebSocketServer.createInstance(NOTIFICATION_PORT);
1315 final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
1316 final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
1318 return Response.status(Status.OK).location(uriToWebsocketServer).build();
1322 * Load parameter for subscribing to stream from input composite node
1326 * @return enum object if its string value is equal to {@code paramName}. In other cases null.
1328 private <T> T parseEnumTypeParameter(final ContainerNode value, final Class<T> classDescriptor,
1329 final String paramName) {
1330 final QNameModule salRemoteAugment = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
1331 EVENT_SUBSCRIPTION_AUGMENT_REVISION);
1332 final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode = value.getChild(new NodeIdentifier(
1333 QName.create(salRemoteAugment, paramName)));
1334 if (!enumNode.isPresent()) {
1337 final Object rawValue = enumNode.get().getValue();
1338 if (!(rawValue instanceof String)) {
1342 return resolveAsEnum(classDescriptor, (String) rawValue);
1346 * Checks whether {@code value} is one of the string representation of enumeration {@code classDescriptor}
1348 * @return enum object if string value of {@code classDescriptor} enumeration is equal to {@code value}. Other cases
1351 private <T> T parserURIEnumParameter(final Class<T> classDescriptor, final String value) {
1352 if (Strings.isNullOrEmpty(value)) {
1355 return resolveAsEnum(classDescriptor, value);
1358 private <T> T resolveAsEnum(final Class<T> classDescriptor, final String value) {
1359 final T[] enumConstants = classDescriptor.getEnumConstants();
1360 if (enumConstants != null) {
1361 for (final T enm : classDescriptor.getEnumConstants()) {
1362 if (((Enum<?>) enm).name().equals(value)) {
1370 private Map<String, String> resolveValuesFromUri(final String uri) {
1371 final Map<String, String> result = new HashMap<>();
1372 final String[] tokens = uri.split("/");
1373 for (int i = 1; i < tokens.length; i++) {
1374 final String[] parameterTokens = tokens[i].split("=");
1375 if (parameterTokens.length == 2) {
1376 result.put(parameterTokens[0], parameterTokens[1]);
1382 private Module findModule(final DOMMountPoint mountPoint, final Node<?> data) {
1383 Module module = null;
1384 if (data instanceof NodeWrapper) {
1385 module = findModule(mountPoint, (NodeWrapper<?>) data);
1386 } else if (data != null) {
1387 final URI namespace = data.getNodeType().getNamespace();
1388 if (mountPoint != null) {
1389 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1391 module = controllerContext.findModuleByNamespace(namespace);
1394 if (module != null) {
1397 throw new RestconfDocumentedException(
1398 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
1399 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1402 private Module findModule(final DOMMountPoint mountPoint, final NodeWrapper<?> data) {
1403 final URI namespace = data.getNamespace();
1404 Preconditions.<URI> checkNotNull(namespace);
1406 Module module = null;
1407 if (mountPoint != null) {
1408 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1409 if (module == null) {
1410 module = controllerContext.findModuleByName(mountPoint, namespace.toString());
1413 module = controllerContext.findModuleByNamespace(namespace);
1414 if (module == null) {
1415 module = controllerContext.findModuleByName(namespace.toString());
1422 private InstanceIdentifierContext addLastIdentifierFromData(final InstanceIdentifierContext identifierWithSchemaNode,
1423 final CompositeNode data, final DataSchemaNode schemaOfData, final SchemaContext schemaContext) {
1424 YangInstanceIdentifier instanceIdentifier = null;
1425 if (identifierWithSchemaNode != null) {
1426 instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
1429 final YangInstanceIdentifier iiOriginal = instanceIdentifier;
1430 InstanceIdentifierBuilder iiBuilder = null;
1431 if (iiOriginal == null) {
1432 iiBuilder = YangInstanceIdentifier.builder();
1434 iiBuilder = YangInstanceIdentifier.builder(iiOriginal);
1437 if ((schemaOfData instanceof ListSchemaNode)) {
1438 final HashMap<QName, Object> keys = resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
1439 iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
1441 iiBuilder.node(schemaOfData.getQName());
1444 final YangInstanceIdentifier instance = iiBuilder.toInstance();
1445 DOMMountPoint mountPoint = null;
1446 final SchemaContext schemaCtx = null;
1447 if (identifierWithSchemaNode != null) {
1448 mountPoint = identifierWithSchemaNode.getMountPoint();
1451 return new InstanceIdentifierContext(instance, schemaOfData, mountPoint,schemaContext);
1454 private HashMap<QName, Object> resolveKeysFromData(final ListSchemaNode listNode, final CompositeNode dataNode) {
1455 final HashMap<QName, Object> keyValues = new HashMap<QName, Object>();
1456 final List<QName> _keyDefinition = listNode.getKeyDefinition();
1457 for (final QName key : _keyDefinition) {
1458 SimpleNode<? extends Object> head = null;
1459 final String localName = key.getLocalName();
1460 final List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
1461 if (simpleNodesByName != null) {
1462 head = Iterables.getFirst(simpleNodesByName, null);
1465 Object dataNodeKeyValueObject = null;
1467 dataNodeKeyValueObject = head.getValue();
1470 if (dataNodeKeyValueObject == null) {
1471 throw new RestconfDocumentedException("Data contains list \"" + dataNode.getNodeType().getLocalName()
1472 + "\" which does not contain key: \"" + key.getLocalName() + "\"", ErrorType.PROTOCOL,
1473 ErrorTag.INVALID_VALUE);
1476 keyValues.put(key, dataNodeKeyValueObject);
1482 private boolean endsWithMountPoint(final String identifier) {
1483 return identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/");
1486 private boolean representsMountPointRootData(final Node<?> data) {
1487 final URI namespace = namespace(data);
1488 return (SchemaContext.NAME.getNamespace().equals(namespace) /*
1489 * || MOUNT_POINT_MODULE_NAME .equals( namespace .
1492 && SchemaContext.NAME.getLocalName().equals(localName(data));
1495 private String addMountPointIdentifier(final String identifier) {
1496 final boolean endsWith = identifier.endsWith("/");
1498 return (identifier + ControllerContext.MOUNT);
1501 return identifier + "/" + ControllerContext.MOUNT;
1505 * @deprecated method will be removed in Lithium release
1506 * we don't wish to use Node and CompositeNode anywhere
1509 public CompositeNode normalizeNode(final Node<?> node, final DataSchemaNode schema, final DOMMountPoint mountPoint) {
1510 if (schema == null) {
1511 final String localName = node == null ? null :
1512 node instanceof NodeWrapper ? ((NodeWrapper<?>)node).getLocalName() :
1513 node.getNodeType().getLocalName();
1515 throw new RestconfDocumentedException("Data schema node was not found for " + localName,
1516 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1519 if (!(schema instanceof DataNodeContainer)) {
1520 throw new RestconfDocumentedException("Root element has to be container or list yang datatype.",
1521 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1524 if ((node instanceof NodeWrapper<?>)) {
1525 NodeWrapper<?> nodeWrap = (NodeWrapper<?>) node;
1526 final boolean isChangeAllowed = ((NodeWrapper<?>) node).isChangeAllowed();
1527 if (isChangeAllowed) {
1528 nodeWrap = topLevelElementAsCompositeNodeWrapper((NodeWrapper<?>) node, schema);
1530 this.normalizeNode(nodeWrap, schema, null, mountPoint);
1531 } catch (final IllegalArgumentException e) {
1532 final RestconfDocumentedException restconfDocumentedException = new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1533 restconfDocumentedException.addSuppressed(e);
1534 throw restconfDocumentedException;
1536 if (nodeWrap instanceof CompositeNodeWrapper) {
1537 return ((CompositeNodeWrapper) nodeWrap).unwrap();
1542 if (node instanceof CompositeNode) {
1543 return (CompositeNode) node;
1546 throw new RestconfDocumentedException("Top level element is not interpreted as composite node.",
1547 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
1550 private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1551 final QName previousAugment, final DOMMountPoint mountPoint) {
1552 if (schema == null) {
1553 throw new RestconfDocumentedException("Data has bad format.\n\"" + nodeBuilder.getLocalName()
1554 + "\" does not exist in yang schema.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1557 QName currentAugment = null;
1558 if (nodeBuilder.getQname() != null) {
1559 currentAugment = previousAugment;
1561 currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
1562 if (nodeBuilder.getQname() == null) {
1563 throw new RestconfDocumentedException(
1564 "Data has bad format.\nIf data is in XML format then namespace for \""
1565 + nodeBuilder.getLocalName() + "\" should be \"" + schema.getQName().getNamespace()
1566 + "\".\n" + "If data is in JSON format then module name for \""
1567 + nodeBuilder.getLocalName() + "\" should be corresponding to namespace \""
1568 + schema.getQName().getNamespace() + "\".", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1572 if (nodeBuilder instanceof CompositeNodeWrapper) {
1573 if (schema instanceof DataNodeContainer) {
1574 normalizeCompositeNode((CompositeNodeWrapper) nodeBuilder, (DataNodeContainer) schema, mountPoint,
1576 } else if (schema instanceof AnyXmlSchemaNode) {
1577 normalizeAnyXmlNode((CompositeNodeWrapper) nodeBuilder, (AnyXmlSchemaNode) schema);
1579 } else if (nodeBuilder instanceof SimpleNodeWrapper) {
1580 normalizeSimpleNode((SimpleNodeWrapper) nodeBuilder, schema, mountPoint);
1581 } else if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1582 normalizeEmptyNode((EmptyNodeWrapper) nodeBuilder, schema);
1586 private void normalizeAnyXmlNode(final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema) {
1587 final List<NodeWrapper<?>> children = compositeNode.getValues();
1588 for (final NodeWrapper<? extends Object> child : children) {
1589 child.setNamespace(schema.getQName().getNamespace());
1590 if (child instanceof CompositeNodeWrapper) {
1591 normalizeAnyXmlNode((CompositeNodeWrapper) child, schema);
1596 private void normalizeEmptyNode(final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema) {
1597 if ((schema instanceof LeafSchemaNode)) {
1598 emptyNodeBuilder.setComposite(false);
1600 if ((schema instanceof ContainerSchemaNode)) {
1601 // FIXME: Add presence check
1602 emptyNodeBuilder.setComposite(true);
1607 private void normalizeSimpleNode(final SimpleNodeWrapper simpleNode, final DataSchemaNode schema,
1608 final DOMMountPoint mountPoint) {
1609 final Object value = simpleNode.getValue();
1610 Object inputValue = value;
1611 final TypeDef typeDef = this.typeDefinition(schema);
1612 TypeDefinition<? extends Object> typeDefinition = typeDef != null ? typeDef.typedef : null;
1614 // For leafrefs, extract the type it is pointing to
1615 if(typeDefinition instanceof LeafrefTypeDefinition) {
1616 if (schema.getQName().equals(typeDef.qName)) {
1617 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema);
1619 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), typeDef.qName);
1623 if (typeDefinition instanceof IdentityrefTypeDefinition) {
1624 inputValue = parseToIdentityValuesDTO(simpleNode, value, inputValue);
1627 Object outputValue = inputValue;
1629 if (typeDefinition != null) {
1630 final Codec<Object, Object> codec = RestCodec.from(typeDefinition, mountPoint);
1631 outputValue = codec == null ? null : codec.deserialize(inputValue);
1634 simpleNode.setValue(outputValue);
1637 private Object parseToIdentityValuesDTO(final SimpleNodeWrapper simpleNode, final Object value, Object inputValue) {
1638 if ((value instanceof String)) {
1639 inputValue = new IdentityValuesDTO(simpleNode.getNamespace().toString(), (String) value, null,
1641 } // else value is already instance of IdentityValuesDTO
1645 private void normalizeCompositeNode(final CompositeNodeWrapper compositeNodeBuilder,
1646 final DataNodeContainer schema, final DOMMountPoint mountPoint, final QName currentAugment) {
1647 final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
1648 checkNodeMultiplicityAccordingToSchema(schema, children);
1649 for (final NodeWrapper<? extends Object> child : children) {
1650 final List<DataSchemaNode> potentialSchemaNodes = ControllerContext.findInstanceDataChildrenByName(
1651 schema, child.getLocalName());
1653 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
1654 final StringBuilder builder = new StringBuilder();
1655 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1656 builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()).append("\n");
1659 throw new RestconfDocumentedException("Node \"" + child.getLocalName()
1660 + "\" is added as augment from more than one module. "
1661 + "Therefore node must have namespace (XML format) or module name (JSON format)."
1662 + "\nThe node is added as augment from modules with namespaces:\n" + builder,
1663 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1666 boolean rightNodeSchemaFound = false;
1667 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1668 if (!rightNodeSchemaFound) {
1669 final QName potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode,
1670 currentAugment, mountPoint);
1671 if (child.getQname() != null) {
1672 this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1673 rightNodeSchemaFound = true;
1678 if (!rightNodeSchemaFound) {
1679 throw new RestconfDocumentedException("Schema node \"" + child.getLocalName()
1680 + "\" was not found in module.", ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT);
1684 if ((schema instanceof ListSchemaNode)) {
1685 final ListSchemaNode listSchemaNode = (ListSchemaNode) schema;
1686 final List<QName> listKeys = listSchemaNode.getKeyDefinition();
1687 for (final QName listKey : listKeys) {
1688 boolean foundKey = false;
1689 for (final NodeWrapper<? extends Object> child : children) {
1690 if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1696 throw new RestconfDocumentedException("Missing key in URI \"" + listKey.getLocalName()
1697 + "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"", ErrorType.PROTOCOL,
1698 ErrorTag.DATA_MISSING);
1704 private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer,
1705 final List<NodeWrapper<?>> nodes) {
1706 final Map<String, Integer> equalNodeNamesToCounts = new HashMap<String, Integer>();
1707 for (final NodeWrapper<?> child : nodes) {
1708 Integer count = equalNodeNamesToCounts.get(child.getLocalName());
1709 equalNodeNamesToCounts.put(child.getLocalName(), count == null ? 1 : ++count);
1712 for (final DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) {
1713 if (childSchemaNode instanceof ContainerSchemaNode || childSchemaNode instanceof LeafSchemaNode) {
1714 final String localName = childSchemaNode.getQName().getLocalName();
1715 final Integer count = equalNodeNamesToCounts.get(localName);
1716 if (count != null && count > 1) {
1717 throw new RestconfDocumentedException("Multiple input data elements were specified for '"
1718 + childSchemaNode.getQName().getLocalName()
1719 + "'. The data for this element type can only be specified once.", ErrorType.APPLICATION,
1720 ErrorTag.BAD_ELEMENT);
1726 private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1727 final QName previousAugment, final DOMMountPoint mountPoint) {
1728 QName validQName = schema.getQName();
1729 QName currentAugment = previousAugment;
1730 if (schema.isAugmenting()) {
1731 currentAugment = schema.getQName();
1732 } else if (previousAugment != null
1733 && !Objects.equal(schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1734 validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1737 String moduleName = null;
1738 if (mountPoint == null) {
1739 moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1741 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1744 if (nodeBuilder.getNamespace() == null || Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace())
1745 || Objects.equal(nodeBuilder.getNamespace().toString(), moduleName)) {
1747 * || Note : this check is wrong -
1748 * can never be true as it compares
1749 * a URI with a String not sure what
1750 * the intention is so commented out
1751 * ... Objects . equal ( nodeBuilder
1752 * . getNamespace ( ) ,
1753 * MOUNT_POINT_MODULE_NAME )
1756 nodeBuilder.setQname(validQName);
1759 return currentAugment;
1762 private URI namespace(final Node<?> data) {
1763 if (data instanceof NodeWrapper) {
1764 return ((NodeWrapper<?>) data).getNamespace();
1765 } else if (data != null) {
1766 return data.getNodeType().getNamespace();
1768 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1772 private String localName(final Node<?> data) {
1773 if (data instanceof NodeWrapper) {
1774 return ((NodeWrapper<?>) data).getLocalName();
1775 } else if (data != null) {
1776 return data.getNodeType().getLocalName();
1778 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1783 * @deprecated method will be removed for Lithium release
1789 private String getName(final Node<?> data) {
1790 if (data instanceof NodeWrapper) {
1791 return ((NodeWrapper<?>) data).getLocalName();
1792 } else if (data != null) {
1793 return data.getNodeType().getLocalName();
1795 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1799 private TypeDef typeDefinition(final TypeDefinition<?> type, final QName nodeQName) {
1800 TypeDefinition<?> baseType = type;
1801 QName qName = nodeQName;
1802 while (baseType.getBaseType() != null) {
1803 if (baseType instanceof ExtendedType) {
1804 qName = baseType.getQName();
1806 baseType = baseType.getBaseType();
1809 return new TypeDef(baseType, qName);
1813 private TypeDef typeDefinition(final DataSchemaNode node) {
1814 if (node instanceof LeafListSchemaNode) {
1815 return typeDefinition(((LeafListSchemaNode)node).getType(), node.getQName());
1816 } else if (node instanceof LeafSchemaNode) {
1817 return typeDefinition(((LeafSchemaNode)node).getType(), node.getQName());
1818 } else if (node instanceof AnyXmlSchemaNode) {
1821 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
1825 private CompositeNode datastoreNormalizedNodeToCompositeNode(final NormalizedNode<?, ?> dataNode, final DataSchemaNode schema) {
1826 Node<?> nodes = null;
1827 if (dataNode == null) {
1828 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.DATA_MISSING,
1829 "No data was found."));
1831 nodes = DataNormalizer.toLegacy(dataNode);
1832 if (nodes != null) {
1833 if (nodes instanceof CompositeNode) {
1834 return (CompositeNode) nodes;
1836 LOG.error("The node " + dataNode.getNodeType() + " couldn't be transformed to compositenode.");
1839 LOG.error("Top level node isn't of type Container or List schema node but "
1840 + schema.getClass().getSimpleName());
1843 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1844 "It wasn't possible to correctly interpret data."));
1847 private NormalizedNode<?, ?> compositeNodeToDatastoreNormalizedNode(final CompositeNode compNode,
1848 final DataSchemaNode schema) {
1849 final List<Node<?>> lst = new ArrayList<Node<?>>();
1851 if (schema instanceof ContainerSchemaNode) {
1852 return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser()
1853 .parse(lst, (ContainerSchemaNode) schema);
1854 } else if (schema instanceof ListSchemaNode) {
1855 return CnSnToNormalizedNodeParserFactory.getInstance().getMapEntryNodeParser()
1856 .parse(lst, (ListSchemaNode) schema);
1859 LOG.error("Top level isn't of type container, list, leaf schema node but " + schema.getClass().getSimpleName());
1861 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1862 "It wasn't possible to translate specified data to datastore readable form."));
1865 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1866 final InstanceIdentifierContext iiWithSchemaNode) {
1867 return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false);
1870 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1871 final InstanceIdentifierContext iiWithSchemaNode, final boolean unwrapLastListNode) {
1872 return new InstanceIdentifierContext(instanceIdentifierToReadableFormForNormalizeNode(
1873 iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(),
1874 iiWithSchemaNode.getMountPoint(),iiWithSchemaNode.getSchemaContext());
1877 private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode(
1878 final YangInstanceIdentifier instIdentifier, final boolean unwrapLastListNode) {
1879 Preconditions.checkNotNull(instIdentifier, "Instance identifier can't be null");
1880 final List<PathArgument> result = new ArrayList<PathArgument>();
1881 final Iterator<PathArgument> iter = instIdentifier.getPathArguments().iterator();
1882 while (iter.hasNext()) {
1883 final PathArgument pathArgument = iter.next();
1884 if (pathArgument instanceof NodeIdentifierWithPredicates && (iter.hasNext() || unwrapLastListNode)) {
1885 result.add(new YangInstanceIdentifier.NodeIdentifier(pathArgument.getNodeType()));
1887 result.add(pathArgument);
1889 return YangInstanceIdentifier.create(result);
1892 private CompositeNodeWrapper topLevelElementAsCompositeNodeWrapper(final NodeWrapper<?> node,
1893 final DataSchemaNode schemaNode) {
1894 if (node instanceof CompositeNodeWrapper) {
1895 return (CompositeNodeWrapper) node;
1896 } else if (node instanceof SimpleNodeWrapper && isDataContainerNode(schemaNode)) {
1897 final SimpleNodeWrapper simpleNodeWrapper = (SimpleNodeWrapper) node;
1898 return new CompositeNodeWrapper(namespace(simpleNodeWrapper), localName(simpleNodeWrapper));
1901 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1902 "Top level element has to be composite node or has to represent data container node."));
1905 private boolean isDataContainerNode(final DataSchemaNode schemaNode) {
1906 if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode) {
1912 public BigInteger getOperationalReceived() {
1913 // TODO Auto-generated method stub
1917 private MapNode makeModuleMapNode(final Set<Module> modules) {
1918 Preconditions.checkNotNull(modules);
1919 final Module restconfModule = getRestconfModule();
1920 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
1921 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
1922 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
1924 final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
1925 .mapBuilder((ListSchemaNode) moduleSchemaNode);
1927 for (final Module module : modules) {
1928 listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode));
1930 return listModuleBuilder.build();
1933 protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) {
1934 Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode,
1935 "moduleSchemaNode has to be of type ListSchemaNode");
1936 final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode;
1937 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = Builders
1938 .mapEntryBuilder(listModuleSchemaNode);
1940 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1941 (listModuleSchemaNode), "name");
1942 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1943 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1944 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName())
1947 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1948 (listModuleSchemaNode), "revision");
1949 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1950 Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode);
1951 final String revision = REVISION_FORMAT.format(module.getRevision());
1952 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision)
1955 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1956 (listModuleSchemaNode), "namespace");
1957 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1958 Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode);
1959 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode)
1960 .withValue(module.getNamespace().toString()).build());
1962 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1963 (listModuleSchemaNode), "feature");
1964 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1965 Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode);
1966 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> featuresBuilder = Builders
1967 .leafSetBuilder((LeafListSchemaNode) featureSchemaNode);
1968 for (final FeatureDefinition feature : module.getFeatures()) {
1969 featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode))
1970 .withValue(feature.getQName().getLocalName()).build());
1972 moduleNodeValues.withChild(featuresBuilder.build());
1974 return moduleNodeValues.build();
1977 protected MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamSchemaNode) {
1978 Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode,
1979 "streamSchemaNode has to be of type ListSchemaNode");
1980 final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode;
1981 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
1982 .mapEntryBuilder(listStreamSchemaNode);
1984 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1985 (listStreamSchemaNode), "name");
1986 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1987 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1988 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName)
1991 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1992 (listStreamSchemaNode), "description");
1993 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1994 Preconditions.checkState(descriptionSchemaNode instanceof LeafSchemaNode);
1995 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode)
1996 .withValue("DESCRIPTION_PLACEHOLDER").build());
1998 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1999 (listStreamSchemaNode), "replay-support");
2000 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2001 Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode);
2002 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode)
2003 .withValue(Boolean.valueOf(true)).build());
2005 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2006 (listStreamSchemaNode), "replay-log-creation-time");
2007 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2008 Preconditions.checkState(replayLogCreationTimeSchemaNode instanceof LeafSchemaNode);
2009 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode)
2010 .withValue("").build());
2012 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2013 (listStreamSchemaNode), "events");
2014 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2015 Preconditions.checkState(eventsSchemaNode instanceof LeafSchemaNode);
2016 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode)
2017 .withValue("").build());
2019 return streamNodeValues.build();