Converted ControllerContext and BrokerFacade xtend code to java.
Addressed review comments:
- Removed SupressWarnings "all"
- Converted use of xext classes to guava
- Removed sneakyThrow
Patch set 4: mistake - no changes
Patch set 5: Converted RestconfImpl
Patch set 6: removed xtend plugin anf dependencies from pom file
Change-Id: I45c22b22fee07a178faba9fcb9e52d3ff12a6697
Signed-off-by: tpantelis <tpanteli@brocade.com>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
</dependency>
- <dependency>
- <groupId>org.eclipse.xtend</groupId>
- <artifactId>org.eclipse.xtend.lib</artifactId>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-remote</artifactId>
<instructions>
<Bundle-Name>MD SAL Restconf Connector</Bundle-Name>
<Private-Package>org.opendaylight.controller.sal.rest.*,
- org.opendaylight.controller.sal.restconf.impl,
- org.eclipse.xtend2.lib,
- org.eclipse.xtend.lib,
- org.eclipse.xtext.xbase.*,</Private-Package>
+ org.opendaylight.controller.sal.restconf.impl,</Private-Package>
<Import-Package>*,
com.sun.jersey.spi.container.servlet</Import-Package>
<Bundle-Activator>org.opendaylight.controller.sal.rest.impl.RestconfProvider</Bundle-Activator>
</instructions>
</configuration>
</plugin>
- <plugin>
- <groupId>org.eclipse.xtend</groupId>
- <artifactId>xtend-maven-plugin</artifactId>
- </plugin>
</plugins>
</build>
<scm>
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.restconf.impl;
+
+import com.google.common.base.Objects;
+
+import java.util.Map;
+import java.util.concurrent.Future;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.core.api.mount.MountInstance;
+import org.opendaylight.controller.sal.rest.impl.RestconfProvider;
+import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNode> {
+ private final static Logger LOG = LoggerFactory.getLogger( BrokerFacade.class );
+
+ private final static BrokerFacade INSTANCE = new BrokerFacade();
+
+ private volatile DataBrokerService dataService;
+ private volatile ConsumerSession context;
+
+ private BrokerFacade() {
+ }
+
+ public void setContext( final ConsumerSession context ) {
+ this.context = context;
+ }
+
+ public void setDataService( final DataBrokerService dataService ) {
+ this.dataService = dataService;
+ }
+
+ public static BrokerFacade getInstance() {
+ return BrokerFacade.INSTANCE;
+ }
+
+ private void checkPreconditions() {
+ if( context == null || dataService == null ) {
+ ResponseException _responseException = new ResponseException( Status.SERVICE_UNAVAILABLE,
+ RestconfProvider.NOT_INITALIZED_MSG );
+ throw _responseException;
+ }
+ }
+
+ @Override
+ public CompositeNode readConfigurationData( final InstanceIdentifier path ) {
+ this.checkPreconditions();
+
+ LOG.trace( "Read Configuration via Restconf: {}", path );
+
+ return dataService.readConfigurationData( path );
+ }
+
+ public CompositeNode readConfigurationDataBehindMountPoint( final MountInstance mountPoint,
+ final InstanceIdentifier path ) {
+ this.checkPreconditions();
+
+ LOG.trace( "Read Configuration via Restconf: {}", path );
+
+ return mountPoint.readConfigurationData( path );
+ }
+
+ @Override
+ public CompositeNode readOperationalData( final InstanceIdentifier path ) {
+ this.checkPreconditions();
+
+ BrokerFacade.LOG.trace( "Read Operational via Restconf: {}", path );
+
+ return dataService.readOperationalData( path );
+ }
+
+ public CompositeNode readOperationalDataBehindMountPoint( final MountInstance mountPoint,
+ final InstanceIdentifier path ) {
+ this.checkPreconditions();
+
+ BrokerFacade.LOG.trace( "Read Operational via Restconf: {}", path );
+
+ return mountPoint.readOperationalData( path );
+ }
+
+ public RpcResult<CompositeNode> invokeRpc( final QName type, final CompositeNode payload ) {
+ this.checkPreconditions();
+
+ final Future<RpcResult<CompositeNode>> future = context.rpc( type, payload );
+
+ try {
+ return future.get();
+ }
+ catch( Exception e ) {
+ throw new ResponseException( e, "Error invoking RPC " + type );
+ }
+ }
+
+ public Future<RpcResult<TransactionStatus>> commitConfigurationDataPut( final InstanceIdentifier path,
+ final CompositeNode payload ) {
+ this.checkPreconditions();
+
+ final DataModificationTransaction transaction = dataService.beginTransaction();
+ BrokerFacade.LOG.trace( "Put Configuration via Restconf: {}", path );
+ transaction.putConfigurationData( path, payload );
+ return transaction.commit();
+ }
+
+ public Future<RpcResult<TransactionStatus>> commitConfigurationDataPutBehindMountPoint(
+ final MountInstance mountPoint, final InstanceIdentifier path, final CompositeNode payload ) {
+ this.checkPreconditions();
+
+ final DataModificationTransaction transaction = mountPoint.beginTransaction();
+ BrokerFacade.LOG.trace( "Put Configuration via Restconf: {}", path );
+ transaction.putConfigurationData( path, payload );
+ return transaction.commit();
+ }
+
+ public Future<RpcResult<TransactionStatus>> commitConfigurationDataPost( final InstanceIdentifier path,
+ final CompositeNode payload ) {
+ this.checkPreconditions();
+
+ final DataModificationTransaction transaction = dataService.beginTransaction();
+ transaction.putConfigurationData( path, payload );
+ Map<InstanceIdentifier, CompositeNode> createdConfigurationData =
+ transaction.getCreatedConfigurationData();
+ CompositeNode createdNode = createdConfigurationData.get( path );
+
+ if( Objects.equal( payload, createdNode ) ) {
+ LOG.trace( "Post Configuration via Restconf: {}", path );
+ return transaction.commit();
+ }
+
+ LOG.trace( "Post Configuration via Restconf was not executed because data already exists: {}",
+ path );
+ return null;
+ }
+
+ public Future<RpcResult<TransactionStatus>> commitConfigurationDataPostBehindMountPoint(
+ final MountInstance mountPoint, final InstanceIdentifier path, final CompositeNode payload ) {
+ this.checkPreconditions();
+
+ final DataModificationTransaction transaction = mountPoint.beginTransaction();
+ transaction.putConfigurationData( path, payload );
+ Map<InstanceIdentifier, CompositeNode> createdConfigurationData =
+ transaction.getCreatedConfigurationData();
+ CompositeNode createdNode = createdConfigurationData.get( path );
+
+ if( Objects.equal( payload, createdNode ) ) {
+ LOG.trace( "Post Configuration via Restconf: {}", path );
+ return transaction.commit();
+ }
+
+ LOG.trace( "Post Configuration via Restconf was not executed because data already exists: {}",
+ path );
+ return null;
+ }
+
+ public Future<RpcResult<TransactionStatus>> commitConfigurationDataDelete( final InstanceIdentifier path ) {
+ this.checkPreconditions();
+
+ final DataModificationTransaction transaction = dataService.beginTransaction();
+ LOG.info( "Delete Configuration via Restconf: {}", path );
+ transaction.removeConfigurationData( path );
+ return transaction.commit();
+ }
+
+ public Future<RpcResult<TransactionStatus>> commitConfigurationDataDeleteBehindMountPoint(
+ final MountInstance mountPoint, final InstanceIdentifier path ) {
+ this.checkPreconditions();
+
+ final DataModificationTransaction transaction = mountPoint.beginTransaction();
+ LOG.info( "Delete Configuration via Restconf: {}", path );
+ transaction.removeConfigurationData( path );
+ return transaction.commit();
+ }
+
+ public void registerToListenDataChanges( final ListenerAdapter listener ) {
+ this.checkPreconditions();
+
+ if( listener.isListening() ) {
+ return;
+ }
+
+ InstanceIdentifier path = listener.getPath();
+ final ListenerRegistration<DataChangeListener> registration =
+ dataService.registerDataChangeListener( path, listener );
+
+ listener.setRegistration( registration );
+ }
+}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.restconf.impl
-
-import javax.ws.rs.core.Response
-import org.opendaylight.controller.md.sal.common.api.data.DataReader
-import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession
-import org.opendaylight.controller.sal.core.api.data.DataBrokerService
-import org.opendaylight.controller.sal.core.api.mount.MountInstance
-import org.opendaylight.controller.sal.rest.impl.RestconfProvider
-import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter
-import org.opendaylight.yangtools.yang.common.QName
-import org.opendaylight.yangtools.yang.common.RpcResult
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
-import org.slf4j.LoggerFactory
-
-class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNode> {
-
-
- val static LOG = LoggerFactory.getLogger(BrokerFacade)
- val static BrokerFacade INSTANCE = new BrokerFacade
-
- @Property
- private ConsumerSession context;
-
- @Property
- private DataBrokerService dataService;
-
- private new() {
- if (INSTANCE !== null) {
- throw new IllegalStateException("Already instantiated");
- }
- }
-
- def static BrokerFacade getInstance() {
- return INSTANCE
- }
-
- private def void checkPreconditions() {
- if (context === null || dataService === null) {
- throw new ResponseException(Response.Status.SERVICE_UNAVAILABLE, RestconfProvider::NOT_INITALIZED_MSG)
- }
- }
-
- override readConfigurationData(InstanceIdentifier path) {
- checkPreconditions
- LOG.trace("Read Configuration via Restconf: {}", path)
- return dataService.readConfigurationData(path);
- }
-
- def readConfigurationDataBehindMountPoint(MountInstance mountPoint, InstanceIdentifier path) {
- checkPreconditions
- LOG.trace("Read Configuration via Restconf: {}", path)
- return mountPoint.readConfigurationData(path);
- }
-
- override readOperationalData(InstanceIdentifier path) {
- checkPreconditions
- LOG.trace("Read Operational via Restconf: {}", path)
- return dataService.readOperationalData(path);
- }
-
- def readOperationalDataBehindMountPoint(MountInstance mountPoint, InstanceIdentifier path) {
- checkPreconditions
- LOG.trace("Read Operational via Restconf: {}", path)
- return mountPoint.readOperationalData(path);
- }
-
- def RpcResult<CompositeNode> invokeRpc(QName type, CompositeNode payload) {
- checkPreconditions
- val future = context.rpc(type, payload);
- return future.get;
- }
-
- def commitConfigurationDataPut(InstanceIdentifier path, CompositeNode payload) {
- checkPreconditions
- val transaction = dataService.beginTransaction;
- LOG.trace("Put Configuration via Restconf: {}", path)
- transaction.putConfigurationData(path, payload);
- return transaction.commit
- }
-
- def commitConfigurationDataPutBehindMountPoint(MountInstance mountPoint, InstanceIdentifier path, CompositeNode payload) {
- checkPreconditions
- val transaction = mountPoint.beginTransaction;
- LOG.trace("Put Configuration via Restconf: {}", path)
- transaction.putConfigurationData(path, payload);
- return transaction.commit
- }
-
- def commitConfigurationDataPost(InstanceIdentifier path, CompositeNode payload) {
- checkPreconditions
- val transaction = dataService.beginTransaction;
- transaction.putConfigurationData(path, payload);
- if (payload == transaction.createdConfigurationData.get(path)) {
- LOG.trace("Post Configuration via Restconf: {}", path)
- return transaction.commit
- }
- LOG.trace("Post Configuration via Restconf was not executed because data already exists: {}", path)
- return null;
- }
-
- def commitConfigurationDataPostBehindMountPoint(MountInstance mountPoint, InstanceIdentifier path, CompositeNode payload) {
- checkPreconditions
- val transaction = mountPoint.beginTransaction;
- transaction.putConfigurationData(path, payload);
- if (payload == transaction.createdConfigurationData.get(path)) {
- LOG.trace("Post Configuration via Restconf: {}", path)
- return transaction.commit
- }
- LOG.trace("Post Configuration via Restconf was not executed because data already exists: {}", path)
- return null;
- }
-
- def commitConfigurationDataDelete(InstanceIdentifier path) {
- checkPreconditions
- val transaction = dataService.beginTransaction;
- LOG.info("Delete Configuration via Restconf: {}", path)
- transaction.removeConfigurationData(path)
- return transaction.commit
- }
-
- def commitConfigurationDataDeleteBehindMountPoint(MountInstance mountPoint, InstanceIdentifier path) {
- checkPreconditions
- val transaction = mountPoint.beginTransaction;
- LOG.info("Delete Configuration via Restconf: {}", path)
- transaction.removeConfigurationData(path)
- return transaction.commit
- }
-
- def registerToListenDataChanges(ListenerAdapter listener) {
- checkPreconditions
- if (listener.listening) {
- return;
- }
- val registration = dataService.registerDataChangeListener(listener.path, listener)
- listener.setRegistration(registration)
- }
-
-}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.restconf.impl;
+
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.opendaylight.controller.sal.core.api.mount.MountInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountService;
+import org.opendaylight.controller.sal.rest.impl.RestUtil;
+import org.opendaylight.controller.sal.rest.impl.RestconfProvider;
+import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
+import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestCodec;
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ControllerContext implements SchemaContextListener {
+ private final static Logger LOG = LoggerFactory.getLogger( ControllerContext.class );
+
+ private final static ControllerContext INSTANCE = new ControllerContext();
+
+ private final static String NULL_VALUE = "null";
+
+ private final static String MOUNT_MODULE = "yang-ext";
+
+ private final static String MOUNT_NODE = "mount";
+
+ public final static String MOUNT = "yang-ext:mount";
+
+ private final static String URI_ENCODING_CHAR_SET = "ISO-8859-1";
+
+ private final BiMap<URI, String> uriToModuleName = HashBiMap.<URI, String> create();
+
+ private final Map<String, URI> moduleNameToUri = uriToModuleName.inverse();
+
+ private final Map<QName, RpcDefinition> qnameToRpc = new ConcurrentHashMap<>();
+
+ private volatile SchemaContext globalSchema;
+ private volatile MountService mountService;
+
+ public void setGlobalSchema( final SchemaContext globalSchema ) {
+ this.globalSchema = globalSchema;
+ }
+
+ public void setMountService( final MountService mountService ) {
+ this.mountService = mountService;
+ }
+
+ private ControllerContext() {
+ }
+
+ public static ControllerContext getInstance() {
+ return ControllerContext.INSTANCE;
+ }
+
+ private void checkPreconditions() {
+ if( globalSchema == null ) {
+ throw new ResponseException( Status.SERVICE_UNAVAILABLE, RestconfProvider.NOT_INITALIZED_MSG );
+ }
+ }
+
+ public void setSchemas( final SchemaContext schemas ) {
+ this.onGlobalContextUpdated( schemas );
+ }
+
+ public InstanceIdWithSchemaNode toInstanceIdentifier( final String restconfInstance ) {
+ return this.toIdentifier( restconfInstance, false );
+ }
+
+ public InstanceIdWithSchemaNode toMountPointIdentifier( final String restconfInstance ) {
+ return this.toIdentifier( restconfInstance, true );
+ }
+
+ private InstanceIdWithSchemaNode toIdentifier( final String restconfInstance,
+ final boolean toMountPointIdentifier ) {
+ this.checkPreconditions();
+
+ Iterable<String> split = Splitter.on( "/" ).split( restconfInstance );
+ final ArrayList<String> encodedPathArgs = Lists.<String> newArrayList( split );
+ final List<String> pathArgs = this.urlPathArgsDecode( encodedPathArgs );
+ this.omitFirstAndLastEmptyString( pathArgs );
+ if( pathArgs.isEmpty() ) {
+ return null;
+ }
+
+ String first = pathArgs.iterator().next();
+ final String startModule = ControllerContext.toModuleName( first );
+ if( startModule == null ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "First node in URI has to be in format \"moduleName:nodeName\"" );
+ }
+
+ InstanceIdentifierBuilder builder = InstanceIdentifier.builder();
+ Module latestModule = this.getLatestModule( globalSchema, startModule );
+ InstanceIdWithSchemaNode iiWithSchemaNode = this.collectPathArguments( builder, pathArgs,
+ latestModule, null, toMountPointIdentifier );
+
+ if( iiWithSchemaNode == null ) {
+ throw new ResponseException( Status.BAD_REQUEST, "URI has bad format" );
+ }
+
+ return iiWithSchemaNode;
+ }
+
+ private List<String> omitFirstAndLastEmptyString( final List<String> list ) {
+ if( list.isEmpty() ) {
+ return list;
+ }
+
+ String head = list.iterator().next();
+ if( head.isEmpty() ) {
+ list.remove( 0 );
+ }
+
+ if( list.isEmpty() ) {
+ return list;
+ }
+
+ String last = list.get( list.size() - 1 );
+ if( last.isEmpty() ) {
+ list.remove( list.size() - 1 );
+ }
+
+ return list;
+ }
+
+ private Module getLatestModule( final SchemaContext schema, final String moduleName ) {
+ Preconditions.checkArgument( schema != null );
+ Preconditions.checkArgument( moduleName != null && !moduleName.isEmpty() );
+
+ Predicate<Module> filter = new Predicate<Module>() {
+ @Override
+ public boolean apply( Module m ) {
+ return Objects.equal( m.getName(), moduleName );
+ }
+ };
+
+ Iterable<Module> modules = Iterables.filter( schema.getModules(), filter );
+ return this.filterLatestModule( modules );
+ }
+
+ private Module filterLatestModule( final Iterable<Module> modules ) {
+ Module latestModule = modules.iterator().hasNext() ? modules.iterator().next() : null;
+ for( final Module module : modules ) {
+ if( module.getRevision().after( latestModule.getRevision() ) ) {
+ latestModule = module;
+ }
+ }
+ return latestModule;
+ }
+
+ public Module findModuleByName( final String moduleName ) {
+ this.checkPreconditions();
+ Preconditions.checkArgument( moduleName != null && !moduleName.isEmpty() );
+ return this.getLatestModule( globalSchema, moduleName );
+ }
+
+ public Module findModuleByName( final MountInstance mountPoint, final String moduleName ) {
+ Preconditions.checkArgument( moduleName != null && mountPoint != null );
+
+ final SchemaContext mountPointSchema = mountPoint.getSchemaContext();
+ return mountPointSchema == null ? null : this.getLatestModule( mountPointSchema, moduleName );
+ }
+
+ public Module findModuleByNamespace( final URI namespace ) {
+ this.checkPreconditions();
+ Preconditions.checkArgument( namespace != null );
+
+ final Set<Module> moduleSchemas = globalSchema.findModuleByNamespace( namespace );
+ return moduleSchemas == null ? null : this.filterLatestModule( moduleSchemas );
+ }
+
+ public Module findModuleByNamespace( final MountInstance mountPoint, final URI namespace ) {
+ Preconditions.checkArgument( namespace != null && mountPoint != null );
+
+ final SchemaContext mountPointSchema = mountPoint.getSchemaContext();
+ Set<Module> moduleSchemas = mountPointSchema == null ? null :
+ mountPointSchema.findModuleByNamespace( namespace );
+ return moduleSchemas == null ? null : this.filterLatestModule( moduleSchemas );
+ }
+
+ public Module findModuleByNameAndRevision( final QName module ) {
+ this.checkPreconditions();
+ Preconditions.checkArgument( module != null && module.getLocalName() != null &&
+ module.getRevision() != null );
+
+ return globalSchema.findModuleByName( module.getLocalName(), module.getRevision() );
+ }
+
+ public Module findModuleByNameAndRevision( final MountInstance mountPoint, final QName module ) {
+ this.checkPreconditions();
+ Preconditions.checkArgument( module != null && module.getLocalName() != null &&
+ module.getRevision() != null && mountPoint != null );
+
+ SchemaContext schemaContext = mountPoint.getSchemaContext();
+ return schemaContext == null ? null :
+ schemaContext.findModuleByName( module.getLocalName(), module.getRevision() );
+ }
+
+ public DataNodeContainer getDataNodeContainerFor( final InstanceIdentifier path ) {
+ this.checkPreconditions();
+
+ final List<PathArgument> elements = path.getPath();
+ PathArgument head = elements.iterator().next();
+ final QName startQName = head.getNodeType();
+ final Module initialModule = globalSchema.findModuleByNamespaceAndRevision(
+ startQName.getNamespace(), startQName.getRevision() );
+ DataNodeContainer node = initialModule;
+ for( final PathArgument element : elements ) {
+ QName _nodeType = element.getNodeType();
+ final DataSchemaNode potentialNode = ControllerContext.childByQName( node, _nodeType );
+ if( potentialNode == null || !this.isListOrContainer( potentialNode ) ) {
+ return null;
+ }
+ node = (DataNodeContainer) potentialNode;
+ }
+
+ return node;
+ }
+
+ public String toFullRestconfIdentifier( final InstanceIdentifier path ) {
+ this.checkPreconditions();
+
+ final List<PathArgument> elements = path.getPath();
+ final StringBuilder builder = new StringBuilder();
+ PathArgument head = elements.iterator().next();
+ final QName startQName = head.getNodeType();
+ final Module initialModule = globalSchema.findModuleByNamespaceAndRevision(
+ startQName.getNamespace(), startQName.getRevision() );
+ DataNodeContainer node = initialModule;
+ for( final PathArgument element : elements ) {
+ QName _nodeType = element.getNodeType();
+ final DataSchemaNode potentialNode = ControllerContext.childByQName( node, _nodeType );
+ if( !this.isListOrContainer( potentialNode ) ) {
+ return null;
+ }
+ node = ((DataNodeContainer) potentialNode);
+ builder.append( this.convertToRestconfIdentifier( element, node ) );
+ }
+
+ return builder.toString();
+ }
+
+ public String findModuleNameByNamespace( final URI namespace ) {
+ this.checkPreconditions();
+
+ String moduleName = this.uriToModuleName.get( namespace );
+ if( moduleName == null ) {
+ final Module module = this.findModuleByNamespace( namespace );
+ if( module != null ) {
+ moduleName = module.getName();
+ this.uriToModuleName.put( namespace, moduleName );
+ }
+ }
+
+ return moduleName;
+ }
+
+ public String findModuleNameByNamespace( final MountInstance mountPoint, final URI namespace ) {
+ final Module module = this.findModuleByNamespace( mountPoint, namespace );
+ return module == null ? null : module.getName();
+ }
+
+ public URI findNamespaceByModuleName( final String moduleName ) {
+ URI namespace = this.moduleNameToUri.get( moduleName );
+ if( namespace == null ) {
+ Module module = this.findModuleByName( moduleName );
+ if( module != null ) {
+ URI _namespace = module.getNamespace();
+ namespace = _namespace;
+ this.uriToModuleName.put( namespace, moduleName );
+ }
+ }
+ return namespace;
+ }
+
+ public URI findNamespaceByModuleName( final MountInstance mountPoint, final String moduleName ) {
+ final Module module = this.findModuleByName( mountPoint, moduleName );
+ return module == null ? null : module.getNamespace();
+ }
+
+ public Set<Module> getAllModules( final MountInstance mountPoint ) {
+ this.checkPreconditions();
+
+ SchemaContext schemaContext = mountPoint == null ? null : mountPoint.getSchemaContext();
+ return schemaContext == null ? null : schemaContext.getModules();
+ }
+
+ public Set<Module> getAllModules() {
+ this.checkPreconditions();
+ return globalSchema.getModules();
+ }
+
+ public CharSequence toRestconfIdentifier( final QName qname ) {
+ this.checkPreconditions();
+
+ String module = this.uriToModuleName.get( qname.getNamespace() );
+ if( module == null ) {
+ final Module moduleSchema = globalSchema.findModuleByNamespaceAndRevision(
+ qname.getNamespace(), qname.getRevision() );
+ if( moduleSchema == null ) {
+ return null;
+ }
+
+ this.uriToModuleName.put( qname.getNamespace(), moduleSchema.getName() );
+ module = moduleSchema.getName();
+ }
+
+ StringBuilder builder = new StringBuilder();
+ builder.append( module );
+ builder.append( ":" );
+ builder.append( qname.getLocalName() );
+ return builder.toString();
+ }
+
+ public CharSequence toRestconfIdentifier( final MountInstance mountPoint, final QName qname ) {
+ if( mountPoint == null ) {
+ return null;
+ }
+
+ SchemaContext schemaContext = mountPoint.getSchemaContext();
+
+ final Module moduleSchema = schemaContext.findModuleByNamespaceAndRevision(
+ qname.getNamespace(), qname.getRevision() );
+ if( moduleSchema == null ) {
+ return null;
+ }
+
+ StringBuilder builder = new StringBuilder();
+ builder.append( moduleSchema.getName() );
+ builder.append( ":" );
+ builder.append( qname.getLocalName() );
+ return builder.toString();
+ }
+
+ private static DataSchemaNode childByQName( final ChoiceNode container, final QName name ) {
+ for( final ChoiceCaseNode caze : container.getCases() ) {
+ final DataSchemaNode ret = ControllerContext.childByQName( caze, name );
+ if( ret != null ) {
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+ private static DataSchemaNode childByQName( final ChoiceCaseNode container, final QName name ) {
+ return container.getDataChildByName( name );
+ }
+
+ private static DataSchemaNode childByQName( final ContainerSchemaNode container, final QName name ) {
+ return ControllerContext.dataNodeChildByQName( container, name );
+ }
+
+ private static DataSchemaNode childByQName( final ListSchemaNode container, final QName name ) {
+ return ControllerContext.dataNodeChildByQName( container, name );
+ }
+
+ private static DataSchemaNode childByQName( final Module container, final QName name ) {
+ return ControllerContext.dataNodeChildByQName( container, name );
+ }
+
+ private static DataSchemaNode childByQName( final DataSchemaNode container, final QName name ) {
+ return null;
+ }
+
+ private static DataSchemaNode dataNodeChildByQName( final DataNodeContainer container, final QName name ) {
+ DataSchemaNode ret = container.getDataChildByName( name );
+ if( ret == null ) {
+ for( final DataSchemaNode node : container.getChildNodes() ) {
+ if( (node instanceof ChoiceCaseNode) ) {
+ final ChoiceCaseNode caseNode = ((ChoiceCaseNode) node);
+ DataSchemaNode childByQName = ControllerContext.childByQName( caseNode, name );
+ if( childByQName != null ) {
+ return childByQName;
+ }
+ }
+ }
+ }
+ return ret;
+ }
+
+ private String toUriString( final Object object ) throws UnsupportedEncodingException {
+ return object == null ? "" :
+ URLEncoder.encode( object.toString(), ControllerContext.URI_ENCODING_CHAR_SET );
+ }
+
+ private InstanceIdWithSchemaNode collectPathArguments( final InstanceIdentifierBuilder builder,
+ final List<String> strings, final DataNodeContainer parentNode, final MountInstance mountPoint,
+ final boolean returnJustMountPoint ) {
+ Preconditions.<List<String>> checkNotNull( strings );
+
+ if( parentNode == null ) {
+ return null;
+ }
+
+ if( strings.isEmpty() ) {
+ return new InstanceIdWithSchemaNode( builder.toInstance(),
+ ((DataSchemaNode) parentNode), mountPoint );
+ }
+
+ String head = strings.iterator().next();
+ final String nodeName = this.toNodeName( head );
+ final String moduleName = ControllerContext.toModuleName( head );
+
+ DataSchemaNode targetNode = null;
+ if( !Strings.isNullOrEmpty( moduleName ) ) {
+ if( Objects.equal( moduleName, ControllerContext.MOUNT_MODULE ) &&
+ Objects.equal( nodeName, ControllerContext.MOUNT_NODE ) ) {
+ if( mountPoint != null ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "Restconf supports just one mount point in URI." );
+ }
+
+ if( mountService == null ) {
+ throw new ResponseException( Status.SERVICE_UNAVAILABLE,
+ "MountService was not found. Finding behind mount points does not work." );
+ }
+
+ final InstanceIdentifier partialPath = builder.toInstance();
+ final MountInstance mount = mountService.getMountPoint( partialPath );
+ if( mount == null ) {
+ LOG.debug( "Instance identifier to missing mount point: {}", partialPath );
+ throw new ResponseException( Status.BAD_REQUEST,
+ "Mount point does not exist." );
+ }
+
+ final SchemaContext mountPointSchema = mount.getSchemaContext();
+ if( mountPointSchema == null ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "Mount point does not contain any schema with modules." );
+ }
+
+ if( returnJustMountPoint ) {
+ InstanceIdentifier instance = InstanceIdentifier.builder().toInstance();
+ return new InstanceIdWithSchemaNode( instance, mountPointSchema, mount );
+ }
+
+ if( strings.size() == 1 ) {
+ InstanceIdentifier instance = InstanceIdentifier.builder().toInstance();
+ return new InstanceIdWithSchemaNode( instance, mountPointSchema, mount );
+ }
+
+ final String moduleNameBehindMountPoint = toModuleName( strings.get( 1 ) );
+ if( moduleNameBehindMountPoint == null ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "First node after mount point in URI has to be in format \"moduleName:nodeName\"" );
+ }
+
+ final Module moduleBehindMountPoint = this.getLatestModule( mountPointSchema,
+ moduleNameBehindMountPoint );
+ if( moduleBehindMountPoint == null ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "URI has bad format. \"" + moduleName +
+ "\" module does not exist in mount point." );
+ }
+
+ List<String> subList = strings.subList( 1, strings.size() );
+ return this.collectPathArguments( InstanceIdentifier.builder(), subList, moduleBehindMountPoint,
+ mount, returnJustMountPoint );
+ }
+
+ Module module = null;
+ if( mountPoint == null ) {
+ module = this.getLatestModule( globalSchema, moduleName );
+ if( module == null ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "URI has bad format. \"" + moduleName + "\" module does not exist." );
+ }
+ }
+ else {
+ SchemaContext schemaContext = mountPoint.getSchemaContext();
+ module = schemaContext == null ? null :
+ this.getLatestModule( schemaContext, moduleName );
+ if( module == null ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "URI has bad format. \"" + moduleName +
+ "\" module does not exist in mount point." );
+ }
+ }
+
+ targetNode = this.findInstanceDataChildByNameAndNamespace(
+ parentNode, nodeName, module.getNamespace() );;
+ if( targetNode == null ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "URI has bad format. Possible reasons:\n" +
+ "1. \"" + head + "\" was not found in parent data node.\n" +
+ "2. \"" + head + "\" is behind mount point. Then it should be in format \"/" +
+ MOUNT + "/" + head + "\"." );
+ }
+ } else {
+ final List<DataSchemaNode> potentialSchemaNodes =
+ this.findInstanceDataChildrenByName( parentNode, nodeName );
+ if( potentialSchemaNodes.size() > 1 ) {
+ final StringBuilder strBuilder = new StringBuilder();
+ for( final DataSchemaNode potentialNodeSchema : potentialSchemaNodes ) {
+ strBuilder.append( " " )
+ .append( potentialNodeSchema.getQName().getNamespace() )
+ .append( "\n" );
+ }
+
+ throw new ResponseException( Status.BAD_REQUEST,
+ "URI has bad format. Node \"" + nodeName +
+ "\" is added as augment from more than one module. " +
+ "Therefore the node must have module name and it has to be in format \"moduleName:nodeName\"." +
+ "\nThe node is added as augment from modules with namespaces:\n" +
+ strBuilder.toString() );
+ }
+
+ if( potentialSchemaNodes.isEmpty() ) {
+ throw new ResponseException( Status.BAD_REQUEST, "URI has bad format. \"" + nodeName +
+ "\" was not found in parent data node.\n" );
+ }
+
+ targetNode = potentialSchemaNodes.iterator().next();
+ }
+
+ if( !this.isListOrContainer( targetNode ) ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "URI has bad format. Node \"" + head +
+ "\" must be Container or List yang type." );
+ }
+
+ int consumed = 1;
+ if( (targetNode instanceof ListSchemaNode) ) {
+ final ListSchemaNode listNode = ((ListSchemaNode) targetNode);
+ final int keysSize = listNode.getKeyDefinition().size();
+ if( (strings.size() - consumed) < keysSize ) {
+ throw new ResponseException( Status.BAD_REQUEST, "Missing key for list \"" +
+ listNode.getQName().getLocalName() + "\"." );
+ }
+
+ final List<String> uriKeyValues = strings.subList( consumed, consumed + keysSize );
+ final HashMap<QName, Object> keyValues = new HashMap<QName, Object>();
+ int i = 0;
+ for( final QName key : listNode.getKeyDefinition() ) {
+ {
+ final String uriKeyValue = uriKeyValues.get( i );
+ if( uriKeyValue.equals( NULL_VALUE ) ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "URI has bad format. List \"" + listNode.getQName().getLocalName() +
+ "\" cannot contain \"null\" value as a key." );
+ }
+
+ this.addKeyValue( keyValues, listNode.getDataChildByName( key ),
+ uriKeyValue, mountPoint );
+ i++;
+ }
+ }
+
+ consumed = consumed + i;
+ builder.nodeWithKey( targetNode.getQName(), keyValues );
+ }
+ else {
+ builder.node( targetNode.getQName() );
+ }
+
+ if( (targetNode instanceof DataNodeContainer) ) {
+ final List<String> remaining = strings.subList( consumed, strings.size() );
+ return this.collectPathArguments( builder, remaining,
+ ((DataNodeContainer) targetNode), mountPoint, returnJustMountPoint );
+ }
+
+ return new InstanceIdWithSchemaNode( builder.toInstance(), targetNode, mountPoint );
+ }
+
+ public DataSchemaNode findInstanceDataChildByNameAndNamespace( final DataNodeContainer container,
+ final String name, final URI namespace ) {
+ Preconditions.<URI> checkNotNull( namespace );
+
+ final List<DataSchemaNode> potentialSchemaNodes = this.findInstanceDataChildrenByName( container, name );
+
+ Predicate<DataSchemaNode> filter = new Predicate<DataSchemaNode>() {
+ @Override
+ public boolean apply( DataSchemaNode node ) {
+ return Objects.equal( node.getQName().getNamespace(), namespace );
+ }
+ };
+
+ Iterable<DataSchemaNode> result = Iterables.filter( potentialSchemaNodes, filter );
+ return Iterables.getFirst( result, null );
+ }
+
+ public List<DataSchemaNode> findInstanceDataChildrenByName( final DataNodeContainer container,
+ final String name ) {
+ Preconditions.<DataNodeContainer> checkNotNull( container );
+ Preconditions.<String> checkNotNull( name );
+
+ List<DataSchemaNode> instantiatedDataNodeContainers = new ArrayList<DataSchemaNode>();
+ this.collectInstanceDataNodeContainers( instantiatedDataNodeContainers, container, name );
+ return instantiatedDataNodeContainers;
+ }
+
+ private void collectInstanceDataNodeContainers( final List<DataSchemaNode> potentialSchemaNodes,
+ final DataNodeContainer container, final String name ) {
+
+ Set<DataSchemaNode> childNodes = container.getChildNodes();
+
+ Predicate<DataSchemaNode> filter = new Predicate<DataSchemaNode>() {
+ @Override
+ public boolean apply( DataSchemaNode node ) {
+ return Objects.equal( node.getQName().getLocalName(), name );
+ }
+ };
+
+ Iterable<DataSchemaNode> nodes = Iterables.filter( childNodes, filter );
+
+ // Can't combine this loop with the filter above because the filter is lazily-applied
+ // by Iterables.filter.
+ for( final DataSchemaNode potentialNode : nodes ) {
+ if( this.isInstantiatedDataSchema( potentialNode ) ) {
+ potentialSchemaNodes.add( potentialNode );
+ }
+ }
+
+ Iterable<ChoiceNode> choiceNodes = Iterables.<ChoiceNode> filter( container.getChildNodes(),
+ ChoiceNode.class );
+
+ final Function<ChoiceNode, Set<ChoiceCaseNode>> choiceFunction =
+ new Function<ChoiceNode, Set<ChoiceCaseNode>>() {
+ @Override
+ public Set<ChoiceCaseNode> apply( final ChoiceNode node ) {
+ return node.getCases();
+ }
+ };
+
+ Iterable<Set<ChoiceCaseNode>> map = Iterables.<ChoiceNode, Set<ChoiceCaseNode>> transform(
+ choiceNodes, choiceFunction );
+
+ final Iterable<ChoiceCaseNode> allCases = Iterables.<ChoiceCaseNode> concat( map );
+ for( final ChoiceCaseNode caze : allCases ) {
+ this.collectInstanceDataNodeContainers( potentialSchemaNodes, caze, name );
+ }
+ }
+
+ public boolean isInstantiatedDataSchema( final DataSchemaNode node ) {
+ return node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode ||
+ node instanceof ContainerSchemaNode || node instanceof ListSchemaNode;
+ }
+
+ private void addKeyValue( final HashMap<QName, Object> map, final DataSchemaNode node,
+ final String uriValue, final MountInstance mountPoint ) {
+ Preconditions.<String> checkNotNull( uriValue );
+ Preconditions.checkArgument( (node instanceof LeafSchemaNode) );
+
+ final String urlDecoded = urlPathArgDecode( uriValue );
+ final TypeDefinition<? extends Object> typedef = ((LeafSchemaNode) node).getType();
+ Codec<Object, Object> codec = RestCodec.from( typedef, mountPoint );
+
+ Object decoded = codec == null ? null : codec.deserialize( urlDecoded );
+ String additionalInfo = "";
+ if( decoded == null ) {
+ TypeDefinition<? extends Object> baseType = RestUtil.resolveBaseTypeFrom( typedef );
+ if( (baseType instanceof IdentityrefTypeDefinition) ) {
+ decoded = this.toQName( urlDecoded );
+ additionalInfo = "For key which is of type identityref it should be in format module_name:identity_name.";
+ }
+ }
+
+ if( decoded == null ) {
+ throw new ResponseException( Status.BAD_REQUEST, uriValue + " from URI can\'t be resolved. " +
+ additionalInfo );
+ }
+
+ map.put( node.getQName(), decoded );
+ }
+
+ private static String toModuleName( final String str ) {
+ Preconditions.<String> checkNotNull( str );
+ if( str.contains( ":" ) ) {
+ final String[] args = str.split( ":" );
+ if( args.length == 2 ) {
+ return args[0];
+ }
+ }
+ return null;
+ }
+
+ private String toNodeName( final String str ) {
+ if( str.contains( ":" ) ) {
+ final String[] args = str.split( ":" );
+ if( args.length == 2 ) {
+ return args[1];
+ }
+ }
+ return str;
+ }
+
+ private QName toQName( final String name ) {
+ final String module = toModuleName( name );
+ final String node = this.toNodeName( name );
+ Set<Module> modules = globalSchema.getModules();
+
+ final Comparator<Module> comparator = new Comparator<Module>() {
+ @Override
+ public int compare( final Module o1, final Module o2 ) {
+ return o1.getRevision().compareTo( o2.getRevision() );
+ }
+ };
+
+ List<Module> sorted = new ArrayList<Module>( modules );
+ Collections.<Module> sort( new ArrayList<Module>( modules ), comparator );
+
+ final Function<Module, QName> transform = new Function<Module, QName>() {
+ @Override
+ public QName apply( final Module m ) {
+ return QName.create( m.getNamespace(), m.getRevision(), m.getName() );
+ }
+ };
+
+ final Predicate<QName> findFirst = new Predicate<QName>() {
+ @Override
+ public boolean apply( final QName qn ) {
+ return Objects.equal( module, qn.getLocalName() );
+ }
+ };
+
+ Optional<QName> namespace = FluentIterable.from( sorted )
+ .transform( transform )
+ .firstMatch( findFirst );
+ return namespace.isPresent() ? QName.create( namespace.get(), node ) : null;
+ }
+
+ private boolean isListOrContainer( final DataSchemaNode node ) {
+ return node instanceof ListSchemaNode || node instanceof ContainerSchemaNode;
+ }
+
+ public RpcDefinition getRpcDefinition( final String name ) {
+ final QName validName = this.toQName( name );
+ return validName == null ? null : this.qnameToRpc.get( validName );
+ }
+
+ @Override
+ public void onGlobalContextUpdated( final SchemaContext context ) {
+ if( context != null ) {
+ this.qnameToRpc.clear();
+ this.setGlobalSchema( context );
+ Set<RpcDefinition> _operations = context.getOperations();
+ for( final RpcDefinition operation : _operations ) {
+ {
+ this.qnameToRpc.put( operation.getQName(), operation );
+ }
+ }
+ }
+ }
+
+ public List<String> urlPathArgsDecode( final List<String> strings ) {
+ try {
+ List<String> decodedPathArgs = new ArrayList<String>();
+ for( final String pathArg : strings ) {
+ String _decode = URLDecoder.decode( pathArg, URI_ENCODING_CHAR_SET );
+ decodedPathArgs.add( _decode );
+ }
+ return decodedPathArgs;
+ }
+ catch( UnsupportedEncodingException e ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "Invalid URL path '" + strings + "': " + e.getMessage() );
+ }
+ }
+
+ public String urlPathArgDecode( final String pathArg ) {
+ if( pathArg != null ) {
+ try {
+ return URLDecoder.decode( pathArg, URI_ENCODING_CHAR_SET );
+ }
+ catch( UnsupportedEncodingException e ) {
+ throw new ResponseException( Status.BAD_REQUEST,
+ "Invalid URL path arg '" + pathArg + "': " + e.getMessage() );
+ }
+ }
+
+ return null;
+ }
+
+ private CharSequence convertToRestconfIdentifier( final PathArgument argument,
+ final DataNodeContainer node ) {
+ if( argument instanceof NodeIdentifier && node instanceof ContainerSchemaNode ) {
+ return convertToRestconfIdentifier( (NodeIdentifier) argument, (ContainerSchemaNode) node );
+ }
+ else if( argument instanceof NodeIdentifierWithPredicates && node instanceof ListSchemaNode ) {
+ return convertToRestconfIdentifier( (NodeIdentifierWithPredicates) argument, (ListSchemaNode) node );
+ }
+ else if( argument != null && node != null ) {
+ throw new IllegalArgumentException(
+ "Conversion of generic path argument is not supported" );
+ }
+ else {
+ throw new IllegalArgumentException( "Unhandled parameter types: "
+ + Arrays.<Object> asList( argument, node ).toString() );
+ }
+ }
+
+ private CharSequence convertToRestconfIdentifier( final NodeIdentifier argument,
+ final ContainerSchemaNode node ) {
+ StringBuilder builder = new StringBuilder();
+ builder.append( "/" );
+ QName nodeType = argument.getNodeType();
+ builder.append( this.toRestconfIdentifier( nodeType ) );
+ return builder.toString();
+ }
+
+ private CharSequence convertToRestconfIdentifier( final NodeIdentifierWithPredicates argument,
+ final ListSchemaNode node ) {
+ QName nodeType = argument.getNodeType();
+ final CharSequence nodeIdentifier = this.toRestconfIdentifier( nodeType );
+ final Map<QName, Object> keyValues = argument.getKeyValues();
+
+ StringBuilder builder = new StringBuilder();
+ builder.append( "/" );
+ builder.append( nodeIdentifier );
+ builder.append( "/" );
+
+ List<QName> keyDefinition = node.getKeyDefinition();
+ boolean hasElements = false;
+ for( final QName key : keyDefinition ) {
+ if( !hasElements ) {
+ hasElements = true;
+ }
+ else {
+ builder.append( "/" );
+ }
+
+ try {
+ builder.append( this.toUriString( keyValues.get( key ) ) );
+ } catch( UnsupportedEncodingException e ) {
+ LOG.error( "Error parsing URI: " + keyValues.get( key ), e );
+ return null;
+ }
+ }
+
+ return builder.toString();
+ }
+
+ private static DataSchemaNode childByQName( final Object container, final QName name ) {
+ if( container instanceof ChoiceCaseNode ) {
+ return childByQName( (ChoiceCaseNode) container, name );
+ }
+ else if( container instanceof ChoiceNode ) {
+ return childByQName( (ChoiceNode) container, name );
+ }
+ else if( container instanceof ContainerSchemaNode ) {
+ return childByQName( (ContainerSchemaNode) container, name );
+ }
+ else if( container instanceof ListSchemaNode ) {
+ return childByQName( (ListSchemaNode) container, name );
+ }
+ else if( container instanceof DataSchemaNode ) {
+ return childByQName( (DataSchemaNode) container, name );
+ }
+ else if( container instanceof Module ) {
+ return childByQName( (Module) container, name );
+ }
+ else {
+ throw new IllegalArgumentException( "Unhandled parameter types: "
+ + Arrays.<Object> asList( container, name ).toString() );
+ }
+ }
+}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.restconf.impl
-
-import com.google.common.base.Preconditions
-import com.google.common.base.Splitter
-import com.google.common.collect.BiMap
-import com.google.common.collect.FluentIterable
-import com.google.common.collect.HashBiMap
-import com.google.common.collect.Lists
-import java.net.URI
-import java.net.URLDecoder
-import java.net.URLEncoder
-import java.util.ArrayList
-import java.util.HashMap
-import java.util.List
-import java.util.Map
-import java.util.concurrent.ConcurrentHashMap
-import org.opendaylight.controller.sal.core.api.mount.MountInstance
-import org.opendaylight.controller.sal.core.api.mount.MountService
-import org.opendaylight.controller.sal.rest.impl.RestUtil
-import org.opendaylight.controller.sal.rest.impl.RestconfProvider
-import org.opendaylight.yangtools.yang.common.QName
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument
-import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
-import org.opendaylight.yangtools.yang.model.api.Module
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition
-import org.opendaylight.yangtools.yang.model.api.SchemaContext
-import org.opendaylight.yangtools.yang.model.api.SchemaContextListener
-import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
-import org.slf4j.LoggerFactory
-
-import static com.google.common.base.Preconditions.*
-import static javax.ws.rs.core.Response.Status.*
-
-class ControllerContext implements SchemaContextListener {
- val static LOG = LoggerFactory.getLogger(ControllerContext)
- val static ControllerContext INSTANCE = new ControllerContext
- val static NULL_VALUE = "null"
- val static MOUNT_MODULE = "yang-ext"
- val static MOUNT_NODE = "mount"
- public val static MOUNT = "yang-ext:mount"
- val static URI_ENCODING_CHAR_SET = "ISO-8859-1"
- val static URI_SLASH_PLACEHOLDER = "%2F";
-
- @Property
- var SchemaContext globalSchema;
-
- @Property
- var MountService mountService;
-
- private val BiMap<URI, String> uriToModuleName = HashBiMap.create();
- private val Map<String, URI> moduleNameToUri = uriToModuleName.inverse();
- private val Map<QName, RpcDefinition> qnameToRpc = new ConcurrentHashMap();
-
- private new() {
- if (INSTANCE !== null) {
- throw new IllegalStateException("Already instantiated");
- }
- }
-
- static def getInstance() {
- return INSTANCE
- }
-
- private def void checkPreconditions() {
- if (globalSchema === null) {
- throw new ResponseException(SERVICE_UNAVAILABLE, RestconfProvider::NOT_INITALIZED_MSG)
- }
- }
-
- def setSchemas(SchemaContext schemas) {
- onGlobalContextUpdated(schemas)
- }
-
- def InstanceIdWithSchemaNode toInstanceIdentifier(String restconfInstance) {
- return restconfInstance.toIdentifier(false)
- }
-
- def InstanceIdWithSchemaNode toMountPointIdentifier(String restconfInstance) {
- return restconfInstance.toIdentifier(true)
- }
-
- private def InstanceIdWithSchemaNode toIdentifier(String restconfInstance, boolean toMountPointIdentifier) {
- checkPreconditions
- val encodedPathArgs = Lists.newArrayList(Splitter.on("/").split(restconfInstance))
- val pathArgs = urlPathArgsDecode(encodedPathArgs)
- pathArgs.omitFirstAndLastEmptyString
- if (pathArgs.empty) {
- return null;
- }
- val startModule = pathArgs.head.toModuleName();
- if (startModule === null) {
- throw new ResponseException(BAD_REQUEST, "First node in URI has to be in format \"moduleName:nodeName\"")
- }
- var InstanceIdWithSchemaNode iiWithSchemaNode = null;
- if (toMountPointIdentifier) {
- iiWithSchemaNode = collectPathArguments(InstanceIdentifier.builder(), pathArgs,
- globalSchema.getLatestModule(startModule), null, true);
- } else {
- iiWithSchemaNode = collectPathArguments(InstanceIdentifier.builder(), pathArgs,
- globalSchema.getLatestModule(startModule), null, false);
- }
- if (iiWithSchemaNode === null) {
- throw new ResponseException(BAD_REQUEST, "URI has bad format")
- }
- return iiWithSchemaNode
- }
-
- private def omitFirstAndLastEmptyString(List<String> list) {
- if (list.empty) {
- return list;
- }
- if (list.head.empty) {
- list.remove(0)
- }
- if (list.empty) {
- return list;
- }
- if (list.last.empty) {
- list.remove(list.indexOf(list.last))
- }
- return list;
- }
-
- private def getLatestModule(SchemaContext schema, String moduleName) {
- checkArgument(schema !== null);
- checkArgument(moduleName !== null && !moduleName.empty)
- val modules = schema.modules.filter[m|m.name == moduleName]
- return modules.filterLatestModule
- }
-
- private def filterLatestModule(Iterable<Module> modules) {
- var latestModule = modules.head
- for (module : modules) {
- if (module.revision.after(latestModule.revision)) {
- latestModule = module
- }
- }
- return latestModule
- }
-
- def findModuleByName(String moduleName) {
- checkPreconditions
- checkArgument(moduleName !== null && !moduleName.empty)
- return globalSchema.getLatestModule(moduleName)
- }
-
- def findModuleByName(MountInstance mountPoint, String moduleName) {
- checkArgument(moduleName !== null && mountPoint !== null)
- val mountPointSchema = mountPoint.schemaContext;
- return mountPointSchema?.getLatestModule(moduleName);
- }
-
- def findModuleByNamespace(URI namespace) {
- checkPreconditions
- checkArgument(namespace !== null)
- val moduleSchemas = globalSchema.findModuleByNamespace(namespace)
- return moduleSchemas?.filterLatestModule
- }
-
- def findModuleByNamespace(MountInstance mountPoint, URI namespace) {
- checkArgument(namespace !== null && mountPoint !== null)
- val mountPointSchema = mountPoint.schemaContext;
- val moduleSchemas = mountPointSchema?.findModuleByNamespace(namespace)
- return moduleSchemas?.filterLatestModule
- }
-
- def findModuleByNameAndRevision(QName module) {
- checkPreconditions
- checkArgument(module !== null && module.localName !== null && module.revision !== null)
- return globalSchema.findModuleByName(module.localName, module.revision)
- }
-
- def findModuleByNameAndRevision(MountInstance mountPoint, QName module) {
- checkPreconditions
- checkArgument(module !== null && module.localName !== null && module.revision !== null && mountPoint !== null)
- return mountPoint.schemaContext?.findModuleByName(module.localName, module.revision)
- }
-
- def getDataNodeContainerFor(InstanceIdentifier path) {
- checkPreconditions
- val elements = path.path;
- val startQName = elements.head.nodeType;
- val initialModule = globalSchema.findModuleByNamespaceAndRevision(startQName.namespace, startQName.revision)
- var node = initialModule as DataNodeContainer;
- for (element : elements) {
- val potentialNode = node.childByQName(element.nodeType);
- if (potentialNode === null || !potentialNode.listOrContainer) {
- return null
- }
- node = potentialNode as DataNodeContainer
- }
- return node
- }
-
- def String toFullRestconfIdentifier(InstanceIdentifier path) {
- checkPreconditions
- val elements = path.path;
- val ret = new StringBuilder();
- val startQName = elements.head.nodeType;
- val initialModule = globalSchema.findModuleByNamespaceAndRevision(startQName.namespace, startQName.revision)
- var node = initialModule as DataNodeContainer;
- for (element : elements) {
- val potentialNode = node.childByQName(element.nodeType);
- if (!potentialNode.listOrContainer) {
- return null
- }
- node = potentialNode as DataNodeContainer
- ret.append(element.convertToRestconfIdentifier(node));
- }
- return ret.toString
- }
-
- private def dispatch CharSequence convertToRestconfIdentifier(NodeIdentifier argument, ContainerSchemaNode node) {
- '''/«argument.nodeType.toRestconfIdentifier()»'''
- }
-
- private def dispatch CharSequence convertToRestconfIdentifier(NodeIdentifierWithPredicates argument, ListSchemaNode node) {
- val nodeIdentifier = argument.nodeType.toRestconfIdentifier();
- val keyValues = argument.keyValues;
- return '''/«nodeIdentifier»/«FOR key : node.keyDefinition SEPARATOR "/"»«keyValues.get(key).toUriString»«ENDFOR»'''
- }
-
- private def dispatch CharSequence convertToRestconfIdentifier(PathArgument argument, DataNodeContainer node) {
- throw new IllegalArgumentException("Conversion of generic path argument is not supported");
- }
-
- def findModuleNameByNamespace(URI namespace) {
- checkPreconditions
- var moduleName = uriToModuleName.get(namespace)
- if (moduleName === null) {
- val module = findModuleByNamespace(namespace)
- if (module === null) return null
- moduleName = module.name
- uriToModuleName.put(namespace, moduleName)
- }
- return moduleName
- }
-
- def findModuleNameByNamespace(MountInstance mountPoint, URI namespace) {
- val module = mountPoint.findModuleByNamespace(namespace);
- return module?.name
- }
-
- def findNamespaceByModuleName(String moduleName) {
- var namespace = moduleNameToUri.get(moduleName)
- if (namespace === null) {
- var module = findModuleByName(moduleName)
- if(module === null) return null
- namespace = module.namespace
- uriToModuleName.put(namespace, moduleName)
- }
- return namespace
- }
-
- def findNamespaceByModuleName(MountInstance mountPoint, String moduleName) {
- val module = mountPoint.findModuleByName(moduleName)
- return module?.namespace
- }
-
- def getAllModules(MountInstance mountPoint) {
- checkPreconditions
- return mountPoint?.schemaContext?.modules
- }
-
- def getAllModules() {
- checkPreconditions
- return globalSchema.modules
- }
-
- def CharSequence toRestconfIdentifier(QName qname) {
- checkPreconditions
- var module = uriToModuleName.get(qname.namespace)
- if (module === null) {
- val moduleSchema = globalSchema.findModuleByNamespaceAndRevision(qname.namespace, qname.revision);
- if(moduleSchema === null) return null
- uriToModuleName.put(qname.namespace, moduleSchema.name)
- module = moduleSchema.name;
- }
- return '''«module»:«qname.localName»''';
- }
-
- def CharSequence toRestconfIdentifier(MountInstance mountPoint, QName qname) {
- val moduleSchema = mountPoint?.schemaContext.findModuleByNamespaceAndRevision(qname.namespace, qname.revision);
- if(moduleSchema === null) return null
- val module = moduleSchema.name;
- return '''«module»:«qname.localName»''';
- }
-
- private static dispatch def DataSchemaNode childByQName(ChoiceNode container, QName name) {
- for (caze : container.cases) {
- val ret = caze.childByQName(name)
- if (ret !== null) {
- return ret;
- }
- }
- return null;
- }
-
- private static dispatch def DataSchemaNode childByQName(ChoiceCaseNode container, QName name) {
- val ret = container.getDataChildByName(name);
- return ret;
- }
-
- private static dispatch def DataSchemaNode childByQName(ContainerSchemaNode container, QName name) {
- return container.dataNodeChildByQName(name);
- }
-
- private static dispatch def DataSchemaNode childByQName(ListSchemaNode container, QName name) {
- return container.dataNodeChildByQName(name);
- }
-
- private static dispatch def DataSchemaNode childByQName(Module container, QName name) {
- return container.dataNodeChildByQName(name);
- }
-
- private static dispatch def DataSchemaNode childByQName(DataSchemaNode container, QName name) {
- return null;
- }
-
- private static def DataSchemaNode dataNodeChildByQName(DataNodeContainer container, QName name) {
- var ret = container.getDataChildByName(name);
- if (ret === null) {
-
- // Find in Choice Cases
- for (node : container.childNodes) {
- if (node instanceof ChoiceCaseNode) {
- val caseNode = (node as ChoiceCaseNode);
- ret = caseNode.childByQName(name);
- if (ret !== null) {
- return ret;
- }
- }
- }
- }
- return ret;
- }
-
- private def toUriString(Object object) {
- if(object === null) return "";
- return URLEncoder.encode(object.toString,URI_ENCODING_CHAR_SET)
- }
-
- private def InstanceIdWithSchemaNode collectPathArguments(InstanceIdentifierBuilder builder, List<String> strings,
- DataNodeContainer parentNode, MountInstance mountPoint, boolean returnJustMountPoint) {
- checkNotNull(strings)
- if (parentNode === null) {
- return null;
- }
- if (strings.empty) {
- return new InstanceIdWithSchemaNode(builder.toInstance, parentNode as DataSchemaNode, mountPoint)
- }
-
- val nodeName = strings.head.toNodeName
- val moduleName = strings.head.toModuleName
- var DataSchemaNode targetNode = null
- if (!moduleName.nullOrEmpty) {
- // if it is mount point
- if (moduleName == MOUNT_MODULE && nodeName == MOUNT_NODE) {
- if (mountPoint !== null) {
- throw new ResponseException(BAD_REQUEST, "Restconf supports just one mount point in URI.")
- }
-
- if (mountService === null) {
- throw new ResponseException(SERVICE_UNAVAILABLE, "MountService was not found. "
- + "Finding behind mount points does not work."
- )
- }
-
- val partialPath = builder.toInstance;
- val mount = mountService.getMountPoint(partialPath)
- if (mount === null) {
- LOG.debug("Instance identifier to missing mount point: {}", partialPath)
- throw new ResponseException(BAD_REQUEST, "Mount point does not exist.")
- }
-
- val mountPointSchema = mount.schemaContext;
- if (mountPointSchema === null) {
- throw new ResponseException(BAD_REQUEST, "Mount point does not contain any schema with modules.")
- }
-
- if (returnJustMountPoint) {
- return new InstanceIdWithSchemaNode(InstanceIdentifier.builder().toInstance, mountPointSchema, mount)
- }
-
- if (strings.size == 1) { // any data node is not behind mount point
- return new InstanceIdWithSchemaNode(InstanceIdentifier.builder().toInstance, mountPointSchema, mount)
- }
-
- val moduleNameBehindMountPoint = strings.get(1).toModuleName()
- if (moduleNameBehindMountPoint === null) {
- throw new ResponseException(BAD_REQUEST,
- "First node after mount point in URI has to be in format \"moduleName:nodeName\"")
- }
-
- val moduleBehindMountPoint = mountPointSchema.getLatestModule(moduleNameBehindMountPoint)
- if (moduleBehindMountPoint === null) {
- throw new ResponseException(BAD_REQUEST,
- "URI has bad format. \"" + moduleName + "\" module does not exist in mount point.")
- }
-
- return collectPathArguments(InstanceIdentifier.builder(), strings.subList(1, strings.size),
- moduleBehindMountPoint, mount, returnJustMountPoint);
- }
-
- var Module module = null;
- if (mountPoint === null) {
- module = globalSchema.getLatestModule(moduleName)
- if (module === null) {
- throw new ResponseException(BAD_REQUEST,
- "URI has bad format. \"" + moduleName + "\" module does not exist.")
- }
- } else {
- module = mountPoint.schemaContext?.getLatestModule(moduleName)
- if (module === null) {
- throw new ResponseException(BAD_REQUEST,
- "URI has bad format. \"" + moduleName + "\" module does not exist in mount point.")
- }
- }
- targetNode = parentNode.findInstanceDataChildByNameAndNamespace(nodeName, module.namespace)
- if (targetNode === null) {
- throw new ResponseException(BAD_REQUEST, "URI has bad format. Possible reasons:\n" +
- "1. \"" + strings.head + "\" was not found in parent data node.\n" +
- "2. \"" + strings.head + "\" is behind mount point. Then it should be in format \"/" + MOUNT + "/" + strings.head + "\".")
- }
- } else { // string without module name
- val potentialSchemaNodes = parentNode.findInstanceDataChildrenByName(nodeName)
- if (potentialSchemaNodes.size > 1) {
- val StringBuilder namespacesOfPotentialModules = new StringBuilder;
- for (potentialNodeSchema : potentialSchemaNodes) {
- namespacesOfPotentialModules.append(" ").append(potentialNodeSchema.QName.namespace.toString).append("\n")
- }
- throw new ResponseException(BAD_REQUEST, "URI has bad format. Node \"" + nodeName + "\" is added as augment from more than one module. "
- + "Therefore the node must have module name and it has to be in format \"moduleName:nodeName\"."
- + "\nThe node is added as augment from modules with namespaces:\n" + namespacesOfPotentialModules)
- }
- targetNode = potentialSchemaNodes.head
- if (targetNode === null) {
- throw new ResponseException(BAD_REQUEST, "URI has bad format. \"" + nodeName + "\" was not found in parent data node.\n")
- }
- }
-
- if (!targetNode.isListOrContainer) {
- throw new ResponseException(BAD_REQUEST,"URI has bad format. Node \"" + strings.head + "\" must be Container or List yang type.")
- }
- // Number of consumed elements
- var consumed = 1;
- if (targetNode instanceof ListSchemaNode) {
- val listNode = targetNode as ListSchemaNode;
- val keysSize = listNode.keyDefinition.size
-
- // every key has to be filled
- if ((strings.length - consumed) < keysSize) {
- throw new ResponseException(BAD_REQUEST,"Missing key for list \"" + listNode.QName.localName + "\".")
- }
- val uriKeyValues = strings.subList(consumed, consumed + keysSize);
- val keyValues = new HashMap<QName, Object>();
- var i = 0;
- for (key : listNode.keyDefinition) {
- val uriKeyValue = uriKeyValues.get(i);
-
- // key value cannot be NULL
- if (uriKeyValue.equals(NULL_VALUE)) {
- throw new ResponseException(BAD_REQUEST, "URI has bad format. List \"" + listNode.QName.localName
- + "\" cannot contain \"null\" value as a key."
- )
- }
- keyValues.addKeyValue(listNode.getDataChildByName(key), uriKeyValue, mountPoint);
- i = i + 1;
- }
- consumed = consumed + i;
- builder.nodeWithKey(targetNode.QName, keyValues);
- } else {
-
- // Only one instance of node is allowed
- builder.node(targetNode.QName);
- }
- if (targetNode instanceof DataNodeContainer) {
- val remaining = strings.subList(consumed, strings.length);
- val result = builder.collectPathArguments(remaining, targetNode as DataNodeContainer, mountPoint, returnJustMountPoint);
- return result
- }
-
- return new InstanceIdWithSchemaNode(builder.toInstance, targetNode, mountPoint)
- }
-
- def DataSchemaNode findInstanceDataChildByNameAndNamespace(DataNodeContainer container,
- String name, URI namespace) {
- Preconditions.checkNotNull(namespace)
- val potentialSchemaNodes = container.findInstanceDataChildrenByName(name)
- return potentialSchemaNodes.filter[n|n.QName.namespace == namespace].head
- }
-
- def List<DataSchemaNode> findInstanceDataChildrenByName(DataNodeContainer container, String name) {
- Preconditions.checkNotNull(container)
- Preconditions.checkNotNull(name)
- val instantiatedDataNodeContainers = new ArrayList
- instantiatedDataNodeContainers.collectInstanceDataNodeContainers(container, name)
- return instantiatedDataNodeContainers
- }
-
- private def void collectInstanceDataNodeContainers(List<DataSchemaNode> potentialSchemaNodes, DataNodeContainer container,
- String name) {
- val nodes = container.childNodes.filter[n|n.QName.localName == name]
- for (potentialNode : nodes) {
- if (potentialNode.isInstantiatedDataSchema) {
- potentialSchemaNodes.add(potentialNode)
- }
- }
- val allCases = container.childNodes.filter(ChoiceNode).map[cases].flatten
- for (caze : allCases) {
- collectInstanceDataNodeContainers(potentialSchemaNodes, caze, name)
- }
- }
-
- def boolean isInstantiatedDataSchema(DataSchemaNode node) {
- switch node {
- LeafSchemaNode: return true
- LeafListSchemaNode: return true
- ContainerSchemaNode: return true
- ListSchemaNode: return true
- default: return false
- }
- }
-
- private def void addKeyValue(HashMap<QName, Object> map, DataSchemaNode node, String uriValue, MountInstance mountPoint) {
- checkNotNull(uriValue);
- checkArgument(node instanceof LeafSchemaNode);
- val urlDecoded = URLDecoder.decode(uriValue);
- val typedef = (node as LeafSchemaNode).type;
-
- var decoded = RestCodec.from(typedef, mountPoint)?.deserialize(urlDecoded)
- var additionalInfo = ""
- if(decoded === null) {
- var baseType = RestUtil.resolveBaseTypeFrom(typedef)
- if(baseType instanceof IdentityrefTypeDefinition) {
- decoded = toQName(urlDecoded)
- additionalInfo = "For key which is of type identityref it should be in format module_name:identity_name."
- }
- }
- if (decoded === null) {
- throw new ResponseException(BAD_REQUEST, uriValue + " from URI can't be resolved. "+ additionalInfo )
- }
-
- map.put(node.QName, decoded);
- }
-
- private static def String toModuleName(String str) {
- checkNotNull(str)
- if (str.contains(":")) {
- val args = str.split(":");
- if (args.size === 2) {
- return args.get(0);
- }
- }
- return null;
- }
-
- private def String toNodeName(String str) {
- if (str.contains(":")) {
- val args = str.split(":");
- if (args.size === 2) {
- return args.get(1);
- }
- }
- return str;
- }
-
- private def QName toQName(String name) {
- val module = name.toModuleName;
- val node = name.toNodeName;
- val namespace = FluentIterable.from(globalSchema.modules.sort[o1,o2 | o1.revision.compareTo(o2.revision)])
- .transform[QName.create(namespace,revision,it.name)].findFirst[module == localName]
- if (namespace === null) {
- return null
- }
- return QName.create(namespace, node);
- }
-
- private def boolean isListOrContainer(DataSchemaNode node) {
- return ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode))
- }
-
- def getRpcDefinition(String name) {
- val validName = name.toQName
- if (validName === null) {
- return null
- }
- return qnameToRpc.get(validName)
- }
-
- override onGlobalContextUpdated(SchemaContext context) {
- if (context !== null) {
- qnameToRpc.clear
- this.globalSchema = context;
- for (operation : context.operations) {
- val qname = operation.QName;
- qnameToRpc.put(qname, operation);
- }
- }
- }
-
-
- def urlPathArgsDecode(List<String> strings) {
- val List<String> decodedPathArgs = new ArrayList();
- for (pathArg : strings) {
- decodedPathArgs.add(URLDecoder.decode(pathArg, URI_ENCODING_CHAR_SET))
- }
- return decodedPathArgs
- }
-
- def urlPathArgDecode(String pathArg) {
- if (pathArg !== null) {
- return URLDecoder.decode(pathArg, URI_ENCODING_CHAR_SET)
- }
- return null
- }
-
-}
public ResponseException(Status status, String msg) {
super(Response.status(status).type(MediaType.TEXT_PLAIN_TYPE).entity(msg).build());
}
+
+ public ResponseException(Throwable cause, String msg) {
+ super(cause, Response.status(Status.INTERNAL_SERVER_ERROR).
+ type(MediaType.TEXT_PLAIN_TYPE).entity(msg).build());
+ }
}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.restconf.impl;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.core.api.mount.MountInstance;
+import org.opendaylight.controller.sal.rest.api.RestconfService;
+import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
+import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
+import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
+import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.restconf.impl.RestCodec;
+import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
+import org.opendaylight.controller.sal.restconf.impl.StructuredData;
+import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.controller.sal.streams.listeners.Notificator;
+import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder;
+import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.EmptyType;
+import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
+import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
+
+@SuppressWarnings("all")
+public class RestconfImpl implements RestconfService {
+ private final static RestconfImpl INSTANCE = new RestconfImpl();
+
+ private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
+
+ private final static SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+
+ private final static String RESTCONF_MODULE_DRAFT02_REVISION = "2013-10-19";
+
+ private final static String RESTCONF_MODULE_DRAFT02_NAME = "ietf-restconf";
+
+ private final static String RESTCONF_MODULE_DRAFT02_NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf";
+
+ private final static String RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE = "restconf";
+
+ private final static String RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE = "restconf";
+
+ private final static String RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE = "modules";
+
+ private final static String RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE = "module";
+
+ private final static String RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE = "streams";
+
+ private final static String RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE = "stream";
+
+ private final static String RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE = "operations";
+
+ private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
+
+ private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
+
+ private BrokerFacade broker;
+
+ private ControllerContext controllerContext;
+
+ public void setBroker(final BrokerFacade broker) {
+ this.broker = broker;
+ }
+
+ public void setControllerContext(final ControllerContext controllerContext) {
+ this.controllerContext = controllerContext;
+ }
+
+ private RestconfImpl() {
+ }
+
+ public static RestconfImpl getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public StructuredData getModules() {
+ final Module restconfModule = this.getRestconfModule();
+
+ final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
+ final DataSchemaNode moduleSchemaNode =
+ this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
+
+ Set<Module> allModules = this.controllerContext.getAllModules();
+ for (final Module module : allModules) {
+ CompositeNode moduleCompositeNode = this.toModuleCompositeNode(module, moduleSchemaNode);
+ modulesAsData.add(moduleCompositeNode);
+ }
+
+ final DataSchemaNode modulesSchemaNode =
+ this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
+ QName qName = modulesSchemaNode.getQName();
+ final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
+ return new StructuredData(modulesNode, modulesSchemaNode, null);
+ }
+
+ @Override
+ public StructuredData getAvailableStreams() {
+ Set<String> availableStreams = Notificator.getStreamNames();
+
+ final List<Node<?>> streamsAsData = new ArrayList<Node<?>>();
+ Module restconfModule = this.getRestconfModule();
+ final DataSchemaNode streamSchemaNode =
+ this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE);
+ for (final String streamName : availableStreams) {
+ streamsAsData.add(this.toStreamCompositeNode(streamName, streamSchemaNode));
+ }
+
+ final DataSchemaNode streamsSchemaNode =
+ this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE);
+ QName qName = streamsSchemaNode.getQName();
+ final CompositeNode streamsNode = NodeFactory.createImmutableCompositeNode(qName, null, streamsAsData);
+ return new StructuredData(streamsNode, streamsSchemaNode, null);
+ }
+
+ @Override
+ public StructuredData getModules(final String identifier) {
+ Set<Module> modules = null;
+ MountInstance mountPoint = null;
+ if (identifier.contains(ControllerContext.MOUNT)) {
+ InstanceIdWithSchemaNode mountPointIdentifier =
+ this.controllerContext.toMountPointIdentifier(identifier);
+ mountPoint = mountPointIdentifier.getMountPoint();
+ modules = this.controllerContext.getAllModules(mountPoint);
+ }
+ else {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "URI has bad format. If modules behind mount point should be showed, URI has to end with " +
+ ControllerContext.MOUNT);
+ }
+
+ final List<Node<?>> modulesAsData = new ArrayList<Node<?>>();
+ Module restconfModule = this.getRestconfModule();
+ final DataSchemaNode moduleSchemaNode =
+ this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
+
+ for (final Module module : modules) {
+ modulesAsData.add(this.toModuleCompositeNode(module, moduleSchemaNode));
+ }
+
+ final DataSchemaNode modulesSchemaNode =
+ this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
+ QName qName = modulesSchemaNode.getQName();
+ final CompositeNode modulesNode = NodeFactory.createImmutableCompositeNode(qName, null, modulesAsData);
+ return new StructuredData(modulesNode, modulesSchemaNode, mountPoint);
+ }
+
+ @Override
+ public StructuredData getModule(final String identifier) {
+ final QName moduleNameAndRevision = this.getModuleNameAndRevision(identifier);
+ Module module = null;
+ MountInstance mountPoint = null;
+ if (identifier.contains(ControllerContext.MOUNT)) {
+ InstanceIdWithSchemaNode mountPointIdentifier =
+ this.controllerContext.toMountPointIdentifier(identifier);
+ mountPoint = mountPointIdentifier.getMountPoint();
+ module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
+ }
+ else {
+ module = this.controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
+ }
+
+ if (module == null) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Module with name '" + moduleNameAndRevision.getLocalName() + "' and revision '" +
+ moduleNameAndRevision.getRevision() + "' was not found.");
+ }
+
+ Module restconfModule = this.getRestconfModule();
+ final DataSchemaNode moduleSchemaNode =
+ this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
+ final CompositeNode moduleNode = this.toModuleCompositeNode(module, moduleSchemaNode);
+ return new StructuredData(moduleNode, moduleSchemaNode, mountPoint);
+ }
+
+ @Override
+ public StructuredData getOperations() {
+ Set<Module> allModules = this.controllerContext.getAllModules();
+ return this.operationsFromModulesToStructuredData(allModules, null);
+ }
+
+ @Override
+ public StructuredData getOperations(final String identifier) {
+ Set<Module> modules = null;
+ MountInstance mountPoint = null;
+ if (identifier.contains(ControllerContext.MOUNT)) {
+ InstanceIdWithSchemaNode mountPointIdentifier =
+ this.controllerContext.toMountPointIdentifier(identifier);
+ mountPoint = mountPointIdentifier.getMountPoint();
+ modules = this.controllerContext.getAllModules(mountPoint);
+ }
+ else {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "URI has bad format. If operations behind mount point should be showed, URI has to end with " +
+ ControllerContext.MOUNT);
+ }
+
+ return this.operationsFromModulesToStructuredData(modules, mountPoint);
+ }
+
+ private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
+ final MountInstance mountPoint) {
+ final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
+ Module restconfModule = this.getRestconfModule();
+ final DataSchemaNode operationsSchemaNode =
+ this.getSchemaNode(restconfModule, RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE);
+ QName qName = operationsSchemaNode.getQName();
+ SchemaPath path = operationsSchemaNode.getPath();
+ ContainerSchemaNodeBuilder containerSchemaNodeBuilder =
+ new ContainerSchemaNodeBuilder(RESTCONF_MODULE_DRAFT02_NAME, 0, qName, path);
+ final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
+ for (final Module module : modules) {
+ Set<RpcDefinition> rpcs = module.getRpcs();
+ for (final RpcDefinition rpc : rpcs) {
+ QName rpcQName = rpc.getQName();
+ SimpleNode<Object> immutableSimpleNode =
+ NodeFactory.<Object>createImmutableSimpleNode(rpcQName, null, null);
+ operationsAsData.add(immutableSimpleNode);
+
+ String name = module.getName();
+ LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, null);
+ final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
+ fakeRpcSchemaNode.setAugmenting(true);
+
+ EmptyType instance = EmptyType.getInstance();
+ fakeRpcSchemaNode.setType(instance);
+ fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build());
+ }
+ }
+
+ final CompositeNode operationsNode =
+ NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
+ ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
+ return new StructuredData(operationsNode, schemaNode, mountPoint);
+ }
+
+ private Module getRestconfModule() {
+ QName qName = QName.create(RESTCONF_MODULE_DRAFT02_NAMESPACE, RESTCONF_MODULE_DRAFT02_REVISION,
+ RESTCONF_MODULE_DRAFT02_NAME);
+ final Module restconfModule = this.controllerContext.findModuleByNameAndRevision(qName);
+ if (restconfModule == null) {
+ throw new ResponseException(Status.INTERNAL_SERVER_ERROR, "Restconf module was not found.");
+ }
+
+ return restconfModule;
+ }
+
+ private QName getModuleNameAndRevision(final String identifier) {
+ final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
+ String moduleNameAndRevision = "";
+ if (mountIndex >= 0) {
+ moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
+ }
+ else {
+ moduleNameAndRevision = identifier;
+ }
+
+ Splitter splitter = Splitter.on("/").omitEmptyStrings();
+ Iterable<String> split = splitter.split(moduleNameAndRevision);
+ final List<String> pathArgs = Lists.<String>newArrayList(split);
+ if (pathArgs.size() < 2) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'");
+ }
+
+ try {
+ final String moduleName = pathArgs.get( 0 );
+ String revision = pathArgs.get(1);
+ final Date moduleRevision = REVISION_FORMAT.parse(revision);
+ return QName.create(null, moduleRevision, moduleName);
+ }
+ catch (ParseException e) {
+ throw new ResponseException(Status.BAD_REQUEST, "URI has bad format. It should be \'moduleName/yyyy-MM-dd\'");
+ }
+ }
+
+ private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
+ final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
+ List<DataSchemaNode> instanceDataChildrenByName =
+ this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) streamSchemaNode),
+ "name");
+ final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+ streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(), null,
+ streamName));
+
+ instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
+ ((DataNodeContainer) streamSchemaNode), "description");
+ final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+ streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
+ "DESCRIPTION_PLACEHOLDER"));
+
+ instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
+ ((DataNodeContainer) streamSchemaNode), "replay-support");
+ final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+ streamNodeValues.add(NodeFactory.<Boolean>createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
+ Boolean.valueOf(true)));
+
+ instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
+ ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
+ final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+ streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
+ null, ""));
+
+ instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
+ ((DataNodeContainer) streamSchemaNode), "events");
+ final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+ streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(eventsSchemaNode.getQName(),
+ null, ""));
+
+ return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
+ }
+
+ private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
+ final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
+ List<DataSchemaNode> instanceDataChildrenByName =
+ this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) moduleSchemaNode), "name");
+ final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+ moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(nameSchemaNode.getQName(),
+ null, module.getName()));
+
+ instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
+ ((DataNodeContainer) moduleSchemaNode), "revision");
+ final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+ Date _revision = module.getRevision();
+ moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
+ REVISION_FORMAT.format(_revision)));
+
+ instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
+ ((DataNodeContainer) moduleSchemaNode), "namespace");
+ final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+ moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
+ module.getNamespace().toString()));
+
+ instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName(
+ ((DataNodeContainer) moduleSchemaNode), "feature");
+ final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
+ for (final FeatureDefinition feature : module.getFeatures()) {
+ moduleNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(featureSchemaNode.getQName(), null,
+ feature.getQName().getLocalName()));
+ }
+
+ return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
+ }
+
+ private DataSchemaNode getSchemaNode(final Module restconfModule, final String schemaNodeName) {
+ Set<GroupingDefinition> groupings = restconfModule.getGroupings();
+
+ final Predicate<GroupingDefinition> filter = new Predicate<GroupingDefinition>() {
+ @Override
+ public boolean apply(final GroupingDefinition g) {
+ return Objects.equal(g.getQName().getLocalName(),
+ RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE);
+ }
+ };
+
+ Iterable<GroupingDefinition> filteredGroups = Iterables.filter(groupings, filter);
+
+ final GroupingDefinition restconfGrouping = Iterables.getFirst(filteredGroups, null);
+
+ List<DataSchemaNode> instanceDataChildrenByName =
+ this.controllerContext.findInstanceDataChildrenByName(restconfGrouping,
+ RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE);
+ final DataSchemaNode restconfContainer = Iterables.getFirst(instanceDataChildrenByName, null);
+
+ if (Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE)) {
+ List<DataSchemaNode> instances =
+ this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
+ RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE);
+ return Iterables.getFirst(instances, null);
+ }
+ else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE)) {
+ List<DataSchemaNode> instances =
+ this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
+ RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE);
+ return Iterables.getFirst(instances, null);
+ }
+ else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE)) {
+ List<DataSchemaNode> instances =
+ this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
+ RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE);
+ final DataSchemaNode modules = Iterables.getFirst(instances, null);
+ instances = this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) modules),
+ RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE);
+ return Iterables.getFirst(instances, null);
+ }
+ else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)) {
+ List<DataSchemaNode> instances =
+ this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
+ RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
+ return Iterables.getFirst(instances, null);
+ }
+ else if(Objects.equal(schemaNodeName, RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)) {
+ List<DataSchemaNode> instances =
+ this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) restconfContainer),
+ RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE);
+ final DataSchemaNode modules = Iterables.getFirst(instances, null);
+ instances = this.controllerContext.findInstanceDataChildrenByName(((DataNodeContainer) modules),
+ RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE);
+ return Iterables.getFirst(instances, null);
+ }
+
+ return null;
+ }
+
+ @Override
+ public Object getRoot() {
+ return null;
+ }
+
+ @Override
+ public StructuredData invokeRpc(final String identifier, final CompositeNode payload) {
+ final RpcDefinition rpc = this.resolveIdentifierInInvokeRpc(identifier);
+ if (Objects.equal(rpc.getQName().getNamespace().toString(), SAL_REMOTE_NAMESPACE) &&
+ Objects.equal(rpc.getQName().getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
+
+ final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
+ final SimpleNode<? extends Object> pathNode = value == null ? null :
+ value.getFirstSimpleByName( QName.create(rpc.getQName(), "path") );
+ final Object pathValue = pathNode == null ? null : pathNode.getValue();
+
+ if (!(pathValue instanceof InstanceIdentifier)) {
+ throw new ResponseException(Status.INTERNAL_SERVER_ERROR,
+ "Instance identifier was not normalized correctly.");
+ }
+
+ final InstanceIdentifier pathIdentifier = ((InstanceIdentifier) pathValue);
+ String streamName = null;
+ if (!Iterables.isEmpty(pathIdentifier.getPath())) {
+ String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier);
+ streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier);
+ }
+
+ if (Strings.isNullOrEmpty(streamName)) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Path is empty or contains data node which is not Container or List build-in type.");
+ }
+
+ final SimpleNode<String> streamNameNode = NodeFactory.<String>createImmutableSimpleNode(
+ QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
+ final List<Node<?>> output = new ArrayList<Node<?>>();
+ output.add(streamNameNode);
+
+ final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(
+ rpc.getOutput().getQName(), null, output, null, null);
+
+ if (!Notificator.existListenerFor(pathIdentifier)) {
+ Notificator.createListener(pathIdentifier, streamName);
+ }
+
+ return new StructuredData(responseData, rpc.getOutput(), null);
+ }
+
+ RpcDefinition rpcDefinition = this.controllerContext.getRpcDefinition(identifier);
+ return this.callRpc(rpcDefinition, payload);
+ }
+
+ @Override
+ public StructuredData invokeRpc(final String identifier, final String noPayload) {
+ if (!Strings.isNullOrEmpty(noPayload)) {
+ throw new ResponseException(Status.UNSUPPORTED_MEDIA_TYPE,
+ "Content-Type contains unsupported Media Type.");
+ }
+
+ final RpcDefinition rpc = this.resolveIdentifierInInvokeRpc(identifier);
+ return this.callRpc(rpc, null);
+ }
+
+ private RpcDefinition resolveIdentifierInInvokeRpc(final String identifier) {
+ if (identifier.indexOf("/") < 0) {
+ final String identifierDecoded = this.controllerContext.urlPathArgDecode(identifier);
+ final RpcDefinition rpc = this.controllerContext.getRpcDefinition(identifierDecoded);
+ if (rpc != null) {
+ return rpc;
+ }
+
+ throw new ResponseException(Status.NOT_FOUND, "RPC does not exist.");
+ }
+
+ final String slashErrorMsg = String.format(
+ "Identifier %n%s%ncan\'t contain slash character (/).%nIf slash is part of identifier name then use %%2F placeholder.",
+ identifier);
+
+ throw new ResponseException(Status.NOT_FOUND, slashErrorMsg);
+ }
+
+ private StructuredData callRpc(final RpcDefinition rpc, final CompositeNode payload) {
+ if (rpc == null) {
+ throw new ResponseException(Status.NOT_FOUND, "RPC does not exist.");
+ }
+
+ CompositeNode rpcRequest = null;
+ if (payload == null) {
+ rpcRequest = NodeFactory.createMutableCompositeNode(rpc.getQName(), null, null, null, null);
+ }
+ else {
+ final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
+ final List<Node<?>> input = new ArrayList<Node<?>>();
+ input.add(value);
+
+ rpcRequest = NodeFactory.createMutableCompositeNode(rpc.getQName(), null, input, null, null);
+ }
+
+ final RpcResult<CompositeNode> rpcResult = broker.invokeRpc(rpc.getQName(), rpcRequest);
+
+ if (!rpcResult.isSuccessful()) {
+ throw new ResponseException(Status.INTERNAL_SERVER_ERROR, "Operation failed");
+ }
+
+ CompositeNode result = rpcResult.getResult();
+ if (result == null) {
+ return null;
+ }
+
+ return new StructuredData(result, rpc.getOutput(), null);
+ }
+
+ @Override
+ public StructuredData readConfigurationData(final String identifier) {
+ final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
+ CompositeNode data = null;
+ MountInstance mountPoint = iiWithData.getMountPoint();
+ if (mountPoint != null) {
+ data = broker.readConfigurationDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
+ }
+ else {
+ data = broker.readConfigurationData(iiWithData.getInstanceIdentifier());
+ }
+
+ return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
+ }
+
+ @Override
+ public StructuredData readOperationalData(final String identifier) {
+ final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
+ CompositeNode data = null;
+ MountInstance mountPoint = iiWithData.getMountPoint();
+ if (mountPoint != null) {
+ data = broker.readOperationalDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier());
+ }
+ else {
+ data = broker.readOperationalData(iiWithData.getInstanceIdentifier());
+ }
+
+ return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint);
+ }
+
+ @Override
+ public Response updateConfigurationData(final String identifier, final CompositeNode payload) {
+ final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
+ MountInstance mountPoint = iiWithData.getMountPoint();
+ final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
+ RpcResult<TransactionStatus> status = null;
+
+ try {
+ if (mountPoint != null) {
+ status = broker.commitConfigurationDataPutBehindMountPoint(
+ mountPoint, iiWithData.getInstanceIdentifier(), value).get();
+ } else {
+ status = broker.commitConfigurationDataPut(iiWithData.getInstanceIdentifier(), value).get();
+ }
+ }
+ catch( Exception e ) {
+ throw new ResponseException( e, "Error updating data" );
+ }
+
+ if( status.getResult() == TransactionStatus.COMMITED )
+ return Response.status(Status.OK).build();
+
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ @Override
+ public Response createConfigurationData(final String identifier, final CompositeNode payload) {
+ URI payloadNS = this.namespace(payload);
+ if (payloadNS == null) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
+ }
+
+ InstanceIdWithSchemaNode iiWithData = null;
+ CompositeNode value = null;
+ if (this.representsMountPointRootData(payload)) {
+ // payload represents mount point data and URI represents path to the mount point
+
+ if (this.endsWithMountPoint(identifier)) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "URI has bad format. URI should be without \"" + ControllerContext.MOUNT +
+ "\" for POST operation.");
+ }
+
+ final String completeIdentifier = this.addMountPointIdentifier(identifier);
+ iiWithData = this.controllerContext.toInstanceIdentifier(completeIdentifier);
+
+ value = this.normalizeNode(payload, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
+ }
+ else {
+ final InstanceIdWithSchemaNode incompleteInstIdWithData =
+ this.controllerContext.toInstanceIdentifier(identifier);
+ final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode();
+ MountInstance mountPoint = incompleteInstIdWithData.getMountPoint();
+ final Module module = this.findModule(mountPoint, payload);
+ if (module == null) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Module was not found for \"" + payloadNS + "\"");
+ }
+
+ String payloadName = this.getName(payload);
+ final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
+ parentSchema, payloadName, module.getNamespace());
+ value = this.normalizeNode(payload, schemaNode, mountPoint);
+
+ iiWithData = this.addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode);
+ }
+
+ RpcResult<TransactionStatus> status = null;
+ MountInstance mountPoint = iiWithData.getMountPoint();
+ try {
+ if (mountPoint != null) {
+ Future<RpcResult<TransactionStatus>> future =
+ broker.commitConfigurationDataPostBehindMountPoint(
+ mountPoint, iiWithData.getInstanceIdentifier(), value);
+ status = future == null ? null : future.get();
+ }
+ else {
+ Future<RpcResult<TransactionStatus>> future =
+ broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
+ status = future == null ? null : future.get();
+ }
+ }
+ catch( Exception e ) {
+ throw new ResponseException( e, "Error creating data" );
+ }
+
+ if (status == null) {
+ return Response.status(Status.ACCEPTED).build();
+ }
+
+ if( status.getResult() == TransactionStatus.COMMITED )
+ return Response.status(Status.NO_CONTENT).build();
+
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ @Override
+ public Response createConfigurationData(final CompositeNode payload) {
+ URI payloadNS = this.namespace(payload);
+ if (payloadNS == null) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
+ }
+
+ final Module module = this.findModule(null, payload);
+ if (module == null) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)");
+ }
+
+ String payloadName = this.getName(payload);
+ final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(
+ module, payloadName, module.getNamespace());
+ final CompositeNode value = this.normalizeNode(payload, schemaNode, null);
+ final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode);
+ RpcResult<TransactionStatus> status = null;
+ MountInstance mountPoint = iiWithData.getMountPoint();
+
+ try {
+ if (mountPoint != null) {
+ Future<RpcResult<TransactionStatus>> future =
+ broker.commitConfigurationDataPostBehindMountPoint(
+ mountPoint, iiWithData.getInstanceIdentifier(), value);
+ status = future == null ? null : future.get();
+ }
+ else {
+ Future<RpcResult<TransactionStatus>> future =
+ broker.commitConfigurationDataPost(iiWithData.getInstanceIdentifier(), value);
+ status = future == null ? null : future.get();
+ }
+ }
+ catch( Exception e ) {
+ throw new ResponseException( e, "Error creating data" );
+ }
+
+ if (status == null) {
+ return Response.status(Status.ACCEPTED).build();
+ }
+
+ if( status.getResult() == TransactionStatus.COMMITED )
+ return Response.status(Status.NO_CONTENT).build();
+
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ @Override
+ public Response deleteConfigurationData(final String identifier) {
+ final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
+ RpcResult<TransactionStatus> status = null;
+ MountInstance mountPoint = iiWithData.getMountPoint();
+
+ try {
+ if (mountPoint != null) {
+ status = broker.commitConfigurationDataDeleteBehindMountPoint(
+ mountPoint, iiWithData.getInstanceIdentifier()).get();
+ }
+ else {
+ status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier()).get();
+ }
+ }
+ catch( Exception e ) {
+ throw new ResponseException( e, "Error creating data" );
+ }
+
+ if( status.getResult() == TransactionStatus.COMMITED )
+ return Response.status(Status.OK).build();
+
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ @Override
+ public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
+ final String streamName = Notificator.createStreamNameFromUri(identifier);
+ if (Strings.isNullOrEmpty(streamName)) {
+ throw new ResponseException(Status.BAD_REQUEST, "Stream name is empty.");
+ }
+
+ final ListenerAdapter listener = Notificator.getListenerFor(streamName);
+ if (listener == null) {
+ throw new ResponseException(Status.BAD_REQUEST, "Stream was not found.");
+ }
+
+ broker.registerToListenDataChanges(listener);
+
+ final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
+ UriBuilder port = uriBuilder.port(WebSocketServer.PORT);
+ final URI uriToWebsocketServer = port.replacePath(streamName).build();
+
+ return Response.status(Status.OK).location(uriToWebsocketServer).build();
+ }
+
+ private Module findModule(final MountInstance mountPoint, final CompositeNode data) {
+ if (data instanceof CompositeNodeWrapper) {
+ return findModule(mountPoint, (CompositeNodeWrapper)data);
+ }
+ else if (data != null) {
+ URI namespace = data.getNodeType().getNamespace();
+ if (mountPoint != null) {
+ return this.controllerContext.findModuleByNamespace(mountPoint, namespace);
+ }
+ else {
+ return this.controllerContext.findModuleByNamespace(namespace);
+ }
+ }
+ else {
+ throw new IllegalArgumentException("Unhandled parameter types: " +
+ Arrays.<Object>asList(mountPoint, data).toString());
+ }
+ }
+
+ private Module findModule(final MountInstance mountPoint, final CompositeNodeWrapper data) {
+ URI namespace = data.getNamespace();
+ Preconditions.<URI>checkNotNull(namespace);
+
+ Module module = null;
+ if (mountPoint != null) {
+ module = this.controllerContext.findModuleByNamespace(mountPoint, namespace);
+ if (module == null) {
+ module = this.controllerContext.findModuleByName(mountPoint, namespace.toString());
+ }
+ }
+ else {
+ module = this.controllerContext.findModuleByNamespace(namespace);
+ if (module == null) {
+ module = this.controllerContext.findModuleByName(namespace.toString());
+ }
+ }
+
+ return module;
+ }
+
+ private InstanceIdWithSchemaNode addLastIdentifierFromData(
+ final InstanceIdWithSchemaNode identifierWithSchemaNode,
+ final CompositeNode data, final DataSchemaNode schemaOfData) {
+ InstanceIdentifier instanceIdentifier = null;
+ if (identifierWithSchemaNode != null) {
+ instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
+ }
+
+ final InstanceIdentifier iiOriginal = instanceIdentifier;
+ InstanceIdentifierBuilder iiBuilder = null;
+ if (iiOriginal == null) {
+ iiBuilder = InstanceIdentifier.builder();
+ }
+ else {
+ iiBuilder = InstanceIdentifier.builder(iiOriginal);
+ }
+
+ if ((schemaOfData instanceof ListSchemaNode)) {
+ HashMap<QName,Object> keys = this.resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
+ iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
+ }
+ else {
+ iiBuilder.node(schemaOfData.getQName());
+ }
+
+ InstanceIdentifier instance = iiBuilder.toInstance();
+ MountInstance mountPoint = null;
+ if (identifierWithSchemaNode != null) {
+ mountPoint=identifierWithSchemaNode.getMountPoint();
+ }
+
+ return new InstanceIdWithSchemaNode(instance, schemaOfData, mountPoint);
+ }
+
+ private HashMap<QName,Object> resolveKeysFromData(final ListSchemaNode listNode,
+ final CompositeNode dataNode) {
+ final HashMap<QName,Object> keyValues = new HashMap<QName, Object>();
+ List<QName> _keyDefinition = listNode.getKeyDefinition();
+ for (final QName key : _keyDefinition) {
+ SimpleNode<? extends Object> head = null;
+ String localName = key.getLocalName();
+ List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
+ if (simpleNodesByName != null) {
+ head = Iterables.getFirst(simpleNodesByName, null);
+ }
+
+ Object dataNodeKeyValueObject = null;
+ if (head != null) {
+ dataNodeKeyValueObject = head.getValue();
+ }
+
+ if (dataNodeKeyValueObject == null) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Data contains list \"" + dataNode.getNodeType().getLocalName() +
+ "\" which does not contain key: \"" + key.getLocalName() + "\"");
+ }
+
+ keyValues.put(key, dataNodeKeyValueObject);
+ }
+
+ return keyValues;
+ }
+
+ private boolean endsWithMountPoint(final String identifier) {
+ return identifier.endsWith(ControllerContext.MOUNT) ||
+ identifier.endsWith(ControllerContext.MOUNT + "/");
+ }
+
+ private boolean representsMountPointRootData(final CompositeNode data) {
+ URI namespace = this.namespace(data);
+ return (SchemaContext.NAME.getNamespace().equals( namespace ) /* ||
+ MOUNT_POINT_MODULE_NAME.equals( namespace.toString() )*/ ) &&
+ SchemaContext.NAME.getLocalName().equals( this.localName(data) );
+ }
+
+ private String addMountPointIdentifier(final String identifier) {
+ boolean endsWith = identifier.endsWith("/");
+ if (endsWith) {
+ return (identifier + ControllerContext.MOUNT);
+ }
+
+ return identifier + "/" + ControllerContext.MOUNT;
+ }
+
+ private CompositeNode normalizeNode(final CompositeNode node, final DataSchemaNode schema,
+ final MountInstance mountPoint) {
+ if (schema == null) {
+ QName nodeType = node == null ? null : node.getNodeType();
+ String localName = nodeType == null ? null : nodeType.getLocalName();
+ String _plus = ("Data schema node was not found for " + localName);
+ throw new ResponseException(Status.INTERNAL_SERVER_ERROR,
+ "Data schema node was not found for " + localName );
+ }
+
+ if (!(schema instanceof DataNodeContainer)) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Root element has to be container or list yang datatype.");
+ }
+
+ if ((node instanceof CompositeNodeWrapper)) {
+ boolean isChangeAllowed = ((CompositeNodeWrapper) node).isChangeAllowed();
+ if (isChangeAllowed) {
+ try {
+ this.normalizeNode(((CompositeNodeWrapper) node), schema, null, mountPoint);
+ }
+ catch (NumberFormatException e) {
+ throw new ResponseException(Status.BAD_REQUEST, e.getMessage());
+ }
+ }
+
+ return ((CompositeNodeWrapper) node).unwrap();
+ }
+
+ return node;
+ }
+
+ private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder,
+ final DataSchemaNode schema, final QName previousAugment,
+ final MountInstance mountPoint) {
+ if (schema == null) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Data has bad format.\n\"" + nodeBuilder.getLocalName() +
+ "\" does not exist in yang schema.");
+ }
+
+ QName currentAugment = null;
+ if (nodeBuilder.getQname() != null) {
+ currentAugment = previousAugment;
+ }
+ else {
+ currentAugment = this.normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
+ if (nodeBuilder.getQname() == null) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Data has bad format.\nIf data is in XML format then namespace for \"" +
+ nodeBuilder.getLocalName() +
+ "\" should be \"" + schema.getQName().getNamespace() + "\".\n" +
+ "If data is in JSON format then module name for \"" + nodeBuilder.getLocalName() +
+ "\" should be corresponding to namespace \"" +
+ schema.getQName().getNamespace() + "\".");
+ }
+ }
+
+ if ((nodeBuilder instanceof CompositeNodeWrapper)) {
+ final List<NodeWrapper<?>> children = ((CompositeNodeWrapper) nodeBuilder).getValues();
+ for (final NodeWrapper<? extends Object> child : children) {
+ final List<DataSchemaNode> potentialSchemaNodes =
+ this.controllerContext.findInstanceDataChildrenByName(
+ ((DataNodeContainer) schema), child.getLocalName());
+
+ if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
+ StringBuilder builder = new StringBuilder();
+ for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
+ builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString())
+ .append("\n");
+ }
+
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Node \"" + child.getLocalName() +
+ "\" is added as augment from more than one module. " +
+ "Therefore node must have namespace (XML format) or module name (JSON format)." +
+ "\nThe node is added as augment from modules with namespaces:\n" + builder);
+ }
+
+ boolean rightNodeSchemaFound = false;
+ for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
+ if (!rightNodeSchemaFound) {
+ final QName potentialCurrentAugment =
+ this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint);
+ if (child.getQname() != null ) {
+ this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
+ rightNodeSchemaFound = true;
+ }
+ }
+ }
+
+ if (!rightNodeSchemaFound) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Schema node \"" + child.getLocalName() + "\" was not found in module.");
+ }
+ }
+
+ if ((schema instanceof ListSchemaNode)) {
+ final List<QName> listKeys = ((ListSchemaNode) schema).getKeyDefinition();
+ for (final QName listKey : listKeys) {
+ boolean foundKey = false;
+ for (final NodeWrapper<? extends Object> child : children) {
+ if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
+ foundKey = true;
+ }
+ }
+
+ if (!foundKey) {
+ throw new ResponseException(Status.BAD_REQUEST,
+ "Missing key in URI \"" + listKey.getLocalName() +
+ "\" of list \"" + schema.getQName().getLocalName() + "\"");
+ }
+ }
+ }
+ }
+ else {
+ if ((nodeBuilder instanceof SimpleNodeWrapper)) {
+ final SimpleNodeWrapper simpleNode = ((SimpleNodeWrapper) nodeBuilder);
+ final Object value = simpleNode.getValue();
+ Object inputValue = value;
+ TypeDefinition<? extends Object> typeDefinition = this.typeDefinition(schema);
+ if ((typeDefinition instanceof IdentityrefTypeDefinition)) {
+ if ((value instanceof String)) {
+ inputValue = new IdentityValuesDTO( nodeBuilder.getNamespace().toString(),
+ (String) value, null, (String) value );
+ } // else value is already instance of IdentityValuesDTO
+ }
+
+ Codec<Object,Object> codec = RestCodec.from(typeDefinition, mountPoint);
+ Object outputValue = codec == null ? null : codec.deserialize(inputValue);
+
+ simpleNode.setValue(outputValue);
+ }
+ else {
+ if ((nodeBuilder instanceof EmptyNodeWrapper)) {
+ final EmptyNodeWrapper emptyNodeBuilder = ((EmptyNodeWrapper) nodeBuilder);
+ if ((schema instanceof LeafSchemaNode)) {
+ emptyNodeBuilder.setComposite(false);
+ }
+ else {
+ if ((schema instanceof ContainerSchemaNode)) {
+ // FIXME: Add presence check
+ emptyNodeBuilder.setComposite(true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder,
+ final DataSchemaNode schema, final QName previousAugment,
+ final MountInstance mountPoint) {
+ QName validQName = schema.getQName();
+ QName currentAugment = previousAugment;
+ if (schema.isAugmenting()) {
+ currentAugment = schema.getQName();
+ }
+ else if (previousAugment != null &&
+ !Objects.equal( schema.getQName().getNamespace(), previousAugment.getNamespace())) {
+ validQName = QName.create(currentAugment, schema.getQName().getLocalName());
+ }
+
+ String moduleName = null;
+ if (mountPoint == null) {
+ moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
+ }
+ else {
+ moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
+ }
+
+ if (nodeBuilder.getNamespace() == null ||
+ Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) ||
+ Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /*||
+ Note: this check is wrong - can never be true as it compares a URI with a String
+ not sure what the intention is so commented out...
+ Objects.equal(nodeBuilder.getNamespace(), MOUNT_POINT_MODULE_NAME)*/ ) {
+
+ nodeBuilder.setQname(validQName);
+ }
+
+ return currentAugment;
+ }
+
+ private URI namespace(final CompositeNode data) {
+ if (data instanceof CompositeNodeWrapper) {
+ return ((CompositeNodeWrapper)data).getNamespace();
+ }
+ else if (data != null) {
+ return data.getNodeType().getNamespace();
+ }
+ else {
+ throw new IllegalArgumentException("Unhandled parameter types: " +
+ Arrays.<Object>asList(data).toString());
+ }
+ }
+
+ private String localName(final CompositeNode data) {
+ if (data instanceof CompositeNodeWrapper) {
+ return ((CompositeNodeWrapper)data).getLocalName();
+ }
+ else if (data != null) {
+ return data.getNodeType().getLocalName();
+ }
+ else {
+ throw new IllegalArgumentException("Unhandled parameter types: " +
+ Arrays.<Object>asList(data).toString());
+ }
+ }
+
+ private String getName(final CompositeNode data) {
+ if (data instanceof CompositeNodeWrapper) {
+ return ((CompositeNodeWrapper)data).getLocalName();
+ }
+ else if (data != null) {
+ return data.getNodeType().getLocalName();
+ }
+ else {
+ throw new IllegalArgumentException("Unhandled parameter types: " +
+ Arrays.<Object>asList(data).toString());
+ }
+ }
+
+ private TypeDefinition<? extends Object> _typeDefinition(final LeafSchemaNode node) {
+ TypeDefinition<?> baseType = node.getType();
+ while (baseType.getBaseType() != null) {
+ baseType = baseType.getBaseType();
+ }
+
+ return baseType;
+ }
+
+ private TypeDefinition<? extends Object> typeDefinition(final LeafListSchemaNode node) {
+ TypeDefinition<?> baseType = node.getType();
+ while (baseType.getBaseType() != null) {
+ baseType = baseType.getBaseType();
+ }
+
+ return baseType;
+ }
+
+ private TypeDefinition<? extends Object> typeDefinition(final DataSchemaNode node) {
+ if (node instanceof LeafListSchemaNode) {
+ return typeDefinition((LeafListSchemaNode)node);
+ }
+ else if (node instanceof LeafSchemaNode) {
+ return _typeDefinition((LeafSchemaNode)node);
+ }
+ else {
+ throw new IllegalArgumentException("Unhandled parameter types: " +
+ Arrays.<Object>asList(node).toString());
+ }
+ }
+}
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.restconf.impl
-
-import com.google.common.base.Preconditions
-import com.google.common.base.Splitter
-import com.google.common.collect.Lists
-import java.net.URI
-import java.text.ParseException
-import java.text.SimpleDateFormat
-import java.util.ArrayList
-import java.util.HashMap
-import java.util.List
-import java.util.Set
-import javax.ws.rs.core.Response
-import javax.ws.rs.core.UriInfo
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus
-import org.opendaylight.controller.sal.core.api.mount.MountInstance
-import org.opendaylight.controller.sal.rest.api.RestconfService
-import org.opendaylight.controller.sal.streams.listeners.Notificator
-import org.opendaylight.controller.sal.streams.websockets.WebSocketServer
-import org.opendaylight.yangtools.yang.common.QName
-import org.opendaylight.yangtools.yang.common.RpcResult
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder
-import org.opendaylight.yangtools.yang.data.api.Node
-import org.opendaylight.yangtools.yang.data.impl.NodeFactory
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
-import org.opendaylight.yangtools.yang.model.api.Module
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition
-import org.opendaylight.yangtools.yang.model.api.SchemaContext
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition
-import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
-import org.opendaylight.yangtools.yang.model.util.EmptyType
-import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder
-import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder
-
-import static javax.ws.rs.core.Response.Status.*
-
-class RestconfImpl implements RestconfService {
-
- val static RestconfImpl INSTANCE = new RestconfImpl
- val static MOUNT_POINT_MODULE_NAME = "ietf-netconf"
- val static REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
- val static RESTCONF_MODULE_DRAFT02_REVISION = "2013-10-19"
- val static RESTCONF_MODULE_DRAFT02_NAME = "ietf-restconf"
- val static RESTCONF_MODULE_DRAFT02_NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf"
- val static RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE = "restconf"
- val static RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE = "restconf"
- val static RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE = "modules"
- val static RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE = "module"
- val static RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE = "streams"
- val static RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE = "stream"
- val static RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE = "operations"
- val static SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"
- val static SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription"
-
- @Property
- BrokerFacade broker
-
- @Property
- extension ControllerContext controllerContext
-
- private new() {
- if (INSTANCE !== null) {
- throw new IllegalStateException("Already instantiated");
- }
- }
-
- static def getInstance() {
- return INSTANCE
- }
-
- override getModules() {
- val restconfModule = getRestconfModule()
- val List<Node<?>> modulesAsData = new ArrayList
- val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
- for (module : allModules) {
- modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
- }
- val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
- val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
- return new StructuredData(modulesNode, modulesSchemaNode, null)
- }
-
- override getAvailableStreams(){
- var Set<String> availableStreams = Notificator.getStreamNames();
- val List<Node<?>> streamsAsData = new ArrayList
- val streamSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE)
- for (String streamName:availableStreams){
- streamsAsData.add(streamName.toStreamCompositeNode(streamSchemaNode))
- }
- val streamsSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE)
- val streamsNode = NodeFactory.createImmutableCompositeNode(streamsSchemaNode.QName, null, streamsAsData)
- return new StructuredData(streamsNode, streamsSchemaNode, null)
- }
- override getModules(String identifier) {
- var Set<Module> modules = null
- var MountInstance mountPoint = null
- if (identifier.contains(ControllerContext.MOUNT)) {
- mountPoint = identifier.toMountPointIdentifier.mountPoint
- modules = mountPoint.allModules
- } else {
- throw new ResponseException(BAD_REQUEST, "URI has bad format. If modules behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
- }
- val List<Node<?>> modulesAsData = new ArrayList
- val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
- for (module : modules) {
- modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
- }
- val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
- val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
- return new StructuredData(modulesNode, modulesSchemaNode, mountPoint)
- }
-
- override getModule(String identifier) {
- val moduleNameAndRevision = identifier.moduleNameAndRevision
- var Module module = null
- var MountInstance mountPoint = null
- if (identifier.contains(ControllerContext.MOUNT)) {
- mountPoint = identifier.toMountPointIdentifier.mountPoint
- module = mountPoint.findModuleByNameAndRevision(moduleNameAndRevision)
- } else {
- module = findModuleByNameAndRevision(moduleNameAndRevision)
- }
- if (module === null) {
- throw new ResponseException(BAD_REQUEST,
- "Module with name '" + moduleNameAndRevision.localName + "' and revision '" +
- moduleNameAndRevision.revision + "' was not found.")
- }
- val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
- val moduleNode = module.toModuleCompositeNode(moduleSchemaNode)
- return new StructuredData(moduleNode, moduleSchemaNode, mountPoint)
- }
-
- override getOperations() {
- return operationsFromModulesToStructuredData(allModules,null)
- }
-
- override getOperations(String identifier) {
- var Set<Module> modules = null
- var MountInstance mountPoint = null
- if (identifier.contains(ControllerContext.MOUNT)) {
- mountPoint = identifier.toMountPointIdentifier.mountPoint
- modules = mountPoint.allModules
- } else {
- throw new ResponseException(BAD_REQUEST, "URI has bad format. If operations behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
- }
- return operationsFromModulesToStructuredData(modules,mountPoint)
- }
-
- private def StructuredData operationsFromModulesToStructuredData(Set<Module> modules,MountInstance mountPoint) {
- val List<Node<?>> operationsAsData = new ArrayList
- val operationsSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE)
- val fakeOperationsSchemaNode = new ContainerSchemaNodeBuilder(RESTCONF_MODULE_DRAFT02_NAME, 0, operationsSchemaNode.QName, operationsSchemaNode.path)
- for (module : modules) {
- for (rpc : module.rpcs) {
- operationsAsData.add(NodeFactory.createImmutableSimpleNode(rpc.QName, null, null))
- val fakeRpcSchemaNode = new LeafSchemaNodeBuilder(module.name, 0, rpc.QName, null)
- fakeRpcSchemaNode.setAugmenting(true)
- fakeRpcSchemaNode.setType(EmptyType.instance)
- fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build)
- }
- }
- val operationsNode = NodeFactory.createImmutableCompositeNode(operationsSchemaNode.QName, null, operationsAsData)
- return new StructuredData(operationsNode, fakeOperationsSchemaNode.build, mountPoint)
- }
-
- private def Module getRestconfModule() {
- val restconfModule = findModuleByNameAndRevision(
- QName.create(RESTCONF_MODULE_DRAFT02_NAMESPACE, RESTCONF_MODULE_DRAFT02_REVISION,
- RESTCONF_MODULE_DRAFT02_NAME))
- if (restconfModule === null) {
- throw new ResponseException(INTERNAL_SERVER_ERROR, "Restconf module was not found.")
- }
- return restconfModule
- }
-
- private def QName getModuleNameAndRevision(String identifier) {
- val indexOfMountPointFirstLetter = identifier.indexOf(ControllerContext.MOUNT)
- var moduleNameAndRevision = "";
- if (indexOfMountPointFirstLetter !== -1) { // module and revision is behind mount point string
- moduleNameAndRevision = identifier.substring(indexOfMountPointFirstLetter + ControllerContext.MOUNT.length)
- } else (
- moduleNameAndRevision = identifier
- )
- val pathArgs = Lists.newArrayList(Splitter.on("/").omitEmptyStrings.split(moduleNameAndRevision))
- if (pathArgs.length < 2) {
- throw new ResponseException(BAD_REQUEST,
- "URI has bad format. End of URI should be in format 'moduleName/yyyy-MM-dd'")
- }
- try {
- val moduleName = pathArgs.head
- val moduleRevision = REVISION_FORMAT.parse(pathArgs.get(1))
- return QName.create(null, moduleRevision, moduleName)
- } catch(ParseException e) {
- throw new ResponseException(BAD_REQUEST, "URI has bad format. It should be 'moduleName/yyyy-MM-dd'")
- }
- }
-
- private def CompositeNode toStreamCompositeNode(String streamName, DataSchemaNode streamSchemaNode) {
- val List<Node<?>> streamNodeValues = new ArrayList
- val nameSchemaNode = (streamSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("name").head
- streamNodeValues.add(NodeFactory.createImmutableSimpleNode(nameSchemaNode.QName, null, streamName))
-
- val descriptionSchemaNode = (streamSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("description").head
- streamNodeValues.add(NodeFactory.createImmutableSimpleNode(descriptionSchemaNode.QName, null, "DESCRIPTION_PLACEHOLDER"))
-
- val replaySupportSchemaNode = (streamSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("replay-support").head
- streamNodeValues.add(NodeFactory.createImmutableSimpleNode(replaySupportSchemaNode.QName, null, true))
-
- val replayLogCreationTimeSchemaNode = (streamSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("replay-log-creation-time").head
- streamNodeValues.add(NodeFactory.createImmutableSimpleNode(replayLogCreationTimeSchemaNode.QName, null, ""))
-
- val eventsSchemaNode = (streamSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("events").head
- streamNodeValues.add(NodeFactory.createImmutableSimpleNode(eventsSchemaNode.QName, null, ""))
-
- return NodeFactory.createImmutableCompositeNode(streamSchemaNode.QName, null, streamNodeValues)
- }
- private def CompositeNode toModuleCompositeNode(Module module, DataSchemaNode moduleSchemaNode) {
- val List<Node<?>> moduleNodeValues = new ArrayList
- val nameSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("name").head
- moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(nameSchemaNode.QName, null, module.name))
- val revisionSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("revision").head
- moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(revisionSchemaNode.QName, null, REVISION_FORMAT.format(module.revision)))
- val namespaceSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("namespace").head
- moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(namespaceSchemaNode.QName, null, module.namespace.toString))
- val featureSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("feature").head
- for (feature : module.features) {
- moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(featureSchemaNode.QName, null, feature.QName.localName))
- }
- return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.QName, null, moduleNodeValues)
- }
-
- private def DataSchemaNode getSchemaNode(Module restconfModule, String schemaNodeName) {
- val restconfGrouping = restconfModule.groupings.filter[g|g.QName.localName == RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE].head
- val restconfContainer = restconfGrouping.findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE).head
- if (schemaNodeName == RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE) {
- return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE).head
- } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE) {
- return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE).head
- } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE) {
- val modules = (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_STREAMS_CONTAINER_SCHEMA_NODE).head
- return (modules as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_STREAM_LIST_SCHEMA_NODE).head
- }else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE) {
- return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
- } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE) {
- val modules = (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
- return (modules as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE).head
- }
- return null
- }
-
- override getRoot() {
- return null;
- }
-
- override invokeRpc(String identifier, CompositeNode payload) {
- val rpc = resolveIdentifierInInvokeRpc(identifier)
- if (rpc.QName.namespace.toString == SAL_REMOTE_NAMESPACE && rpc.QName.localName == SAL_REMOTE_RPC_SUBSRCIBE) {
- val value = normalizeNode(payload, rpc.input, null)
- val pathNode = value?.getFirstSimpleByName(QName.create(rpc.QName, "path"))
- val pathValue = pathNode?.value
- if (pathValue === null && !(pathValue instanceof InstanceIdentifier)) {
- throw new ResponseException(INTERNAL_SERVER_ERROR, "Instance identifier was not normalized correctly.");
- }
- val pathIdentifier = (pathValue as InstanceIdentifier)
- var String streamName = null
- if (!pathIdentifier.path.nullOrEmpty) {
- streamName = Notificator.createStreamNameFromUri(pathIdentifier.toFullRestconfIdentifier)
- }
- if (streamName.nullOrEmpty) {
- throw new ResponseException(BAD_REQUEST, "Path is empty or contains data node which is not Container or List build-in type.");
- }
- val streamNameNode = NodeFactory.createImmutableSimpleNode(QName.create(rpc.output.QName, "stream-name"), null, streamName)
- val List<Node<?>> output = new ArrayList
- output.add(streamNameNode)
- val responseData = NodeFactory.createMutableCompositeNode(rpc.output.QName, null, output, null, null)
-
- if (!Notificator.existListenerFor(pathIdentifier)) {
- Notificator.createListener(pathIdentifier, streamName)
- }
-
- return new StructuredData(responseData, rpc.output, null)
- }
- return callRpc(identifier.rpcDefinition, payload)
- }
-
- override invokeRpc(String identifier, String noPayload) {
- if (!noPayload.nullOrEmpty) {
- throw new ResponseException(UNSUPPORTED_MEDIA_TYPE, "Content-Type contains unsupported Media Type.");
- }
- val rpc = resolveIdentifierInInvokeRpc(identifier)
- return callRpc(rpc, null)
- }
-
- private def resolveIdentifierInInvokeRpc(String identifier) {
- if (identifier.indexOf("/") === -1) {
- val identifierDecoded = identifier.urlPathArgDecode
- val rpc = identifierDecoded.rpcDefinition
- if (rpc !== null) {
- return rpc
- }
- throw new ResponseException(NOT_FOUND, "RPC does not exist.");
- }
- val slashErrorMsg = String.format(
- "Identifier %n%s%ncan't contain slash character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier)
- throw new ResponseException(NOT_FOUND, slashErrorMsg);
- }
-
- private def StructuredData callRpc(RpcDefinition rpc, CompositeNode payload) {
- if (rpc === null) {
- throw new ResponseException(NOT_FOUND, "RPC does not exist.");
- }
- var CompositeNode rpcRequest;
- if (payload === null) {
- rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, null, null, null)
- } else {
- val value = normalizeNode(payload, rpc.input, null)
- val List<Node<?>> input = new ArrayList
- input.add(value)
- rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
- }
- val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest);
- if (!rpcResult.successful) {
- throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed")
- }
- if (rpcResult.result === null) {
- return null
- }
- return new StructuredData(rpcResult.result, rpc.output, null)
- }
-
- override readConfigurationData(String identifier) {
- val iiWithData = identifier.toInstanceIdentifier
- var CompositeNode data = null;
- if (iiWithData.mountPoint !== null) {
- data = broker.readConfigurationDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
- } else {
- data = broker.readConfigurationData(iiWithData.getInstanceIdentifier);
- }
- return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
- }
-
- override readOperationalData(String identifier) {
- val iiWithData = identifier.toInstanceIdentifier
- var CompositeNode data = null;
- if (iiWithData.mountPoint !== null) {
- data = broker.readOperationalDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
- } else {
- data = broker.readOperationalData(iiWithData.getInstanceIdentifier);
- }
- return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
- }
-
- override updateConfigurationData(String identifier, CompositeNode payload) {
- val iiWithData = identifier.toInstanceIdentifier
- val value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
- var RpcResult<TransactionStatus> status = null
- if (iiWithData.mountPoint !== null) {
- status = broker.commitConfigurationDataPutBehindMountPoint(iiWithData.mountPoint,
- iiWithData.instanceIdentifier, value).get()
- } else {
- status = broker.commitConfigurationDataPut(iiWithData.instanceIdentifier, value).get();
- }
- switch status.result {
- case TransactionStatus.COMMITED: Response.status(OK).build
- default: Response.status(INTERNAL_SERVER_ERROR).build
- }
- }
-
- override createConfigurationData(String identifier, CompositeNode payload) {
- if (payload.namespace === null) {
- throw new ResponseException(BAD_REQUEST,
- "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
- }
- var InstanceIdWithSchemaNode iiWithData;
- var CompositeNode value;
- if (payload.representsMountPointRootData) { // payload represents mount point data and URI represents path to the mount point
- if (identifier.endsWithMountPoint) {
- throw new ResponseException(BAD_REQUEST,
- "URI has bad format. URI should be without \"" + ControllerContext.MOUNT + "\" for POST operation.");
- }
- val completIdentifier = identifier.addMountPointIdentifier
- iiWithData = completIdentifier.toInstanceIdentifier
- value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
- } else {
- val uncompleteInstIdWithData = identifier.toInstanceIdentifier
- val parentSchema = uncompleteInstIdWithData.schemaNode as DataNodeContainer
- val module = uncompleteInstIdWithData.mountPoint.findModule(payload)
- if (module === null) {
- throw new ResponseException(BAD_REQUEST, "Module was not found for \"" + payload.namespace + "\"")
- }
- val schemaNode = parentSchema.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
- value = normalizeNode(payload, schemaNode, uncompleteInstIdWithData.mountPoint)
- iiWithData = uncompleteInstIdWithData.addLastIdentifierFromData(value, schemaNode)
- }
- var RpcResult<TransactionStatus> status = null
- if (iiWithData.mountPoint !== null) {
- status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
- iiWithData.instanceIdentifier, value)?.get();
- } else {
- status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
- }
- if (status === null) {
- return Response.status(ACCEPTED).build
- }
- switch status.result {
- case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
- default: Response.status(INTERNAL_SERVER_ERROR).build
- }
- }
-
- override createConfigurationData(CompositeNode payload) {
- if (payload.namespace === null) {
- throw new ResponseException(BAD_REQUEST,
- "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
- }
- val module = findModule(null, payload)
- if (module === null) {
- throw new ResponseException(BAD_REQUEST,
- "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)");
- }
- val schemaNode = module.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
- val value = normalizeNode(payload, schemaNode, null)
- val iiWithData = addLastIdentifierFromData(null, value, schemaNode)
- var RpcResult<TransactionStatus> status = null
- if (iiWithData.mountPoint !== null) {
- status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
- iiWithData.instanceIdentifier, value)?.get();
- } else {
- status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
- }
- if (status === null) {
- return Response.status(ACCEPTED).build
- }
- switch status.result {
- case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
- default: Response.status(INTERNAL_SERVER_ERROR).build
- }
- }
-
- override deleteConfigurationData(String identifier) {
- val iiWithData = identifier.toInstanceIdentifier
- var RpcResult<TransactionStatus> status = null
- if (iiWithData.mountPoint !== null) {
- status = broker.commitConfigurationDataDeleteBehindMountPoint(iiWithData.mountPoint,
- iiWithData.getInstanceIdentifier).get;
- } else {
- status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier).get;
- }
- switch status.result {
- case TransactionStatus.COMMITED: Response.status(OK).build
- default: Response.status(INTERNAL_SERVER_ERROR).build
- }
- }
-
- override subscribeToStream(String identifier, UriInfo uriInfo) {
- val streamName = Notificator.createStreamNameFromUri(identifier)
- if (streamName.nullOrEmpty) {
- throw new ResponseException(BAD_REQUEST, "Stream name is empty.")
- }
- val listener = Notificator.getListenerFor(streamName);
- if (listener === null) {
- throw new ResponseException(BAD_REQUEST, "Stream was not found.")
- }
- broker.registerToListenDataChanges(listener)
- val uriBuilder = uriInfo.getAbsolutePathBuilder()
- val uriToWebsocketServer = uriBuilder.port(WebSocketServer.PORT).replacePath(streamName).build()
- return Response.status(OK).location(uriToWebsocketServer).build
- }
-
- private def dispatch URI namespace(CompositeNode data) {
- return data.nodeType.namespace
- }
-
- private def dispatch URI namespace(CompositeNodeWrapper data) {
- return data.namespace
- }
-
- private def dispatch String localName(CompositeNode data) {
- return data.nodeType.localName
- }
-
- private def dispatch String localName(CompositeNodeWrapper data) {
- return data.localName
- }
-
- private def dispatch Module findModule(MountInstance mountPoint, CompositeNode data) {
- if (mountPoint !== null) {
- return mountPoint.findModuleByNamespace(data.nodeType.namespace)
- } else {
- return findModuleByNamespace(data.nodeType.namespace)
- }
- }
-
- private def dispatch Module findModule(MountInstance mountPoint, CompositeNodeWrapper data) {
- Preconditions.checkNotNull(data.namespace)
- var Module module = null;
- if (mountPoint !== null) {
- module = mountPoint.findModuleByNamespace(data.namespace) // namespace from XML
- if (module === null) {
- module = mountPoint.findModuleByName(data.namespace.toString) // namespace (module name) from JSON
- }
- } else {
- module = data.namespace.findModuleByNamespace // namespace from XML
- if (module === null) {
- module = data.namespace.toString.findModuleByName // namespace (module name) from JSON
- }
- }
- return module
- }
-
- private def dispatch getName(CompositeNode data) {
- return data.nodeType.localName
- }
-
- private def dispatch getName(CompositeNodeWrapper data) {
- return data.localName
- }
-
- private def InstanceIdWithSchemaNode addLastIdentifierFromData(InstanceIdWithSchemaNode identifierWithSchemaNode,
- CompositeNode data, DataSchemaNode schemaOfData) {
- val iiOriginal = identifierWithSchemaNode?.instanceIdentifier
- var InstanceIdentifierBuilder iiBuilder = null
- if (iiOriginal === null) {
- iiBuilder = InstanceIdentifier.builder
- } else {
- iiBuilder = InstanceIdentifier.builder(iiOriginal)
- }
-
- if (schemaOfData instanceof ListSchemaNode) {
- iiBuilder.nodeWithKey(schemaOfData.QName, (schemaOfData as ListSchemaNode).resolveKeysFromData(data))
- } else {
- iiBuilder.node(schemaOfData.QName)
- }
- return new InstanceIdWithSchemaNode(iiBuilder.toInstance, schemaOfData, identifierWithSchemaNode?.mountPoint)
- }
-
- private def resolveKeysFromData(ListSchemaNode listNode, CompositeNode dataNode) {
- val keyValues = new HashMap<QName, Object>();
- for (key : listNode.keyDefinition) {
- val dataNodeKeyValueObject = dataNode.getSimpleNodesByName(key.localName)?.head?.value
- if (dataNodeKeyValueObject === null) {
- throw new ResponseException(BAD_REQUEST,
- "Data contains list \"" + dataNode.nodeType.localName + "\" which does not contain key: \"" +
- key.localName + "\"")
- }
- keyValues.put(key, dataNodeKeyValueObject);
- }
- return keyValues
- }
-
- private def endsWithMountPoint(String identifier) {
- return (identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/"))
- }
-
- private def representsMountPointRootData(CompositeNode data) {
- return ((data.namespace == SchemaContext.NAME.namespace || data.namespace == MOUNT_POINT_MODULE_NAME) &&
- data.localName == SchemaContext.NAME.localName)
- }
-
- private def addMountPointIdentifier(String identifier) {
- if (identifier.endsWith("/")) {
- return identifier + ControllerContext.MOUNT
- }
- return identifier + "/" + ControllerContext.MOUNT
- }
-
- private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema, MountInstance mountPoint) {
- if (schema === null) {
- throw new ResponseException(INTERNAL_SERVER_ERROR,
- "Data schema node was not found for " + node?.nodeType?.localName)
- }
- if (!(schema instanceof DataNodeContainer)) {
- throw new ResponseException(BAD_REQUEST, "Root element has to be container or list yang datatype.");
- }
- if (node instanceof CompositeNodeWrapper) {
- if ((node as CompositeNodeWrapper).changeAllowed) {
- try {
- normalizeNode(node as CompositeNodeWrapper, schema, null, mountPoint)
- } catch (NumberFormatException e) {
- throw new ResponseException(BAD_REQUEST,e.message)
- }
- }
- return (node as CompositeNodeWrapper).unwrap()
- }
- return node
- }
-
- private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
- MountInstance mountPoint) {
- if (schema === null) {
- throw new ResponseException(BAD_REQUEST,
- "Data has bad format.\n\"" + nodeBuilder.localName + "\" does not exist in yang schema.");
- }
-
- var QName currentAugment;
- if (nodeBuilder.qname !== null) {
- currentAugment = previousAugment
- } else {
- currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint)
- if (nodeBuilder.qname === null) {
- throw new ResponseException(BAD_REQUEST,
- "Data has bad format.\nIf data is in XML format then namespace for \"" + nodeBuilder.localName +
- "\" should be \"" + schema.QName.namespace + "\".\n" +
- "If data is in JSON format then module name for \"" + nodeBuilder.localName +
- "\" should be corresponding to namespace \"" + schema.QName.namespace + "\".");
- }
- }
-
- if (nodeBuilder instanceof CompositeNodeWrapper) {
- val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
- for (child : children) {
- val potentialSchemaNodes = (schema as DataNodeContainer).findInstanceDataChildrenByName(child.localName)
- if (potentialSchemaNodes.size > 1 && child.namespace === null) {
- val StringBuilder namespacesOfPotentialModules = new StringBuilder;
- for (potentialSchemaNode : potentialSchemaNodes) {
- namespacesOfPotentialModules.append(" ").append(potentialSchemaNode.QName.namespace.toString).append("\n")
- }
- throw new ResponseException(BAD_REQUEST,
- "Node \"" + child.localName + "\" is added as augment from more than one module. "
- + "Therefore node must have namespace (XML format) or module name (JSON format)."
- + "\nThe node is added as augment from modules with namespaces:\n" + namespacesOfPotentialModules)
- }
- var rightNodeSchemaFound = false
- for (potentialSchemaNode : potentialSchemaNodes) {
- if (!rightNodeSchemaFound) {
- val potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode, currentAugment,
- mountPoint)
- if (child.qname !== null) {
- normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint)
- rightNodeSchemaFound = true
- }
- }
- }
- if (!rightNodeSchemaFound) {
- throw new ResponseException(BAD_REQUEST,
- "Schema node \"" + child.localName + "\" was not found in module.")
- }
- }
- if (schema instanceof ListSchemaNode) {
- val listKeys = (schema as ListSchemaNode).keyDefinition
- for (listKey : listKeys) {
- var foundKey = false
- for (child : children) {
- if (child.unwrap.nodeType.localName == listKey.localName) {
- foundKey = true;
- }
- }
- if (!foundKey) {
- throw new ResponseException(BAD_REQUEST,
- "Missing key in URI \"" + listKey.localName + "\" of list \"" + schema.QName.localName +
- "\"")
- }
- }
- }
- } else if (nodeBuilder instanceof SimpleNodeWrapper) {
- val simpleNode = (nodeBuilder as SimpleNodeWrapper)
- val value = simpleNode.value
- var inputValue = value;
-
- if (schema.typeDefinition instanceof IdentityrefTypeDefinition) {
- if (value instanceof String) {
- inputValue = new IdentityValuesDTO(nodeBuilder.namespace.toString, value as String, null,value as String);
- } // else value is already instance of IdentityValuesDTO
- }
-
- val outputValue = RestCodec.from(schema.typeDefinition, mountPoint)?.deserialize(inputValue);
- simpleNode.setValue(outputValue)
- } else if (nodeBuilder instanceof EmptyNodeWrapper) {
- val emptyNodeBuilder = nodeBuilder as EmptyNodeWrapper
- if (schema instanceof LeafSchemaNode) {
- emptyNodeBuilder.setComposite(false);
- } else if (schema instanceof ContainerSchemaNode) {
-
- // FIXME: Add presence check
- emptyNodeBuilder.setComposite(true);
- }
- }
- }
-
- private def dispatch TypeDefinition<?> typeDefinition(LeafSchemaNode node) {
- var baseType = node.type
- while (baseType.baseType !== null) {
- baseType = baseType.baseType;
- }
- baseType
- }
-
- private def dispatch TypeDefinition<?> typeDefinition(LeafListSchemaNode node) {
- var TypeDefinition<?> baseType = node.type
- while (baseType.baseType !== null) {
- baseType = baseType.baseType;
- }
- baseType
- }
-
- private def QName normalizeNodeName(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
- MountInstance mountPoint) {
- var validQName = schema.QName
- var currentAugment = previousAugment;
- if (schema.augmenting) {
- currentAugment = schema.QName
- } else if (previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) {
- validQName = QName.create(currentAugment, schema.QName.localName);
- }
- var String moduleName = null;
- if (mountPoint === null) {
- moduleName = controllerContext.findModuleNameByNamespace(validQName.namespace);
- } else {
- moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.namespace)
- }
- if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
- nodeBuilder.namespace.toString == moduleName || nodeBuilder.namespace == MOUNT_POINT_MODULE_NAME) {
- nodeBuilder.qname = validQName
- }
- return currentAugment
- }
-
-}
--- /dev/null
+/*
+ * Copyright (c) ${year} Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.Future;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
+import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.core.api.mount.MountInstance;
+import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
+import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
+import org.opendaylight.controller.sal.restconf.impl.ResponseException;
+import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.controller.sal.streams.listeners.Notificator;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.Futures;
+
+/**
+ * Unit tests for BrokerFacade.
+ *
+ * @author Thomas Pantelis
+ */
+public class BrokerFacadeTest {
+
+ @Mock
+ DataBrokerService dataBroker;
+
+ @Mock
+ DataModificationTransaction mockTransaction;
+
+ @Mock
+ ConsumerSession mockConsumerSession;
+
+ @Mock
+ MountInstance mockMountInstance;
+
+ BrokerFacade brokerFacade = BrokerFacade.getInstance();
+
+ CompositeNode dataNode = TestUtils.readInputToCnSn( "/parts/ietf-interfaces_interfaces.xml",
+ XmlToCompositeNodeProvider.INSTANCE );
+
+ QName qname = QName.create( "node" );
+
+ InstanceIdentifier instanceID = InstanceIdentifier.builder().node( qname ).toInstance();
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks( this );
+
+ brokerFacade.setDataService( dataBroker );
+ brokerFacade.setContext( mockConsumerSession );
+ }
+
+ @Test
+ public void testReadConfigurationData() {
+ when( dataBroker.readConfigurationData( instanceID ) ).thenReturn( dataNode );
+
+ CompositeNode actualNode = brokerFacade.readConfigurationData( instanceID );
+
+ assertSame( "readConfigurationData", dataNode, actualNode );
+ }
+
+ @Test
+ public void testReadConfigurationDataBehindMountPoint() {
+ when( mockMountInstance.readConfigurationData( instanceID ) ).thenReturn( dataNode );
+
+ CompositeNode actualNode = brokerFacade.readConfigurationDataBehindMountPoint(
+ mockMountInstance, instanceID );
+
+ assertSame( "readConfigurationDataBehindMountPoint", dataNode, actualNode );
+ }
+
+ @Test
+ public void testReadOperationalData() {
+ when( dataBroker.readOperationalData( instanceID ) ).thenReturn( dataNode );
+
+ CompositeNode actualNode = brokerFacade.readOperationalData( instanceID );
+
+ assertSame( "readOperationalData", dataNode, actualNode );
+ }
+
+ @Test
+ public void testReadOperationalDataBehindMountPoint() {
+ when( mockMountInstance.readOperationalData( instanceID ) ).thenReturn( dataNode );
+
+ CompositeNode actualNode = brokerFacade.readOperationalDataBehindMountPoint(
+ mockMountInstance, instanceID );
+
+ assertSame( "readOperationalDataBehindMountPoint", dataNode, actualNode );
+ }
+
+ @Test(expected=ResponseException.class)
+ public void testReadOperationalDataWithNoDataBroker() {
+ brokerFacade.setDataService( null );
+
+ brokerFacade.readOperationalData( instanceID );
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testInvokeRpc() {
+ RpcResult<CompositeNode> expResult = mock( RpcResult.class );
+ Future<RpcResult<CompositeNode>> future = Futures.immediateFuture( expResult );
+ when( mockConsumerSession.rpc( qname, dataNode ) ).thenReturn( future );
+
+ RpcResult<CompositeNode> actualResult = brokerFacade.invokeRpc( qname, dataNode );
+
+ assertSame( "invokeRpc", expResult, actualResult );
+ }
+
+ @Test(expected=ResponseException.class)
+ public void testInvokeRpcWithException() {
+ Exception mockEx = new Exception( "mock" );
+ Future<RpcResult<CompositeNode>> future = Futures.immediateFailedFuture( mockEx );
+ when( mockConsumerSession.rpc( qname, dataNode ) ).thenReturn( future );
+
+ brokerFacade.invokeRpc( qname, dataNode );
+ }
+
+ @Test(expected=ResponseException.class)
+ public void testInvokeRpcWithNoConsumerSession() {
+ brokerFacade.setContext( null );
+
+ brokerFacade.invokeRpc( qname, dataNode );
+ }
+
+ @Test
+ public void testCommitConfigurationDataPut() {
+ Future<RpcResult<TransactionStatus>> expFuture = Futures.immediateFuture( null );
+
+ when( dataBroker.beginTransaction() ).thenReturn( mockTransaction );
+ mockTransaction.putConfigurationData( instanceID, dataNode );
+ when( mockTransaction.commit() ).thenReturn( expFuture );
+
+ Future<RpcResult<TransactionStatus>> actualFuture =
+ brokerFacade.commitConfigurationDataPut( instanceID, dataNode );
+
+ assertSame( "invokeRpc", expFuture, actualFuture );
+
+ InOrder inOrder = inOrder( dataBroker, mockTransaction );
+ inOrder.verify( dataBroker ).beginTransaction();
+ inOrder.verify( mockTransaction ).putConfigurationData( instanceID, dataNode );
+ inOrder.verify( mockTransaction ).commit();
+ }
+
+ @Test
+ public void testCommitConfigurationDataPutBehindMountPoint() {
+ Future<RpcResult<TransactionStatus>> expFuture = Futures.immediateFuture( null );
+
+ when( mockMountInstance.beginTransaction() ).thenReturn( mockTransaction );
+ mockTransaction.putConfigurationData( instanceID, dataNode );
+ when( mockTransaction.commit() ).thenReturn( expFuture );
+
+ Future<RpcResult<TransactionStatus>> actualFuture =
+ brokerFacade.commitConfigurationDataPutBehindMountPoint(
+ mockMountInstance, instanceID, dataNode );
+
+ assertSame( "invokeRpc", expFuture, actualFuture );
+
+ InOrder inOrder = inOrder( mockMountInstance, mockTransaction );
+ inOrder.verify( mockMountInstance ).beginTransaction();
+ inOrder.verify( mockTransaction ).putConfigurationData( instanceID, dataNode );
+ inOrder.verify( mockTransaction ).commit();
+ }
+
+ @Test
+ public void testCommitConfigurationDataPost() {
+ Future<RpcResult<TransactionStatus>> expFuture = Futures.immediateFuture( null );
+
+ Map<InstanceIdentifier, CompositeNode> nodeMap =
+ new ImmutableMap.Builder<InstanceIdentifier,CompositeNode>()
+ .put( instanceID, dataNode ).build();
+
+ when( dataBroker.beginTransaction() ).thenReturn( mockTransaction );
+ mockTransaction.putConfigurationData( instanceID, dataNode );
+ when( mockTransaction.getCreatedConfigurationData() ).thenReturn( nodeMap );
+ when( mockTransaction.commit() ).thenReturn( expFuture );
+
+ Future<RpcResult<TransactionStatus>> actualFuture =
+ brokerFacade.commitConfigurationDataPost( instanceID, dataNode );
+
+ assertSame( "commitConfigurationDataPut", expFuture, actualFuture );
+
+ InOrder inOrder = inOrder( dataBroker, mockTransaction );
+ inOrder.verify( dataBroker ).beginTransaction();
+ inOrder.verify( mockTransaction ).putConfigurationData( instanceID, dataNode );
+ inOrder.verify( mockTransaction ).commit();
+ }
+
+ @Test
+ public void testCommitConfigurationDataPostAlreadyExists() {
+ when( dataBroker.beginTransaction() ).thenReturn( mockTransaction );
+ mockTransaction.putConfigurationData( instanceID, dataNode );
+ when( mockTransaction.getCreatedConfigurationData() )
+ .thenReturn( Collections.<InstanceIdentifier,CompositeNode>emptyMap() );
+
+ Future<RpcResult<TransactionStatus>> actualFuture =
+ brokerFacade.commitConfigurationDataPost( instanceID, dataNode );
+
+ assertNull( "Retruned non-null Future", actualFuture );
+ verify( mockTransaction, never() ).commit();
+ }
+
+ @Test
+ public void testCommitConfigurationDataPostBehindMountPoint() {
+ Future<RpcResult<TransactionStatus>> expFuture = Futures.immediateFuture( null );
+
+ Map<InstanceIdentifier, CompositeNode> nodeMap =
+ new ImmutableMap.Builder<InstanceIdentifier,CompositeNode>()
+ .put( instanceID, dataNode ).build();
+
+ when( mockMountInstance.beginTransaction() ).thenReturn( mockTransaction );
+ mockTransaction.putConfigurationData( instanceID, dataNode );
+ when( mockTransaction.getCreatedConfigurationData() ).thenReturn( nodeMap );
+ when( mockTransaction.commit() ).thenReturn( expFuture );
+
+ Future<RpcResult<TransactionStatus>> actualFuture =
+ brokerFacade.commitConfigurationDataPostBehindMountPoint( mockMountInstance,
+ instanceID, dataNode );
+
+ assertSame( "commitConfigurationDataPostBehindMountPoint", expFuture, actualFuture );
+
+ InOrder inOrder = inOrder( mockMountInstance, mockTransaction );
+ inOrder.verify( mockMountInstance ).beginTransaction();
+ inOrder.verify( mockTransaction ).putConfigurationData( instanceID, dataNode );
+ inOrder.verify( mockTransaction ).commit();
+ }
+
+ @Test
+ public void testCommitConfigurationDataPostBehindMountPointAlreadyExists() {
+
+ when( mockMountInstance.beginTransaction() ).thenReturn( mockTransaction );
+ mockTransaction.putConfigurationData( instanceID, dataNode );
+ when( mockTransaction.getCreatedConfigurationData() )
+ .thenReturn( Collections.<InstanceIdentifier,CompositeNode>emptyMap() );
+
+ Future<RpcResult<TransactionStatus>> actualFuture =
+ brokerFacade.commitConfigurationDataPostBehindMountPoint( mockMountInstance,
+ instanceID, dataNode );
+
+ assertNull( "Retruned non-null Future", actualFuture );
+ verify( mockTransaction, never() ).commit();
+ }
+
+ @Test
+ public void testCommitConfigurationDataDelete() {
+ Future<RpcResult<TransactionStatus>> expFuture = Futures.immediateFuture( null );
+
+ when( dataBroker.beginTransaction() ).thenReturn( mockTransaction );
+ mockTransaction.removeConfigurationData( instanceID );
+ when( mockTransaction.commit() ).thenReturn( expFuture );
+
+ Future<RpcResult<TransactionStatus>> actualFuture =
+ brokerFacade.commitConfigurationDataDelete( instanceID );
+
+ assertSame( "commitConfigurationDataDelete", expFuture, actualFuture );
+
+ InOrder inOrder = inOrder( dataBroker, mockTransaction );
+ inOrder.verify( dataBroker ).beginTransaction();
+ inOrder.verify( mockTransaction ).removeConfigurationData( instanceID );
+ inOrder.verify( mockTransaction ).commit();
+ }
+
+ @Test
+ public void testCommitConfigurationDataDeleteBehindMountPoint() {
+ Future<RpcResult<TransactionStatus>> expFuture = Futures.immediateFuture( null );
+
+ when( mockMountInstance.beginTransaction() ).thenReturn( mockTransaction );
+ mockTransaction.removeConfigurationData( instanceID );
+ when( mockTransaction.commit() ).thenReturn( expFuture );
+
+ Future<RpcResult<TransactionStatus>> actualFuture =
+ brokerFacade.commitConfigurationDataDeleteBehindMountPoint(
+ mockMountInstance, instanceID );
+
+ assertSame( "commitConfigurationDataDeleteBehindMountPoint", expFuture, actualFuture );
+
+ InOrder inOrder = inOrder( mockMountInstance, mockTransaction );
+ inOrder.verify( mockMountInstance ).beginTransaction();
+ inOrder.verify( mockTransaction ).removeConfigurationData( instanceID );
+ inOrder.verify( mockTransaction ).commit();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testRegisterToListenDataChanges() {
+ ListenerAdapter listener = Notificator.createListener( instanceID, "stream" );
+
+ ListenerRegistration<DataChangeListener> mockRegistration = mock( ListenerRegistration.class );
+ when( dataBroker.registerDataChangeListener( instanceID, listener ) )
+ .thenReturn( mockRegistration );
+
+ brokerFacade.registerToListenDataChanges( listener );
+
+ verify( dataBroker ).registerDataChangeListener( instanceID, listener );
+
+ assertEquals( "isListening", true, listener.isListening() );
+
+ brokerFacade.registerToListenDataChanges( listener );
+ verifyNoMoreInteractions( dataBroker );
+ }
+}