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 enum UriParameters {
79 PRETTY_PRINT( "prettyPrint"),
82 private String uriParameterName;
83 UriParameters(String uriParameterName) {
84 this.uriParameterName = uriParameterName;
88 public String toString() {
89 return uriParameterName;
93 private final static RestconfImpl INSTANCE = new RestconfImpl();
95 private static final int CHAR_NOT_FOUND = -1;
97 private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
99 private final static SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
101 private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
103 private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
105 private BrokerFacade broker;
107 private ControllerContext controllerContext;
109 public void setBroker(final BrokerFacade broker) {
110 this.broker = broker;
113 public void setControllerContext(final ControllerContext controllerContext) {
114 this.controllerContext = controllerContext;
117 private RestconfImpl() {
120 public static RestconfImpl getInstance() {
125 public StructuredData getModules(final UriInfo uriInfo) {
126 final Module restconfModule = this.getRestconfModule();
128 final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
129 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
130 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
132 Set<Module> allModules = this.controllerContext.getAllModules();
133 for (final Module module : allModules) {
134 CompositeNode moduleCompositeNode = this.toModuleCompositeNode(module, moduleSchemaNode);
135 modulesAsData.add(moduleCompositeNode);
138 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
139 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
140 QName qName = modulesSchemaNode.getQName();
141 final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
142 return new StructuredData(modulesNode, modulesSchemaNode, null,parsePrettyPrintParameter( uriInfo ));
146 public StructuredData getAvailableStreams(final UriInfo uriInfo) {
147 Set<String> availableStreams = Notificator.getStreamNames();
149 final List<Node<?>> streamsAsData = new ArrayList<Node<?>>();
150 Module restconfModule = this.getRestconfModule();
151 final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
152 restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
153 for (final String streamName : availableStreams) {
154 streamsAsData.add(this.toStreamCompositeNode(streamName, streamSchemaNode));
157 final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
158 restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
159 QName qName = streamsSchemaNode.getQName();
160 final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData);
161 return new StructuredData(streamsNode, streamsSchemaNode, null,parsePrettyPrintParameter( uriInfo ));
165 public StructuredData getModules(final String identifier,final UriInfo uriInfo) {
166 Set<Module> modules = null;
167 MountInstance mountPoint = null;
168 if (identifier.contains(ControllerContext.MOUNT)) {
169 InstanceIdWithSchemaNode mountPointIdentifier =
170 this.controllerContext.toMountPointIdentifier(identifier);
171 mountPoint = mountPointIdentifier.getMountPoint();
172 modules = this.controllerContext.getAllModules(mountPoint);
175 throw new RestconfDocumentedException(
176 "URI has bad format. If modules behind mount point should be showed, URI has to end with " +
177 ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
180 final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
181 Module restconfModule = this.getRestconfModule();
182 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
183 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
185 for (final Module module : modules) {
186 modulesAsData.add(this.toModuleCompositeNode(module, moduleSchemaNode));
189 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
190 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
191 QName qName = modulesSchemaNode.getQName();
192 final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
193 return new StructuredData(modulesNode, modulesSchemaNode, mountPoint,parsePrettyPrintParameter( uriInfo ));
197 public StructuredData getModule(final String identifier,final UriInfo uriInfo) {
198 final QName moduleNameAndRevision = this.getModuleNameAndRevision(identifier);
199 Module module = null;
200 MountInstance mountPoint = null;
201 if (identifier.contains(ControllerContext.MOUNT)) {
202 InstanceIdWithSchemaNode mountPointIdentifier =
203 this.controllerContext.toMountPointIdentifier(identifier);
204 mountPoint = mountPointIdentifier.getMountPoint();
205 module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
208 module = this.controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
211 if (module == null) {
212 throw new RestconfDocumentedException(
213 "Module with name '" + moduleNameAndRevision.getLocalName() + "' and revision '" +
214 moduleNameAndRevision.getRevision() + "' was not found.",
215 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
218 Module restconfModule = this.getRestconfModule();
219 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
220 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
221 final CompositeNode moduleNode = this.toModuleCompositeNode(module, moduleSchemaNode);
222 return new StructuredData(moduleNode, moduleSchemaNode, mountPoint,parsePrettyPrintParameter( uriInfo ));
226 public StructuredData getOperations(final UriInfo uriInfo) {
227 Set<Module> allModules = this.controllerContext.getAllModules();
228 return this.operationsFromModulesToStructuredData(allModules, null,parsePrettyPrintParameter(uriInfo));
232 public StructuredData getOperations(final String identifier,final UriInfo uriInfo) {
233 Set<Module> modules = null;
234 MountInstance mountPoint = null;
235 if (identifier.contains(ControllerContext.MOUNT)) {
236 InstanceIdWithSchemaNode mountPointIdentifier =
237 this.controllerContext.toMountPointIdentifier(identifier);
238 mountPoint = mountPointIdentifier.getMountPoint();
239 modules = this.controllerContext.getAllModules(mountPoint);
242 throw new RestconfDocumentedException(
243 "URI has bad format. If operations behind mount point should be showed, URI has to end with " +
244 ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
247 return this.operationsFromModulesToStructuredData(modules, mountPoint,parsePrettyPrintParameter(uriInfo));
250 private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
251 final MountInstance mountPoint,boolean prettyPrint) {
252 final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
253 Module restconfModule = this.getRestconfModule();
254 final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
255 restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
256 QName qName = operationsSchemaNode.getQName();
257 SchemaPath path = operationsSchemaNode.getPath();
258 ContainerSchemaNodeBuilder containerSchemaNodeBuilder =
259 new ContainerSchemaNodeBuilder(Draft02.RestConfModule.NAME, 0, qName, path);
260 final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
261 for (final Module module : modules) {
262 Set<RpcDefinition> rpcs = module.getRpcs();
263 for (final RpcDefinition rpc : rpcs) {
264 QName rpcQName = rpc.getQName();
265 SimpleNode<Object> immutableSimpleNode =
266 NodeFactory.<Object>createImmutableSimpleNode(rpcQName, null, null);
267 operationsAsData.add(immutableSimpleNode);
269 String name = module.getName();
270 LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName,
271 SchemaPath.create(true, QName.create("dummy")));
272 final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
273 fakeRpcSchemaNode.setAugmenting(true);
275 EmptyType instance = EmptyType.getInstance();
276 fakeRpcSchemaNode.setType(instance);
277 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build());
281 final CompositeNode operationsNode =
282 NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
283 ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
284 return new StructuredData(operationsNode, schemaNode, mountPoint,prettyPrint);
287 private Module getRestconfModule() {
288 Module restconfModule = controllerContext.getRestconfModule();
289 if (restconfModule == null) {
290 throw new RestconfDocumentedException(
291 "ietf-restconf module was not found.", ErrorType.APPLICATION,
292 ErrorTag.OPERATION_NOT_SUPPORTED );
295 return restconfModule;
298 private QName getModuleNameAndRevision(final String identifier) {
299 final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
300 String moduleNameAndRevision = "";
301 if (mountIndex >= 0) {
302 moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
305 moduleNameAndRevision = identifier;
308 Splitter splitter = Splitter.on("/").omitEmptyStrings();
309 Iterable<String> split = splitter.split(moduleNameAndRevision);
310 final List<String> pathArgs = Lists.<String>newArrayList(split);
311 if (pathArgs.size() < 2) {
312 throw new RestconfDocumentedException(
313 "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'",
314 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
318 final String moduleName = pathArgs.get( 0 );
319 String revision = pathArgs.get(1);
320 final Date moduleRevision = REVISION_FORMAT.parse(revision);
321 return QName.create(null, moduleRevision, moduleName);
323 catch (ParseException e) {
324 throw new RestconfDocumentedException(
325 "URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
326 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
330 private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
331 final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
332 List<DataSchemaNode> instanceDataChildrenByName =
333 this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) streamSchemaNode),
335 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
336 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(), null,
339 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
340 ((DataNodeContainer) streamSchemaNode), "description");
341 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
342 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
343 "DESCRIPTION_PLACEHOLDER"));
345 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
346 ((DataNodeContainer) streamSchemaNode), "replay-support");
347 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
348 streamNodeValues.add(NodeFactory.<Boolean>createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
349 Boolean.valueOf(true)));
351 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
352 ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
353 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
354 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
357 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
358 ((DataNodeContainer) streamSchemaNode), "events");
359 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
360 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(eventsSchemaNode.getQName(),
363 return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
366 private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
367 final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
368 List<DataSchemaNode> instanceDataChildrenByName =
369 this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) moduleSchemaNode), "name");
370 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
371 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(),
372 null, module.getName()));
374 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
375 ((DataNodeContainer) moduleSchemaNode), "revision");
376 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
377 Date _revision = module.getRevision();
378 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
379 REVISION_FORMAT.format(_revision)));
381 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
382 ((DataNodeContainer) moduleSchemaNode), "namespace");
383 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
384 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
385 module.getNamespace().toString()));
387 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
388 ((DataNodeContainer) moduleSchemaNode), "feature");
389 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
390 for (final FeatureDefinition feature : module.getFeatures()) {
391 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(featureSchemaNode.getQName(), null,
392 feature.getQName().getLocalName()));
395 return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
399 public Object getRoot() {
404 public StructuredData invokeRpc(final String identifier, final CompositeNode payload,final UriInfo uriInfo) {
405 final RpcExecutor rpc = this.resolveIdentifierInInvokeRpc(identifier);
406 QName rpcName = rpc.getRpcDefinition().getQName();
407 URI rpcNamespace = rpcName.getNamespace();
408 if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) &&
409 Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
410 return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition(),parsePrettyPrintParameter(uriInfo));
413 validateInput( rpc.getRpcDefinition().getInput(), payload );
415 return callRpc(rpc, payload,parsePrettyPrintParameter(uriInfo));
418 private void validateInput(final DataSchemaNode inputSchema, final CompositeNode payload) {
419 if( inputSchema != null && payload == null )
421 //expected a non null payload
422 throw new RestconfDocumentedException( "Input is required.",
424 ErrorTag.MALFORMED_MESSAGE );
426 else if( inputSchema == null && payload != null )
428 //did not expect any input
429 throw new RestconfDocumentedException( "No input expected.",
431 ErrorTag.MALFORMED_MESSAGE );
435 //TODO: Validate "mandatory" and "config" values here??? Or should those be
436 // validate in a more central location inside MD-SAL core.
440 private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload,
441 final RpcDefinition rpc,final boolean prettyPrint) {
442 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
443 final SimpleNode<? extends Object> pathNode = value == null ? null :
444 value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") );
445 final Object pathValue = pathNode == null ? null : pathNode.getValue();
447 if (!(pathValue instanceof InstanceIdentifier)) {
448 throw new RestconfDocumentedException(
449 "Instance identifier was not normalized correctly.",
450 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED );
453 final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue);
454 String streamName = null;
455 if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
456 String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier);
457 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier);
460 if (Strings.isNullOrEmpty(streamName)) {
461 throw new RestconfDocumentedException(
462 "Path is empty or contains data node which is not Container or List build-in type.",
463 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
466 final SimpleNode<String> streamNameNode = NodeFactory.<String>createImmutableSimpleNode(
467 QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
468 final List<Node<?>> output = new ArrayList<Node<?>>();
469 output.add(streamNameNode);
471 final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(
472 rpc.getOutput().getQName(), null, output, null, null);
474 if (!Notificator.existListenerFor(pathIdentifier)) {
475 Notificator.createListener(pathIdentifier, streamName);
478 return new StructuredData(responseData, rpc.getOutput(), null,prettyPrint);
482 public StructuredData invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
483 if (StringUtils.isNotBlank(noPayload)) {
484 throw new RestconfDocumentedException(
485 "Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
487 return invokeRpc( identifier, (CompositeNode)null,uriInfo);
490 private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
491 String identifierEncoded = null;
492 MountInstance mountPoint = null;
493 if (identifier.contains(ControllerContext.MOUNT)) {
494 // mounted RPC call - look up mount instance.
495 InstanceIdWithSchemaNode mountPointId = controllerContext
496 .toMountPointIdentifier(identifier);
497 mountPoint = mountPointId.getMountPoint();
499 int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
500 + ControllerContext.MOUNT.length() + 1;
501 String remoteRpcName = identifier.substring(startOfRemoteRpcName);
502 identifierEncoded = remoteRpcName;
504 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
505 final String slashErrorMsg = String
506 .format("Identifier %n%s%ncan\'t contain slash "
507 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.",
509 throw new RestconfDocumentedException(
510 slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
512 identifierEncoded = identifier;
515 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
516 RpcDefinition rpc = controllerContext.getRpcDefinition(identifierDecoded);
519 throw new RestconfDocumentedException(
520 "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
523 if (mountPoint == null) {
524 return new BrokerRpcExecutor(rpc, broker);
526 return new MountPointRpcExecutor(rpc, mountPoint);
531 private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload,boolean prettyPrint) {
532 if (rpcExecutor == null) {
533 throw new RestconfDocumentedException(
534 "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
537 CompositeNode rpcRequest = null;
538 RpcDefinition rpc = rpcExecutor.getRpcDefinition();
539 QName rpcName = rpc.getQName();
541 if (payload == null) {
542 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
544 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
545 List<Node<?>> input = Collections.<Node<?>> singletonList(value);
546 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
549 RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
551 checkRpcSuccessAndThrowException(rpcResult);
553 if (rpcResult.getResult() == null) {
557 if( rpc.getOutput() == null )
559 return null; //no output, nothing to send back.
562 return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null,prettyPrint);
565 private void checkRpcSuccessAndThrowException(final RpcResult<CompositeNode> rpcResult) {
566 if (rpcResult.isSuccessful() == false) {
568 Collection<RpcError> rpcErrors = rpcResult.getErrors();
569 if( rpcErrors == null || rpcErrors.isEmpty() ) {
570 throw new RestconfDocumentedException(
571 "The operation was not successful and there were no RPC errors returned",
572 ErrorType.RPC, ErrorTag.OPERATION_FAILED );
575 List<RestconfError> errorList = Lists.newArrayList();
576 for( RpcError rpcError: rpcErrors ) {
577 errorList.add( new RestconfError( rpcError ) );
580 throw new RestconfDocumentedException( errorList );
585 public StructuredData readConfigurationData(final String identifier, final UriInfo uriInfo) {
586 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
587 CompositeNode data = null;
588 MountInstance mountPoint = iiWithData.getMountPoint();
589 if (mountPoint != null) {
590 data = broker.readConfigurationDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
593 data = broker.readConfigurationData(iiWithData.getInstanceIdentifier());
596 data = pruneDataAtDepth( data, parseDepthParameter( uriInfo ) );
597 boolean prettyPrintMode = parsePrettyPrintParameter( uriInfo );
598 return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint(),prettyPrintMode);
601 @SuppressWarnings("unchecked")
602 private <T extends Node<?>> T pruneDataAtDepth( final T node, final Integer depth ) {
603 if( depth == null ) {
607 if( node instanceof CompositeNode ) {
608 ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
610 for( Node<?> childNode: ((CompositeNode)node).getValue() ) {
611 newChildNodes.add( pruneDataAtDepth( childNode, depth - 1 ) );
615 return (T) ImmutableCompositeNode.create( node.getNodeType(), newChildNodes.build() );
622 private Integer parseDepthParameter( final UriInfo info ) {
623 String param = info.getQueryParameters( false ).getFirst( UriParameters.DEPTH.toString() );
624 if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) {
629 Integer depth = Integer.valueOf( param );
631 throw new RestconfDocumentedException( new RestconfError(
632 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depth,
633 null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
638 catch( NumberFormatException e ) {
639 throw new RestconfDocumentedException( new RestconfError(
640 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
641 "Invalid depth parameter: " + e.getMessage(),
642 null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
647 public StructuredData readOperationalData(final String identifier, final UriInfo info) {
648 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
649 CompositeNode data = null;
650 MountInstance mountPoint = iiWithData.getMountPoint();
651 if (mountPoint != null) {
652 data = broker.readOperationalDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
655 data = broker.readOperationalData(iiWithData.getInstanceIdentifier());
658 data = pruneDataAtDepth( data, parseDepthParameter( info ) );
659 boolean prettyPrintMode = parsePrettyPrintParameter( info );
660 return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint,prettyPrintMode);
663 private boolean parsePrettyPrintParameter(UriInfo info) {
664 String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
665 return Boolean.parseBoolean(param);
669 public Response updateConfigurationData(final String identifier, final CompositeNode payload) {
670 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
672 validateInput(iiWithData.getSchemaNode(), payload);
674 MountInstance mountPoint = iiWithData.getMountPoint();
675 final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
676 RpcResult<TransactionStatus> status = null;
679 if (mountPoint != null) {
680 status = broker.commitConfigurationDataPutBehindMountPoint(
681 mountPoint, iiWithData.getInstanceIdentifier(), value).get();
683 status = broker.commitConfigurationDataPut(iiWithData.getInstanceIdentifier(), value).get();
686 catch( Exception e ) {
687 throw new RestconfDocumentedException( "Error updating data", e );
690 if( status.getResult() == TransactionStatus.COMMITED ) {
691 return Response.status(Status.OK).build();
694 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
698 public Response createConfigurationData(final String identifier, final CompositeNode payload) {
699 if( payload == null ) {
700 throw new RestconfDocumentedException( "Input is required.",
702 ErrorTag.MALFORMED_MESSAGE );
705 URI payloadNS = this.namespace(payload);
706 if (payloadNS == null) {
707 throw new RestconfDocumentedException(
708 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
709 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
712 InstanceIdWithSchemaNode iiWithData = null;
713 CompositeNode value = null;
714 if (this.representsMountPointRootData(payload)) {
715 // payload represents mount point data and URI represents path to the mount point
717 if (this.endsWithMountPoint(identifier)) {
718 throw new RestconfDocumentedException(
719 "URI has bad format. URI should be without \"" + ControllerContext.MOUNT +
720 "\" for POST operation.",
721 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
724 final String completeIdentifier = this.addMountPointIdentifier(identifier);
725 iiWithData = this.controllerContext.toInstanceIdentifier(completeIdentifier);
727 value = this.normalizeNode(payload, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
730 final InstanceIdWithSchemaNode incompleteInstIdWithData =
731 this.controllerContext.toInstanceIdentifier(identifier);
732 final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode();
733 MountInstance mountPoint = incompleteInstIdWithData.getMountPoint();
734 final Module module = this.findModule(mountPoint, payload);
735 if (module == null) {
736 throw new RestconfDocumentedException(
737 "Module was not found for \"" + payloadNS + "\"",
738 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
741 String payloadName = this.getName(payload);
742 final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
743 parentSchema, payloadName, module.getNamespace());
744 value = this.normalizeNode(payload, schemaNode, mountPoint);
746 iiWithData = this.addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode);
749 RpcResult<TransactionStatus> status = null;
750 MountInstance mountPoint = iiWithData.getMountPoint();
752 if (mountPoint != null) {
753 Future<RpcResult<TransactionStatus>> future =
754 broker.commitConfigurationDataPostBehindMountPoint(
755 mountPoint, iiWithData.getInstanceIdentifier(), value);
756 status = future == null ? null : future.get();
759 Future<RpcResult<TransactionStatus>> future =
760 broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
761 status = future == null ? null : future.get();
764 catch( Exception e ) {
765 throw new RestconfDocumentedException( "Error creating data", e );
768 if (status == null) {
769 return Response.status(Status.ACCEPTED).build();
772 if( status.getResult() == TransactionStatus.COMMITED ) {
773 return Response.status(Status.NO_CONTENT).build();
776 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
780 public Response createConfigurationData(final CompositeNode payload) {
781 if( payload == null ) {
782 throw new RestconfDocumentedException( "Input is required.",
784 ErrorTag.MALFORMED_MESSAGE );
787 URI payloadNS = this.namespace(payload);
788 if (payloadNS == null) {
789 throw new RestconfDocumentedException(
790 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
791 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
794 final Module module = this.findModule(null, payload);
795 if (module == null) {
796 throw new RestconfDocumentedException(
797 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
798 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
801 String payloadName = this.getName(payload);
802 final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
803 module, payloadName, module.getNamespace());
804 final CompositeNode value = this.normalizeNode(payload, schemaNode, null);
805 final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode);
806 RpcResult<TransactionStatus> status = null;
807 MountInstance mountPoint = iiWithData.getMountPoint();
810 if (mountPoint != null) {
811 Future<RpcResult<TransactionStatus>> future =
812 broker.commitConfigurationDataPostBehindMountPoint(
813 mountPoint, iiWithData.getInstanceIdentifier(), value);
814 status = future == null ? null : future.get();
817 Future<RpcResult<TransactionStatus>> future =
818 broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
819 status = future == null ? null : future.get();
822 catch( Exception e ) {
823 throw new RestconfDocumentedException( "Error creating data", e );
826 if (status == null) {
827 return Response.status(Status.ACCEPTED).build();
830 if( status.getResult() == TransactionStatus.COMMITED ) {
831 return Response.status(Status.NO_CONTENT).build();
834 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
838 public Response deleteConfigurationData(final String identifier) {
839 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
840 RpcResult<TransactionStatus> status = null;
841 MountInstance mountPoint = iiWithData.getMountPoint();
844 if (mountPoint != null) {
845 status = broker.commitConfigurationDataDeleteBehindMountPoint(
846 mountPoint, iiWithData.getInstanceIdentifier()).get();
849 status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier()).get();
852 catch( Exception e ) {
853 throw new RestconfDocumentedException( "Error creating data", e );
856 if( status.getResult() == TransactionStatus.COMMITED ) {
857 return Response.status(Status.OK).build();
860 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
864 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
865 final String streamName = Notificator.createStreamNameFromUri(identifier);
866 if (Strings.isNullOrEmpty(streamName)) {
867 throw new RestconfDocumentedException(
868 "Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
871 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
872 if (listener == null) {
873 throw new RestconfDocumentedException(
874 "Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
877 broker.registerToListenDataChanges(listener);
879 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
880 UriBuilder port = uriBuilder.port(WebSocketServer.PORT);
881 final URI uriToWebsocketServer = port.replacePath(streamName).build();
883 return Response.status(Status.OK).location(uriToWebsocketServer).build();
886 private Module findModule(final MountInstance mountPoint, final CompositeNode data) {
887 if (data instanceof CompositeNodeWrapper) {
888 return findModule(mountPoint, (CompositeNodeWrapper)data);
890 else if (data != null) {
891 URI namespace = data.getNodeType().getNamespace();
892 if (mountPoint != null) {
893 return this.controllerContext.findModuleByNamespace(mountPoint, namespace);
896 return this.controllerContext.findModuleByNamespace(namespace);
900 throw new IllegalArgumentException("Unhandled parameter types: " +
901 Arrays.<Object>asList(mountPoint, data).toString());
905 private Module findModule(final MountInstance mountPoint, final CompositeNodeWrapper data) {
906 URI namespace = data.getNamespace();
907 Preconditions.<URI>checkNotNull(namespace);
909 Module module = null;
910 if (mountPoint != null) {
911 module = this.controllerContext.findModuleByNamespace(mountPoint, namespace);
912 if (module == null) {
913 module = this.controllerContext.findModuleByName(mountPoint, namespace.toString());
917 module = this.controllerContext.findModuleByNamespace(namespace);
918 if (module == null) {
919 module = this.controllerContext.findModuleByName(namespace.toString());
926 private InstanceIdWithSchemaNode addLastIdentifierFromData(
927 final InstanceIdWithSchemaNode identifierWithSchemaNode,
928 final CompositeNode data, final DataSchemaNode schemaOfData) {
929 InstanceIdentifier instanceIdentifier = null;
930 if (identifierWithSchemaNode != null) {
931 instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
934 final InstanceIdentifier iiOriginal = instanceIdentifier;
935 InstanceIdentifierBuilder iiBuilder = null;
936 if (iiOriginal == null) {
937 iiBuilder = InstanceIdentifier.builder();
940 iiBuilder = InstanceIdentifier.builder(iiOriginal);
943 if ((schemaOfData instanceof ListSchemaNode)) {
944 HashMap<QName,Object> keys = this.resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
945 iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
948 iiBuilder.node(schemaOfData.getQName());
951 InstanceIdentifier instance = iiBuilder.toInstance();
952 MountInstance mountPoint = null;
953 if (identifierWithSchemaNode != null) {
954 mountPoint=identifierWithSchemaNode.getMountPoint();
957 return new InstanceIdWithSchemaNode(instance, schemaOfData, mountPoint);
960 private HashMap<QName,Object> resolveKeysFromData(final ListSchemaNode listNode,
961 final CompositeNode dataNode) {
962 final HashMap<QName,Object> keyValues = new HashMap<QName, Object>();
963 List<QName> _keyDefinition = listNode.getKeyDefinition();
964 for (final QName key : _keyDefinition) {
965 SimpleNode<? extends Object> head = null;
966 String localName = key.getLocalName();
967 List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
968 if (simpleNodesByName != null) {
969 head = Iterables.getFirst(simpleNodesByName, null);
972 Object dataNodeKeyValueObject = null;
974 dataNodeKeyValueObject = head.getValue();
977 if (dataNodeKeyValueObject == null) {
978 throw new RestconfDocumentedException(
979 "Data contains list \"" + dataNode.getNodeType().getLocalName() +
980 "\" which does not contain key: \"" + key.getLocalName() + "\"",
981 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
984 keyValues.put(key, dataNodeKeyValueObject);
990 private boolean endsWithMountPoint(final String identifier) {
991 return identifier.endsWith(ControllerContext.MOUNT) ||
992 identifier.endsWith(ControllerContext.MOUNT + "/");
995 private boolean representsMountPointRootData(final CompositeNode data) {
996 URI namespace = this.namespace(data);
997 return (SchemaContext.NAME.getNamespace().equals( namespace ) /* ||
998 MOUNT_POINT_MODULE_NAME.equals( namespace.toString() )*/ ) &&
999 SchemaContext.NAME.getLocalName().equals( this.localName(data) );
1002 private String addMountPointIdentifier(final String identifier) {
1003 boolean endsWith = identifier.endsWith("/");
1005 return (identifier + ControllerContext.MOUNT);
1008 return identifier + "/" + ControllerContext.MOUNT;
1011 private CompositeNode normalizeNode(final CompositeNode node, final DataSchemaNode schema,
1012 final MountInstance mountPoint) {
1013 if (schema == null) {
1014 QName nodeType = node == null ? null : node.getNodeType();
1015 String localName = nodeType == null ? null : nodeType.getLocalName();
1017 throw new RestconfDocumentedException(
1018 "Data schema node was not found for " + localName,
1019 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1022 if (!(schema instanceof DataNodeContainer)) {
1023 throw new RestconfDocumentedException(
1024 "Root element has to be container or list yang datatype.",
1025 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1028 if ((node instanceof CompositeNodeWrapper)) {
1029 boolean isChangeAllowed = ((CompositeNodeWrapper) node).isChangeAllowed();
1030 if (isChangeAllowed) {
1032 this.normalizeNode(((CompositeNodeWrapper) node), schema, null, mountPoint);
1034 catch (IllegalArgumentException e) {
1035 throw new RestconfDocumentedException(
1036 e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1040 return ((CompositeNodeWrapper) node).unwrap();
1046 private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder,
1047 final DataSchemaNode schema, final QName previousAugment,
1048 final MountInstance mountPoint) {
1049 if (schema == null) {
1050 throw new RestconfDocumentedException(
1051 "Data has bad format.\n\"" + nodeBuilder.getLocalName() +
1052 "\" does not exist in yang schema.",
1053 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1056 QName currentAugment = null;
1057 if (nodeBuilder.getQname() != null) {
1058 currentAugment = previousAugment;
1061 currentAugment = this.normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
1062 if (nodeBuilder.getQname() == null) {
1063 throw new RestconfDocumentedException(
1064 "Data has bad format.\nIf data is in XML format then namespace for \"" +
1065 nodeBuilder.getLocalName() +
1066 "\" should be \"" + schema.getQName().getNamespace() + "\".\n" +
1067 "If data is in JSON format then module name for \"" + nodeBuilder.getLocalName() +
1068 "\" should be corresponding to namespace \"" +
1069 schema.getQName().getNamespace() + "\".",
1070 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1074 if ( nodeBuilder instanceof CompositeNodeWrapper ) {
1075 if( schema instanceof DataNodeContainer ) {
1076 normalizeCompositeNode( (CompositeNodeWrapper)nodeBuilder, (DataNodeContainer)schema,
1077 mountPoint, currentAugment );
1079 else if( schema instanceof AnyXmlSchemaNode ) {
1080 normalizeAnyXmlNode( (CompositeNodeWrapper)nodeBuilder, (AnyXmlSchemaNode)schema );
1083 else if ( nodeBuilder instanceof SimpleNodeWrapper ) {
1084 normalizeSimpleNode( (SimpleNodeWrapper) nodeBuilder, schema, mountPoint );
1086 else if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1087 normalizeEmptyNode( (EmptyNodeWrapper) nodeBuilder, schema );
1091 private void normalizeAnyXmlNode( final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema ) {
1092 List<NodeWrapper<?>> children = compositeNode.getValues();
1093 for( NodeWrapper<? extends Object> child : children ) {
1094 child.setNamespace( schema.getQName().getNamespace() );
1095 if( child instanceof CompositeNodeWrapper ) {
1096 normalizeAnyXmlNode( (CompositeNodeWrapper)child, schema );
1101 private void normalizeEmptyNode( final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema ) {
1102 if ((schema instanceof LeafSchemaNode)) {
1103 emptyNodeBuilder.setComposite(false);
1106 if ((schema instanceof ContainerSchemaNode)) {
1107 // FIXME: Add presence check
1108 emptyNodeBuilder.setComposite(true);
1113 private void normalizeSimpleNode( final SimpleNodeWrapper simpleNode, final DataSchemaNode schema,
1114 final MountInstance mountPoint ) {
1115 final Object value = simpleNode.getValue();
1116 Object inputValue = value;
1117 TypeDefinition<? extends Object> typeDefinition = this.typeDefinition(schema);
1118 if ((typeDefinition instanceof IdentityrefTypeDefinition)) {
1119 if ((value instanceof String)) {
1120 inputValue = new IdentityValuesDTO( simpleNode.getNamespace().toString(),
1121 (String) value, null, (String) value );
1122 } // else value is already instance of IdentityValuesDTO
1125 Object outputValue = inputValue;
1127 if( typeDefinition != null ) {
1128 Codec<Object,Object> codec = RestCodec.from(typeDefinition, mountPoint);
1129 outputValue = codec == null ? null : codec.deserialize(inputValue);
1132 simpleNode.setValue(outputValue);
1135 private void normalizeCompositeNode( final CompositeNodeWrapper compositeNodeBuilder,
1136 final DataNodeContainer schema, final MountInstance mountPoint,
1137 final QName currentAugment ) {
1138 final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
1139 for (final NodeWrapper<? extends Object> child : children) {
1140 final List<DataSchemaNode> potentialSchemaNodes =
1141 this.controllerContext.findInstanceDataChildrenByName(
1142 schema, child.getLocalName());
1144 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
1145 StringBuilder builder = new StringBuilder();
1146 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1147 builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString())
1151 throw new RestconfDocumentedException(
1152 "Node \"" + child.getLocalName() +
1153 "\" is added as augment from more than one module. " +
1154 "Therefore node must have namespace (XML format) or module name (JSON format)." +
1155 "\nThe node is added as augment from modules with namespaces:\n" + builder,
1156 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1159 boolean rightNodeSchemaFound = false;
1160 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1161 if (!rightNodeSchemaFound) {
1162 final QName potentialCurrentAugment =
1163 this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint);
1164 if (child.getQname() != null ) {
1165 this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1166 rightNodeSchemaFound = true;
1171 if (!rightNodeSchemaFound) {
1172 throw new RestconfDocumentedException(
1173 "Schema node \"" + child.getLocalName() + "\" was not found in module.",
1174 ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
1178 if ((schema instanceof ListSchemaNode)) {
1179 ListSchemaNode listSchemaNode = (ListSchemaNode)schema;
1180 final List<QName> listKeys = listSchemaNode.getKeyDefinition();
1181 for (final QName listKey : listKeys) {
1182 boolean foundKey = false;
1183 for (final NodeWrapper<? extends Object> child : children) {
1184 if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1190 throw new RestconfDocumentedException(
1191 "Missing key in URI \"" + listKey.getLocalName() +
1192 "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"",
1193 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1199 private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder,
1200 final DataSchemaNode schema, final QName previousAugment,
1201 final MountInstance mountPoint) {
1202 QName validQName = schema.getQName();
1203 QName currentAugment = previousAugment;
1204 if (schema.isAugmenting()) {
1205 currentAugment = schema.getQName();
1207 else if (previousAugment != null &&
1208 !Objects.equal( schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1209 validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1212 String moduleName = null;
1213 if (mountPoint == null) {
1214 moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1217 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1220 if (nodeBuilder.getNamespace() == null ||
1221 Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) ||
1222 Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /*||
1223 Note: this check is wrong - can never be true as it compares a URI with a String
1224 not sure what the intention is so commented out...
1225 Objects.equal(nodeBuilder.getNamespace(), MOUNT_POINT_MODULE_NAME)*/ ) {
1227 nodeBuilder.setQname(validQName);
1230 return currentAugment;
1233 private URI namespace(final CompositeNode data) {
1234 if (data instanceof CompositeNodeWrapper) {
1235 return ((CompositeNodeWrapper)data).getNamespace();
1237 else if (data != null) {
1238 return data.getNodeType().getNamespace();
1241 throw new IllegalArgumentException("Unhandled parameter types: " +
1242 Arrays.<Object>asList(data).toString());
1246 private String localName(final CompositeNode data) {
1247 if (data instanceof CompositeNodeWrapper) {
1248 return ((CompositeNodeWrapper)data).getLocalName();
1250 else if (data != null) {
1251 return data.getNodeType().getLocalName();
1254 throw new IllegalArgumentException("Unhandled parameter types: " +
1255 Arrays.<Object>asList(data).toString());
1259 private String getName(final CompositeNode data) {
1260 if (data instanceof CompositeNodeWrapper) {
1261 return ((CompositeNodeWrapper)data).getLocalName();
1263 else if (data != null) {
1264 return data.getNodeType().getLocalName();
1267 throw new IllegalArgumentException("Unhandled parameter types: " +
1268 Arrays.<Object>asList(data).toString());
1272 private TypeDefinition<? extends Object> _typeDefinition(final LeafSchemaNode node) {
1273 TypeDefinition<?> baseType = node.getType();
1274 while (baseType.getBaseType() != null) {
1275 baseType = baseType.getBaseType();
1281 private TypeDefinition<? extends Object> typeDefinition(final LeafListSchemaNode node) {
1282 TypeDefinition<?> baseType = node.getType();
1283 while (baseType.getBaseType() != null) {
1284 baseType = baseType.getBaseType();
1290 private TypeDefinition<? extends Object> typeDefinition(final DataSchemaNode node) {
1291 if (node instanceof LeafListSchemaNode) {
1292 return typeDefinition((LeafListSchemaNode)node);
1294 else if (node instanceof LeafSchemaNode) {
1295 return _typeDefinition((LeafSchemaNode)node);
1297 else if (node instanceof AnyXmlSchemaNode) {
1301 throw new IllegalArgumentException("Unhandled parameter types: " +
1302 Arrays.<Object>asList(node).toString());