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 RpcDefinition resultNodeSchema = null;
553 final NormalizedNode<?, ?> resultData = result.getResult();
554 if (result != null && result.getResult() != null) {
555 resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode();
558 return new NormalizedNodeContext(new InstanceIdentifierContext<RpcDefinition>(null,
559 resultNodeSchema, mountPoint, schemaContext), resultData);
562 private DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
563 if (response == null) {
567 final DOMRpcResult retValue = response.get();
568 if (retValue.getErrors() == null || retValue.getErrors().isEmpty()) {
571 throw new RestconfDocumentedException("RpcError message", null, retValue.getErrors());
573 catch (final InterruptedException e) {
574 throw new RestconfDocumentedException(
575 "The operation was interrupted while executing and did not complete.", ErrorType.RPC,
576 ErrorTag.PARTIAL_OPERATION);
578 catch (final ExecutionException e) {
579 Throwable cause = e.getCause();
580 if (cause instanceof CancellationException) {
581 throw new RestconfDocumentedException("The operation was cancelled while executing.", ErrorType.RPC,
582 ErrorTag.PARTIAL_OPERATION);
583 } else if (cause != null) {
584 while (cause.getCause() != null) {
585 cause = cause.getCause();
588 if (cause instanceof IllegalArgumentException) {
589 throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
590 ErrorTag.INVALID_VALUE);
593 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
596 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
602 private void validateInput(final SchemaNode inputSchema, final NormalizedNodeContext payload) {
603 if (inputSchema != null && payload.getData() == null) {
604 // expected a non null payload
605 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
606 } else if (inputSchema == null && payload.getData() != null) {
607 // did not expect any input
608 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
612 // TODO: Validate "mandatory" and "config" values here??? Or should those be
614 // validate in a more central location inside MD-SAL core.
619 * @deprecated method will be removed for Lithium release
625 private void validateInput(final DataSchemaNode inputSchema, final Node<?> payload) {
626 if (inputSchema != null && payload == null) {
627 // expected a non null payload
628 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
629 } else if (inputSchema == null && payload != null) {
630 // did not expect any input
631 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
635 // TODO: Validate "mandatory" and "config" values here??? Or should those be
637 // validate in a more central location inside MD-SAL core.
641 private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
642 final ContainerNode value = (ContainerNode) payload.getData();
643 final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
644 final Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(new NodeIdentifier(
645 QName.create(payload.getInstanceIdentifierContext().getSchemaNode().getQName(), "path")));
646 final Object pathValue = path.isPresent() ? path.get().getValue() : null;
648 if (!(pathValue instanceof YangInstanceIdentifier)) {
649 throw new RestconfDocumentedException("Instance identifier was not normalized correctly.",
650 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
653 final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
654 String streamName = null;
655 if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
656 final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
658 LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class,
659 DATASTORE_PARAM_NAME);
660 datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
662 DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
663 scope = scope == null ? DEFAULT_SCOPE : scope;
665 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
666 + "/scope=" + scope);
669 if (Strings.isNullOrEmpty(streamName)) {
670 throw new RestconfDocumentedException(
671 "Path is empty or contains value node which is not Container or List build-in type.",
672 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
675 final QName outputQname = QName.create(rpcQName, "output");
676 final QName streamNameQname = QName.create(rpcQName, "stream-name");
678 final ContainerNode output = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
679 .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
681 if (!Notificator.existListenerFor(streamName)) {
682 final YangInstanceIdentifier normalizedPathIdentifier = controllerContext.toNormalized(pathIdentifier);
683 Notificator.createListener(normalizedPathIdentifier, streamName);
686 final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
688 return Futures.immediateCheckedFuture(defaultDOMRpcResult);
692 public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
693 if (StringUtils.isNotBlank(noPayload)) {
694 throw new RestconfDocumentedException("Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
697 String identifierEncoded = null;
698 DOMMountPoint mountPoint = null;
699 final SchemaContext schemaContext;
700 if (identifier.contains(ControllerContext.MOUNT)) {
701 // mounted RPC call - look up mount instance.
702 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
703 mountPoint = mountPointId.getMountPoint();
704 schemaContext = mountPoint.getSchemaContext();
705 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
706 + ControllerContext.MOUNT.length() + 1;
707 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
708 identifierEncoded = remoteRpcName;
710 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
711 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
712 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
713 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
715 identifierEncoded = identifier;
716 schemaContext = controllerContext.getGlobalSchema();
719 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
721 RpcDefinition rpc = null;
722 if (mountPoint == null) {
723 rpc = controllerContext.getRpcDefinition(identifierDecoded);
725 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
729 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
732 if (rpc.getInput() != null) {
733 // FIXME : find a correct Error from specification
734 throw new IllegalStateException("RPC " + rpc + " does'n need input value!");
737 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
738 if (mountPoint != null) {
739 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
740 if ( ! mountRpcServices.isPresent()) {
741 throw new RestconfDocumentedException("Rpc service is missing.");
743 response = mountRpcServices.get().invokeRpc(rpc.getPath(), null);
745 response = broker.invokeRpc(rpc.getPath(), null);
748 final DOMRpcResult result = checkRpcResponse(response);
750 DataSchemaNode resultNodeSchema = null;
751 NormalizedNode<?, ?> resultData = null;
752 if (result != null && result.getResult() != null) {
753 resultData = result.getResult();
754 final ContainerSchemaNode rpcDataSchemaNode =
755 SchemaContextUtil.getRpcDataSchema(schemaContext, rpc.getOutput().getPath());
756 resultNodeSchema = rpcDataSchemaNode.getDataChildByName(result.getResult().getNodeType());
759 return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
760 schemaContext), resultData);
763 private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
764 String identifierEncoded = null;
765 DOMMountPoint mountPoint = null;
766 if (identifier.contains(ControllerContext.MOUNT)) {
767 // mounted RPC call - look up mount instance.
768 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
769 mountPoint = mountPointId.getMountPoint();
771 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
772 + ControllerContext.MOUNT.length() + 1;
773 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
774 identifierEncoded = remoteRpcName;
776 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
777 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
778 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
779 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
781 identifierEncoded = identifier;
784 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
786 RpcDefinition rpc = null;
787 if (mountPoint == null) {
788 rpc = controllerContext.getRpcDefinition(identifierDecoded);
790 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
794 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
797 if (mountPoint == null) {
798 return new BrokerRpcExecutor(rpc, broker);
800 return new MountPointRpcExecutor(rpc, mountPoint);
805 private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
806 final String[] splittedIdentifier = identifierDecoded.split(":");
807 if (splittedIdentifier.length != 2) {
808 throw new RestconfDocumentedException(identifierDecoded
809 + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION,
810 ErrorTag.INVALID_VALUE);
812 for (final Module module : schemaContext.getModules()) {
813 if (module.getName().equals(splittedIdentifier[0])) {
814 for (final RpcDefinition rpcDefinition : module.getRpcs()) {
815 if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) {
816 return rpcDefinition;
825 * @deprecated method will be removed for Lithium release
828 private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload, final boolean prettyPrint) {
829 if (rpcExecutor == null) {
830 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
833 CompositeNode rpcRequest = null;
834 final RpcDefinition rpc = rpcExecutor.getRpcDefinition();
835 final QName rpcName = rpc.getQName();
837 if (payload == null) {
838 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
840 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
841 final List<Node<?>> input = Collections.<Node<?>> singletonList(value);
842 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
845 final RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
847 checkRpcSuccessAndThrowException(rpcResult);
849 if (rpcResult.getResult() == null) {
853 if (rpc.getOutput() == null) {
854 return null; // no output, nothing to send back.
857 return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null, prettyPrint);
860 private void checkRpcSuccessAndThrowException(final RpcResult<CompositeNode> rpcResult) {
861 if (rpcResult.isSuccessful() == false) {
863 throw new RestconfDocumentedException("The operation was not successful", null,
864 rpcResult.getErrors());
869 public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
870 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
871 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
872 NormalizedNode<?, ?> data = null;
873 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
874 if (mountPoint != null) {
875 data = broker.readConfigurationData(mountPoint, normalizedII);
877 data = broker.readConfigurationData(normalizedII);
880 throw new RestconfDocumentedException(
881 "Request could not be completed because the relevant data model content does not exist.",
882 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
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 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
935 if (mountPoint != null) {
936 data = broker.readOperationalData(mountPoint, normalizedII);
938 data = broker.readOperationalData(normalizedII);
941 throw new RestconfDocumentedException(
942 "Request could not be completed because the relevant data model content does not exist.",
943 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
945 return new NormalizedNodeContext(iiWithData, data);
948 private boolean parsePrettyPrintParameter(final UriInfo info) {
949 final String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
950 return Boolean.parseBoolean(param);
954 public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
955 Preconditions.checkNotNull(identifier);
956 final InstanceIdentifierContext<DataSchemaNode> iiWithData =
957 (InstanceIdentifierContext<DataSchemaNode>) payload.getInstanceIdentifierContext();
959 validateInput(iiWithData.getSchemaNode(), payload);
960 validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier());
961 validateListKeysEqualityInPayloadAndUri(iiWithData, payload.getData());
963 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
964 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
967 * There is a small window where another write transaction could be updating the same data
968 * simultaneously and we get an OptimisticLockFailedException. This error is likely
969 * transient and The WriteTransaction#submit API docs state that a retry will likely
970 * succeed. So we'll try again if that scenario occurs. If it fails a third time then it
971 * probably will never succeed so we'll fail in that case.
973 * By retrying we're attempting to hide the internal implementation of the data store and
974 * how it handles concurrent updates from the restconf client. The client has instructed us
975 * to put the data and we should make every effort to do so without pushing optimistic lock
976 * failures back to the client and forcing them to handle it via retry (and having to
977 * document the behavior).
982 if (mountPoint != null) {
983 broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
985 broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet();
989 } catch (final TransactionCommitFailedException e) {
990 if(e instanceof OptimisticLockFailedException) {
992 LOG.debug("Got OptimisticLockFailedException on last try - failing");
993 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
996 LOG.debug("Got OptimisticLockFailedException - trying again");
998 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
1003 return Response.status(Status.OK).build();
1006 private void validateTopLevelNodeName(final NormalizedNodeContext node,
1007 final YangInstanceIdentifier identifier) {
1009 final String payloadName = node.getData().getNodeType().getLocalName();
1010 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1013 if ( ! pathArguments.hasNext()) {
1015 if ( ! node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) {
1016 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1017 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1021 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1022 if ( ! payloadName.equals(identifierName)) {
1023 throw new RestconfDocumentedException("Payload name (" + payloadName
1024 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1025 ErrorTag.MALFORMED_MESSAGE);
1031 * @deprecated method will be removed for Lithium release
1037 private void validateTopLevelNodeName(final Node<?> node,
1038 final YangInstanceIdentifier identifier) {
1039 final String payloadName = getName(node);
1040 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1043 if (!pathArguments.hasNext()) {
1045 if (!node.getNodeType().equals(NETCONF_BASE_QNAME)) {
1046 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1047 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1051 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1052 if (!payloadName.equals(identifierName)) {
1053 throw new RestconfDocumentedException("Payload name (" + payloadName
1054 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1055 ErrorTag.MALFORMED_MESSAGE);
1061 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1063 * @throws RestconfDocumentedException
1064 * if key values or key count in payload and URI isn't equal
1067 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext<DataSchemaNode> iiWithData,
1068 final NormalizedNode<?, ?> payload) {
1069 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1070 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1071 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1072 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1073 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1075 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1081 * @deprecated method will be removed for Lithium release
1083 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1085 * @throws RestconfDocumentedException
1086 * if key values or key count in payload and URI isn't equal
1090 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
1091 final CompositeNode payload) {
1092 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1093 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1094 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1095 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1096 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1098 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1103 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final NormalizedNode<?, ?> payload,
1104 final List<QName> keyDefinitions) {
1105 for (final QName keyDefinition : keyDefinitions) {
1106 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1107 // should be caught during parsing URI to InstanceIdentifier
1108 if (uriKeyValue == null) {
1109 final String errMsg = "Missing key " + keyDefinition + " in URI.";
1110 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1112 // TODO thing about the possibility to fix
1113 // final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1114 // if (payloadKeyValues.isEmpty()) {
1115 // final String errMsg = "Missing key " + keyDefinition.getLocalName() + " in the message body.";
1116 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1119 // final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1120 // if (!uriKeyValue.equals(payloadKeyValue)) {
1121 // final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
1122 // "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. ";
1123 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1129 * @deprecated method will be removed for Lithium release
1131 * @param uriKeyValues
1133 * @param keyDefinitions
1136 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final CompositeNode payload,
1137 final List<QName> keyDefinitions) {
1138 for (final QName keyDefinition : keyDefinitions) {
1139 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1140 // should be caught during parsing URI to InstanceIdentifier
1141 if (uriKeyValue == null) {
1142 throw new RestconfDocumentedException("Missing key " + keyDefinition + " in URI.", ErrorType.PROTOCOL,
1143 ErrorTag.DATA_MISSING);
1145 final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1146 if (payloadKeyValues.isEmpty()) {
1147 throw new RestconfDocumentedException("Missing key " + keyDefinition.getLocalName()
1148 + " in the message body.", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1151 final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1152 if (!uriKeyValue.equals(payloadKeyValue)) {
1153 throw new RestconfDocumentedException("The value '" + uriKeyValue + "' for key '"
1154 + keyDefinition.getLocalName() + "' specified in the URI doesn't match the value '"
1155 + payloadKeyValue + "' specified in the message body. ", ErrorType.PROTOCOL,
1156 ErrorTag.INVALID_VALUE);
1162 public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
1163 return createConfigurationData(payload, uriInfo);
1166 // FIXME create RestconfIdetifierHelper and move this method there
1167 private YangInstanceIdentifier checkConsistencyOfNormalizedNodeContext(final NormalizedNodeContext payload) {
1168 Preconditions.checkArgument(payload != null);
1169 Preconditions.checkArgument(payload.getData() != null);
1170 Preconditions.checkArgument(payload.getData().getNodeType() != null);
1171 Preconditions.checkArgument(payload.getInstanceIdentifierContext() != null);
1172 Preconditions.checkArgument(payload.getInstanceIdentifierContext().getInstanceIdentifier() != null);
1174 final QName payloadNodeQname = payload.getData().getNodeType();
1175 final YangInstanceIdentifier yangIdent = payload.getInstanceIdentifierContext().getInstanceIdentifier();
1176 if (payloadNodeQname.compareTo(yangIdent.getLastPathArgument().getNodeType()) > 0) {
1179 final InstanceIdentifierContext parentContext = payload.getInstanceIdentifierContext();
1180 final SchemaNode parentSchemaNode = parentContext.getSchemaNode();
1181 if(parentSchemaNode instanceof DataNodeContainer) {
1182 final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode;
1183 for (final DataSchemaNode child : cast.getChildNodes()) {
1184 if (payloadNodeQname.compareTo(child.getQName()) == 0) {
1185 return YangInstanceIdentifier.builder(yangIdent).node(child.getQName()).build();
1189 if (parentSchemaNode instanceof RpcDefinition) {
1192 final String errMsg = "Error parsing input: DataSchemaNode has not children";
1193 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1197 public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
1198 if (payload == null) {
1199 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1202 final URI payloadNS = payload.getData().getNodeType().getNamespace();
1203 if (payloadNS == null) {
1204 throw new RestconfDocumentedException(
1205 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
1206 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1209 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
1210 final InstanceIdentifierContext<DataSchemaNode> iiWithData = (InstanceIdentifierContext<DataSchemaNode>) payload.getInstanceIdentifierContext();
1211 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1212 final YangInstanceIdentifier resultII;
1214 if (mountPoint != null) {
1215 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData()).checkedGet();
1217 broker.commitConfigurationDataPost(normalizedII, payload.getData()).checkedGet();
1219 } catch(final RestconfDocumentedException e) {
1221 } catch (final Exception e) {
1222 throw new RestconfDocumentedException("Error creating data", e);
1225 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
1226 // FIXME: Provide path to result.
1227 final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII);
1228 if (location != null) {
1229 responseBuilder.location(location);
1231 return responseBuilder.build();
1234 private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
1235 final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
1236 uriBuilder.path("config");
1238 uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
1239 } catch (final Exception e) {
1240 LOG.debug("Location for instance identifier"+normalizedII+"wasn't created", e);
1243 return uriBuilder.build();
1247 public Response deleteConfigurationData(final String identifier) {
1248 final InstanceIdentifierContext<DataSchemaNode> iiWithData = controllerContext.toInstanceIdentifier(identifier);
1249 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
1250 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1253 if (mountPoint != null) {
1254 broker.commitConfigurationDataDelete(mountPoint, normalizedII);
1256 broker.commitConfigurationDataDelete(normalizedII).get();
1258 } catch (final Exception e) {
1259 final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
1260 Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
1261 if (searchedException.isPresent()) {
1262 throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
1264 throw new RestconfDocumentedException("Error while deleting data", e);
1266 return Response.status(Status.OK).build();
1270 * Subscribes to some path in schema context (stream) to listen on changes on this stream.
1272 * Additional parameters for subscribing to stream are loaded via rpc input parameters:
1274 * <li>datastore</li> - default CONFIGURATION (other values of {@link LogicalDatastoreType} enum type)
1275 * <li>scope</li> - default BASE (other values of {@link DataChangeScope})
1279 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
1280 final String streamName = Notificator.createStreamNameFromUri(identifier);
1281 if (Strings.isNullOrEmpty(streamName)) {
1282 throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1285 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
1286 if (listener == null) {
1287 throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
1290 final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
1291 final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
1292 paramToValues.get(DATASTORE_PARAM_NAME));
1293 if (datastore == null) {
1294 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)",
1295 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1297 final DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME));
1298 if (scope == null) {
1299 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)",
1300 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1303 broker.registerToListenDataChanges(datastore, scope, listener);
1305 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
1306 int notificationPort = NOTIFICATION_PORT;
1308 final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
1309 notificationPort = webSocketServerInstance.getPort();
1310 } catch (final NullPointerException e) {
1311 WebSocketServer.createInstance(NOTIFICATION_PORT);
1313 final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
1314 final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
1316 return Response.status(Status.OK).location(uriToWebsocketServer).build();
1320 * Load parameter for subscribing to stream from input composite node
1324 * @return enum object if its string value is equal to {@code paramName}. In other cases null.
1326 private <T> T parseEnumTypeParameter(final ContainerNode value, final Class<T> classDescriptor,
1327 final String paramName) {
1328 final QNameModule salRemoteAugment = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
1329 EVENT_SUBSCRIPTION_AUGMENT_REVISION);
1330 final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode = value.getChild(new NodeIdentifier(
1331 QName.create(salRemoteAugment, paramName)));
1332 if (!enumNode.isPresent()) {
1335 final Object rawValue = enumNode.get().getValue();
1336 if (!(rawValue instanceof String)) {
1340 return resolveAsEnum(classDescriptor, (String) rawValue);
1344 * Checks whether {@code value} is one of the string representation of enumeration {@code classDescriptor}
1346 * @return enum object if string value of {@code classDescriptor} enumeration is equal to {@code value}. Other cases
1349 private <T> T parserURIEnumParameter(final Class<T> classDescriptor, final String value) {
1350 if (Strings.isNullOrEmpty(value)) {
1353 return resolveAsEnum(classDescriptor, value);
1356 private <T> T resolveAsEnum(final Class<T> classDescriptor, final String value) {
1357 final T[] enumConstants = classDescriptor.getEnumConstants();
1358 if (enumConstants != null) {
1359 for (final T enm : classDescriptor.getEnumConstants()) {
1360 if (((Enum<?>) enm).name().equals(value)) {
1368 private Map<String, String> resolveValuesFromUri(final String uri) {
1369 final Map<String, String> result = new HashMap<>();
1370 final String[] tokens = uri.split("/");
1371 for (int i = 1; i < tokens.length; i++) {
1372 final String[] parameterTokens = tokens[i].split("=");
1373 if (parameterTokens.length == 2) {
1374 result.put(parameterTokens[0], parameterTokens[1]);
1380 private Module findModule(final DOMMountPoint mountPoint, final Node<?> data) {
1381 Module module = null;
1382 if (data instanceof NodeWrapper) {
1383 module = findModule(mountPoint, (NodeWrapper<?>) data);
1384 } else if (data != null) {
1385 final URI namespace = data.getNodeType().getNamespace();
1386 if (mountPoint != null) {
1387 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1389 module = controllerContext.findModuleByNamespace(namespace);
1392 if (module != null) {
1395 throw new RestconfDocumentedException(
1396 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
1397 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1400 private Module findModule(final DOMMountPoint mountPoint, final NodeWrapper<?> data) {
1401 final URI namespace = data.getNamespace();
1402 Preconditions.<URI> checkNotNull(namespace);
1404 Module module = null;
1405 if (mountPoint != null) {
1406 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1407 if (module == null) {
1408 module = controllerContext.findModuleByName(mountPoint, namespace.toString());
1411 module = controllerContext.findModuleByNamespace(namespace);
1412 if (module == null) {
1413 module = controllerContext.findModuleByName(namespace.toString());
1420 private InstanceIdentifierContext addLastIdentifierFromData(final InstanceIdentifierContext identifierWithSchemaNode,
1421 final CompositeNode data, final DataSchemaNode schemaOfData, final SchemaContext schemaContext) {
1422 YangInstanceIdentifier instanceIdentifier = null;
1423 if (identifierWithSchemaNode != null) {
1424 instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
1427 final YangInstanceIdentifier iiOriginal = instanceIdentifier;
1428 InstanceIdentifierBuilder iiBuilder = null;
1429 if (iiOriginal == null) {
1430 iiBuilder = YangInstanceIdentifier.builder();
1432 iiBuilder = YangInstanceIdentifier.builder(iiOriginal);
1435 if ((schemaOfData instanceof ListSchemaNode)) {
1436 final HashMap<QName, Object> keys = resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
1437 iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
1439 iiBuilder.node(schemaOfData.getQName());
1442 final YangInstanceIdentifier instance = iiBuilder.toInstance();
1443 DOMMountPoint mountPoint = null;
1444 final SchemaContext schemaCtx = null;
1445 if (identifierWithSchemaNode != null) {
1446 mountPoint = identifierWithSchemaNode.getMountPoint();
1449 return new InstanceIdentifierContext(instance, schemaOfData, mountPoint,schemaContext);
1452 private HashMap<QName, Object> resolveKeysFromData(final ListSchemaNode listNode, final CompositeNode dataNode) {
1453 final HashMap<QName, Object> keyValues = new HashMap<QName, Object>();
1454 final List<QName> _keyDefinition = listNode.getKeyDefinition();
1455 for (final QName key : _keyDefinition) {
1456 SimpleNode<? extends Object> head = null;
1457 final String localName = key.getLocalName();
1458 final List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
1459 if (simpleNodesByName != null) {
1460 head = Iterables.getFirst(simpleNodesByName, null);
1463 Object dataNodeKeyValueObject = null;
1465 dataNodeKeyValueObject = head.getValue();
1468 if (dataNodeKeyValueObject == null) {
1469 throw new RestconfDocumentedException("Data contains list \"" + dataNode.getNodeType().getLocalName()
1470 + "\" which does not contain key: \"" + key.getLocalName() + "\"", ErrorType.PROTOCOL,
1471 ErrorTag.INVALID_VALUE);
1474 keyValues.put(key, dataNodeKeyValueObject);
1480 private boolean endsWithMountPoint(final String identifier) {
1481 return identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/");
1484 private boolean representsMountPointRootData(final Node<?> data) {
1485 final URI namespace = namespace(data);
1486 return (SchemaContext.NAME.getNamespace().equals(namespace) /*
1487 * || MOUNT_POINT_MODULE_NAME .equals( namespace .
1490 && SchemaContext.NAME.getLocalName().equals(localName(data));
1493 private String addMountPointIdentifier(final String identifier) {
1494 final boolean endsWith = identifier.endsWith("/");
1496 return (identifier + ControllerContext.MOUNT);
1499 return identifier + "/" + ControllerContext.MOUNT;
1503 * @deprecated method will be removed in Lithium release
1504 * we don't wish to use Node and CompositeNode anywhere
1507 public CompositeNode normalizeNode(final Node<?> node, final DataSchemaNode schema, final DOMMountPoint mountPoint) {
1508 if (schema == null) {
1509 final String localName = node == null ? null :
1510 node instanceof NodeWrapper ? ((NodeWrapper<?>)node).getLocalName() :
1511 node.getNodeType().getLocalName();
1513 throw new RestconfDocumentedException("Data schema node was not found for " + localName,
1514 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1517 if (!(schema instanceof DataNodeContainer)) {
1518 throw new RestconfDocumentedException("Root element has to be container or list yang datatype.",
1519 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1522 if ((node instanceof NodeWrapper<?>)) {
1523 NodeWrapper<?> nodeWrap = (NodeWrapper<?>) node;
1524 final boolean isChangeAllowed = ((NodeWrapper<?>) node).isChangeAllowed();
1525 if (isChangeAllowed) {
1526 nodeWrap = topLevelElementAsCompositeNodeWrapper((NodeWrapper<?>) node, schema);
1528 this.normalizeNode(nodeWrap, schema, null, mountPoint);
1529 } catch (final IllegalArgumentException e) {
1530 final RestconfDocumentedException restconfDocumentedException = new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1531 restconfDocumentedException.addSuppressed(e);
1532 throw restconfDocumentedException;
1534 if (nodeWrap instanceof CompositeNodeWrapper) {
1535 return ((CompositeNodeWrapper) nodeWrap).unwrap();
1540 if (node instanceof CompositeNode) {
1541 return (CompositeNode) node;
1544 throw new RestconfDocumentedException("Top level element is not interpreted as composite node.",
1545 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
1548 private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1549 final QName previousAugment, final DOMMountPoint mountPoint) {
1550 if (schema == null) {
1551 throw new RestconfDocumentedException("Data has bad format.\n\"" + nodeBuilder.getLocalName()
1552 + "\" does not exist in yang schema.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1555 QName currentAugment = null;
1556 if (nodeBuilder.getQname() != null) {
1557 currentAugment = previousAugment;
1559 currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
1560 if (nodeBuilder.getQname() == null) {
1561 throw new RestconfDocumentedException(
1562 "Data has bad format.\nIf data is in XML format then namespace for \""
1563 + nodeBuilder.getLocalName() + "\" should be \"" + schema.getQName().getNamespace()
1564 + "\".\n" + "If data is in JSON format then module name for \""
1565 + nodeBuilder.getLocalName() + "\" should be corresponding to namespace \""
1566 + schema.getQName().getNamespace() + "\".", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1570 if (nodeBuilder instanceof CompositeNodeWrapper) {
1571 if (schema instanceof DataNodeContainer) {
1572 normalizeCompositeNode((CompositeNodeWrapper) nodeBuilder, (DataNodeContainer) schema, mountPoint,
1574 } else if (schema instanceof AnyXmlSchemaNode) {
1575 normalizeAnyXmlNode((CompositeNodeWrapper) nodeBuilder, (AnyXmlSchemaNode) schema);
1577 } else if (nodeBuilder instanceof SimpleNodeWrapper) {
1578 normalizeSimpleNode((SimpleNodeWrapper) nodeBuilder, schema, mountPoint);
1579 } else if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1580 normalizeEmptyNode((EmptyNodeWrapper) nodeBuilder, schema);
1584 private void normalizeAnyXmlNode(final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema) {
1585 final List<NodeWrapper<?>> children = compositeNode.getValues();
1586 for (final NodeWrapper<? extends Object> child : children) {
1587 child.setNamespace(schema.getQName().getNamespace());
1588 if (child instanceof CompositeNodeWrapper) {
1589 normalizeAnyXmlNode((CompositeNodeWrapper) child, schema);
1594 private void normalizeEmptyNode(final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema) {
1595 if ((schema instanceof LeafSchemaNode)) {
1596 emptyNodeBuilder.setComposite(false);
1598 if ((schema instanceof ContainerSchemaNode)) {
1599 // FIXME: Add presence check
1600 emptyNodeBuilder.setComposite(true);
1605 private void normalizeSimpleNode(final SimpleNodeWrapper simpleNode, final DataSchemaNode schema,
1606 final DOMMountPoint mountPoint) {
1607 final Object value = simpleNode.getValue();
1608 Object inputValue = value;
1609 final TypeDef typeDef = this.typeDefinition(schema);
1610 TypeDefinition<? extends Object> typeDefinition = typeDef != null ? typeDef.typedef : null;
1612 // For leafrefs, extract the type it is pointing to
1613 if(typeDefinition instanceof LeafrefTypeDefinition) {
1614 if (schema.getQName().equals(typeDef.qName)) {
1615 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema);
1617 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), typeDef.qName);
1621 if (typeDefinition instanceof IdentityrefTypeDefinition) {
1622 inputValue = parseToIdentityValuesDTO(simpleNode, value, inputValue);
1625 Object outputValue = inputValue;
1627 if (typeDefinition != null) {
1628 final Codec<Object, Object> codec = RestCodec.from(typeDefinition, mountPoint);
1629 outputValue = codec == null ? null : codec.deserialize(inputValue);
1632 simpleNode.setValue(outputValue);
1635 private Object parseToIdentityValuesDTO(final SimpleNodeWrapper simpleNode, final Object value, Object inputValue) {
1636 if ((value instanceof String)) {
1637 inputValue = new IdentityValuesDTO(simpleNode.getNamespace().toString(), (String) value, null,
1639 } // else value is already instance of IdentityValuesDTO
1643 private void normalizeCompositeNode(final CompositeNodeWrapper compositeNodeBuilder,
1644 final DataNodeContainer schema, final DOMMountPoint mountPoint, final QName currentAugment) {
1645 final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
1646 checkNodeMultiplicityAccordingToSchema(schema, children);
1647 for (final NodeWrapper<? extends Object> child : children) {
1648 final List<DataSchemaNode> potentialSchemaNodes = ControllerContext.findInstanceDataChildrenByName(
1649 schema, child.getLocalName());
1651 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
1652 final StringBuilder builder = new StringBuilder();
1653 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1654 builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()).append("\n");
1657 throw new RestconfDocumentedException("Node \"" + child.getLocalName()
1658 + "\" is added as augment from more than one module. "
1659 + "Therefore node must have namespace (XML format) or module name (JSON format)."
1660 + "\nThe node is added as augment from modules with namespaces:\n" + builder,
1661 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1664 boolean rightNodeSchemaFound = false;
1665 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1666 if (!rightNodeSchemaFound) {
1667 final QName potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode,
1668 currentAugment, mountPoint);
1669 if (child.getQname() != null) {
1670 this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1671 rightNodeSchemaFound = true;
1676 if (!rightNodeSchemaFound) {
1677 throw new RestconfDocumentedException("Schema node \"" + child.getLocalName()
1678 + "\" was not found in module.", ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT);
1682 if ((schema instanceof ListSchemaNode)) {
1683 final ListSchemaNode listSchemaNode = (ListSchemaNode) schema;
1684 final List<QName> listKeys = listSchemaNode.getKeyDefinition();
1685 for (final QName listKey : listKeys) {
1686 boolean foundKey = false;
1687 for (final NodeWrapper<? extends Object> child : children) {
1688 if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1694 throw new RestconfDocumentedException("Missing key in URI \"" + listKey.getLocalName()
1695 + "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"", ErrorType.PROTOCOL,
1696 ErrorTag.DATA_MISSING);
1702 private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer,
1703 final List<NodeWrapper<?>> nodes) {
1704 final Map<String, Integer> equalNodeNamesToCounts = new HashMap<String, Integer>();
1705 for (final NodeWrapper<?> child : nodes) {
1706 Integer count = equalNodeNamesToCounts.get(child.getLocalName());
1707 equalNodeNamesToCounts.put(child.getLocalName(), count == null ? 1 : ++count);
1710 for (final DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) {
1711 if (childSchemaNode instanceof ContainerSchemaNode || childSchemaNode instanceof LeafSchemaNode) {
1712 final String localName = childSchemaNode.getQName().getLocalName();
1713 final Integer count = equalNodeNamesToCounts.get(localName);
1714 if (count != null && count > 1) {
1715 throw new RestconfDocumentedException("Multiple input data elements were specified for '"
1716 + childSchemaNode.getQName().getLocalName()
1717 + "'. The data for this element type can only be specified once.", ErrorType.APPLICATION,
1718 ErrorTag.BAD_ELEMENT);
1724 private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1725 final QName previousAugment, final DOMMountPoint mountPoint) {
1726 QName validQName = schema.getQName();
1727 QName currentAugment = previousAugment;
1728 if (schema.isAugmenting()) {
1729 currentAugment = schema.getQName();
1730 } else if (previousAugment != null
1731 && !Objects.equal(schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1732 validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1735 String moduleName = null;
1736 if (mountPoint == null) {
1737 moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1739 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1742 if (nodeBuilder.getNamespace() == null || Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace())
1743 || Objects.equal(nodeBuilder.getNamespace().toString(), moduleName)) {
1745 * || Note : this check is wrong -
1746 * can never be true as it compares
1747 * a URI with a String not sure what
1748 * the intention is so commented out
1749 * ... Objects . equal ( nodeBuilder
1750 * . getNamespace ( ) ,
1751 * MOUNT_POINT_MODULE_NAME )
1754 nodeBuilder.setQname(validQName);
1757 return currentAugment;
1760 private URI namespace(final Node<?> data) {
1761 if (data instanceof NodeWrapper) {
1762 return ((NodeWrapper<?>) data).getNamespace();
1763 } else if (data != null) {
1764 return data.getNodeType().getNamespace();
1766 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1770 private String localName(final Node<?> data) {
1771 if (data instanceof NodeWrapper) {
1772 return ((NodeWrapper<?>) data).getLocalName();
1773 } else if (data != null) {
1774 return data.getNodeType().getLocalName();
1776 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1781 * @deprecated method will be removed for Lithium release
1787 private String getName(final Node<?> data) {
1788 if (data instanceof NodeWrapper) {
1789 return ((NodeWrapper<?>) data).getLocalName();
1790 } else if (data != null) {
1791 return data.getNodeType().getLocalName();
1793 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1797 private TypeDef typeDefinition(final TypeDefinition<?> type, final QName nodeQName) {
1798 TypeDefinition<?> baseType = type;
1799 QName qName = nodeQName;
1800 while (baseType.getBaseType() != null) {
1801 if (baseType instanceof ExtendedType) {
1802 qName = baseType.getQName();
1804 baseType = baseType.getBaseType();
1807 return new TypeDef(baseType, qName);
1811 private TypeDef typeDefinition(final DataSchemaNode node) {
1812 if (node instanceof LeafListSchemaNode) {
1813 return typeDefinition(((LeafListSchemaNode)node).getType(), node.getQName());
1814 } else if (node instanceof LeafSchemaNode) {
1815 return typeDefinition(((LeafSchemaNode)node).getType(), node.getQName());
1816 } else if (node instanceof AnyXmlSchemaNode) {
1819 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
1823 private CompositeNode datastoreNormalizedNodeToCompositeNode(final NormalizedNode<?, ?> dataNode, final DataSchemaNode schema) {
1824 Node<?> nodes = null;
1825 if (dataNode == null) {
1826 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.DATA_MISSING,
1827 "No data was found."));
1829 nodes = DataNormalizer.toLegacy(dataNode);
1830 if (nodes != null) {
1831 if (nodes instanceof CompositeNode) {
1832 return (CompositeNode) nodes;
1834 LOG.error("The node " + dataNode.getNodeType() + " couldn't be transformed to compositenode.");
1837 LOG.error("Top level node isn't of type Container or List schema node but "
1838 + schema.getClass().getSimpleName());
1841 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1842 "It wasn't possible to correctly interpret data."));
1845 private NormalizedNode<?, ?> compositeNodeToDatastoreNormalizedNode(final CompositeNode compNode,
1846 final DataSchemaNode schema) {
1847 final List<Node<?>> lst = new ArrayList<Node<?>>();
1849 if (schema instanceof ContainerSchemaNode) {
1850 return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser()
1851 .parse(lst, (ContainerSchemaNode) schema);
1852 } else if (schema instanceof ListSchemaNode) {
1853 return CnSnToNormalizedNodeParserFactory.getInstance().getMapEntryNodeParser()
1854 .parse(lst, (ListSchemaNode) schema);
1857 LOG.error("Top level isn't of type container, list, leaf schema node but " + schema.getClass().getSimpleName());
1859 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1860 "It wasn't possible to translate specified data to datastore readable form."));
1863 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1864 final InstanceIdentifierContext iiWithSchemaNode) {
1865 return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false);
1868 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1869 final InstanceIdentifierContext iiWithSchemaNode, final boolean unwrapLastListNode) {
1870 return new InstanceIdentifierContext(instanceIdentifierToReadableFormForNormalizeNode(
1871 iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(),
1872 iiWithSchemaNode.getMountPoint(),iiWithSchemaNode.getSchemaContext());
1875 private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode(
1876 final YangInstanceIdentifier instIdentifier, final boolean unwrapLastListNode) {
1877 Preconditions.checkNotNull(instIdentifier, "Instance identifier can't be null");
1878 final List<PathArgument> result = new ArrayList<PathArgument>();
1879 final Iterator<PathArgument> iter = instIdentifier.getPathArguments().iterator();
1880 while (iter.hasNext()) {
1881 final PathArgument pathArgument = iter.next();
1882 if (pathArgument instanceof NodeIdentifierWithPredicates && (iter.hasNext() || unwrapLastListNode)) {
1883 result.add(new YangInstanceIdentifier.NodeIdentifier(pathArgument.getNodeType()));
1885 result.add(pathArgument);
1887 return YangInstanceIdentifier.create(result);
1890 private CompositeNodeWrapper topLevelElementAsCompositeNodeWrapper(final NodeWrapper<?> node,
1891 final DataSchemaNode schemaNode) {
1892 if (node instanceof CompositeNodeWrapper) {
1893 return (CompositeNodeWrapper) node;
1894 } else if (node instanceof SimpleNodeWrapper && isDataContainerNode(schemaNode)) {
1895 final SimpleNodeWrapper simpleNodeWrapper = (SimpleNodeWrapper) node;
1896 return new CompositeNodeWrapper(namespace(simpleNodeWrapper), localName(simpleNodeWrapper));
1899 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1900 "Top level element has to be composite node or has to represent data container node."));
1903 private boolean isDataContainerNode(final DataSchemaNode schemaNode) {
1904 if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode) {
1910 public BigInteger getOperationalReceived() {
1911 // TODO Auto-generated method stub
1915 private MapNode makeModuleMapNode(final Set<Module> modules) {
1916 Preconditions.checkNotNull(modules);
1917 final Module restconfModule = getRestconfModule();
1918 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
1919 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
1920 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
1922 final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
1923 .mapBuilder((ListSchemaNode) moduleSchemaNode);
1925 for (final Module module : modules) {
1926 listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode));
1928 return listModuleBuilder.build();
1931 protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) {
1932 Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode,
1933 "moduleSchemaNode has to be of type ListSchemaNode");
1934 final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode;
1935 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = Builders
1936 .mapEntryBuilder(listModuleSchemaNode);
1938 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1939 (listModuleSchemaNode), "name");
1940 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1941 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1942 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName())
1945 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1946 (listModuleSchemaNode), "revision");
1947 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1948 Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode);
1949 final String revision = REVISION_FORMAT.format(module.getRevision());
1950 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision)
1953 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1954 (listModuleSchemaNode), "namespace");
1955 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1956 Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode);
1957 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode)
1958 .withValue(module.getNamespace().toString()).build());
1960 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1961 (listModuleSchemaNode), "feature");
1962 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1963 Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode);
1964 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> featuresBuilder = Builders
1965 .leafSetBuilder((LeafListSchemaNode) featureSchemaNode);
1966 for (final FeatureDefinition feature : module.getFeatures()) {
1967 featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode))
1968 .withValue(feature.getQName().getLocalName()).build());
1970 moduleNodeValues.withChild(featuresBuilder.build());
1972 return moduleNodeValues.build();
1975 protected MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamSchemaNode) {
1976 Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode,
1977 "streamSchemaNode has to be of type ListSchemaNode");
1978 final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode;
1979 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
1980 .mapEntryBuilder(listStreamSchemaNode);
1982 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1983 (listStreamSchemaNode), "name");
1984 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1985 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1986 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName)
1989 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1990 (listStreamSchemaNode), "description");
1991 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1992 Preconditions.checkState(descriptionSchemaNode instanceof LeafSchemaNode);
1993 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode)
1994 .withValue("DESCRIPTION_PLACEHOLDER").build());
1996 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1997 (listStreamSchemaNode), "replay-support");
1998 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1999 Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode);
2000 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode)
2001 .withValue(Boolean.valueOf(true)).build());
2003 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2004 (listStreamSchemaNode), "replay-log-creation-time");
2005 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2006 Preconditions.checkState(replayLogCreationTimeSchemaNode instanceof LeafSchemaNode);
2007 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode)
2008 .withValue("").build());
2010 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2011 (listStreamSchemaNode), "events");
2012 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2013 Preconditions.checkState(eventsSchemaNode instanceof LeafSchemaNode);
2014 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode)
2015 .withValue("").build());
2017 return streamNodeValues.build();