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.Iterables;
16 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;
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;
36 import org.apache.commons.lang3.StringUtils;
37 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
38 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
39 import org.opendaylight.controller.sal.rest.api.Draft02;
40 import org.opendaylight.controller.sal.rest.api.RestconfService;
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.restconf.impl.BrokerFacade;
45 import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
46 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
47 import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
48 import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
49 import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
50 import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
51 import org.opendaylight.controller.sal.restconf.impl.RestCodec;
52 import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
53 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
54 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
55 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
56 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
57 import org.opendaylight.controller.sal.streams.listeners.Notificator;
58 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
59 import org.opendaylight.yangtools.concepts.Codec;
60 import org.opendaylight.yangtools.yang.common.QName;
61 import org.opendaylight.yangtools.yang.common.RpcError;
62 import org.opendaylight.yangtools.yang.common.RpcResult;
63 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
64 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
65 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder;
66 import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
67 import org.opendaylight.yangtools.yang.data.api.Node;
68 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
69 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
70 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
71 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
72 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
73 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
74 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
75 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
76 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
77 import org.opendaylight.yangtools.yang.model.api.Module;
78 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
79 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
80 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
81 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
82 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
83 import org.opendaylight.yangtools.yang.model.util.EmptyType;
84 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
85 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
87 public class RestconfImpl implements RestconfService {
88 private final static RestconfImpl INSTANCE = new RestconfImpl();
90 private static final int CHAR_NOT_FOUND = -1;
92 private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
94 private final static SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
96 private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
98 private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
100 private BrokerFacade broker;
102 private ControllerContext controllerContext;
104 public void setBroker(final BrokerFacade broker) {
105 this.broker = broker;
108 public void setControllerContext(final ControllerContext controllerContext) {
109 this.controllerContext = controllerContext;
112 private RestconfImpl() {
115 public static RestconfImpl getInstance() {
120 public StructuredData getModules() {
121 final Module restconfModule = this.getRestconfModule();
123 final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
124 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
125 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
127 Set<Module> allModules = this.controllerContext.getAllModules();
128 for (final Module module : allModules) {
129 CompositeNode moduleCompositeNode = this.toModuleCompositeNode(module, moduleSchemaNode);
130 modulesAsData.add(moduleCompositeNode);
133 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
134 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
135 QName qName = modulesSchemaNode.getQName();
136 final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
137 return new StructuredData(modulesNode, modulesSchemaNode, null);
141 public StructuredData getAvailableStreams() {
142 Set<String> availableStreams = Notificator.getStreamNames();
144 final List<Node<?>> streamsAsData = new ArrayList<Node<?>>();
145 Module restconfModule = this.getRestconfModule();
146 final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
147 restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
148 for (final String streamName : availableStreams) {
149 streamsAsData.add(this.toStreamCompositeNode(streamName, streamSchemaNode));
152 final DataSchemaNode streamsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
153 restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
154 QName qName = streamsSchemaNode.getQName();
155 final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData);
156 return new StructuredData(streamsNode, streamsSchemaNode, null);
160 public StructuredData getModules(final String identifier) {
161 Set<Module> modules = null;
162 MountInstance mountPoint = null;
163 if (identifier.contains(ControllerContext.MOUNT)) {
164 InstanceIdWithSchemaNode mountPointIdentifier =
165 this.controllerContext.toMountPointIdentifier(identifier);
166 mountPoint = mountPointIdentifier.getMountPoint();
167 modules = this.controllerContext.getAllModules(mountPoint);
170 throw new RestconfDocumentedException(
171 "URI has bad format. If modules behind mount point should be showed, URI has to end with " +
172 ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
175 final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
176 Module restconfModule = this.getRestconfModule();
177 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
178 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
180 for (final Module module : modules) {
181 modulesAsData.add(this.toModuleCompositeNode(module, moduleSchemaNode));
184 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
185 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
186 QName qName = modulesSchemaNode.getQName();
187 final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
188 return new StructuredData(modulesNode, modulesSchemaNode, mountPoint);
192 public StructuredData getModule(final String identifier) {
193 final QName moduleNameAndRevision = this.getModuleNameAndRevision(identifier);
194 Module module = null;
195 MountInstance mountPoint = null;
196 if (identifier.contains(ControllerContext.MOUNT)) {
197 InstanceIdWithSchemaNode mountPointIdentifier =
198 this.controllerContext.toMountPointIdentifier(identifier);
199 mountPoint = mountPointIdentifier.getMountPoint();
200 module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
203 module = this.controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
206 if (module == null) {
207 throw new RestconfDocumentedException(
208 "Module with name '" + moduleNameAndRevision.getLocalName() + "' and revision '" +
209 moduleNameAndRevision.getRevision() + "' was not found.",
210 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
213 Module restconfModule = this.getRestconfModule();
214 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
215 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
216 final CompositeNode moduleNode = this.toModuleCompositeNode(module, moduleSchemaNode);
217 return new StructuredData(moduleNode, moduleSchemaNode, mountPoint);
221 public StructuredData getOperations() {
222 Set<Module> allModules = this.controllerContext.getAllModules();
223 return this.operationsFromModulesToStructuredData(allModules, null);
227 public StructuredData getOperations(final String identifier) {
228 Set<Module> modules = null;
229 MountInstance mountPoint = null;
230 if (identifier.contains(ControllerContext.MOUNT)) {
231 InstanceIdWithSchemaNode mountPointIdentifier =
232 this.controllerContext.toMountPointIdentifier(identifier);
233 mountPoint = mountPointIdentifier.getMountPoint();
234 modules = this.controllerContext.getAllModules(mountPoint);
237 throw new RestconfDocumentedException(
238 "URI has bad format. If operations behind mount point should be showed, URI has to end with " +
239 ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
242 return this.operationsFromModulesToStructuredData(modules, mountPoint);
245 private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
246 final MountInstance mountPoint) {
247 final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
248 Module restconfModule = this.getRestconfModule();
249 final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
250 restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
251 QName qName = operationsSchemaNode.getQName();
252 SchemaPath path = operationsSchemaNode.getPath();
253 ContainerSchemaNodeBuilder containerSchemaNodeBuilder =
254 new ContainerSchemaNodeBuilder(Draft02.RestConfModule.NAME, 0, qName, path);
255 final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
256 for (final Module module : modules) {
257 Set<RpcDefinition> rpcs = module.getRpcs();
258 for (final RpcDefinition rpc : rpcs) {
259 QName rpcQName = rpc.getQName();
260 SimpleNode<Object> immutableSimpleNode =
261 NodeFactory.<Object>createImmutableSimpleNode(rpcQName, null, null);
262 operationsAsData.add(immutableSimpleNode);
264 String name = module.getName();
265 LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, null);
266 final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
267 fakeRpcSchemaNode.setAugmenting(true);
269 EmptyType instance = EmptyType.getInstance();
270 fakeRpcSchemaNode.setType(instance);
271 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build());
275 final CompositeNode operationsNode =
276 NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
277 ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
278 return new StructuredData(operationsNode, schemaNode, mountPoint);
281 private Module getRestconfModule() {
282 Module restconfModule = controllerContext.getRestconfModule();
283 if (restconfModule == null) {
284 throw new RestconfDocumentedException(
285 "ietf-restconf module was not found.", ErrorType.APPLICATION,
286 ErrorTag.OPERATION_NOT_SUPPORTED );
289 return restconfModule;
292 private QName getModuleNameAndRevision(final String identifier) {
293 final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
294 String moduleNameAndRevision = "";
295 if (mountIndex >= 0) {
296 moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
299 moduleNameAndRevision = identifier;
302 Splitter splitter = Splitter.on("/").omitEmptyStrings();
303 Iterable<String> split = splitter.split(moduleNameAndRevision);
304 final List<String> pathArgs = Lists.<String>newArrayList(split);
305 if (pathArgs.size() < 2) {
306 throw new RestconfDocumentedException(
307 "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'",
308 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
312 final String moduleName = pathArgs.get( 0 );
313 String revision = pathArgs.get(1);
314 final Date moduleRevision = REVISION_FORMAT.parse(revision);
315 return QName.create(null, moduleRevision, moduleName);
317 catch (ParseException e) {
318 throw new RestconfDocumentedException(
319 "URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
320 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
324 private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
325 final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
326 List<DataSchemaNode> instanceDataChildrenByName =
327 this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) streamSchemaNode),
329 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
330 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(), null,
333 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
334 ((DataNodeContainer) streamSchemaNode), "description");
335 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
336 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
337 "DESCRIPTION_PLACEHOLDER"));
339 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
340 ((DataNodeContainer) streamSchemaNode), "replay-support");
341 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
342 streamNodeValues.add(NodeFactory.<Boolean>createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
343 Boolean.valueOf(true)));
345 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
346 ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
347 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
348 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
351 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
352 ((DataNodeContainer) streamSchemaNode), "events");
353 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
354 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(eventsSchemaNode.getQName(),
357 return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
360 private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
361 final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
362 List<DataSchemaNode> instanceDataChildrenByName =
363 this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) moduleSchemaNode), "name");
364 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
365 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(),
366 null, module.getName()));
368 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
369 ((DataNodeContainer) moduleSchemaNode), "revision");
370 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
371 Date _revision = module.getRevision();
372 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
373 REVISION_FORMAT.format(_revision)));
375 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
376 ((DataNodeContainer) moduleSchemaNode), "namespace");
377 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
378 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
379 module.getNamespace().toString()));
381 instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
382 ((DataNodeContainer) moduleSchemaNode), "feature");
383 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
384 for (final FeatureDefinition feature : module.getFeatures()) {
385 moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(featureSchemaNode.getQName(), null,
386 feature.getQName().getLocalName()));
389 return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
393 public Object getRoot() {
398 public StructuredData invokeRpc(final String identifier, final CompositeNode payload) {
399 final RpcExecutor rpc = this.resolveIdentifierInInvokeRpc(identifier);
400 QName rpcName = rpc.getRpcDefinition().getQName();
401 URI rpcNamespace = rpcName.getNamespace();
402 if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) &&
403 Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
405 return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition());
408 return callRpc(rpc, payload);
411 private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload,
412 final RpcDefinition rpc) {
413 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
414 final SimpleNode<? extends Object> pathNode = value == null ? null :
415 value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") );
416 final Object pathValue = pathNode == null ? null : pathNode.getValue();
418 if (!(pathValue instanceof InstanceIdentifier)) {
419 throw new RestconfDocumentedException(
420 "Instance identifier was not normalized correctly.",
421 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED );
424 final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue);
425 String streamName = null;
426 if (!Iterables.isEmpty(pathIdentifier.getPath())) {
427 String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier);
428 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier);
431 if (Strings.isNullOrEmpty(streamName)) {
432 throw new RestconfDocumentedException(
433 "Path is empty or contains data node which is not Container or List build-in type.",
434 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
437 final SimpleNode<String> streamNameNode = NodeFactory.<String>createImmutableSimpleNode(
438 QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
439 final List<Node<?>> output = new ArrayList<Node<?>>();
440 output.add(streamNameNode);
442 final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(
443 rpc.getOutput().getQName(), null, output, null, null);
445 if (!Notificator.existListenerFor(pathIdentifier)) {
446 Notificator.createListener(pathIdentifier, streamName);
449 return new StructuredData(responseData, rpc.getOutput(), null);
453 public StructuredData invokeRpc(final String identifier, final String noPayload) {
454 if (StringUtils.isNotBlank(noPayload)) {
455 throw new RestconfDocumentedException(
456 "Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
458 final RpcExecutor rpc = resolveIdentifierInInvokeRpc(identifier);
459 return callRpc(rpc, null);
462 private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
463 String identifierEncoded = null;
464 MountInstance mountPoint = null;
465 if (identifier.contains(ControllerContext.MOUNT)) {
466 // mounted RPC call - look up mount instance.
467 InstanceIdWithSchemaNode mountPointId = controllerContext
468 .toMountPointIdentifier(identifier);
469 mountPoint = mountPointId.getMountPoint();
471 int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
472 + ControllerContext.MOUNT.length() + 1;
473 String remoteRpcName = identifier.substring(startOfRemoteRpcName);
474 identifierEncoded = remoteRpcName;
476 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
477 final String slashErrorMsg = String
478 .format("Identifier %n%s%ncan\'t contain slash "
479 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.",
481 throw new RestconfDocumentedException(
482 slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
484 identifierEncoded = identifier;
487 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
488 RpcDefinition rpc = controllerContext.getRpcDefinition(identifierDecoded);
491 throw new RestconfDocumentedException(
492 "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
495 if (mountPoint == null) {
496 return new BrokerRpcExecutor(rpc, broker);
498 return new MountPointRpcExecutor(rpc, mountPoint);
503 private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload) {
504 if (rpcExecutor == null) {
505 throw new RestconfDocumentedException(
506 "RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT );
509 CompositeNode rpcRequest = null;
510 RpcDefinition rpc = rpcExecutor.getRpcDefinition();
511 QName rpcName = rpc.getQName();
513 if (payload == null) {
514 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
516 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
517 List<Node<?>> input = Collections.<Node<?>> singletonList(value);
518 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
521 RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
523 checkRpcSuccessAndThrowException(rpcResult);
525 if (rpcResult.getResult() == null) {
529 if( rpc.getOutput() == null )
531 return null; //no output, nothing to send back.
534 return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null);
537 private void checkRpcSuccessAndThrowException(RpcResult<CompositeNode> rpcResult) {
538 if (rpcResult.isSuccessful() == false) {
540 Collection<RpcError> rpcErrors = rpcResult.getErrors();
541 if( rpcErrors == null || rpcErrors.isEmpty() ) {
542 throw new RestconfDocumentedException(
543 "The operation was not successful and there were no RPC errors returned",
544 ErrorType.RPC, ErrorTag.OPERATION_FAILED );
547 List<RestconfError> errorList = Lists.newArrayList();
548 for( RpcError rpcError: rpcErrors ) {
549 errorList.add( new RestconfError( rpcError ) );
552 throw new RestconfDocumentedException( errorList );
557 public StructuredData readConfigurationData(final String identifier) {
558 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
559 CompositeNode data = null;
560 MountInstance mountPoint = iiWithData.getMountPoint();
561 if (mountPoint != null) {
562 data = broker.readConfigurationDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
565 data = broker.readConfigurationData(iiWithData.getInstanceIdentifier());
568 return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
572 public StructuredData readOperationalData(final String identifier) {
573 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
574 CompositeNode data = null;
575 MountInstance mountPoint = iiWithData.getMountPoint();
576 if (mountPoint != null) {
577 data = broker.readOperationalDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
580 data = broker.readOperationalData(iiWithData.getInstanceIdentifier());
583 return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint);
587 public Response updateConfigurationData(final String identifier, final CompositeNode payload) {
588 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
589 MountInstance mountPoint = iiWithData.getMountPoint();
590 final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
591 RpcResult<TransactionStatus> status = null;
594 if (mountPoint != null) {
595 status = broker.commitConfigurationDataPutBehindMountPoint(
596 mountPoint, iiWithData.getInstanceIdentifier(), value).get();
598 status = broker.commitConfigurationDataPut(iiWithData.getInstanceIdentifier(), value).get();
601 catch( Exception e ) {
602 throw new RestconfDocumentedException( "Error updating data", e );
605 if( status.getResult() == TransactionStatus.COMMITED )
606 return Response.status(Status.OK).build();
608 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
612 public Response createConfigurationData(final String identifier, final CompositeNode payload) {
613 URI payloadNS = this.namespace(payload);
614 if (payloadNS == null) {
615 throw new RestconfDocumentedException(
616 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
617 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
620 InstanceIdWithSchemaNode iiWithData = null;
621 CompositeNode value = null;
622 if (this.representsMountPointRootData(payload)) {
623 // payload represents mount point data and URI represents path to the mount point
625 if (this.endsWithMountPoint(identifier)) {
626 throw new RestconfDocumentedException(
627 "URI has bad format. URI should be without \"" + ControllerContext.MOUNT +
628 "\" for POST operation.",
629 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
632 final String completeIdentifier = this.addMountPointIdentifier(identifier);
633 iiWithData = this.controllerContext.toInstanceIdentifier(completeIdentifier);
635 value = this.normalizeNode(payload, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
638 final InstanceIdWithSchemaNode incompleteInstIdWithData =
639 this.controllerContext.toInstanceIdentifier(identifier);
640 final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode();
641 MountInstance mountPoint = incompleteInstIdWithData.getMountPoint();
642 final Module module = this.findModule(mountPoint, payload);
643 if (module == null) {
644 throw new RestconfDocumentedException(
645 "Module was not found for \"" + payloadNS + "\"",
646 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
649 String payloadName = this.getName(payload);
650 final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
651 parentSchema, payloadName, module.getNamespace());
652 value = this.normalizeNode(payload, schemaNode, mountPoint);
654 iiWithData = this.addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode);
657 RpcResult<TransactionStatus> status = null;
658 MountInstance mountPoint = iiWithData.getMountPoint();
660 if (mountPoint != null) {
661 Future<RpcResult<TransactionStatus>> future =
662 broker.commitConfigurationDataPostBehindMountPoint(
663 mountPoint, iiWithData.getInstanceIdentifier(), value);
664 status = future == null ? null : future.get();
667 Future<RpcResult<TransactionStatus>> future =
668 broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
669 status = future == null ? null : future.get();
672 catch( Exception e ) {
673 throw new RestconfDocumentedException( "Error creating data", e );
676 if (status == null) {
677 return Response.status(Status.ACCEPTED).build();
680 if( status.getResult() == TransactionStatus.COMMITED )
681 return Response.status(Status.NO_CONTENT).build();
683 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
687 public Response createConfigurationData(final CompositeNode payload) {
688 URI payloadNS = this.namespace(payload);
689 if (payloadNS == null) {
690 throw new RestconfDocumentedException(
691 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
692 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
695 final Module module = this.findModule(null, payload);
696 if (module == null) {
697 throw new RestconfDocumentedException(
698 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
699 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE );
702 String payloadName = this.getName(payload);
703 final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
704 module, payloadName, module.getNamespace());
705 final CompositeNode value = this.normalizeNode(payload, schemaNode, null);
706 final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode);
707 RpcResult<TransactionStatus> status = null;
708 MountInstance mountPoint = iiWithData.getMountPoint();
711 if (mountPoint != null) {
712 Future<RpcResult<TransactionStatus>> future =
713 broker.commitConfigurationDataPostBehindMountPoint(
714 mountPoint, iiWithData.getInstanceIdentifier(), value);
715 status = future == null ? null : future.get();
718 Future<RpcResult<TransactionStatus>> future =
719 broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
720 status = future == null ? null : future.get();
723 catch( Exception e ) {
724 throw new RestconfDocumentedException( "Error creating data", e );
727 if (status == null) {
728 return Response.status(Status.ACCEPTED).build();
731 if( status.getResult() == TransactionStatus.COMMITED )
732 return Response.status(Status.NO_CONTENT).build();
734 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
738 public Response deleteConfigurationData(final String identifier) {
739 final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
740 RpcResult<TransactionStatus> status = null;
741 MountInstance mountPoint = iiWithData.getMountPoint();
744 if (mountPoint != null) {
745 status = broker.commitConfigurationDataDeleteBehindMountPoint(
746 mountPoint, iiWithData.getInstanceIdentifier()).get();
749 status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier()).get();
752 catch( Exception e ) {
753 throw new RestconfDocumentedException( "Error creating data", e );
756 if( status.getResult() == TransactionStatus.COMMITED )
757 return Response.status(Status.OK).build();
759 return Response.status(Status.INTERNAL_SERVER_ERROR).build();
763 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
764 final String streamName = Notificator.createStreamNameFromUri(identifier);
765 if (Strings.isNullOrEmpty(streamName)) {
766 throw new RestconfDocumentedException(
767 "Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
770 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
771 if (listener == null) {
772 throw new RestconfDocumentedException(
773 "Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT );
776 broker.registerToListenDataChanges(listener);
778 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
779 UriBuilder port = uriBuilder.port(WebSocketServer.PORT);
780 final URI uriToWebsocketServer = port.replacePath(streamName).build();
782 return Response.status(Status.OK).location(uriToWebsocketServer).build();
785 private Module findModule(final MountInstance mountPoint, final CompositeNode data) {
786 if (data instanceof CompositeNodeWrapper) {
787 return findModule(mountPoint, (CompositeNodeWrapper)data);
789 else if (data != null) {
790 URI namespace = data.getNodeType().getNamespace();
791 if (mountPoint != null) {
792 return this.controllerContext.findModuleByNamespace(mountPoint, namespace);
795 return this.controllerContext.findModuleByNamespace(namespace);
799 throw new IllegalArgumentException("Unhandled parameter types: " +
800 Arrays.<Object>asList(mountPoint, data).toString());
804 private Module findModule(final MountInstance mountPoint, final CompositeNodeWrapper data) {
805 URI namespace = data.getNamespace();
806 Preconditions.<URI>checkNotNull(namespace);
808 Module module = null;
809 if (mountPoint != null) {
810 module = this.controllerContext.findModuleByNamespace(mountPoint, namespace);
811 if (module == null) {
812 module = this.controllerContext.findModuleByName(mountPoint, namespace.toString());
816 module = this.controllerContext.findModuleByNamespace(namespace);
817 if (module == null) {
818 module = this.controllerContext.findModuleByName(namespace.toString());
825 private InstanceIdWithSchemaNode addLastIdentifierFromData(
826 final InstanceIdWithSchemaNode identifierWithSchemaNode,
827 final CompositeNode data, final DataSchemaNode schemaOfData) {
828 InstanceIdentifier instanceIdentifier = null;
829 if (identifierWithSchemaNode != null) {
830 instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
833 final InstanceIdentifier iiOriginal = instanceIdentifier;
834 InstanceIdentifierBuilder iiBuilder = null;
835 if (iiOriginal == null) {
836 iiBuilder = InstanceIdentifier.builder();
839 iiBuilder = InstanceIdentifier.builder(iiOriginal);
842 if ((schemaOfData instanceof ListSchemaNode)) {
843 HashMap<QName,Object> keys = this.resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
844 iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
847 iiBuilder.node(schemaOfData.getQName());
850 InstanceIdentifier instance = iiBuilder.toInstance();
851 MountInstance mountPoint = null;
852 if (identifierWithSchemaNode != null) {
853 mountPoint=identifierWithSchemaNode.getMountPoint();
856 return new InstanceIdWithSchemaNode(instance, schemaOfData, mountPoint);
859 private HashMap<QName,Object> resolveKeysFromData(final ListSchemaNode listNode,
860 final CompositeNode dataNode) {
861 final HashMap<QName,Object> keyValues = new HashMap<QName, Object>();
862 List<QName> _keyDefinition = listNode.getKeyDefinition();
863 for (final QName key : _keyDefinition) {
864 SimpleNode<? extends Object> head = null;
865 String localName = key.getLocalName();
866 List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
867 if (simpleNodesByName != null) {
868 head = Iterables.getFirst(simpleNodesByName, null);
871 Object dataNodeKeyValueObject = null;
873 dataNodeKeyValueObject = head.getValue();
876 if (dataNodeKeyValueObject == null) {
877 throw new RestconfDocumentedException(
878 "Data contains list \"" + dataNode.getNodeType().getLocalName() +
879 "\" which does not contain key: \"" + key.getLocalName() + "\"",
880 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
883 keyValues.put(key, dataNodeKeyValueObject);
889 private boolean endsWithMountPoint(final String identifier) {
890 return identifier.endsWith(ControllerContext.MOUNT) ||
891 identifier.endsWith(ControllerContext.MOUNT + "/");
894 private boolean representsMountPointRootData(final CompositeNode data) {
895 URI namespace = this.namespace(data);
896 return (SchemaContext.NAME.getNamespace().equals( namespace ) /* ||
897 MOUNT_POINT_MODULE_NAME.equals( namespace.toString() )*/ ) &&
898 SchemaContext.NAME.getLocalName().equals( this.localName(data) );
901 private String addMountPointIdentifier(final String identifier) {
902 boolean endsWith = identifier.endsWith("/");
904 return (identifier + ControllerContext.MOUNT);
907 return identifier + "/" + ControllerContext.MOUNT;
910 private CompositeNode normalizeNode(final CompositeNode node, final DataSchemaNode schema,
911 final MountInstance mountPoint) {
912 if (schema == null) {
913 QName nodeType = node == null ? null : node.getNodeType();
914 String localName = nodeType == null ? null : nodeType.getLocalName();
916 throw new RestconfDocumentedException(
917 "Data schema node was not found for " + localName,
918 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
921 if (!(schema instanceof DataNodeContainer)) {
922 throw new RestconfDocumentedException(
923 "Root element has to be container or list yang datatype.",
924 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
927 if ((node instanceof CompositeNodeWrapper)) {
928 boolean isChangeAllowed = ((CompositeNodeWrapper) node).isChangeAllowed();
929 if (isChangeAllowed) {
931 this.normalizeNode(((CompositeNodeWrapper) node), schema, null, mountPoint);
933 catch (IllegalArgumentException e) {
934 throw new RestconfDocumentedException(
935 e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
939 return ((CompositeNodeWrapper) node).unwrap();
945 private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder,
946 final DataSchemaNode schema, final QName previousAugment,
947 final MountInstance mountPoint) {
948 if (schema == null) {
949 throw new RestconfDocumentedException(
950 "Data has bad format.\n\"" + nodeBuilder.getLocalName() +
951 "\" does not exist in yang schema.",
952 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
955 QName currentAugment = null;
956 if (nodeBuilder.getQname() != null) {
957 currentAugment = previousAugment;
960 currentAugment = this.normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
961 if (nodeBuilder.getQname() == null) {
962 throw new RestconfDocumentedException(
963 "Data has bad format.\nIf data is in XML format then namespace for \"" +
964 nodeBuilder.getLocalName() +
965 "\" should be \"" + schema.getQName().getNamespace() + "\".\n" +
966 "If data is in JSON format then module name for \"" + nodeBuilder.getLocalName() +
967 "\" should be corresponding to namespace \"" +
968 schema.getQName().getNamespace() + "\".",
969 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
973 if ((nodeBuilder instanceof CompositeNodeWrapper)) {
974 final List<NodeWrapper<?>> children = ((CompositeNodeWrapper) nodeBuilder).getValues();
975 for (final NodeWrapper<? extends Object> child : children) {
976 final List<DataSchemaNode> potentialSchemaNodes =
977 this.controllerContext.findInstanceDataChildrenByName(
978 ((DataNodeContainer) schema), child.getLocalName());
980 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
981 StringBuilder builder = new StringBuilder();
982 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
983 builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString())
987 throw new RestconfDocumentedException(
988 "Node \"" + child.getLocalName() +
989 "\" is added as augment from more than one module. " +
990 "Therefore node must have namespace (XML format) or module name (JSON format)." +
991 "\nThe node is added as augment from modules with namespaces:\n" + builder,
992 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
995 boolean rightNodeSchemaFound = false;
996 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
997 if (!rightNodeSchemaFound) {
998 final QName potentialCurrentAugment =
999 this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint);
1000 if (child.getQname() != null ) {
1001 this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1002 rightNodeSchemaFound = true;
1007 if (!rightNodeSchemaFound) {
1008 throw new RestconfDocumentedException(
1009 "Schema node \"" + child.getLocalName() + "\" was not found in module.",
1010 ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
1014 if ((schema instanceof ListSchemaNode)) {
1015 final List<QName> listKeys = ((ListSchemaNode) schema).getKeyDefinition();
1016 for (final QName listKey : listKeys) {
1017 boolean foundKey = false;
1018 for (final NodeWrapper<? extends Object> child : children) {
1019 if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1025 throw new RestconfDocumentedException(
1026 "Missing key in URI \"" + listKey.getLocalName() +
1027 "\" of list \"" + schema.getQName().getLocalName() + "\"",
1028 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
1034 if ((nodeBuilder instanceof SimpleNodeWrapper)) {
1035 final SimpleNodeWrapper simpleNode = ((SimpleNodeWrapper) nodeBuilder);
1036 final Object value = simpleNode.getValue();
1037 Object inputValue = value;
1038 TypeDefinition<? extends Object> typeDefinition = this.typeDefinition(schema);
1039 if ((typeDefinition instanceof IdentityrefTypeDefinition)) {
1040 if ((value instanceof String)) {
1041 inputValue = new IdentityValuesDTO( nodeBuilder.getNamespace().toString(),
1042 (String) value, null, (String) value );
1043 } // else value is already instance of IdentityValuesDTO
1046 Codec<Object,Object> codec = RestCodec.from(typeDefinition, mountPoint);
1047 Object outputValue = codec == null ? null : codec.deserialize(inputValue);
1049 simpleNode.setValue(outputValue);
1052 if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1053 final EmptyNodeWrapper emptyNodeBuilder = ((EmptyNodeWrapper) nodeBuilder);
1054 if ((schema instanceof LeafSchemaNode)) {
1055 emptyNodeBuilder.setComposite(false);
1058 if ((schema instanceof ContainerSchemaNode)) {
1059 // FIXME: Add presence check
1060 emptyNodeBuilder.setComposite(true);
1068 private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder,
1069 final DataSchemaNode schema, final QName previousAugment,
1070 final MountInstance mountPoint) {
1071 QName validQName = schema.getQName();
1072 QName currentAugment = previousAugment;
1073 if (schema.isAugmenting()) {
1074 currentAugment = schema.getQName();
1076 else if (previousAugment != null &&
1077 !Objects.equal( schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1078 validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1081 String moduleName = null;
1082 if (mountPoint == null) {
1083 moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1086 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1089 if (nodeBuilder.getNamespace() == null ||
1090 Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) ||
1091 Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /*||
1092 Note: this check is wrong - can never be true as it compares a URI with a String
1093 not sure what the intention is so commented out...
1094 Objects.equal(nodeBuilder.getNamespace(), MOUNT_POINT_MODULE_NAME)*/ ) {
1096 nodeBuilder.setQname(validQName);
1099 return currentAugment;
1102 private URI namespace(final CompositeNode data) {
1103 if (data instanceof CompositeNodeWrapper) {
1104 return ((CompositeNodeWrapper)data).getNamespace();
1106 else if (data != null) {
1107 return data.getNodeType().getNamespace();
1110 throw new IllegalArgumentException("Unhandled parameter types: " +
1111 Arrays.<Object>asList(data).toString());
1115 private String localName(final CompositeNode data) {
1116 if (data instanceof CompositeNodeWrapper) {
1117 return ((CompositeNodeWrapper)data).getLocalName();
1119 else if (data != null) {
1120 return data.getNodeType().getLocalName();
1123 throw new IllegalArgumentException("Unhandled parameter types: " +
1124 Arrays.<Object>asList(data).toString());
1128 private String getName(final CompositeNode data) {
1129 if (data instanceof CompositeNodeWrapper) {
1130 return ((CompositeNodeWrapper)data).getLocalName();
1132 else if (data != null) {
1133 return data.getNodeType().getLocalName();
1136 throw new IllegalArgumentException("Unhandled parameter types: " +
1137 Arrays.<Object>asList(data).toString());
1141 private TypeDefinition<? extends Object> _typeDefinition(final LeafSchemaNode node) {
1142 TypeDefinition<?> baseType = node.getType();
1143 while (baseType.getBaseType() != null) {
1144 baseType = baseType.getBaseType();
1150 private TypeDefinition<? extends Object> typeDefinition(final LeafListSchemaNode node) {
1151 TypeDefinition<?> baseType = node.getType();
1152 while (baseType.getBaseType() != null) {
1153 baseType = baseType.getBaseType();
1159 private TypeDefinition<? extends Object> typeDefinition(final DataSchemaNode node) {
1160 if (node instanceof LeafListSchemaNode) {
1161 return typeDefinition((LeafListSchemaNode)node);
1163 else if (node instanceof LeafSchemaNode) {
1164 return _typeDefinition((LeafSchemaNode)node);
1167 throw new IllegalArgumentException("Unhandled parameter types: " +
1168 Arrays.<Object>asList(node).toString());