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;
30 import java.util.concurrent.Future;
31 import javax.ws.rs.core.Response;
32 import javax.ws.rs.core.Response.Status;
33 import javax.ws.rs.core.UriBuilder;
34 import javax.ws.rs.core.UriInfo;
35 import org.apache.commons.lang3.StringUtils;
36 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
37 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
38 import org.opendaylight.controller.sal.rest.api.Draft02;
39 import org.opendaylight.controller.sal.rest.api.RestconfService;
40 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
41 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
42 import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
43 import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
44 import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
45 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
46 import org.opendaylight.controller.sal.streams.listeners.Notificator;
47 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
48 import org.opendaylight.yangtools.concepts.Codec;
49 import org.opendaylight.yangtools.yang.common.QName;
50 import org.opendaylight.yangtools.yang.common.RpcError;
51 import org.opendaylight.yangtools.yang.common.RpcResult;
52 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
53 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
54 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder;
55 import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
56 import org.opendaylight.yangtools.yang.data.api.Node;
57 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
58 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
59 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
60 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
61 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
62 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
63 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
64 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
65 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
66 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
67 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
68 import org.opendaylight.yangtools.yang.model.api.Module;
69 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
70 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
71 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
72 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
73 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
74 import org.opendaylight.yangtools.yang.model.util.EmptyType;
75 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
76 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
78 public class RestconfImpl implements RestconfService {
79 private enum UriParameters {
80 PRETTY_PRINT( "prettyPrint"),
83 private String uriParameterName;
84 UriParameters(String uriParameterName) {
85 this.uriParameterName = uriParameterName;
89 public String toString() {
90 return uriParameterName;
94 private final static RestconfImpl INSTANCE = new RestconfImpl();
96 private static final int CHAR_NOT_FOUND = -1;
98 private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
100 private final static SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
102 private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
104 private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
106 private BrokerFacade broker;
108 private ControllerContext controllerContext;
110 public void setBroker(final BrokerFacade broker) {
111 this.broker = broker;
114 public void setControllerContext(final ControllerContext controllerContext) {
115 this.controllerContext = controllerContext;
118 private RestconfImpl() {
121 public static RestconfImpl getInstance() {
126 public StructuredData getModules(final UriInfo uriInfo) {
127 final Module restconfModule = this.getRestconfModule();
129 final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
130 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
131 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
133 Set<Module> allModules = this.controllerContext.getAllModules();
134 for (final Module module : allModules) {
135 CompositeNode moduleCompositeNode = this.toModuleCompositeNode(module, moduleSchemaNode);
136 modulesAsData.add(moduleCompositeNode);
139 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
140 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
141 QName qName = modulesSchemaNode.getQName();
142 final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
143 return new StructuredData(modulesNode, modulesSchemaNode, null,parsePrettyPrintParameter( uriInfo ));
147 public StructuredData getAvailableStreams(final UriInfo uriInfo) {
148 Set<String> availableStreams = Notificator.getStreamNames();
150 final List<Node<?>> streamsAsData = new ArrayList<Node<?>>();
151 Module restconfModule = this.getRestconfModule();
152 final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
153 restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
154 for (final String streamName : availableStreams) {
155 streamsAsData.add(this.toStreamCompositeNode(streamName, streamSchemaNode));
158 final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
159 restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
160 QName qName = streamsSchemaNode.getQName();
161 final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData);
162 return new StructuredData(streamsNode, streamsSchemaNode, null,parsePrettyPrintParameter( uriInfo ));
166 public StructuredData getModules(final String identifier,final UriInfo uriInfo) {
167 Set<Module> modules = null;
168 MountInstance mountPoint = null;
169 if (identifier.contains(ControllerContext.MOUNT)) {
170 InstanceIdWithSchemaNode mountPointIdentifier =
171 this.controllerContext.toMountPointIdentifier(identifier);
172 mountPoint = mountPointIdentifier.getMountPoint();
173 modules = this.controllerContext.getAllModules(mountPoint);
176 throw new RestconfDocumentedException(
177 "URI has bad format. If modules behind mount point should be showed, URI has to end with " +
178 ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
181 final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
182 Module restconfModule = this.getRestconfModule();
183 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
184 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
186 for (final Module module : modules) {
187 modulesAsData.add(this.toModuleCompositeNode(module, moduleSchemaNode));
190 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
191 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
192 QName qName = modulesSchemaNode.getQName();
193 final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
194 return new StructuredData(modulesNode, modulesSchemaNode, mountPoint,parsePrettyPrintParameter( uriInfo ));
198 public StructuredData getModule(final String identifier,final UriInfo uriInfo) {
199 final QName moduleNameAndRevision = this.getModuleNameAndRevision(identifier);
200 Module module = null;
201 MountInstance mountPoint = null;
202 if (identifier.contains(ControllerContext.MOUNT)) {
203 InstanceIdWithSchemaNode mountPointIdentifier =
204 this.controllerContext.toMountPointIdentifier(identifier);
205 mountPoint = mountPointIdentifier.getMountPoint();
206 module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
209 module = this.controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
212 if (module == null) {
213 throw new RestconfDocumentedException(
214 "Module with name '" + moduleNameAndRevision.getLocalName() + "' and revision '" +
215 moduleNameAndRevision.getRevision() + "' was not found.",
216 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
219 Module restconfModule = this.getRestconfModule();
220 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
221 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
222 final CompositeNode moduleNode = this.toModuleCompositeNode(module, moduleSchemaNode);
223 return new StructuredData(moduleNode, moduleSchemaNode, mountPoint,parsePrettyPrintParameter( uriInfo ));
227 public StructuredData getOperations(final UriInfo uriInfo) {
228 Set<Module> allModules = this.controllerContext.getAllModules();
229 return this.operationsFromModulesToStructuredData(allModules, null,parsePrettyPrintParameter(uriInfo));
233 public StructuredData getOperations(final String identifier,final UriInfo uriInfo) {
234 Set<Module> modules = null;
235 MountInstance mountPoint = null;
236 if (identifier.contains(ControllerContext.MOUNT)) {
237 InstanceIdWithSchemaNode mountPointIdentifier =
238 this.controllerContext.toMountPointIdentifier(identifier);
239 mountPoint = mountPointIdentifier.getMountPoint();
240 modules = this.controllerContext.getAllModules(mountPoint);
243 throw new RestconfDocumentedException(
244 "URI has bad format. If operations behind mount point should be showed, URI has to end with " +
245 ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
248 return this.operationsFromModulesToStructuredData(modules, mountPoint,parsePrettyPrintParameter(uriInfo));
251 private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
252 final MountInstance mountPoint,boolean prettyPrint) {
253 final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
254 Module restconfModule = this.getRestconfModule();
255 final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
256 restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
257 QName qName = operationsSchemaNode.getQName();
258 SchemaPath path = operationsSchemaNode.getPath();
259 ContainerSchemaNodeBuilder containerSchemaNodeBuilder =
260 new ContainerSchemaNodeBuilder(Draft02.RestConfModule.NAME, 0, qName, path);
261 final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
262 for (final Module module : modules) {
263 Set<RpcDefinition> rpcs = module.getRpcs();
264 for (final RpcDefinition rpc : rpcs) {
265 QName rpcQName = rpc.getQName();
266 SimpleNode<Object> immutableSimpleNode =
267 NodeFactory.<Object>createImmutableSimpleNode(rpcQName, null, null);
268 operationsAsData.add(immutableSimpleNode);
270 String name = module.getName();
271 LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName,
272 SchemaPath.create(true, QName.create("dummy")));
273 final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
274 fakeRpcSchemaNode.setAugmenting(true);
276 EmptyType instance = EmptyType.getInstance();
277 fakeRpcSchemaNode.setType(instance);
278 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build());
282 final CompositeNode operationsNode =
283 NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
284 ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
285 return new StructuredData(operationsNode, schemaNode, mountPoint,prettyPrint);
288 private Module getRestconfModule() {
289 Module restconfModule = controllerContext.getRestconfModule();
290 if (restconfModule == null) {
291 throw new RestconfDocumentedException(
292 "ietf-restconf module was not found.", ErrorType.APPLICATION,
293 ErrorTag.OPERATION_NOT_SUPPORTED );
296 return restconfModule;
299 private QName getModuleNameAndRevision(final String identifier) {
300 final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
301 String moduleNameAndRevision = "";
302 if (mountIndex >= 0) {
303 moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
306 moduleNameAndRevision = identifier;
309 Splitter splitter = Splitter.on("/").omitEmptyStrings();
310 Iterable<String> split = splitter.split(moduleNameAndRevision);
311 final List<String> pathArgs = Lists.<String>newArrayList(split);
312 if (pathArgs.size() < 2) {
313 throw new RestconfDocumentedException(
314 "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'",
315 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
319 final String moduleName = pathArgs.get( 0 );
320 String revision = pathArgs.get(1);
321 final Date moduleRevision = REVISION_FORMAT.parse(revision);
322 return QName.create(null, moduleRevision, moduleName);
324 catch (ParseException e) {
325 throw new RestconfDocumentedException(
326 "URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
327 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
331 private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
332 final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
333 List<DataSchemaNode> instanceDataChildrenByName =
334 this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) streamSchemaNode),
336 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
337 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(), null,
340 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
341 ((DataNodeContainer) streamSchemaNode), "description");
342 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
343 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
344 "DESCRIPTION_PLACEHOLDER"));
346 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
347 ((DataNodeContainer) streamSchemaNode), "replay-support");
348 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
349 streamNodeValues.add(NodeFactory.<Boolean>createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
350 Boolean.valueOf(true)));
352 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
353 ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
354 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
355 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
358 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
359 ((DataNodeContainer) streamSchemaNode), "events");
360 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
361 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(eventsSchemaNode.getQName(),
364 return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
367 private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
368 final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
369 List<DataSchemaNode> instanceDataChildrenByName =
370 this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) moduleSchemaNode), "name");
371 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
372 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(),
373 null, module.getName()));
375 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
376 ((DataNodeContainer) moduleSchemaNode), "revision");
377 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
378 Date _revision = module.getRevision();
379 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
380 REVISION_FORMAT.format(_revision)));
382 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
383 ((DataNodeContainer) moduleSchemaNode), "namespace");
384 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
385 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
386 module.getNamespace().toString()));
388 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
389 ((DataNodeContainer) moduleSchemaNode), "feature");
390 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
391 for (final FeatureDefinition feature : module.getFeatures()) {
392 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(featureSchemaNode.getQName(), null,
393 feature.getQName().getLocalName()));
396 return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
400 public Object getRoot() {
405 public StructuredData invokeRpc(final String identifier, final CompositeNode payload,final UriInfo uriInfo) {
406 final RpcExecutor rpc = this.resolveIdentifierInInvokeRpc(identifier);
407 QName rpcName = rpc.getRpcDefinition().getQName();
408 URI rpcNamespace = rpcName.getNamespace();
409 if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) &&
410 Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
411 return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition(),parsePrettyPrintParameter(uriInfo));
414 validateInput( rpc.getRpcDefinition().getInput(), payload );
416 return callRpc(rpc, payload,parsePrettyPrintParameter(uriInfo));
419 private void validateInput(final DataSchemaNode inputSchema, final CompositeNode payload) {
420 if( inputSchema != null && payload == null )
422 //expected a non null payload
423 throw new RestconfDocumentedException( "Input is required.",
425 ErrorTag.MALFORMED_MESSAGE );
427 else if( inputSchema == null && payload != null )
429 //did not expect any input
430 throw new RestconfDocumentedException( "No input expected.",
432 ErrorTag.MALFORMED_MESSAGE );
436 //TODO: Validate "mandatory" and "config" values here??? Or should those be
437 // validate in a more central location inside MD-SAL core.
441 private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload,
442 final RpcDefinition rpc,final boolean prettyPrint) {
443 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
444 final SimpleNode<? extends Object> pathNode = value == null ? null :
445 value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") );
446 final Object pathValue = pathNode == null ? null : pathNode.getValue();
448 if (!(pathValue instanceof InstanceIdentifier)) {
449 throw new RestconfDocumentedException(
450 "Instance identifier was not normalized correctly.",
451 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED );
454 final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue);
455 String streamName = null;
456 if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
457 String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier);
458 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier);
461 if (Strings.isNullOrEmpty(streamName)) {
462 throw new RestconfDocumentedException(
463 "Path is empty or contains data node which is not Container or List build-in type.",
464 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
467 final SimpleNode<String> streamNameNode = NodeFactory.<String>createImmutableSimpleNode(
468 QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
469 final List<Node<?>> output = new ArrayList<Node<?>>();
470 output.add(streamNameNode);
472 final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(
473 rpc.getOutput().getQName(), null, output, null, null);
475 if (!Notificator.existListenerFor(pathIdentifier)) {
476 Notificator.createListener(pathIdentifier, streamName);
479 return new StructuredData(responseData, rpc.getOutput(), null,prettyPrint);
483 public StructuredData invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
484 if (StringUtils.isNotBlank(noPayload)) {
485 throw new RestconfDocumentedException(
486 "Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
488 return invokeRpc( identifier, (CompositeNode)null,uriInfo);
491 private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
492 String identifierEncoded = null;
493 MountInstance mountPoint = null;
494 if (identifier.contains(ControllerContext.MOUNT)) {
495 // mounted RPC call - look up mount instance.
496 InstanceIdWithSchemaNode mountPointId = controllerContext
497 .toMountPointIdentifier(identifier);
498 mountPoint = mountPointId.getMountPoint();
500 int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
501 + ControllerContext.MOUNT.length() + 1;
502 String remoteRpcName = identifier.substring(startOfRemoteRpcName);
503 identifierEncoded = remoteRpcName;
505 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
506 final String slashErrorMsg = String
507 .format("Identifier %n%s%ncan\'t contain slash "
508 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.",
510 throw new RestconfDocumentedException(
511 slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
513 identifierEncoded = identifier;
516 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
517 RpcDefinition rpc = controllerContext.getRpcDefinition(identifierDecoded);
520 throw new RestconfDocumentedException(
521 "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
524 if (mountPoint == null) {
525 return new BrokerRpcExecutor(rpc, broker);
527 return new MountPointRpcExecutor(rpc, mountPoint);
532 private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload,boolean prettyPrint) {
533 if (rpcExecutor == null) {
534 throw new RestconfDocumentedException(
535 "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
538 CompositeNode rpcRequest = null;
539 RpcDefinition rpc = rpcExecutor.getRpcDefinition();
540 QName rpcName = rpc.getQName();
542 if (payload == null) {
543 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
545 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
546 List<Node<?>> input = Collections.<Node<?>> singletonList(value);
547 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
550 RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
552 checkRpcSuccessAndThrowException(rpcResult);
554 if (rpcResult.getResult() == null) {
558 if( rpc.getOutput() == null )
560 return null; //no output, nothing to send back.
563 return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null,prettyPrint);
566 private void checkRpcSuccessAndThrowException(final RpcResult<CompositeNode> rpcResult) {
567 if (rpcResult.isSuccessful() == false) {
569 Collection<RpcError> rpcErrors = rpcResult.getErrors();
570 if( rpcErrors == null || rpcErrors.isEmpty() ) {
571 throw new RestconfDocumentedException(
572 "The operation was not successful and there were no RPC errors returned",
573 ErrorType.RPC, ErrorTag.OPERATION_FAILED );
576 List<RestconfError> errorList = Lists.newArrayList();
577 for( RpcError rpcError: rpcErrors ) {
578 errorList.add( new RestconfError( rpcError ) );
581 throw new RestconfDocumentedException( errorList );
586 public StructuredData readConfigurationData(final String identifier, final UriInfo uriInfo) {
587 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
588 CompositeNode data = null;
589 MountInstance mountPoint = iiWithData.getMountPoint();
590 if (mountPoint != null) {
591 data = broker.readConfigurationDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
594 data = broker.readConfigurationData(iiWithData.getInstanceIdentifier());
597 data = pruneDataAtDepth( data, parseDepthParameter( uriInfo ) );
598 boolean prettyPrintMode = parsePrettyPrintParameter( uriInfo );
599 return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint(),prettyPrintMode);
602 @SuppressWarnings("unchecked")
603 private <T extends Node<?>> T pruneDataAtDepth( final T node, final Integer depth ) {
604 if( depth == null ) {
608 if( node instanceof CompositeNode ) {
609 ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
611 for( Node<?> childNode: ((CompositeNode)node).getValue() ) {
612 newChildNodes.add( pruneDataAtDepth( childNode, depth - 1 ) );
616 return (T) ImmutableCompositeNode.create( node.getNodeType(), newChildNodes.build() );
623 private Integer parseDepthParameter( final UriInfo info ) {
624 String param = info.getQueryParameters( false ).getFirst( UriParameters.DEPTH.toString() );
625 if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) {
630 Integer depth = Integer.valueOf( param );
632 throw new RestconfDocumentedException( new RestconfError(
633 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depth,
634 null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
639 catch( NumberFormatException e ) {
640 throw new RestconfDocumentedException( new RestconfError(
641 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
642 "Invalid depth parameter: " + e.getMessage(),
643 null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
648 public StructuredData readOperationalData(final String identifier, final UriInfo info) {
649 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
650 CompositeNode data = null;
651 MountInstance mountPoint = iiWithData.getMountPoint();
652 if (mountPoint != null) {
653 data = broker.readOperationalDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
656 data = broker.readOperationalData(iiWithData.getInstanceIdentifier());
659 data = pruneDataAtDepth( data, parseDepthParameter( info ) );
660 boolean prettyPrintMode = parsePrettyPrintParameter( info );
661 return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint,prettyPrintMode);
664 private boolean parsePrettyPrintParameter(UriInfo info) {
665 String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
666 return Boolean.parseBoolean(param);
670 public Response updateConfigurationData(final String identifier, final CompositeNode payload) {
671 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
673 validateInput(iiWithData.getSchemaNode(), payload);
675 MountInstance mountPoint = iiWithData.getMountPoint();
676 final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
677 RpcResult<TransactionStatus> status = null;
680 if (mountPoint != null) {
681 status = broker.commitConfigurationDataPutBehindMountPoint(
682 mountPoint, iiWithData.getInstanceIdentifier(), value).get();
684 status = broker.commitConfigurationDataPut(iiWithData.getInstanceIdentifier(), value).get();
687 catch( Exception e ) {
688 throw new RestconfDocumentedException( "Error updating data", e );
691 if( status.getResult() == TransactionStatus.COMMITED ) {
692 return Response.status(Status.OK).build();
695 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
699 public Response createConfigurationData(final String identifier, final CompositeNode payload) {
700 if( payload == null ) {
701 throw new RestconfDocumentedException( "Input is required.",
703 ErrorTag.MALFORMED_MESSAGE );
706 URI payloadNS = this.namespace(payload);
707 if (payloadNS == null) {
708 throw new RestconfDocumentedException(
709 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
710 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
713 InstanceIdWithSchemaNode iiWithData = null;
714 CompositeNode value = null;
715 if (this.representsMountPointRootData(payload)) {
716 // payload represents mount point data and URI represents path to the mount point
718 if (this.endsWithMountPoint(identifier)) {
719 throw new RestconfDocumentedException(
720 "URI has bad format. URI should be without \"" + ControllerContext.MOUNT +
721 "\" for POST operation.",
722 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
725 final String completeIdentifier = this.addMountPointIdentifier(identifier);
726 iiWithData = this.controllerContext.toInstanceIdentifier(completeIdentifier);
728 value = this.normalizeNode(payload, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
731 final InstanceIdWithSchemaNode incompleteInstIdWithData =
732 this.controllerContext.toInstanceIdentifier(identifier);
733 final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode();
734 MountInstance mountPoint = incompleteInstIdWithData.getMountPoint();
735 final Module module = this.findModule(mountPoint, payload);
736 if (module == null) {
737 throw new RestconfDocumentedException(
738 "Module was not found for \"" + payloadNS + "\"",
739 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
742 String payloadName = this.getName(payload);
743 final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
744 parentSchema, payloadName, module.getNamespace());
745 value = this.normalizeNode(payload, schemaNode, mountPoint);
747 iiWithData = this.addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode);
750 RpcResult<TransactionStatus> status = null;
751 MountInstance mountPoint = iiWithData.getMountPoint();
753 if (mountPoint != null) {
754 Future<RpcResult<TransactionStatus>> future =
755 broker.commitConfigurationDataPostBehindMountPoint(
756 mountPoint, iiWithData.getInstanceIdentifier(), value);
757 status = future == null ? null : future.get();
760 Future<RpcResult<TransactionStatus>> future =
761 broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
762 status = future == null ? null : future.get();
765 catch( Exception e ) {
766 throw new RestconfDocumentedException( "Error creating data", e );
769 if (status == null) {
770 return Response.status(Status.ACCEPTED).build();
773 if( status.getResult() == TransactionStatus.COMMITED ) {
774 return Response.status(Status.NO_CONTENT).build();
777 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
781 public Response createConfigurationData(final CompositeNode payload) {
782 if( payload == null ) {
783 throw new RestconfDocumentedException( "Input is required.",
785 ErrorTag.MALFORMED_MESSAGE );
788 URI payloadNS = this.namespace(payload);
789 if (payloadNS == null) {
790 throw new RestconfDocumentedException(
791 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
792 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
795 final Module module = this.findModule(null, payload);
796 if (module == null) {
797 throw new RestconfDocumentedException(
798 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
799 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
802 String payloadName = this.getName(payload);
803 final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
804 module, payloadName, module.getNamespace());
805 final CompositeNode value = this.normalizeNode(payload, schemaNode, null);
806 final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode);
807 RpcResult<TransactionStatus> status = null;
808 MountInstance mountPoint = iiWithData.getMountPoint();
811 if (mountPoint != null) {
812 Future<RpcResult<TransactionStatus>> future =
813 broker.commitConfigurationDataPostBehindMountPoint(
814 mountPoint, iiWithData.getInstanceIdentifier(), value);
815 status = future == null ? null : future.get();
818 Future<RpcResult<TransactionStatus>> future =
819 broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
820 status = future == null ? null : future.get();
823 catch( Exception e ) {
824 throw new RestconfDocumentedException( "Error creating data", e );
827 if (status == null) {
828 return Response.status(Status.ACCEPTED).build();
831 if( status.getResult() == TransactionStatus.COMMITED ) {
832 return Response.status(Status.NO_CONTENT).build();
835 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
839 public Response deleteConfigurationData(final String identifier) {
840 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
841 RpcResult<TransactionStatus> status = null;
842 MountInstance mountPoint = iiWithData.getMountPoint();
845 if (mountPoint != null) {
846 status = broker.commitConfigurationDataDeleteBehindMountPoint(
847 mountPoint, iiWithData.getInstanceIdentifier()).get();
850 status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier()).get();
853 catch( Exception e ) {
854 throw new RestconfDocumentedException( "Error creating data", e );
857 if( status.getResult() == TransactionStatus.COMMITED ) {
858 return Response.status(Status.OK).build();
861 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
865 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
866 final String streamName = Notificator.createStreamNameFromUri(identifier);
867 if (Strings.isNullOrEmpty(streamName)) {
868 throw new RestconfDocumentedException(
869 "Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
872 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
873 if (listener == null) {
874 throw new RestconfDocumentedException(
875 "Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
878 broker.registerToListenDataChanges(listener);
880 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
881 UriBuilder port = uriBuilder.port(WebSocketServer.PORT);
882 final URI uriToWebsocketServer = port.replacePath(streamName).build();
884 return Response.status(Status.OK).location(uriToWebsocketServer).build();
887 private Module findModule(final MountInstance mountPoint, final CompositeNode data) {
888 if (data instanceof CompositeNodeWrapper) {
889 return findModule(mountPoint, (CompositeNodeWrapper)data);
891 else if (data != null) {
892 URI namespace = data.getNodeType().getNamespace();
893 if (mountPoint != null) {
894 return this.controllerContext.findModuleByNamespace(mountPoint, namespace);
897 return this.controllerContext.findModuleByNamespace(namespace);
901 throw new IllegalArgumentException("Unhandled parameter types: " +
902 Arrays.<Object>asList(mountPoint, data).toString());
906 private Module findModule(final MountInstance mountPoint, final CompositeNodeWrapper data) {
907 URI namespace = data.getNamespace();
908 Preconditions.<URI>checkNotNull(namespace);
910 Module module = null;
911 if (mountPoint != null) {
912 module = this.controllerContext.findModuleByNamespace(mountPoint, namespace);
913 if (module == null) {
914 module = this.controllerContext.findModuleByName(mountPoint, namespace.toString());
918 module = this.controllerContext.findModuleByNamespace(namespace);
919 if (module == null) {
920 module = this.controllerContext.findModuleByName(namespace.toString());
927 private InstanceIdWithSchemaNode addLastIdentifierFromData(
928 final InstanceIdWithSchemaNode identifierWithSchemaNode,
929 final CompositeNode data, final DataSchemaNode schemaOfData) {
930 InstanceIdentifier instanceIdentifier = null;
931 if (identifierWithSchemaNode != null) {
932 instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
935 final InstanceIdentifier iiOriginal = instanceIdentifier;
936 InstanceIdentifierBuilder iiBuilder = null;
937 if (iiOriginal == null) {
938 iiBuilder = InstanceIdentifier.builder();
941 iiBuilder = InstanceIdentifier.builder(iiOriginal);
944 if ((schemaOfData instanceof ListSchemaNode)) {
945 HashMap<QName,Object> keys = this.resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
946 iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
949 iiBuilder.node(schemaOfData.getQName());
952 InstanceIdentifier instance = iiBuilder.toInstance();
953 MountInstance mountPoint = null;
954 if (identifierWithSchemaNode != null) {
955 mountPoint=identifierWithSchemaNode.getMountPoint();
958 return new InstanceIdWithSchemaNode(instance, schemaOfData, mountPoint);
961 private HashMap<QName,Object> resolveKeysFromData(final ListSchemaNode listNode,
962 final CompositeNode dataNode) {
963 final HashMap<QName,Object> keyValues = new HashMap<QName, Object>();
964 List<QName> _keyDefinition = listNode.getKeyDefinition();
965 for (final QName key : _keyDefinition) {
966 SimpleNode<? extends Object> head = null;
967 String localName = key.getLocalName();
968 List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
969 if (simpleNodesByName != null) {
970 head = Iterables.getFirst(simpleNodesByName, null);
973 Object dataNodeKeyValueObject = null;
975 dataNodeKeyValueObject = head.getValue();
978 if (dataNodeKeyValueObject == null) {
979 throw new RestconfDocumentedException(
980 "Data contains list \"" + dataNode.getNodeType().getLocalName() +
981 "\" which does not contain key: \"" + key.getLocalName() + "\"",
982 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
985 keyValues.put(key, dataNodeKeyValueObject);
991 private boolean endsWithMountPoint(final String identifier) {
992 return identifier.endsWith(ControllerContext.MOUNT) ||
993 identifier.endsWith(ControllerContext.MOUNT + "/");
996 private boolean representsMountPointRootData(final CompositeNode data) {
997 URI namespace = this.namespace(data);
998 return (SchemaContext.NAME.getNamespace().equals( namespace ) /* ||
999 MOUNT_POINT_MODULE_NAME.equals( namespace.toString() )*/ ) &&
1000 SchemaContext.NAME.getLocalName().equals( this.localName(data) );
1003 private String addMountPointIdentifier(final String identifier) {
1004 boolean endsWith = identifier.endsWith("/");
1006 return (identifier + ControllerContext.MOUNT);
1009 return identifier + "/" + ControllerContext.MOUNT;
1012 private CompositeNode normalizeNode(final CompositeNode node, final DataSchemaNode schema,
1013 final MountInstance mountPoint) {
1014 if (schema == null) {
1015 QName nodeType = node == null ? null : node.getNodeType();
1016 String localName = nodeType == null ? null : nodeType.getLocalName();
1018 throw new RestconfDocumentedException(
1019 "Data schema node was not found for " + localName,
1020 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1023 if (!(schema instanceof DataNodeContainer)) {
1024 throw new RestconfDocumentedException(
1025 "Root element has to be container or list yang datatype.",
1026 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1029 if ((node instanceof CompositeNodeWrapper)) {
1030 boolean isChangeAllowed = ((CompositeNodeWrapper) node).isChangeAllowed();
1031 if (isChangeAllowed) {
1033 this.normalizeNode(((CompositeNodeWrapper) node), schema, null, mountPoint);
1035 catch (IllegalArgumentException e) {
1036 throw new RestconfDocumentedException(
1037 e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1041 return ((CompositeNodeWrapper) node).unwrap();
1047 private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder,
1048 final DataSchemaNode schema, final QName previousAugment,
1049 final MountInstance mountPoint) {
1050 if (schema == null) {
1051 throw new RestconfDocumentedException(
1052 "Data has bad format.\n\"" + nodeBuilder.getLocalName() +
1053 "\" does not exist in yang schema.",
1054 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1057 QName currentAugment = null;
1058 if (nodeBuilder.getQname() != null) {
1059 currentAugment = previousAugment;
1062 currentAugment = this.normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
1063 if (nodeBuilder.getQname() == null) {
1064 throw new RestconfDocumentedException(
1065 "Data has bad format.\nIf data is in XML format then namespace for \"" +
1066 nodeBuilder.getLocalName() +
1067 "\" should be \"" + schema.getQName().getNamespace() + "\".\n" +
1068 "If data is in JSON format then module name for \"" + nodeBuilder.getLocalName() +
1069 "\" should be corresponding to namespace \"" +
1070 schema.getQName().getNamespace() + "\".",
1071 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1075 if ( nodeBuilder instanceof CompositeNodeWrapper ) {
1076 if( schema instanceof DataNodeContainer ) {
1077 normalizeCompositeNode( (CompositeNodeWrapper)nodeBuilder, (DataNodeContainer)schema,
1078 mountPoint, currentAugment );
1080 else if( schema instanceof AnyXmlSchemaNode ) {
1081 normalizeAnyXmlNode( (CompositeNodeWrapper)nodeBuilder, (AnyXmlSchemaNode)schema );
1084 else if ( nodeBuilder instanceof SimpleNodeWrapper ) {
1085 normalizeSimpleNode( (SimpleNodeWrapper) nodeBuilder, schema, mountPoint );
1087 else if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1088 normalizeEmptyNode( (EmptyNodeWrapper) nodeBuilder, schema );
1092 private void normalizeAnyXmlNode( final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema ) {
1093 List<NodeWrapper<?>> children = compositeNode.getValues();
1094 for( NodeWrapper<? extends Object> child : children ) {
1095 child.setNamespace( schema.getQName().getNamespace() );
1096 if( child instanceof CompositeNodeWrapper ) {
1097 normalizeAnyXmlNode( (CompositeNodeWrapper)child, schema );
1102 private void normalizeEmptyNode( final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema ) {
1103 if ((schema instanceof LeafSchemaNode)) {
1104 emptyNodeBuilder.setComposite(false);
1107 if ((schema instanceof ContainerSchemaNode)) {
1108 // FIXME: Add presence check
1109 emptyNodeBuilder.setComposite(true);
1114 private void normalizeSimpleNode( final SimpleNodeWrapper simpleNode, final DataSchemaNode schema,
1115 final MountInstance mountPoint ) {
1116 final Object value = simpleNode.getValue();
1117 Object inputValue = value;
1118 TypeDefinition<? extends Object> typeDefinition = this.typeDefinition(schema);
1119 if ((typeDefinition instanceof IdentityrefTypeDefinition)) {
1120 if ((value instanceof String)) {
1121 inputValue = new IdentityValuesDTO( simpleNode.getNamespace().toString(),
1122 (String) value, null, (String) value );
1123 } // else value is already instance of IdentityValuesDTO
1126 Object outputValue = inputValue;
1128 if( typeDefinition != null ) {
1129 Codec<Object,Object> codec = RestCodec.from(typeDefinition, mountPoint);
1130 outputValue = codec == null ? null : codec.deserialize(inputValue);
1133 simpleNode.setValue(outputValue);
1136 private void normalizeCompositeNode( final CompositeNodeWrapper compositeNodeBuilder,
1137 final DataNodeContainer schema, final MountInstance mountPoint,
1138 final QName currentAugment ) {
1139 final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
1140 checkNodeMultiplicityAccordingToSchema(schema,children);
1141 for (final NodeWrapper<? extends Object> child : children) {
1142 final List<DataSchemaNode> potentialSchemaNodes =
1143 this.controllerContext.findInstanceDataChildrenByName(
1144 schema, child.getLocalName());
1146 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
1147 StringBuilder builder = new StringBuilder();
1148 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1149 builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString())
1153 throw new RestconfDocumentedException(
1154 "Node \"" + child.getLocalName() +
1155 "\" is added as augment from more than one module. " +
1156 "Therefore node must have namespace (XML format) or module name (JSON format)." +
1157 "\nThe node is added as augment from modules with namespaces:\n" + builder,
1158 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1161 boolean rightNodeSchemaFound = false;
1162 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1163 if (!rightNodeSchemaFound) {
1164 final QName potentialCurrentAugment =
1165 this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint);
1166 if (child.getQname() != null ) {
1167 this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1168 rightNodeSchemaFound = true;
1173 if (!rightNodeSchemaFound) {
1174 throw new RestconfDocumentedException(
1175 "Schema node \"" + child.getLocalName() + "\" was not found in module.",
1176 ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
1180 if ((schema instanceof ListSchemaNode)) {
1181 ListSchemaNode listSchemaNode = (ListSchemaNode)schema;
1182 final List<QName> listKeys = listSchemaNode.getKeyDefinition();
1183 for (final QName listKey : listKeys) {
1184 boolean foundKey = false;
1185 for (final NodeWrapper<? extends Object> child : children) {
1186 if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1192 throw new RestconfDocumentedException(
1193 "Missing key in URI \"" + listKey.getLocalName() +
1194 "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"",
1195 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1201 private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer,
1202 final List<NodeWrapper<?>> nodes) {
1203 Map<String, Integer> equalNodeNamesToCounts = new HashMap<String, Integer>();
1204 for (NodeWrapper<?> child : nodes) {
1205 Integer count = equalNodeNamesToCounts.get(child.getLocalName());
1206 equalNodeNamesToCounts.put(child.getLocalName(), count == null ? 1 : ++count);
1209 for (DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) {
1210 if (childSchemaNode instanceof ContainerSchemaNode || childSchemaNode instanceof LeafSchemaNode) {
1211 String localName = childSchemaNode.getQName().getLocalName();
1212 Integer count = equalNodeNamesToCounts.get(localName);
1213 if (count != null && count > 1) {
1214 throw new RestconfDocumentedException(
1215 "Multiple input data elements were specified for '"
1216 + childSchemaNode.getQName().getLocalName()
1217 + "'. The data for this element type can only be specified once.",
1218 ErrorType.APPLICATION, ErrorTag.BAD_ELEMENT);
1224 private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder,
1225 final DataSchemaNode schema, final QName previousAugment,
1226 final MountInstance mountPoint) {
1227 QName validQName = schema.getQName();
1228 QName currentAugment = previousAugment;
1229 if (schema.isAugmenting()) {
1230 currentAugment = schema.getQName();
1232 else if (previousAugment != null &&
1233 !Objects.equal( schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1234 validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1237 String moduleName = null;
1238 if (mountPoint == null) {
1239 moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1242 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1245 if (nodeBuilder.getNamespace() == null ||
1246 Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) ||
1247 Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /*||
1248 Note: this check is wrong - can never be true as it compares a URI with a String
1249 not sure what the intention is so commented out...
1250 Objects.equal(nodeBuilder.getNamespace(), MOUNT_POINT_MODULE_NAME)*/ ) {
1252 nodeBuilder.setQname(validQName);
1255 return currentAugment;
1258 private URI namespace(final CompositeNode data) {
1259 if (data instanceof CompositeNodeWrapper) {
1260 return ((CompositeNodeWrapper)data).getNamespace();
1262 else if (data != null) {
1263 return data.getNodeType().getNamespace();
1266 throw new IllegalArgumentException("Unhandled parameter types: " +
1267 Arrays.<Object>asList(data).toString());
1271 private String localName(final CompositeNode data) {
1272 if (data instanceof CompositeNodeWrapper) {
1273 return ((CompositeNodeWrapper)data).getLocalName();
1275 else if (data != null) {
1276 return data.getNodeType().getLocalName();
1279 throw new IllegalArgumentException("Unhandled parameter types: " +
1280 Arrays.<Object>asList(data).toString());
1284 private String getName(final CompositeNode data) {
1285 if (data instanceof CompositeNodeWrapper) {
1286 return ((CompositeNodeWrapper)data).getLocalName();
1288 else if (data != null) {
1289 return data.getNodeType().getLocalName();
1292 throw new IllegalArgumentException("Unhandled parameter types: " +
1293 Arrays.<Object>asList(data).toString());
1297 private TypeDefinition<? extends Object> _typeDefinition(final LeafSchemaNode node) {
1298 TypeDefinition<?> baseType = node.getType();
1299 while (baseType.getBaseType() != null) {
1300 baseType = baseType.getBaseType();
1306 private TypeDefinition<? extends Object> typeDefinition(final LeafListSchemaNode node) {
1307 TypeDefinition<?> baseType = node.getType();
1308 while (baseType.getBaseType() != null) {
1309 baseType = baseType.getBaseType();
1315 private TypeDefinition<? extends Object> typeDefinition(final DataSchemaNode node) {
1316 if (node instanceof LeafListSchemaNode) {
1317 return typeDefinition((LeafListSchemaNode)node);
1319 else if (node instanceof LeafSchemaNode) {
1320 return _typeDefinition((LeafSchemaNode)node);
1322 else if (node instanceof AnyXmlSchemaNode) {
1326 throw new IllegalArgumentException("Unhandled parameter types: " +
1327 Arrays.<Object>asList(node).toString());