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.collect.Sets;
22 import com.google.common.util.concurrent.CheckedFuture;
23 import com.google.common.util.concurrent.Futures;
24 import java.math.BigInteger;
26 import java.net.URISyntaxException;
27 import java.text.ParseException;
28 import java.text.SimpleDateFormat;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.Date;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.List;
38 import java.util.concurrent.CancellationException;
39 import java.util.concurrent.ExecutionException;
40 import javax.ws.rs.core.Response;
41 import javax.ws.rs.core.Response.ResponseBuilder;
42 import javax.ws.rs.core.Response.Status;
43 import javax.ws.rs.core.UriBuilder;
44 import javax.ws.rs.core.UriInfo;
45 import org.apache.commons.lang3.StringUtils;
46 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
47 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
48 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
49 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
50 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
51 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
52 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
53 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
54 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
55 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
56 import org.opendaylight.controller.sal.rest.api.Draft02;
57 import org.opendaylight.controller.sal.rest.api.RestconfService;
58 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
59 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
60 import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
61 import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
62 import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
63 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
64 import org.opendaylight.controller.sal.streams.listeners.Notificator;
65 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
66 import org.opendaylight.yangtools.concepts.Codec;
67 import org.opendaylight.yangtools.yang.common.QName;
68 import org.opendaylight.yangtools.yang.common.QNameModule;
69 import org.opendaylight.yangtools.yang.common.RpcResult;
70 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
71 import org.opendaylight.yangtools.yang.data.api.Node;
72 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
75 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
76 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
77 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
78 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
80 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
81 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
82 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
83 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
84 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
85 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
86 import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
87 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
88 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
89 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
90 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
91 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
92 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
93 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
94 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
95 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
96 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
97 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
98 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
99 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
100 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
101 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
102 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
103 import org.opendaylight.yangtools.yang.model.api.Module;
104 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
105 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
106 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
107 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
108 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
109 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
110 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
111 import org.opendaylight.yangtools.yang.model.util.EmptyType;
112 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
113 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
114 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
115 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
116 import org.slf4j.Logger;
117 import org.slf4j.LoggerFactory;
119 public class RestconfImpl implements RestconfService {
121 private enum UriParameters {
122 PRETTY_PRINT("prettyPrint"),
125 private String uriParameterName;
127 UriParameters(final String uriParameterName) {
128 this.uriParameterName = uriParameterName;
132 public String toString() {
133 return uriParameterName;
137 private static class TypeDef {
138 public final TypeDefinition<? extends Object> typedef;
139 public final QName qName;
141 TypeDef(final TypeDefinition<? extends Object> typedef, final QName qName) {
142 this.typedef = typedef;
147 private final static RestconfImpl INSTANCE = new RestconfImpl();
149 private static final int NOTIFICATION_PORT = 8181;
151 private static final int CHAR_NOT_FOUND = -1;
153 private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
155 private final static SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
157 private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
159 private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
161 private BrokerFacade broker;
163 private ControllerContext controllerContext;
165 private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class);
167 private static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE;
169 private static final LogicalDatastoreType DEFAULT_DATASTORE = LogicalDatastoreType.CONFIGURATION;
171 private static final URI NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = URI.create("urn:sal:restconf:event:subscription");
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;
183 private static final QNameModule SAL_REMOTE_AUGMENT;
185 private static final YangInstanceIdentifier.AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER;
189 final Date eventSubscriptionAugRevision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
190 NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME );
191 SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
192 eventSubscriptionAugRevision);
193 SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"),
194 QName.create(SAL_REMOTE_AUGMENT, "datastore")));
195 } catch (final ParseException e) {
196 throw new RestconfDocumentedException(
197 "It wasn't possible to convert revision date of sal-remote-augment to date", ErrorType.APPLICATION,
198 ErrorTag.OPERATION_FAILED);
199 } catch (final URISyntaxException e) {
200 throw new RestconfDocumentedException(
201 "It wasn't possible to create instance of URI class with "+NETCONF_BASE+" URI", ErrorType.APPLICATION,
202 ErrorTag.OPERATION_FAILED);
206 public void setBroker(final BrokerFacade broker) {
207 this.broker = broker;
210 public void setControllerContext(final ControllerContext controllerContext) {
211 this.controllerContext = controllerContext;
214 private RestconfImpl() {
217 public static RestconfImpl getInstance() {
222 public NormalizedNodeContext getModules(final UriInfo uriInfo) {
223 final Set<Module> allModules = controllerContext.getAllModules();
224 final MapNode allModuleMap = makeModuleMapNode(allModules);
226 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
228 final Module restconfModule = getRestconfModule();
229 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
230 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
231 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
233 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
234 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
235 moduleContainerBuilder.withChild(allModuleMap);
237 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
238 null, schemaContext), moduleContainerBuilder.build());
242 * Valid only for mount point
245 public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
246 Preconditions.checkNotNull(identifier);
247 if ( ! identifier.contains(ControllerContext.MOUNT)) {
248 final String errMsg = "URI has bad format. If modules behind mount point should be showed,"
249 + " URI has to end with " + ControllerContext.MOUNT;
250 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
253 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
254 final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
255 final Set<Module> modules = controllerContext.getAllModules(mountPoint);
256 final SchemaContext schemaContext = mountPoint.getSchemaContext();
257 final MapNode mountPointModulesMap = makeModuleMapNode(modules);
259 final Module restconfModule = getRestconfModule();
260 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
261 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
262 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
264 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
265 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
266 moduleContainerBuilder.withChild(mountPointModulesMap);
268 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
269 mountPoint, controllerContext.getGlobalSchema()), moduleContainerBuilder.build());
273 public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
274 Preconditions.checkNotNull(identifier);
275 final QName moduleNameAndRevision = getModuleNameAndRevision(identifier);
276 Module module = null;
277 DOMMountPoint mountPoint = null;
278 final SchemaContext schemaContext;
279 if (identifier.contains(ControllerContext.MOUNT)) {
280 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
281 mountPoint = mountPointIdentifier.getMountPoint();
282 module = controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
283 schemaContext = mountPoint.getSchemaContext();
285 module = controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
286 schemaContext = controllerContext.getGlobalSchema();
289 if (module == null) {
290 final String errMsg = "Module with name '" + moduleNameAndRevision.getLocalName()
291 + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found.";
292 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
295 final Module restconfModule = getRestconfModule();
296 final Set<Module> modules = Collections.singleton(module);
297 final MapNode moduleMap = makeModuleMapNode(modules);
299 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
300 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
301 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
303 return new NormalizedNodeContext(new InstanceIdentifierContext(null, moduleSchemaNode, mountPoint,
304 schemaContext), moduleMap);
308 public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
309 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
310 final Set<String> availableStreams = Notificator.getStreamNames();
311 final Module restconfModule = getRestconfModule();
312 final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
313 Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
314 Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
316 final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamsBuilder = Builders
317 .mapBuilder((ListSchemaNode) streamSchemaNode);
319 for (final String streamName : availableStreams) {
320 listStreamsBuilder.withChild(toStreamEntryNode(streamName, streamSchemaNode));
323 final DataSchemaNode streamsContainerSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
324 restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
325 Preconditions.checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode);
327 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder =
328 Builders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode);
329 streamsContainerBuilder.withChild(listStreamsBuilder.build());
332 return new NormalizedNodeContext(new InstanceIdentifierContext(null, streamsContainerSchemaNode, null,
333 schemaContext), streamsContainerBuilder.build());
337 public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
338 final Set<Module> allModules = controllerContext.getAllModules();
339 return operationsFromModulesToNormalizedContext(allModules, null);
343 public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
344 Set<Module> modules = null;
345 DOMMountPoint mountPoint = null;
346 if (identifier.contains(ControllerContext.MOUNT)) {
347 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
348 mountPoint = mountPointIdentifier.getMountPoint();
349 modules = controllerContext.getAllModules(mountPoint);
352 final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
353 throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
356 return operationsFromModulesToNormalizedContext(modules, mountPoint);
359 private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set<Module> modules,
360 final DOMMountPoint mountPoint) {
362 // FIXME find best way to change restconf-netconf yang schema for provide this functionality
363 final String errMsg = "We are not able support view operations functionality yet.";
364 throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
368 * @deprecated method will be removed in Lithium release
371 private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
372 final DOMMountPoint mountPoint, final boolean prettyPrint) {
373 final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
374 final Module restconfModule = getRestconfModule();
375 final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
376 restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
377 final QName qName = operationsSchemaNode.getQName();
378 final SchemaPath path = operationsSchemaNode.getPath();
379 final ContainerSchemaNodeBuilder containerSchemaNodeBuilder = new ContainerSchemaNodeBuilder(
380 Draft02.RestConfModule.NAME, 0, qName, path);
381 final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
382 for (final Module module : modules) {
383 final Set<RpcDefinition> rpcs = module.getRpcs();
384 for (final RpcDefinition rpc : rpcs) {
385 final QName rpcQName = rpc.getQName();
386 final SimpleNode<Object> immutableSimpleNode = NodeFactory.<Object> createImmutableSimpleNode(rpcQName, null,
388 operationsAsData.add(immutableSimpleNode);
390 final String name = module.getName();
391 final LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName,
392 SchemaPath.create(true, QName.create("dummy")));
393 final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
394 fakeRpcSchemaNode.setAugmenting(true);
396 final EmptyType instance = EmptyType.getInstance();
397 fakeRpcSchemaNode.setType(instance);
398 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build());
402 final CompositeNode operationsNode = NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
403 final ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
404 return new StructuredData(operationsNode, schemaNode, mountPoint, prettyPrint);
407 private Module getRestconfModule() {
408 final Module restconfModule = controllerContext.getRestconfModule();
409 if (restconfModule == null) {
410 throw new RestconfDocumentedException("ietf-restconf module was not found.", ErrorType.APPLICATION,
411 ErrorTag.OPERATION_NOT_SUPPORTED);
414 return restconfModule;
417 private QName getModuleNameAndRevision(final String identifier) {
418 final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
419 String moduleNameAndRevision = "";
420 if (mountIndex >= 0) {
421 moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
423 moduleNameAndRevision = identifier;
426 final Splitter splitter = Splitter.on("/").omitEmptyStrings();
427 final Iterable<String> split = splitter.split(moduleNameAndRevision);
428 final List<String> pathArgs = Lists.<String> newArrayList(split);
429 if (pathArgs.size() < 2) {
430 throw new RestconfDocumentedException(
431 "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL,
432 ErrorTag.INVALID_VALUE);
436 final String moduleName = pathArgs.get(0);
437 final String revision = pathArgs.get(1);
438 final Date moduleRevision = REVISION_FORMAT.parse(revision);
439 return QName.create(null, moduleRevision, moduleName);
440 } catch (final ParseException e) {
441 throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
442 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
447 * @deprecated method will be removed for Lithium release
448 * so, please use toStreamEntryNode method
451 * @param streamSchemaNode
455 private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
456 final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
457 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
458 ((DataNodeContainer) streamSchemaNode), "name");
459 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
461 .add(NodeFactory.<String> createImmutableSimpleNode(nameSchemaNode.getQName(), null, streamName));
463 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
464 ((DataNodeContainer) streamSchemaNode), "description");
465 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
466 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
467 "DESCRIPTION_PLACEHOLDER"));
469 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
470 ((DataNodeContainer) streamSchemaNode), "replay-support");
471 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
472 streamNodeValues.add(NodeFactory.<Boolean> createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
473 Boolean.valueOf(true)));
475 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
476 ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
477 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
478 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
481 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
482 ((DataNodeContainer) streamSchemaNode), "events");
483 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
484 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(eventsSchemaNode.getQName(), null, ""));
486 return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
490 * @deprecated method will be removed for Lithium release
491 * so, please use toModuleEntryNode method
494 * @param moduleSchemaNode
498 private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
499 final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
500 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
501 ((DataNodeContainer) moduleSchemaNode), "name");
502 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
503 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(nameSchemaNode.getQName(), null,
506 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
507 ((DataNodeContainer) moduleSchemaNode), "revision");
508 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
509 final Date _revision = module.getRevision();
510 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
511 REVISION_FORMAT.format(_revision)));
513 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
514 ((DataNodeContainer) moduleSchemaNode), "namespace");
515 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
516 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
517 module.getNamespace().toString()));
519 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
520 ((DataNodeContainer) moduleSchemaNode), "feature");
521 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
522 for (final FeatureDefinition feature : module.getFeatures()) {
523 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(featureSchemaNode.getQName(), null,
524 feature.getQName().getLocalName()));
527 return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
531 public Object getRoot() {
536 public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
537 final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
538 final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace();
539 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
540 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
541 final SchemaContext schemaContext;
542 if (identifier.contains(MOUNT_POINT_MODULE_NAME) && mountPoint != null) {
543 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
544 if ( ! mountRpcServices.isPresent()) {
545 throw new RestconfDocumentedException("Rpc service is missing.");
547 schemaContext = mountPoint.getSchemaContext();
548 response = mountRpcServices.get().invokeRpc(type, payload.getData());
550 if (namespace.toString().equals(SAL_REMOTE_NAMESPACE)) {
551 response = invokeSalRemoteRpcSubscribeRPC(payload);
553 response = broker.invokeRpc(type, payload.getData());
555 schemaContext = controllerContext.getGlobalSchema();
558 final DOMRpcResult result = checkRpcResponse(response);
560 RpcDefinition resultNodeSchema = null;
561 final NormalizedNode<?, ?> resultData = result.getResult();
562 if (result != null && result.getResult() != null) {
563 resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode();
566 return new NormalizedNodeContext(new InstanceIdentifierContext<RpcDefinition>(null,
567 resultNodeSchema, mountPoint, schemaContext), resultData);
570 private DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
571 if (response == null) {
575 final DOMRpcResult retValue = response.get();
576 if (retValue.getErrors() == null || retValue.getErrors().isEmpty()) {
579 throw new RestconfDocumentedException("RpcError message", null, retValue.getErrors());
581 catch (final InterruptedException e) {
582 throw new RestconfDocumentedException(
583 "The operation was interrupted while executing and did not complete.", ErrorType.RPC,
584 ErrorTag.PARTIAL_OPERATION);
586 catch (final ExecutionException e) {
587 Throwable cause = e.getCause();
588 if (cause instanceof CancellationException) {
589 throw new RestconfDocumentedException("The operation was cancelled while executing.", ErrorType.RPC,
590 ErrorTag.PARTIAL_OPERATION);
591 } else if (cause != null) {
592 while (cause.getCause() != null) {
593 cause = cause.getCause();
596 if (cause instanceof IllegalArgumentException) {
597 throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
598 ErrorTag.INVALID_VALUE);
601 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
604 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
610 private void validateInput(final SchemaNode inputSchema, final NormalizedNodeContext payload) {
611 if (inputSchema != null && payload.getData() == null) {
612 // expected a non null payload
613 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
614 } else if (inputSchema == null && payload.getData() != null) {
615 // did not expect any input
616 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
620 // TODO: Validate "mandatory" and "config" values here??? Or should those be
622 // validate in a more central location inside MD-SAL core.
627 * @deprecated method will be removed for Lithium release
633 private void validateInput(final DataSchemaNode inputSchema, final Node<?> payload) {
634 if (inputSchema != null && payload == null) {
635 // expected a non null payload
636 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
637 } else if (inputSchema == null && payload != null) {
638 // did not expect any input
639 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
643 // TODO: Validate "mandatory" and "config" values here??? Or should those be
645 // validate in a more central location inside MD-SAL core.
649 private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
650 final ContainerNode value = (ContainerNode) payload.getData();
651 final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
652 final Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(new NodeIdentifier(
653 QName.create(payload.getInstanceIdentifierContext().getSchemaNode().getQName(), "path")));
654 final Object pathValue = path.isPresent() ? path.get().getValue() : null;
656 if (!(pathValue instanceof YangInstanceIdentifier)) {
657 throw new RestconfDocumentedException("Instance identifier was not normalized correctly.",
658 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
661 final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
662 String streamName = null;
663 if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
664 final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
666 LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class,
667 DATASTORE_PARAM_NAME);
668 datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
670 DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
671 scope = scope == null ? DEFAULT_SCOPE : scope;
673 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
674 + "/scope=" + scope);
677 if (Strings.isNullOrEmpty(streamName)) {
678 throw new RestconfDocumentedException(
679 "Path is empty or contains value node which is not Container or List build-in type.",
680 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
683 final QName outputQname = QName.create(rpcQName, "output");
684 final QName streamNameQname = QName.create(rpcQName, "stream-name");
686 final ContainerNode output = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
687 .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
689 if (!Notificator.existListenerFor(streamName)) {
690 final YangInstanceIdentifier normalizedPathIdentifier = controllerContext.toNormalized(pathIdentifier);
691 Notificator.createListener(normalizedPathIdentifier, streamName);
694 final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
696 return Futures.immediateCheckedFuture(defaultDOMRpcResult);
700 public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
701 if (StringUtils.isNotBlank(noPayload)) {
702 throw new RestconfDocumentedException("Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
705 String identifierEncoded = null;
706 DOMMountPoint mountPoint = null;
707 final SchemaContext schemaContext;
708 if (identifier.contains(ControllerContext.MOUNT)) {
709 // mounted RPC call - look up mount instance.
710 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
711 mountPoint = mountPointId.getMountPoint();
712 schemaContext = mountPoint.getSchemaContext();
713 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
714 + ControllerContext.MOUNT.length() + 1;
715 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
716 identifierEncoded = remoteRpcName;
718 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
719 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
720 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
721 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
723 identifierEncoded = identifier;
724 schemaContext = controllerContext.getGlobalSchema();
727 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
729 RpcDefinition rpc = null;
730 if (mountPoint == null) {
731 rpc = controllerContext.getRpcDefinition(identifierDecoded);
733 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
737 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
740 if (rpc.getInput() != null) {
741 // FIXME : find a correct Error from specification
742 throw new IllegalStateException("RPC " + rpc + " does'n need input value!");
745 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
746 if (mountPoint != null) {
747 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
748 if ( ! mountRpcServices.isPresent()) {
749 throw new RestconfDocumentedException("Rpc service is missing.");
751 response = mountRpcServices.get().invokeRpc(rpc.getPath(), null);
753 response = broker.invokeRpc(rpc.getPath(), null);
756 final DOMRpcResult result = checkRpcResponse(response);
758 DataSchemaNode resultNodeSchema = null;
759 NormalizedNode<?, ?> resultData = null;
760 if (result != null && result.getResult() != null) {
761 resultData = result.getResult();
762 final ContainerSchemaNode rpcDataSchemaNode =
763 SchemaContextUtil.getRpcDataSchema(schemaContext, rpc.getOutput().getPath());
764 resultNodeSchema = rpcDataSchemaNode.getDataChildByName(result.getResult().getNodeType());
767 return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
768 schemaContext), resultData);
771 private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
772 String identifierEncoded = null;
773 DOMMountPoint mountPoint = null;
774 if (identifier.contains(ControllerContext.MOUNT)) {
775 // mounted RPC call - look up mount instance.
776 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
777 mountPoint = mountPointId.getMountPoint();
779 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
780 + ControllerContext.MOUNT.length() + 1;
781 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
782 identifierEncoded = remoteRpcName;
784 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
785 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
786 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
787 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
789 identifierEncoded = identifier;
792 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
794 RpcDefinition rpc = null;
795 if (mountPoint == null) {
796 rpc = controllerContext.getRpcDefinition(identifierDecoded);
798 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
802 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
805 if (mountPoint == null) {
806 return new BrokerRpcExecutor(rpc, broker);
808 return new MountPointRpcExecutor(rpc, mountPoint);
813 private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
814 final String[] splittedIdentifier = identifierDecoded.split(":");
815 if (splittedIdentifier.length != 2) {
816 throw new RestconfDocumentedException(identifierDecoded
817 + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION,
818 ErrorTag.INVALID_VALUE);
820 for (final Module module : schemaContext.getModules()) {
821 if (module.getName().equals(splittedIdentifier[0])) {
822 for (final RpcDefinition rpcDefinition : module.getRpcs()) {
823 if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) {
824 return rpcDefinition;
833 * @deprecated method will be removed for Lithium release
836 private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload, final boolean prettyPrint) {
837 if (rpcExecutor == null) {
838 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
841 CompositeNode rpcRequest = null;
842 final RpcDefinition rpc = rpcExecutor.getRpcDefinition();
843 final QName rpcName = rpc.getQName();
845 if (payload == null) {
846 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
848 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
849 final List<Node<?>> input = Collections.<Node<?>> singletonList(value);
850 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
853 final RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
855 checkRpcSuccessAndThrowException(rpcResult);
857 if (rpcResult.getResult() == null) {
861 if (rpc.getOutput() == null) {
862 return null; // no output, nothing to send back.
865 return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null, prettyPrint);
868 private void checkRpcSuccessAndThrowException(final RpcResult<CompositeNode> rpcResult) {
869 if (rpcResult.isSuccessful() == false) {
871 throw new RestconfDocumentedException("The operation was not successful", null,
872 rpcResult.getErrors());
877 public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
878 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
879 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
880 NormalizedNode<?, ?> data = null;
881 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
882 if (mountPoint != null) {
883 data = broker.readConfigurationData(mountPoint, normalizedII);
885 data = broker.readConfigurationData(normalizedII);
888 throw new RestconfDocumentedException(
889 "Request could not be completed because the relevant data model content does not exist.",
890 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
892 return new NormalizedNodeContext(iiWithData, data);
895 @SuppressWarnings("unchecked")
896 private <T extends Node<?>> T pruneDataAtDepth(final T node, final Integer depth) {
901 if (node instanceof CompositeNode) {
902 final ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
904 for (final Node<?> childNode : ((CompositeNode) node).getValue()) {
905 newChildNodes.add(pruneDataAtDepth(childNode, depth - 1));
909 return (T) ImmutableCompositeNode.create(node.getNodeType(), newChildNodes.build());
910 } else { // SimpleNode
915 private Integer parseDepthParameter(final UriInfo info) {
916 final String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString());
917 if (Strings.isNullOrEmpty(param) || "unbounded".equals(param)) {
922 final Integer depth = Integer.valueOf(param);
924 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
925 "Invalid depth parameter: " + depth, null,
926 "The depth parameter must be an integer > 1 or \"unbounded\""));
930 } catch (final NumberFormatException e) {
931 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
932 "Invalid depth parameter: " + e.getMessage(), null,
933 "The depth parameter must be an integer > 1 or \"unbounded\""));
938 public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo info) {
939 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
940 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
941 NormalizedNode<?, ?> data = null;
942 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
943 if (mountPoint != null) {
944 data = broker.readOperationalData(mountPoint, normalizedII);
946 data = broker.readOperationalData(normalizedII);
949 throw new RestconfDocumentedException(
950 "Request could not be completed because the relevant data model content does not exist.",
951 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
953 return new NormalizedNodeContext(iiWithData, data);
956 private boolean parsePrettyPrintParameter(final UriInfo info) {
957 final String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
958 return Boolean.parseBoolean(param);
962 public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
963 Preconditions.checkNotNull(identifier);
964 final InstanceIdentifierContext<DataSchemaNode> iiWithData =
965 (InstanceIdentifierContext<DataSchemaNode>) payload.getInstanceIdentifierContext();
967 validateInput(iiWithData.getSchemaNode(), payload);
968 validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier());
969 validateListKeysEqualityInPayloadAndUri(iiWithData, payload.getData());
971 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
972 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
975 * There is a small window where another write transaction could be updating the same data
976 * simultaneously and we get an OptimisticLockFailedException. This error is likely
977 * transient and The WriteTransaction#submit API docs state that a retry will likely
978 * succeed. So we'll try again if that scenario occurs. If it fails a third time then it
979 * probably will never succeed so we'll fail in that case.
981 * By retrying we're attempting to hide the internal implementation of the data store and
982 * how it handles concurrent updates from the restconf client. The client has instructed us
983 * to put the data and we should make every effort to do so without pushing optimistic lock
984 * failures back to the client and forcing them to handle it via retry (and having to
985 * document the behavior).
990 if (mountPoint != null) {
991 broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
993 broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet();
997 } catch (final TransactionCommitFailedException e) {
998 if(e instanceof OptimisticLockFailedException) {
1000 LOG.debug("Got OptimisticLockFailedException on last try - failing");
1001 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
1004 LOG.debug("Got OptimisticLockFailedException - trying again");
1006 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
1011 return Response.status(Status.OK).build();
1014 private void validateTopLevelNodeName(final NormalizedNodeContext node,
1015 final YangInstanceIdentifier identifier) {
1017 final String payloadName = node.getData().getNodeType().getLocalName();
1018 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1021 if ( ! pathArguments.hasNext()) {
1023 if ( ! node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) {
1024 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1025 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1029 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1030 if ( ! payloadName.equals(identifierName)) {
1031 throw new RestconfDocumentedException("Payload name (" + payloadName
1032 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1033 ErrorTag.MALFORMED_MESSAGE);
1039 * @deprecated method will be removed for Lithium release
1045 private void validateTopLevelNodeName(final Node<?> node,
1046 final YangInstanceIdentifier identifier) {
1047 final String payloadName = getName(node);
1048 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1051 if (!pathArguments.hasNext()) {
1053 if (!node.getNodeType().equals(NETCONF_BASE_QNAME)) {
1054 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1055 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1059 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1060 if (!payloadName.equals(identifierName)) {
1061 throw new RestconfDocumentedException("Payload name (" + payloadName
1062 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1063 ErrorTag.MALFORMED_MESSAGE);
1069 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1071 * @throws RestconfDocumentedException
1072 * if key values or key count in payload and URI isn't equal
1075 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext<DataSchemaNode> iiWithData,
1076 final NormalizedNode<?, ?> payload) {
1077 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1078 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1079 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1080 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1081 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1083 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1089 * @deprecated method will be removed for Lithium release
1091 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1093 * @throws RestconfDocumentedException
1094 * if key values or key count in payload and URI isn't equal
1098 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
1099 final CompositeNode payload) {
1100 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1101 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1102 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1103 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1104 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1106 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1111 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final NormalizedNode<?, ?> payload,
1112 final List<QName> keyDefinitions) {
1113 for (final QName keyDefinition : keyDefinitions) {
1114 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1115 // should be caught during parsing URI to InstanceIdentifier
1116 if (uriKeyValue == null) {
1117 final String errMsg = "Missing key " + keyDefinition + " in URI.";
1118 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1120 // TODO thing about the possibility to fix
1121 // final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1122 // if (payloadKeyValues.isEmpty()) {
1123 // final String errMsg = "Missing key " + keyDefinition.getLocalName() + " in the message body.";
1124 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1127 // final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1128 // if (!uriKeyValue.equals(payloadKeyValue)) {
1129 // final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
1130 // "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. ";
1131 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1137 * @deprecated method will be removed for Lithium release
1139 * @param uriKeyValues
1141 * @param keyDefinitions
1144 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final CompositeNode payload,
1145 final List<QName> keyDefinitions) {
1146 for (final QName keyDefinition : keyDefinitions) {
1147 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1148 // should be caught during parsing URI to InstanceIdentifier
1149 if (uriKeyValue == null) {
1150 throw new RestconfDocumentedException("Missing key " + keyDefinition + " in URI.", ErrorType.PROTOCOL,
1151 ErrorTag.DATA_MISSING);
1153 final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1154 if (payloadKeyValues.isEmpty()) {
1155 throw new RestconfDocumentedException("Missing key " + keyDefinition.getLocalName()
1156 + " in the message body.", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1159 final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1160 if (!uriKeyValue.equals(payloadKeyValue)) {
1161 throw new RestconfDocumentedException("The value '" + uriKeyValue + "' for key '"
1162 + keyDefinition.getLocalName() + "' specified in the URI doesn't match the value '"
1163 + payloadKeyValue + "' specified in the message body. ", ErrorType.PROTOCOL,
1164 ErrorTag.INVALID_VALUE);
1170 public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
1171 return createConfigurationData(payload, uriInfo);
1174 // FIXME create RestconfIdetifierHelper and move this method there
1175 private YangInstanceIdentifier checkConsistencyOfNormalizedNodeContext(final NormalizedNodeContext payload) {
1176 Preconditions.checkArgument(payload != null);
1177 Preconditions.checkArgument(payload.getData() != null);
1178 Preconditions.checkArgument(payload.getData().getNodeType() != null);
1179 Preconditions.checkArgument(payload.getInstanceIdentifierContext() != null);
1180 Preconditions.checkArgument(payload.getInstanceIdentifierContext().getInstanceIdentifier() != null);
1182 final QName payloadNodeQname = payload.getData().getNodeType();
1183 final YangInstanceIdentifier yangIdent = payload.getInstanceIdentifierContext().getInstanceIdentifier();
1184 if (payloadNodeQname.compareTo(yangIdent.getLastPathArgument().getNodeType()) > 0) {
1187 final InstanceIdentifierContext parentContext = payload.getInstanceIdentifierContext();
1188 final SchemaNode parentSchemaNode = parentContext.getSchemaNode();
1189 if(parentSchemaNode instanceof DataNodeContainer) {
1190 final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode;
1191 for (final DataSchemaNode child : cast.getChildNodes()) {
1192 if (payloadNodeQname.compareTo(child.getQName()) == 0) {
1193 return YangInstanceIdentifier.builder(yangIdent).node(child.getQName()).build();
1197 if (parentSchemaNode instanceof RpcDefinition) {
1200 final String errMsg = "Error parsing input: DataSchemaNode has not children";
1201 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1205 public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
1206 if (payload == null) {
1207 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1210 final URI payloadNS = payload.getData().getNodeType().getNamespace();
1211 if (payloadNS == null) {
1212 throw new RestconfDocumentedException(
1213 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
1214 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1217 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
1218 final InstanceIdentifierContext<DataSchemaNode> iiWithData = (InstanceIdentifierContext<DataSchemaNode>) payload.getInstanceIdentifierContext();
1219 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1220 final YangInstanceIdentifier resultII;
1222 if (mountPoint != null) {
1223 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData()).checkedGet();
1225 broker.commitConfigurationDataPost(normalizedII, payload.getData()).checkedGet();
1227 } catch(final RestconfDocumentedException e) {
1229 } catch (final Exception e) {
1230 throw new RestconfDocumentedException("Error creating data", e);
1233 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
1234 // FIXME: Provide path to result.
1235 final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII);
1236 if (location != null) {
1237 responseBuilder.location(location);
1239 return responseBuilder.build();
1242 private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
1243 final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
1244 uriBuilder.path("config");
1246 uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
1247 } catch (final Exception e) {
1248 LOG.debug("Location for instance identifier"+normalizedII+"wasn't created", e);
1251 return uriBuilder.build();
1255 public Response deleteConfigurationData(final String identifier) {
1256 final InstanceIdentifierContext<DataSchemaNode> iiWithData = controllerContext.toInstanceIdentifier(identifier);
1257 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
1258 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1261 if (mountPoint != null) {
1262 broker.commitConfigurationDataDelete(mountPoint, normalizedII);
1264 broker.commitConfigurationDataDelete(normalizedII).get();
1266 } catch (final Exception e) {
1267 final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
1268 Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
1269 if (searchedException.isPresent()) {
1270 throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
1272 throw new RestconfDocumentedException("Error while deleting data", e);
1274 return Response.status(Status.OK).build();
1278 * Subscribes to some path in schema context (stream) to listen on changes on this stream.
1280 * Additional parameters for subscribing to stream are loaded via rpc input parameters:
1282 * <li>datastore</li> - default CONFIGURATION (other values of {@link LogicalDatastoreType} enum type)
1283 * <li>scope</li> - default BASE (other values of {@link DataChangeScope})
1287 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
1288 final String streamName = Notificator.createStreamNameFromUri(identifier);
1289 if (Strings.isNullOrEmpty(streamName)) {
1290 throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1293 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
1294 if (listener == null) {
1295 throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
1298 final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
1299 final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
1300 paramToValues.get(DATASTORE_PARAM_NAME));
1301 if (datastore == null) {
1302 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)",
1303 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1305 final DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME));
1306 if (scope == null) {
1307 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)",
1308 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1311 broker.registerToListenDataChanges(datastore, scope, listener);
1313 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
1314 int notificationPort = NOTIFICATION_PORT;
1316 final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
1317 notificationPort = webSocketServerInstance.getPort();
1318 } catch (final NullPointerException e) {
1319 WebSocketServer.createInstance(NOTIFICATION_PORT);
1321 final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
1322 final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
1324 return Response.status(Status.OK).location(uriToWebsocketServer).build();
1328 * Load parameter for subscribing to stream from input composite node
1332 * @return enum object if its string value is equal to {@code paramName}. In other cases null.
1334 private <T> T parseEnumTypeParameter(final ContainerNode value, final Class<T> classDescriptor,
1335 final String paramName) {
1336 final Optional<DataContainerChild<? extends PathArgument, ?>> augNode = value.getChild(SAL_REMOTE_AUG_IDENTIFIER);
1337 if (!augNode.isPresent() && !(augNode instanceof AugmentationNode)) {
1340 final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode =
1341 ((AugmentationNode) augNode.get()).getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName)));
1342 if (!enumNode.isPresent()) {
1345 final Object rawValue = enumNode.get().getValue();
1346 if (!(rawValue instanceof String)) {
1350 return resolveAsEnum(classDescriptor, (String) rawValue);
1354 * Checks whether {@code value} is one of the string representation of enumeration {@code classDescriptor}
1356 * @return enum object if string value of {@code classDescriptor} enumeration is equal to {@code value}. Other cases
1359 private <T> T parserURIEnumParameter(final Class<T> classDescriptor, final String value) {
1360 if (Strings.isNullOrEmpty(value)) {
1363 return resolveAsEnum(classDescriptor, value);
1366 private <T> T resolveAsEnum(final Class<T> classDescriptor, final String value) {
1367 final T[] enumConstants = classDescriptor.getEnumConstants();
1368 if (enumConstants != null) {
1369 for (final T enm : classDescriptor.getEnumConstants()) {
1370 if (((Enum<?>) enm).name().equals(value)) {
1378 private Map<String, String> resolveValuesFromUri(final String uri) {
1379 final Map<String, String> result = new HashMap<>();
1380 final String[] tokens = uri.split("/");
1381 for (int i = 1; i < tokens.length; i++) {
1382 final String[] parameterTokens = tokens[i].split("=");
1383 if (parameterTokens.length == 2) {
1384 result.put(parameterTokens[0], parameterTokens[1]);
1390 private Module findModule(final DOMMountPoint mountPoint, final Node<?> data) {
1391 Module module = null;
1392 if (data instanceof NodeWrapper) {
1393 module = findModule(mountPoint, (NodeWrapper<?>) data);
1394 } else if (data != null) {
1395 final URI namespace = data.getNodeType().getNamespace();
1396 if (mountPoint != null) {
1397 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1399 module = controllerContext.findModuleByNamespace(namespace);
1402 if (module != null) {
1405 throw new RestconfDocumentedException(
1406 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
1407 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1410 private Module findModule(final DOMMountPoint mountPoint, final NodeWrapper<?> data) {
1411 final URI namespace = data.getNamespace();
1412 Preconditions.<URI> checkNotNull(namespace);
1414 Module module = null;
1415 if (mountPoint != null) {
1416 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1417 if (module == null) {
1418 module = controllerContext.findModuleByName(mountPoint, namespace.toString());
1421 module = controllerContext.findModuleByNamespace(namespace);
1422 if (module == null) {
1423 module = controllerContext.findModuleByName(namespace.toString());
1430 private InstanceIdentifierContext addLastIdentifierFromData(final InstanceIdentifierContext identifierWithSchemaNode,
1431 final CompositeNode data, final DataSchemaNode schemaOfData, final SchemaContext schemaContext) {
1432 YangInstanceIdentifier instanceIdentifier = null;
1433 if (identifierWithSchemaNode != null) {
1434 instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
1437 final YangInstanceIdentifier iiOriginal = instanceIdentifier;
1438 InstanceIdentifierBuilder iiBuilder = null;
1439 if (iiOriginal == null) {
1440 iiBuilder = YangInstanceIdentifier.builder();
1442 iiBuilder = YangInstanceIdentifier.builder(iiOriginal);
1445 if ((schemaOfData instanceof ListSchemaNode)) {
1446 final HashMap<QName, Object> keys = resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
1447 iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
1449 iiBuilder.node(schemaOfData.getQName());
1452 final YangInstanceIdentifier instance = iiBuilder.toInstance();
1453 DOMMountPoint mountPoint = null;
1454 final SchemaContext schemaCtx = null;
1455 if (identifierWithSchemaNode != null) {
1456 mountPoint = identifierWithSchemaNode.getMountPoint();
1459 return new InstanceIdentifierContext(instance, schemaOfData, mountPoint,schemaContext);
1462 private HashMap<QName, Object> resolveKeysFromData(final ListSchemaNode listNode, final CompositeNode dataNode) {
1463 final HashMap<QName, Object> keyValues = new HashMap<QName, Object>();
1464 final List<QName> _keyDefinition = listNode.getKeyDefinition();
1465 for (final QName key : _keyDefinition) {
1466 SimpleNode<? extends Object> head = null;
1467 final String localName = key.getLocalName();
1468 final List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
1469 if (simpleNodesByName != null) {
1470 head = Iterables.getFirst(simpleNodesByName, null);
1473 Object dataNodeKeyValueObject = null;
1475 dataNodeKeyValueObject = head.getValue();
1478 if (dataNodeKeyValueObject == null) {
1479 throw new RestconfDocumentedException("Data contains list \"" + dataNode.getNodeType().getLocalName()
1480 + "\" which does not contain key: \"" + key.getLocalName() + "\"", ErrorType.PROTOCOL,
1481 ErrorTag.INVALID_VALUE);
1484 keyValues.put(key, dataNodeKeyValueObject);
1490 private boolean endsWithMountPoint(final String identifier) {
1491 return identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/");
1494 private boolean representsMountPointRootData(final Node<?> data) {
1495 final URI namespace = namespace(data);
1496 return (SchemaContext.NAME.getNamespace().equals(namespace) /*
1497 * || MOUNT_POINT_MODULE_NAME .equals( namespace .
1500 && SchemaContext.NAME.getLocalName().equals(localName(data));
1503 private String addMountPointIdentifier(final String identifier) {
1504 final boolean endsWith = identifier.endsWith("/");
1506 return (identifier + ControllerContext.MOUNT);
1509 return identifier + "/" + ControllerContext.MOUNT;
1513 * @deprecated method will be removed in Lithium release
1514 * we don't wish to use Node and CompositeNode anywhere
1517 public CompositeNode normalizeNode(final Node<?> node, final DataSchemaNode schema, final DOMMountPoint mountPoint) {
1518 if (schema == null) {
1519 final String localName = node == null ? null :
1520 node instanceof NodeWrapper ? ((NodeWrapper<?>)node).getLocalName() :
1521 node.getNodeType().getLocalName();
1523 throw new RestconfDocumentedException("Data schema node was not found for " + localName,
1524 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1527 if (!(schema instanceof DataNodeContainer)) {
1528 throw new RestconfDocumentedException("Root element has to be container or list yang datatype.",
1529 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1532 if ((node instanceof NodeWrapper<?>)) {
1533 NodeWrapper<?> nodeWrap = (NodeWrapper<?>) node;
1534 final boolean isChangeAllowed = ((NodeWrapper<?>) node).isChangeAllowed();
1535 if (isChangeAllowed) {
1536 nodeWrap = topLevelElementAsCompositeNodeWrapper((NodeWrapper<?>) node, schema);
1538 this.normalizeNode(nodeWrap, schema, null, mountPoint);
1539 } catch (final IllegalArgumentException e) {
1540 final RestconfDocumentedException restconfDocumentedException = new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1541 restconfDocumentedException.addSuppressed(e);
1542 throw restconfDocumentedException;
1544 if (nodeWrap instanceof CompositeNodeWrapper) {
1545 return ((CompositeNodeWrapper) nodeWrap).unwrap();
1550 if (node instanceof CompositeNode) {
1551 return (CompositeNode) node;
1554 throw new RestconfDocumentedException("Top level element is not interpreted as composite node.",
1555 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
1558 private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1559 final QName previousAugment, final DOMMountPoint mountPoint) {
1560 if (schema == null) {
1561 throw new RestconfDocumentedException("Data has bad format.\n\"" + nodeBuilder.getLocalName()
1562 + "\" does not exist in yang schema.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1565 QName currentAugment = null;
1566 if (nodeBuilder.getQname() != null) {
1567 currentAugment = previousAugment;
1569 currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
1570 if (nodeBuilder.getQname() == null) {
1571 throw new RestconfDocumentedException(
1572 "Data has bad format.\nIf data is in XML format then namespace for \""
1573 + nodeBuilder.getLocalName() + "\" should be \"" + schema.getQName().getNamespace()
1574 + "\".\n" + "If data is in JSON format then module name for \""
1575 + nodeBuilder.getLocalName() + "\" should be corresponding to namespace \""
1576 + schema.getQName().getNamespace() + "\".", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1580 if (nodeBuilder instanceof CompositeNodeWrapper) {
1581 if (schema instanceof DataNodeContainer) {
1582 normalizeCompositeNode((CompositeNodeWrapper) nodeBuilder, (DataNodeContainer) schema, mountPoint,
1584 } else if (schema instanceof AnyXmlSchemaNode) {
1585 normalizeAnyXmlNode((CompositeNodeWrapper) nodeBuilder, (AnyXmlSchemaNode) schema);
1587 } else if (nodeBuilder instanceof SimpleNodeWrapper) {
1588 normalizeSimpleNode((SimpleNodeWrapper) nodeBuilder, schema, mountPoint);
1589 } else if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1590 normalizeEmptyNode((EmptyNodeWrapper) nodeBuilder, schema);
1594 private void normalizeAnyXmlNode(final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema) {
1595 final List<NodeWrapper<?>> children = compositeNode.getValues();
1596 for (final NodeWrapper<? extends Object> child : children) {
1597 child.setNamespace(schema.getQName().getNamespace());
1598 if (child instanceof CompositeNodeWrapper) {
1599 normalizeAnyXmlNode((CompositeNodeWrapper) child, schema);
1604 private void normalizeEmptyNode(final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema) {
1605 if ((schema instanceof LeafSchemaNode)) {
1606 emptyNodeBuilder.setComposite(false);
1608 if ((schema instanceof ContainerSchemaNode)) {
1609 // FIXME: Add presence check
1610 emptyNodeBuilder.setComposite(true);
1615 private void normalizeSimpleNode(final SimpleNodeWrapper simpleNode, final DataSchemaNode schema,
1616 final DOMMountPoint mountPoint) {
1617 final Object value = simpleNode.getValue();
1618 Object inputValue = value;
1619 final TypeDef typeDef = this.typeDefinition(schema);
1620 TypeDefinition<? extends Object> typeDefinition = typeDef != null ? typeDef.typedef : null;
1622 // For leafrefs, extract the type it is pointing to
1623 if(typeDefinition instanceof LeafrefTypeDefinition) {
1624 if (schema.getQName().equals(typeDef.qName)) {
1625 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema);
1627 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), typeDef.qName);
1631 if (typeDefinition instanceof IdentityrefTypeDefinition) {
1632 inputValue = parseToIdentityValuesDTO(simpleNode, value, inputValue);
1635 Object outputValue = inputValue;
1637 if (typeDefinition != null) {
1638 final Codec<Object, Object> codec = RestCodec.from(typeDefinition, mountPoint);
1639 outputValue = codec == null ? null : codec.deserialize(inputValue);
1642 simpleNode.setValue(outputValue);
1645 private Object parseToIdentityValuesDTO(final SimpleNodeWrapper simpleNode, final Object value, Object inputValue) {
1646 if ((value instanceof String)) {
1647 inputValue = new IdentityValuesDTO(simpleNode.getNamespace().toString(), (String) value, null,
1649 } // else value is already instance of IdentityValuesDTO
1653 private void normalizeCompositeNode(final CompositeNodeWrapper compositeNodeBuilder,
1654 final DataNodeContainer schema, final DOMMountPoint mountPoint, final QName currentAugment) {
1655 final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
1656 checkNodeMultiplicityAccordingToSchema(schema, children);
1657 for (final NodeWrapper<? extends Object> child : children) {
1658 final List<DataSchemaNode> potentialSchemaNodes = ControllerContext.findInstanceDataChildrenByName(
1659 schema, child.getLocalName());
1661 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
1662 final StringBuilder builder = new StringBuilder();
1663 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1664 builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()).append("\n");
1667 throw new RestconfDocumentedException("Node \"" + child.getLocalName()
1668 + "\" is added as augment from more than one module. "
1669 + "Therefore node must have namespace (XML format) or module name (JSON format)."
1670 + "\nThe node is added as augment from modules with namespaces:\n" + builder,
1671 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1674 boolean rightNodeSchemaFound = false;
1675 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1676 if (!rightNodeSchemaFound) {
1677 final QName potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode,
1678 currentAugment, mountPoint);
1679 if (child.getQname() != null) {
1680 this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1681 rightNodeSchemaFound = true;
1686 if (!rightNodeSchemaFound) {
1687 throw new RestconfDocumentedException("Schema node \"" + child.getLocalName()
1688 + "\" was not found in module.", ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT);
1692 if ((schema instanceof ListSchemaNode)) {
1693 final ListSchemaNode listSchemaNode = (ListSchemaNode) schema;
1694 final List<QName> listKeys = listSchemaNode.getKeyDefinition();
1695 for (final QName listKey : listKeys) {
1696 boolean foundKey = false;
1697 for (final NodeWrapper<? extends Object> child : children) {
1698 if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1704 throw new RestconfDocumentedException("Missing key in URI \"" + listKey.getLocalName()
1705 + "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"", ErrorType.PROTOCOL,
1706 ErrorTag.DATA_MISSING);
1712 private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer,
1713 final List<NodeWrapper<?>> nodes) {
1714 final Map<String, Integer> equalNodeNamesToCounts = new HashMap<String, Integer>();
1715 for (final NodeWrapper<?> child : nodes) {
1716 Integer count = equalNodeNamesToCounts.get(child.getLocalName());
1717 equalNodeNamesToCounts.put(child.getLocalName(), count == null ? 1 : ++count);
1720 for (final DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) {
1721 if (childSchemaNode instanceof ContainerSchemaNode || childSchemaNode instanceof LeafSchemaNode) {
1722 final String localName = childSchemaNode.getQName().getLocalName();
1723 final Integer count = equalNodeNamesToCounts.get(localName);
1724 if (count != null && count > 1) {
1725 throw new RestconfDocumentedException("Multiple input data elements were specified for '"
1726 + childSchemaNode.getQName().getLocalName()
1727 + "'. The data for this element type can only be specified once.", ErrorType.APPLICATION,
1728 ErrorTag.BAD_ELEMENT);
1734 private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1735 final QName previousAugment, final DOMMountPoint mountPoint) {
1736 QName validQName = schema.getQName();
1737 QName currentAugment = previousAugment;
1738 if (schema.isAugmenting()) {
1739 currentAugment = schema.getQName();
1740 } else if (previousAugment != null
1741 && !Objects.equal(schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1742 validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1745 String moduleName = null;
1746 if (mountPoint == null) {
1747 moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1749 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1752 if (nodeBuilder.getNamespace() == null || Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace())
1753 || Objects.equal(nodeBuilder.getNamespace().toString(), moduleName)) {
1755 * || Note : this check is wrong -
1756 * can never be true as it compares
1757 * a URI with a String not sure what
1758 * the intention is so commented out
1759 * ... Objects . equal ( nodeBuilder
1760 * . getNamespace ( ) ,
1761 * MOUNT_POINT_MODULE_NAME )
1764 nodeBuilder.setQname(validQName);
1767 return currentAugment;
1770 private URI namespace(final Node<?> data) {
1771 if (data instanceof NodeWrapper) {
1772 return ((NodeWrapper<?>) data).getNamespace();
1773 } else if (data != null) {
1774 return data.getNodeType().getNamespace();
1776 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1780 private String localName(final Node<?> data) {
1781 if (data instanceof NodeWrapper) {
1782 return ((NodeWrapper<?>) data).getLocalName();
1783 } else if (data != null) {
1784 return data.getNodeType().getLocalName();
1786 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1791 * @deprecated method will be removed for Lithium release
1797 private String getName(final Node<?> data) {
1798 if (data instanceof NodeWrapper) {
1799 return ((NodeWrapper<?>) data).getLocalName();
1800 } else if (data != null) {
1801 return data.getNodeType().getLocalName();
1803 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1807 private TypeDef typeDefinition(final TypeDefinition<?> type, final QName nodeQName) {
1808 TypeDefinition<?> baseType = type;
1809 QName qName = nodeQName;
1810 while (baseType.getBaseType() != null) {
1811 if (baseType instanceof ExtendedType) {
1812 qName = baseType.getQName();
1814 baseType = baseType.getBaseType();
1817 return new TypeDef(baseType, qName);
1821 private TypeDef typeDefinition(final DataSchemaNode node) {
1822 if (node instanceof LeafListSchemaNode) {
1823 return typeDefinition(((LeafListSchemaNode)node).getType(), node.getQName());
1824 } else if (node instanceof LeafSchemaNode) {
1825 return typeDefinition(((LeafSchemaNode)node).getType(), node.getQName());
1826 } else if (node instanceof AnyXmlSchemaNode) {
1829 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
1833 private CompositeNode datastoreNormalizedNodeToCompositeNode(final NormalizedNode<?, ?> dataNode, final DataSchemaNode schema) {
1834 Node<?> nodes = null;
1835 if (dataNode == null) {
1836 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.DATA_MISSING,
1837 "No data was found."));
1839 nodes = DataNormalizer.toLegacy(dataNode);
1840 if (nodes != null) {
1841 if (nodes instanceof CompositeNode) {
1842 return (CompositeNode) nodes;
1844 LOG.error("The node " + dataNode.getNodeType() + " couldn't be transformed to compositenode.");
1847 LOG.error("Top level node isn't of type Container or List schema node but "
1848 + schema.getClass().getSimpleName());
1851 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1852 "It wasn't possible to correctly interpret data."));
1855 private NormalizedNode<?, ?> compositeNodeToDatastoreNormalizedNode(final CompositeNode compNode,
1856 final DataSchemaNode schema) {
1857 final List<Node<?>> lst = new ArrayList<Node<?>>();
1859 if (schema instanceof ContainerSchemaNode) {
1860 return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser()
1861 .parse(lst, (ContainerSchemaNode) schema);
1862 } else if (schema instanceof ListSchemaNode) {
1863 return CnSnToNormalizedNodeParserFactory.getInstance().getMapEntryNodeParser()
1864 .parse(lst, (ListSchemaNode) schema);
1867 LOG.error("Top level isn't of type container, list, leaf schema node but " + schema.getClass().getSimpleName());
1869 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1870 "It wasn't possible to translate specified data to datastore readable form."));
1873 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1874 final InstanceIdentifierContext iiWithSchemaNode) {
1875 return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false);
1878 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1879 final InstanceIdentifierContext iiWithSchemaNode, final boolean unwrapLastListNode) {
1880 return new InstanceIdentifierContext(instanceIdentifierToReadableFormForNormalizeNode(
1881 iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(),
1882 iiWithSchemaNode.getMountPoint(),iiWithSchemaNode.getSchemaContext());
1885 private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode(
1886 final YangInstanceIdentifier instIdentifier, final boolean unwrapLastListNode) {
1887 Preconditions.checkNotNull(instIdentifier, "Instance identifier can't be null");
1888 final List<PathArgument> result = new ArrayList<PathArgument>();
1889 final Iterator<PathArgument> iter = instIdentifier.getPathArguments().iterator();
1890 while (iter.hasNext()) {
1891 final PathArgument pathArgument = iter.next();
1892 if (pathArgument instanceof NodeIdentifierWithPredicates && (iter.hasNext() || unwrapLastListNode)) {
1893 result.add(new YangInstanceIdentifier.NodeIdentifier(pathArgument.getNodeType()));
1895 result.add(pathArgument);
1897 return YangInstanceIdentifier.create(result);
1900 private CompositeNodeWrapper topLevelElementAsCompositeNodeWrapper(final NodeWrapper<?> node,
1901 final DataSchemaNode schemaNode) {
1902 if (node instanceof CompositeNodeWrapper) {
1903 return (CompositeNodeWrapper) node;
1904 } else if (node instanceof SimpleNodeWrapper && isDataContainerNode(schemaNode)) {
1905 final SimpleNodeWrapper simpleNodeWrapper = (SimpleNodeWrapper) node;
1906 return new CompositeNodeWrapper(namespace(simpleNodeWrapper), localName(simpleNodeWrapper));
1909 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1910 "Top level element has to be composite node or has to represent data container node."));
1913 private boolean isDataContainerNode(final DataSchemaNode schemaNode) {
1914 if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode) {
1920 public BigInteger getOperationalReceived() {
1921 // TODO Auto-generated method stub
1925 private MapNode makeModuleMapNode(final Set<Module> modules) {
1926 Preconditions.checkNotNull(modules);
1927 final Module restconfModule = getRestconfModule();
1928 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
1929 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
1930 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
1932 final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
1933 .mapBuilder((ListSchemaNode) moduleSchemaNode);
1935 for (final Module module : modules) {
1936 listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode));
1938 return listModuleBuilder.build();
1941 protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) {
1942 Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode,
1943 "moduleSchemaNode has to be of type ListSchemaNode");
1944 final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode;
1945 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = Builders
1946 .mapEntryBuilder(listModuleSchemaNode);
1948 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1949 (listModuleSchemaNode), "name");
1950 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1951 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1952 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName())
1955 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1956 (listModuleSchemaNode), "revision");
1957 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1958 Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode);
1959 final String revision = REVISION_FORMAT.format(module.getRevision());
1960 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision)
1963 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1964 (listModuleSchemaNode), "namespace");
1965 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1966 Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode);
1967 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode)
1968 .withValue(module.getNamespace().toString()).build());
1970 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1971 (listModuleSchemaNode), "feature");
1972 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1973 Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode);
1974 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> featuresBuilder = Builders
1975 .leafSetBuilder((LeafListSchemaNode) featureSchemaNode);
1976 for (final FeatureDefinition feature : module.getFeatures()) {
1977 featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode))
1978 .withValue(feature.getQName().getLocalName()).build());
1980 moduleNodeValues.withChild(featuresBuilder.build());
1982 return moduleNodeValues.build();
1985 protected MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamSchemaNode) {
1986 Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode,
1987 "streamSchemaNode has to be of type ListSchemaNode");
1988 final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode;
1989 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
1990 .mapEntryBuilder(listStreamSchemaNode);
1992 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1993 (listStreamSchemaNode), "name");
1994 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1995 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1996 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName)
1999 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2000 (listStreamSchemaNode), "description");
2001 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2002 Preconditions.checkState(descriptionSchemaNode instanceof LeafSchemaNode);
2003 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode)
2004 .withValue("DESCRIPTION_PLACEHOLDER").build());
2006 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2007 (listStreamSchemaNode), "replay-support");
2008 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2009 Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode);
2010 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode)
2011 .withValue(Boolean.valueOf(true)).build());
2013 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2014 (listStreamSchemaNode), "replay-log-creation-time");
2015 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2016 Preconditions.checkState(replayLogCreationTimeSchemaNode instanceof LeafSchemaNode);
2017 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode)
2018 .withValue("").build());
2020 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2021 (listStreamSchemaNode), "events");
2022 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2023 Preconditions.checkState(eventsSchemaNode instanceof LeafSchemaNode);
2024 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode)
2025 .withValue("").build());
2027 return streamNodeValues.build();