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.Preconditions;
13 import com.google.common.base.Splitter;
14 import com.google.common.base.Strings;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.Iterables;
17 import com.google.common.collect.Lists;
19 import java.text.ParseException;
20 import java.text.SimpleDateFormat;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Date;
26 import java.util.HashMap;
27 import java.util.List;
29 import java.util.concurrent.Future;
30 import javax.ws.rs.core.Response;
31 import javax.ws.rs.core.Response.Status;
32 import javax.ws.rs.core.UriBuilder;
33 import javax.ws.rs.core.UriInfo;
34 import org.apache.commons.lang3.StringUtils;
35 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
36 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
37 import org.opendaylight.controller.sal.rest.api.Draft02;
38 import org.opendaylight.controller.sal.rest.api.RestconfService;
39 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
40 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
41 import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
42 import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
43 import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
44 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
45 import org.opendaylight.controller.sal.streams.listeners.Notificator;
46 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
47 import org.opendaylight.yangtools.concepts.Codec;
48 import org.opendaylight.yangtools.yang.common.QName;
49 import org.opendaylight.yangtools.yang.common.RpcError;
50 import org.opendaylight.yangtools.yang.common.RpcResult;
51 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
52 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
53 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder;
54 import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
55 import org.opendaylight.yangtools.yang.data.api.Node;
56 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
57 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
58 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
59 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
60 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
61 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
62 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
63 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
64 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
65 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
66 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
67 import org.opendaylight.yangtools.yang.model.api.Module;
68 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
69 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
70 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
71 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
72 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
73 import org.opendaylight.yangtools.yang.model.util.EmptyType;
74 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
75 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
77 public class RestconfImpl implements RestconfService {
78 private final static RestconfImpl INSTANCE = new RestconfImpl();
80 private static final int CHAR_NOT_FOUND = -1;
82 private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
84 private final static SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
86 private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
88 private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
90 private BrokerFacade broker;
92 private ControllerContext controllerContext;
94 public void setBroker(final BrokerFacade broker) {
98 public void setControllerContext(final ControllerContext controllerContext) {
99 this.controllerContext = controllerContext;
102 private RestconfImpl() {
105 public static RestconfImpl getInstance() {
110 public StructuredData getModules() {
111 final Module restconfModule = this.getRestconfModule();
113 final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
114 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
115 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
117 Set<Module> allModules = this.controllerContext.getAllModules();
118 for (final Module module : allModules) {
119 CompositeNode moduleCompositeNode = this.toModuleCompositeNode(module, moduleSchemaNode);
120 modulesAsData.add(moduleCompositeNode);
123 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
124 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
125 QName qName = modulesSchemaNode.getQName();
126 final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
127 return new StructuredData(modulesNode, modulesSchemaNode, null);
131 public StructuredData getAvailableStreams() {
132 Set<String> availableStreams = Notificator.getStreamNames();
134 final List<Node<?>> streamsAsData = new ArrayList<Node<?>>();
135 Module restconfModule = this.getRestconfModule();
136 final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
137 restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
138 for (final String streamName : availableStreams) {
139 streamsAsData.add(this.toStreamCompositeNode(streamName, streamSchemaNode));
142 final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
143 restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
144 QName qName = streamsSchemaNode.getQName();
145 final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData);
146 return new StructuredData(streamsNode, streamsSchemaNode, null);
150 public StructuredData getModules(final String identifier) {
151 Set<Module> modules = null;
152 MountInstance mountPoint = null;
153 if (identifier.contains(ControllerContext.MOUNT)) {
154 InstanceIdWithSchemaNode mountPointIdentifier =
155 this.controllerContext.toMountPointIdentifier(identifier);
156 mountPoint = mountPointIdentifier.getMountPoint();
157 modules = this.controllerContext.getAllModules(mountPoint);
160 throw new RestconfDocumentedException(
161 "URI has bad format. If modules behind mount point should be showed, URI has to end with " +
162 ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
165 final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
166 Module restconfModule = this.getRestconfModule();
167 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
168 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
170 for (final Module module : modules) {
171 modulesAsData.add(this.toModuleCompositeNode(module, moduleSchemaNode));
174 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
175 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
176 QName qName = modulesSchemaNode.getQName();
177 final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
178 return new StructuredData(modulesNode, modulesSchemaNode, mountPoint);
182 public StructuredData getModule(final String identifier) {
183 final QName moduleNameAndRevision = this.getModuleNameAndRevision(identifier);
184 Module module = null;
185 MountInstance mountPoint = null;
186 if (identifier.contains(ControllerContext.MOUNT)) {
187 InstanceIdWithSchemaNode mountPointIdentifier =
188 this.controllerContext.toMountPointIdentifier(identifier);
189 mountPoint = mountPointIdentifier.getMountPoint();
190 module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
193 module = this.controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
196 if (module == null) {
197 throw new RestconfDocumentedException(
198 "Module with name '" + moduleNameAndRevision.getLocalName() + "' and revision '" +
199 moduleNameAndRevision.getRevision() + "' was not found.",
200 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
203 Module restconfModule = this.getRestconfModule();
204 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
205 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
206 final CompositeNode moduleNode = this.toModuleCompositeNode(module, moduleSchemaNode);
207 return new StructuredData(moduleNode, moduleSchemaNode, mountPoint);
211 public StructuredData getOperations() {
212 Set<Module> allModules = this.controllerContext.getAllModules();
213 return this.operationsFromModulesToStructuredData(allModules, null);
217 public StructuredData getOperations(final String identifier) {
218 Set<Module> modules = null;
219 MountInstance mountPoint = null;
220 if (identifier.contains(ControllerContext.MOUNT)) {
221 InstanceIdWithSchemaNode mountPointIdentifier =
222 this.controllerContext.toMountPointIdentifier(identifier);
223 mountPoint = mountPointIdentifier.getMountPoint();
224 modules = this.controllerContext.getAllModules(mountPoint);
227 throw new RestconfDocumentedException(
228 "URI has bad format. If operations behind mount point should be showed, URI has to end with " +
229 ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
232 return this.operationsFromModulesToStructuredData(modules, mountPoint);
235 private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
236 final MountInstance mountPoint) {
237 final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
238 Module restconfModule = this.getRestconfModule();
239 final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
240 restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
241 QName qName = operationsSchemaNode.getQName();
242 SchemaPath path = operationsSchemaNode.getPath();
243 ContainerSchemaNodeBuilder containerSchemaNodeBuilder =
244 new ContainerSchemaNodeBuilder(Draft02.RestConfModule.NAME, 0, qName, path);
245 final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
246 for (final Module module : modules) {
247 Set<RpcDefinition> rpcs = module.getRpcs();
248 for (final RpcDefinition rpc : rpcs) {
249 QName rpcQName = rpc.getQName();
250 SimpleNode<Object> immutableSimpleNode =
251 NodeFactory.<Object>createImmutableSimpleNode(rpcQName, null, null);
252 operationsAsData.add(immutableSimpleNode);
254 String name = module.getName();
255 LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName,
256 SchemaPath.create(true, QName.create("dummy")));
257 final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
258 fakeRpcSchemaNode.setAugmenting(true);
260 EmptyType instance = EmptyType.getInstance();
261 fakeRpcSchemaNode.setType(instance);
262 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build());
266 final CompositeNode operationsNode =
267 NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
268 ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
269 return new StructuredData(operationsNode, schemaNode, mountPoint);
272 private Module getRestconfModule() {
273 Module restconfModule = controllerContext.getRestconfModule();
274 if (restconfModule == null) {
275 throw new RestconfDocumentedException(
276 "ietf-restconf module was not found.", ErrorType.APPLICATION,
277 ErrorTag.OPERATION_NOT_SUPPORTED );
280 return restconfModule;
283 private QName getModuleNameAndRevision(final String identifier) {
284 final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
285 String moduleNameAndRevision = "";
286 if (mountIndex >= 0) {
287 moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
290 moduleNameAndRevision = identifier;
293 Splitter splitter = Splitter.on("/").omitEmptyStrings();
294 Iterable<String> split = splitter.split(moduleNameAndRevision);
295 final List<String> pathArgs = Lists.<String>newArrayList(split);
296 if (pathArgs.size() < 2) {
297 throw new RestconfDocumentedException(
298 "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'",
299 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
303 final String moduleName = pathArgs.get( 0 );
304 String revision = pathArgs.get(1);
305 final Date moduleRevision = REVISION_FORMAT.parse(revision);
306 return QName.create(null, moduleRevision, moduleName);
308 catch (ParseException e) {
309 throw new RestconfDocumentedException(
310 "URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
311 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
315 private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
316 final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
317 List<DataSchemaNode> instanceDataChildrenByName =
318 this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) streamSchemaNode),
320 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
321 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(), null,
324 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
325 ((DataNodeContainer) streamSchemaNode), "description");
326 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
327 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
328 "DESCRIPTION_PLACEHOLDER"));
330 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
331 ((DataNodeContainer) streamSchemaNode), "replay-support");
332 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
333 streamNodeValues.add(NodeFactory.<Boolean>createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
334 Boolean.valueOf(true)));
336 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
337 ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
338 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
339 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
342 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
343 ((DataNodeContainer) streamSchemaNode), "events");
344 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
345 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(eventsSchemaNode.getQName(),
348 return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
351 private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
352 final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
353 List<DataSchemaNode> instanceDataChildrenByName =
354 this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) moduleSchemaNode), "name");
355 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
356 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(),
357 null, module.getName()));
359 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
360 ((DataNodeContainer) moduleSchemaNode), "revision");
361 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
362 Date _revision = module.getRevision();
363 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
364 REVISION_FORMAT.format(_revision)));
366 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
367 ((DataNodeContainer) moduleSchemaNode), "namespace");
368 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
369 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
370 module.getNamespace().toString()));
372 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
373 ((DataNodeContainer) moduleSchemaNode), "feature");
374 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
375 for (final FeatureDefinition feature : module.getFeatures()) {
376 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(featureSchemaNode.getQName(), null,
377 feature.getQName().getLocalName()));
380 return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
384 public Object getRoot() {
389 public StructuredData invokeRpc(final String identifier, final CompositeNode payload) {
390 final RpcExecutor rpc = this.resolveIdentifierInInvokeRpc(identifier);
391 QName rpcName = rpc.getRpcDefinition().getQName();
392 URI rpcNamespace = rpcName.getNamespace();
393 if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) &&
394 Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
395 return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition());
398 validateInput( rpc.getRpcDefinition().getInput(), payload );
400 return callRpc(rpc, payload);
403 private void validateInput(DataSchemaNode inputSchema, CompositeNode payload) {
404 if( inputSchema != null && payload == null )
406 //expected a non null payload
407 throw new RestconfDocumentedException( "Input is required.",
409 ErrorTag.MALFORMED_MESSAGE );
411 else if( inputSchema == null && payload != null )
413 //did not expect any input
414 throw new RestconfDocumentedException( "No input expected.",
416 ErrorTag.MALFORMED_MESSAGE );
420 //TODO: Validate "mandatory" and "config" values here??? Or should those be
421 // validate in a more central location inside MD-SAL core.
425 private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload,
426 final RpcDefinition rpc) {
427 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
428 final SimpleNode<? extends Object> pathNode = value == null ? null :
429 value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") );
430 final Object pathValue = pathNode == null ? null : pathNode.getValue();
432 if (!(pathValue instanceof InstanceIdentifier)) {
433 throw new RestconfDocumentedException(
434 "Instance identifier was not normalized correctly.",
435 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED );
438 final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue);
439 String streamName = null;
440 if (!Iterables.isEmpty(pathIdentifier.getPath())) {
441 String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier);
442 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier);
445 if (Strings.isNullOrEmpty(streamName)) {
446 throw new RestconfDocumentedException(
447 "Path is empty or contains data node which is not Container or List build-in type.",
448 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
451 final SimpleNode<String> streamNameNode = NodeFactory.<String>createImmutableSimpleNode(
452 QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
453 final List<Node<?>> output = new ArrayList<Node<?>>();
454 output.add(streamNameNode);
456 final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(
457 rpc.getOutput().getQName(), null, output, null, null);
459 if (!Notificator.existListenerFor(pathIdentifier)) {
460 Notificator.createListener(pathIdentifier, streamName);
463 return new StructuredData(responseData, rpc.getOutput(), null);
467 public StructuredData invokeRpc(final String identifier, final String noPayload) {
468 if (StringUtils.isNotBlank(noPayload)) {
469 throw new RestconfDocumentedException(
470 "Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
472 return invokeRpc( identifier, (CompositeNode)null );
475 private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
476 String identifierEncoded = null;
477 MountInstance mountPoint = null;
478 if (identifier.contains(ControllerContext.MOUNT)) {
479 // mounted RPC call - look up mount instance.
480 InstanceIdWithSchemaNode mountPointId = controllerContext
481 .toMountPointIdentifier(identifier);
482 mountPoint = mountPointId.getMountPoint();
484 int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
485 + ControllerContext.MOUNT.length() + 1;
486 String remoteRpcName = identifier.substring(startOfRemoteRpcName);
487 identifierEncoded = remoteRpcName;
489 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
490 final String slashErrorMsg = String
491 .format("Identifier %n%s%ncan\'t contain slash "
492 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.",
494 throw new RestconfDocumentedException(
495 slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
497 identifierEncoded = identifier;
500 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
501 RpcDefinition rpc = controllerContext.getRpcDefinition(identifierDecoded);
504 throw new RestconfDocumentedException(
505 "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
508 if (mountPoint == null) {
509 return new BrokerRpcExecutor(rpc, broker);
511 return new MountPointRpcExecutor(rpc, mountPoint);
516 private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload) {
517 if (rpcExecutor == null) {
518 throw new RestconfDocumentedException(
519 "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
522 CompositeNode rpcRequest = null;
523 RpcDefinition rpc = rpcExecutor.getRpcDefinition();
524 QName rpcName = rpc.getQName();
526 if (payload == null) {
527 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
529 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
530 List<Node<?>> input = Collections.<Node<?>> singletonList(value);
531 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
534 RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
536 checkRpcSuccessAndThrowException(rpcResult);
538 if (rpcResult.getResult() == null) {
542 if( rpc.getOutput() == null )
544 return null; //no output, nothing to send back.
547 return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null);
550 private void checkRpcSuccessAndThrowException(RpcResult<CompositeNode> rpcResult) {
551 if (rpcResult.isSuccessful() == false) {
553 Collection<RpcError> rpcErrors = rpcResult.getErrors();
554 if( rpcErrors == null || rpcErrors.isEmpty() ) {
555 throw new RestconfDocumentedException(
556 "The operation was not successful and there were no RPC errors returned",
557 ErrorType.RPC, ErrorTag.OPERATION_FAILED );
560 List<RestconfError> errorList = Lists.newArrayList();
561 for( RpcError rpcError: rpcErrors ) {
562 errorList.add( new RestconfError( rpcError ) );
565 throw new RestconfDocumentedException( errorList );
570 public StructuredData readConfigurationData(final String identifier, UriInfo info) {
571 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
572 CompositeNode data = null;
573 MountInstance mountPoint = iiWithData.getMountPoint();
574 if (mountPoint != null) {
575 data = broker.readConfigurationDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
578 data = broker.readConfigurationData(iiWithData.getInstanceIdentifier());
581 data = pruneDataAtDepth( data, parseDepthParameter( info ) );
582 return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
585 @SuppressWarnings("unchecked")
586 private <T extends Node<?>> T pruneDataAtDepth( T node, Integer depth ) {
587 if( depth == null ) {
591 if( node instanceof CompositeNode ) {
592 ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
594 for( Node<?> childNode: ((CompositeNode)node).getValue() ) {
595 newChildNodes.add( pruneDataAtDepth( childNode, depth - 1 ) );
599 return (T) ImmutableCompositeNode.create( node.getNodeType(), newChildNodes.build() );
606 private Integer parseDepthParameter( UriInfo info ) {
607 String param = info.getQueryParameters( false ).getFirst( "depth" );
608 if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) {
613 Integer depth = Integer.valueOf( param );
615 throw new RestconfDocumentedException( new RestconfError(
616 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depth,
617 null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
622 catch( NumberFormatException e ) {
623 throw new RestconfDocumentedException( new RestconfError(
624 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
625 "Invalid depth parameter: " + e.getMessage(),
626 null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
631 public StructuredData readOperationalData(final String identifier, UriInfo info) {
632 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
633 CompositeNode data = null;
634 MountInstance mountPoint = iiWithData.getMountPoint();
635 if (mountPoint != null) {
636 data = broker.readOperationalDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
639 data = broker.readOperationalData(iiWithData.getInstanceIdentifier());
642 data = pruneDataAtDepth( data, parseDepthParameter( info ) );
643 return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint);
647 public Response updateConfigurationData(final String identifier, final CompositeNode payload) {
648 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
650 validateInput(iiWithData.getSchemaNode(), payload);
652 MountInstance mountPoint = iiWithData.getMountPoint();
653 final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
654 RpcResult<TransactionStatus> status = null;
657 if (mountPoint != null) {
658 status = broker.commitConfigurationDataPutBehindMountPoint(
659 mountPoint, iiWithData.getInstanceIdentifier(), value).get();
661 status = broker.commitConfigurationDataPut(iiWithData.getInstanceIdentifier(), value).get();
664 catch( Exception e ) {
665 throw new RestconfDocumentedException( "Error updating data", e );
668 if( status.getResult() == TransactionStatus.COMMITED )
669 return Response.status(Status.OK).build();
671 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
675 public Response createConfigurationData(final String identifier, final CompositeNode payload) {
676 if( payload == null ) {
677 throw new RestconfDocumentedException( "Input is required.",
679 ErrorTag.MALFORMED_MESSAGE );
682 URI payloadNS = this.namespace(payload);
683 if (payloadNS == null) {
684 throw new RestconfDocumentedException(
685 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
686 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
689 InstanceIdWithSchemaNode iiWithData = null;
690 CompositeNode value = null;
691 if (this.representsMountPointRootData(payload)) {
692 // payload represents mount point data and URI represents path to the mount point
694 if (this.endsWithMountPoint(identifier)) {
695 throw new RestconfDocumentedException(
696 "URI has bad format. URI should be without \"" + ControllerContext.MOUNT +
697 "\" for POST operation.",
698 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
701 final String completeIdentifier = this.addMountPointIdentifier(identifier);
702 iiWithData = this.controllerContext.toInstanceIdentifier(completeIdentifier);
704 value = this.normalizeNode(payload, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
707 final InstanceIdWithSchemaNode incompleteInstIdWithData =
708 this.controllerContext.toInstanceIdentifier(identifier);
709 final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode();
710 MountInstance mountPoint = incompleteInstIdWithData.getMountPoint();
711 final Module module = this.findModule(mountPoint, payload);
712 if (module == null) {
713 throw new RestconfDocumentedException(
714 "Module was not found for \"" + payloadNS + "\"",
715 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
718 String payloadName = this.getName(payload);
719 final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
720 parentSchema, payloadName, module.getNamespace());
721 value = this.normalizeNode(payload, schemaNode, mountPoint);
723 iiWithData = this.addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode);
726 RpcResult<TransactionStatus> status = null;
727 MountInstance mountPoint = iiWithData.getMountPoint();
729 if (mountPoint != null) {
730 Future<RpcResult<TransactionStatus>> future =
731 broker.commitConfigurationDataPostBehindMountPoint(
732 mountPoint, iiWithData.getInstanceIdentifier(), value);
733 status = future == null ? null : future.get();
736 Future<RpcResult<TransactionStatus>> future =
737 broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
738 status = future == null ? null : future.get();
741 catch( Exception e ) {
742 throw new RestconfDocumentedException( "Error creating data", e );
745 if (status == null) {
746 return Response.status(Status.ACCEPTED).build();
749 if( status.getResult() == TransactionStatus.COMMITED )
750 return Response.status(Status.NO_CONTENT).build();
752 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
756 public Response createConfigurationData(final CompositeNode payload) {
757 if( payload == null ) {
758 throw new RestconfDocumentedException( "Input is required.",
760 ErrorTag.MALFORMED_MESSAGE );
763 URI payloadNS = this.namespace(payload);
764 if (payloadNS == null) {
765 throw new RestconfDocumentedException(
766 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
767 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
770 final Module module = this.findModule(null, payload);
771 if (module == null) {
772 throw new RestconfDocumentedException(
773 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
774 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
777 String payloadName = this.getName(payload);
778 final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
779 module, payloadName, module.getNamespace());
780 final CompositeNode value = this.normalizeNode(payload, schemaNode, null);
781 final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode);
782 RpcResult<TransactionStatus> status = null;
783 MountInstance mountPoint = iiWithData.getMountPoint();
786 if (mountPoint != null) {
787 Future<RpcResult<TransactionStatus>> future =
788 broker.commitConfigurationDataPostBehindMountPoint(
789 mountPoint, iiWithData.getInstanceIdentifier(), value);
790 status = future == null ? null : future.get();
793 Future<RpcResult<TransactionStatus>> future =
794 broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
795 status = future == null ? null : future.get();
798 catch( Exception e ) {
799 throw new RestconfDocumentedException( "Error creating data", e );
802 if (status == null) {
803 return Response.status(Status.ACCEPTED).build();
806 if( status.getResult() == TransactionStatus.COMMITED )
807 return Response.status(Status.NO_CONTENT).build();
809 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
813 public Response deleteConfigurationData(final String identifier) {
814 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
815 RpcResult<TransactionStatus> status = null;
816 MountInstance mountPoint = iiWithData.getMountPoint();
819 if (mountPoint != null) {
820 status = broker.commitConfigurationDataDeleteBehindMountPoint(
821 mountPoint, iiWithData.getInstanceIdentifier()).get();
824 status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier()).get();
827 catch( Exception e ) {
828 throw new RestconfDocumentedException( "Error creating data", e );
831 if( status.getResult() == TransactionStatus.COMMITED )
832 return Response.status(Status.OK).build();
834 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
838 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
839 final String streamName = Notificator.createStreamNameFromUri(identifier);
840 if (Strings.isNullOrEmpty(streamName)) {
841 throw new RestconfDocumentedException(
842 "Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
845 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
846 if (listener == null) {
847 throw new RestconfDocumentedException(
848 "Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
851 broker.registerToListenDataChanges(listener);
853 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
854 UriBuilder port = uriBuilder.port(WebSocketServer.PORT);
855 final URI uriToWebsocketServer = port.replacePath(streamName).build();
857 return Response.status(Status.OK).location(uriToWebsocketServer).build();
860 private Module findModule(final MountInstance mountPoint, final CompositeNode data) {
861 if (data instanceof CompositeNodeWrapper) {
862 return findModule(mountPoint, (CompositeNodeWrapper)data);
864 else if (data != null) {
865 URI namespace = data.getNodeType().getNamespace();
866 if (mountPoint != null) {
867 return this.controllerContext.findModuleByNamespace(mountPoint, namespace);
870 return this.controllerContext.findModuleByNamespace(namespace);
874 throw new IllegalArgumentException("Unhandled parameter types: " +
875 Arrays.<Object>asList(mountPoint, data).toString());
879 private Module findModule(final MountInstance mountPoint, final CompositeNodeWrapper data) {
880 URI namespace = data.getNamespace();
881 Preconditions.<URI>checkNotNull(namespace);
883 Module module = null;
884 if (mountPoint != null) {
885 module = this.controllerContext.findModuleByNamespace(mountPoint, namespace);
886 if (module == null) {
887 module = this.controllerContext.findModuleByName(mountPoint, namespace.toString());
891 module = this.controllerContext.findModuleByNamespace(namespace);
892 if (module == null) {
893 module = this.controllerContext.findModuleByName(namespace.toString());
900 private InstanceIdWithSchemaNode addLastIdentifierFromData(
901 final InstanceIdWithSchemaNode identifierWithSchemaNode,
902 final CompositeNode data, final DataSchemaNode schemaOfData) {
903 InstanceIdentifier instanceIdentifier = null;
904 if (identifierWithSchemaNode != null) {
905 instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
908 final InstanceIdentifier iiOriginal = instanceIdentifier;
909 InstanceIdentifierBuilder iiBuilder = null;
910 if (iiOriginal == null) {
911 iiBuilder = InstanceIdentifier.builder();
914 iiBuilder = InstanceIdentifier.builder(iiOriginal);
917 if ((schemaOfData instanceof ListSchemaNode)) {
918 HashMap<QName,Object> keys = this.resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
919 iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
922 iiBuilder.node(schemaOfData.getQName());
925 InstanceIdentifier instance = iiBuilder.toInstance();
926 MountInstance mountPoint = null;
927 if (identifierWithSchemaNode != null) {
928 mountPoint=identifierWithSchemaNode.getMountPoint();
931 return new InstanceIdWithSchemaNode(instance, schemaOfData, mountPoint);
934 private HashMap<QName,Object> resolveKeysFromData(final ListSchemaNode listNode,
935 final CompositeNode dataNode) {
936 final HashMap<QName,Object> keyValues = new HashMap<QName, Object>();
937 List<QName> _keyDefinition = listNode.getKeyDefinition();
938 for (final QName key : _keyDefinition) {
939 SimpleNode<? extends Object> head = null;
940 String localName = key.getLocalName();
941 List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
942 if (simpleNodesByName != null) {
943 head = Iterables.getFirst(simpleNodesByName, null);
946 Object dataNodeKeyValueObject = null;
948 dataNodeKeyValueObject = head.getValue();
951 if (dataNodeKeyValueObject == null) {
952 throw new RestconfDocumentedException(
953 "Data contains list \"" + dataNode.getNodeType().getLocalName() +
954 "\" which does not contain key: \"" + key.getLocalName() + "\"",
955 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
958 keyValues.put(key, dataNodeKeyValueObject);
964 private boolean endsWithMountPoint(final String identifier) {
965 return identifier.endsWith(ControllerContext.MOUNT) ||
966 identifier.endsWith(ControllerContext.MOUNT + "/");
969 private boolean representsMountPointRootData(final CompositeNode data) {
970 URI namespace = this.namespace(data);
971 return (SchemaContext.NAME.getNamespace().equals( namespace ) /* ||
972 MOUNT_POINT_MODULE_NAME.equals( namespace.toString() )*/ ) &&
973 SchemaContext.NAME.getLocalName().equals( this.localName(data) );
976 private String addMountPointIdentifier(final String identifier) {
977 boolean endsWith = identifier.endsWith("/");
979 return (identifier + ControllerContext.MOUNT);
982 return identifier + "/" + ControllerContext.MOUNT;
985 private CompositeNode normalizeNode(final CompositeNode node, final DataSchemaNode schema,
986 final MountInstance mountPoint) {
987 if (schema == null) {
988 QName nodeType = node == null ? null : node.getNodeType();
989 String localName = nodeType == null ? null : nodeType.getLocalName();
991 throw new RestconfDocumentedException(
992 "Data schema node was not found for " + localName,
993 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
996 if (!(schema instanceof DataNodeContainer)) {
997 throw new RestconfDocumentedException(
998 "Root element has to be container or list yang datatype.",
999 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1002 if ((node instanceof CompositeNodeWrapper)) {
1003 boolean isChangeAllowed = ((CompositeNodeWrapper) node).isChangeAllowed();
1004 if (isChangeAllowed) {
1006 this.normalizeNode(((CompositeNodeWrapper) node), schema, null, mountPoint);
1008 catch (IllegalArgumentException e) {
1009 throw new RestconfDocumentedException(
1010 e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1014 return ((CompositeNodeWrapper) node).unwrap();
1020 private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder,
1021 final DataSchemaNode schema, final QName previousAugment,
1022 final MountInstance mountPoint) {
1023 if (schema == null) {
1024 throw new RestconfDocumentedException(
1025 "Data has bad format.\n\"" + nodeBuilder.getLocalName() +
1026 "\" does not exist in yang schema.",
1027 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1030 QName currentAugment = null;
1031 if (nodeBuilder.getQname() != null) {
1032 currentAugment = previousAugment;
1035 currentAugment = this.normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
1036 if (nodeBuilder.getQname() == null) {
1037 throw new RestconfDocumentedException(
1038 "Data has bad format.\nIf data is in XML format then namespace for \"" +
1039 nodeBuilder.getLocalName() +
1040 "\" should be \"" + schema.getQName().getNamespace() + "\".\n" +
1041 "If data is in JSON format then module name for \"" + nodeBuilder.getLocalName() +
1042 "\" should be corresponding to namespace \"" +
1043 schema.getQName().getNamespace() + "\".",
1044 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1048 if ( nodeBuilder instanceof CompositeNodeWrapper ) {
1049 if( schema instanceof DataNodeContainer ) {
1050 normalizeCompositeNode( (CompositeNodeWrapper)nodeBuilder, (DataNodeContainer)schema,
1051 mountPoint, currentAugment );
1053 else if( schema instanceof AnyXmlSchemaNode ) {
1054 normalizeAnyXmlNode( (CompositeNodeWrapper)nodeBuilder, (AnyXmlSchemaNode)schema );
1057 else if ( nodeBuilder instanceof SimpleNodeWrapper ) {
1058 normalizeSimpleNode( (SimpleNodeWrapper) nodeBuilder, schema, mountPoint );
1060 else if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1061 normalizeEmptyNode( (EmptyNodeWrapper) nodeBuilder, schema );
1065 private void normalizeAnyXmlNode( CompositeNodeWrapper compositeNode, AnyXmlSchemaNode schema ) {
1066 List<NodeWrapper<?>> children = compositeNode.getValues();
1067 for( NodeWrapper<? extends Object> child : children ) {
1068 child.setNamespace( schema.getQName().getNamespace() );
1069 if( child instanceof CompositeNodeWrapper ) {
1070 normalizeAnyXmlNode( (CompositeNodeWrapper)child, schema );
1075 private void normalizeEmptyNode( EmptyNodeWrapper emptyNodeBuilder, DataSchemaNode schema ) {
1076 if ((schema instanceof LeafSchemaNode)) {
1077 emptyNodeBuilder.setComposite(false);
1080 if ((schema instanceof ContainerSchemaNode)) {
1081 // FIXME: Add presence check
1082 emptyNodeBuilder.setComposite(true);
1087 private void normalizeSimpleNode( SimpleNodeWrapper simpleNode, DataSchemaNode schema,
1088 MountInstance mountPoint ) {
1089 final Object value = simpleNode.getValue();
1090 Object inputValue = value;
1091 TypeDefinition<? extends Object> typeDefinition = this.typeDefinition(schema);
1092 if ((typeDefinition instanceof IdentityrefTypeDefinition)) {
1093 if ((value instanceof String)) {
1094 inputValue = new IdentityValuesDTO( simpleNode.getNamespace().toString(),
1095 (String) value, null, (String) value );
1096 } // else value is already instance of IdentityValuesDTO
1099 Object outputValue = inputValue;
1101 if( typeDefinition != null ) {
1102 Codec<Object,Object> codec = RestCodec.from(typeDefinition, mountPoint);
1103 outputValue = codec == null ? null : codec.deserialize(inputValue);
1106 simpleNode.setValue(outputValue);
1109 private void normalizeCompositeNode( CompositeNodeWrapper compositeNodeBuilder,
1110 DataNodeContainer schema, MountInstance mountPoint,
1111 QName currentAugment ) {
1112 final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
1113 for (final NodeWrapper<? extends Object> child : children) {
1114 final List<DataSchemaNode> potentialSchemaNodes =
1115 this.controllerContext.findInstanceDataChildrenByName(
1116 schema, child.getLocalName());
1118 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
1119 StringBuilder builder = new StringBuilder();
1120 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1121 builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString())
1125 throw new RestconfDocumentedException(
1126 "Node \"" + child.getLocalName() +
1127 "\" is added as augment from more than one module. " +
1128 "Therefore node must have namespace (XML format) or module name (JSON format)." +
1129 "\nThe node is added as augment from modules with namespaces:\n" + builder,
1130 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1133 boolean rightNodeSchemaFound = false;
1134 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1135 if (!rightNodeSchemaFound) {
1136 final QName potentialCurrentAugment =
1137 this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint);
1138 if (child.getQname() != null ) {
1139 this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1140 rightNodeSchemaFound = true;
1145 if (!rightNodeSchemaFound) {
1146 throw new RestconfDocumentedException(
1147 "Schema node \"" + child.getLocalName() + "\" was not found in module.",
1148 ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
1152 if ((schema instanceof ListSchemaNode)) {
1153 ListSchemaNode listSchemaNode = (ListSchemaNode)schema;
1154 final List<QName> listKeys = listSchemaNode.getKeyDefinition();
1155 for (final QName listKey : listKeys) {
1156 boolean foundKey = false;
1157 for (final NodeWrapper<? extends Object> child : children) {
1158 if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1164 throw new RestconfDocumentedException(
1165 "Missing key in URI \"" + listKey.getLocalName() +
1166 "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"",
1167 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1173 private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder,
1174 final DataSchemaNode schema, final QName previousAugment,
1175 final MountInstance mountPoint) {
1176 QName validQName = schema.getQName();
1177 QName currentAugment = previousAugment;
1178 if (schema.isAugmenting()) {
1179 currentAugment = schema.getQName();
1181 else if (previousAugment != null &&
1182 !Objects.equal( schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1183 validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1186 String moduleName = null;
1187 if (mountPoint == null) {
1188 moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1191 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1194 if (nodeBuilder.getNamespace() == null ||
1195 Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) ||
1196 Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /*||
1197 Note: this check is wrong - can never be true as it compares a URI with a String
1198 not sure what the intention is so commented out...
1199 Objects.equal(nodeBuilder.getNamespace(), MOUNT_POINT_MODULE_NAME)*/ ) {
1201 nodeBuilder.setQname(validQName);
1204 return currentAugment;
1207 private URI namespace(final CompositeNode data) {
1208 if (data instanceof CompositeNodeWrapper) {
1209 return ((CompositeNodeWrapper)data).getNamespace();
1211 else if (data != null) {
1212 return data.getNodeType().getNamespace();
1215 throw new IllegalArgumentException("Unhandled parameter types: " +
1216 Arrays.<Object>asList(data).toString());
1220 private String localName(final CompositeNode data) {
1221 if (data instanceof CompositeNodeWrapper) {
1222 return ((CompositeNodeWrapper)data).getLocalName();
1224 else if (data != null) {
1225 return data.getNodeType().getLocalName();
1228 throw new IllegalArgumentException("Unhandled parameter types: " +
1229 Arrays.<Object>asList(data).toString());
1233 private String getName(final CompositeNode data) {
1234 if (data instanceof CompositeNodeWrapper) {
1235 return ((CompositeNodeWrapper)data).getLocalName();
1237 else if (data != null) {
1238 return data.getNodeType().getLocalName();
1241 throw new IllegalArgumentException("Unhandled parameter types: " +
1242 Arrays.<Object>asList(data).toString());
1246 private TypeDefinition<? extends Object> _typeDefinition(final LeafSchemaNode node) {
1247 TypeDefinition<?> baseType = node.getType();
1248 while (baseType.getBaseType() != null) {
1249 baseType = baseType.getBaseType();
1255 private TypeDefinition<? extends Object> typeDefinition(final LeafListSchemaNode node) {
1256 TypeDefinition<?> baseType = node.getType();
1257 while (baseType.getBaseType() != null) {
1258 baseType = baseType.getBaseType();
1264 private TypeDefinition<? extends Object> typeDefinition(final DataSchemaNode node) {
1265 if (node instanceof LeafListSchemaNode) {
1266 return typeDefinition((LeafListSchemaNode)node);
1268 else if (node instanceof LeafSchemaNode) {
1269 return _typeDefinition((LeafSchemaNode)node);
1271 else if (node instanceof AnyXmlSchemaNode) {
1275 throw new IllegalArgumentException("Unhandled parameter types: " +
1276 Arrays.<Object>asList(node).toString());