Merge "BUG-372 Add reconnecting to netconf-connector for unexpected session drop"
authorTony Tkacik <ttkacik@cisco.com>
Wed, 14 May 2014 09:02:00 +0000 (09:02 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 14 May 2014 09:02:00 +0000 (09:02 +0000)
opendaylight/md-sal/sal-rest-connector/pom.xml
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.xtend [deleted file]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend [deleted file]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ResponseException.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend [deleted file]
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java [new file with mode: 0644]
opendaylight/northbound/switchmanager/src/main/java/org/opendaylight/controller/switchmanager/northbound/SwitchNorthbound.java

index d6ec2fd74dfe12cbc72b003f64ef6e36de6b8c46..fe00ab1836dbf7326d2a3352b86c018b4cce139d 100644 (file)
       <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>
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java
new file mode 100644 (file)
index 0000000..68c9340
--- /dev/null
@@ -0,0 +1,207 @@
+/**
+ * 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 );
+    }
+}
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.xtend
deleted file mode 100644 (file)
index d305006..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * 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)
-    }
-
-}
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java
new file mode 100644 (file)
index 0000000..1c076d1
--- /dev/null
@@ -0,0 +1,912 @@
+/**
+ * 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() );
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend
deleted file mode 100644 (file)
index cb02fc8..0000000
+++ /dev/null
@@ -1,637 +0,0 @@
-/*
- * 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
-    }    
-
-}
index b08126b5de96b3063b0f8d187a45aca294cab156..007fb8eabfd97ee39cf8214abb7d22f8da3be93b 100644 (file)
@@ -19,4 +19,9 @@ public class ResponseException extends WebApplicationException {
     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());
+    }
 }
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java
new file mode 100644 (file)
index 0000000..2e198ec
--- /dev/null
@@ -0,0 +1,1170 @@
+/**
+ * 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());
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend
deleted file mode 100644 (file)
index f1901d7..0000000
+++ /dev/null
@@ -1,732 +0,0 @@
-/*
- * 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
-    }
-
-}
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java
new file mode 100644 (file)
index 0000000..987beb0
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * 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 );
+    }
+}
index 662af723ed4861aa339ff99c3cacedb00af20cf2..e30dad24ab18866b1c455a8facf7bf347a0d22b2 100644 (file)
@@ -396,6 +396,85 @@ public class SwitchNorthbound {
         return NorthboundUtils.getResponse(status);
     }
 
+    /**
+     * Get a property of a node
+     *
+     * @param containerName
+     *            Name of the Container (Eg. 'SliceRed')
+     * @param nodeType
+     *            Type of the node being programmed (Eg. 'OF')
+     * @param nodeId
+     *            Node Identifier as specified by
+     *            {@link org.opendaylight.controller.sal.core.Node} (Eg.
+     *            '00:00:00:00:00:03:01:02')
+     * @param propertyName
+     *            Name of the Property. Properties that can be deleted are
+     *            description, forwarding(only in default container) and tier.
+     * @return Property value of the property
+     *
+     *         <pre>
+     *
+     * Example:
+     *
+     * Request URL:
+     * http://localhost:8080/controller/nb/v2/switchmanager/default/node/OF/00:00:00:00:00:00:00:01/property/description
+     *
+     * Response body in XML
+     * &lt;description&gt;
+     *     &#x20;&#x20;&lt;value&gt;switch1&lt;/value&gt;
+     * &lt;/description&gt;
+     *
+     * Response body in JSON
+     * {
+     *     &#x20;&#x20;"value": "switch1"
+     * }
+     * </pre>
+     */
+
+    @Path("/{containerName}/node/{nodeType}/{nodeId}/property/{propertyName}")
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+    @TypeHint(String.class)
+    @StatusCodes({ @ResponseCode(code = 200, condition = "Operation successful"),
+        @ResponseCode(code = 401, condition = "User not authorized to perform this operation"),
+        @ResponseCode(code = 404, condition = "The containerName is not found"),
+        @ResponseCode(code = 503, condition = "One or more of Controller Services are unavailable") })
+    public Property getNodeProperty(@PathParam("containerName") String containerName,
+            @PathParam("nodeType") String nodeType, @PathParam("nodeId") String nodeId,
+            @PathParam("propertyName") String propertyName) {
+
+        if (!isValidContainer(containerName)) {
+            throw new ResourceNotFoundException("Container " + containerName + " does not exist.");
+        }
+        if (!NorthboundUtils.isAuthorized(getUserName(), containerName, Privilege.READ, this)) {
+            throw new UnauthorizedException("User is not authorized to perform this operation on container "
+                    + containerName);
+        }
+        ISwitchManager switchManager = getIfSwitchManagerService(containerName);
+        if (switchManager == null) {
+            throw new ServiceUnavailableException("Switch Manager " + RestMessages.SERVICEUNAVAILABLE.toString());
+        }
+
+        handleNodeAvailability(containerName, nodeType, nodeId);
+        Node node = Node.fromString(nodeType, nodeId);
+        if (node == null) {
+            throw new ResourceNotFoundException(nodeId + " : " + RestMessages.NONODE.toString());
+        }
+        SwitchConfig switchConfig = switchManager.getSwitchConfig(node.toString());
+        if (switchConfig == null) {
+            throw new ResourceNotFoundException(nodeId + " : " + "Config Not Found" );
+        } else {
+            Map<String, Property> nodeProperties = new HashMap<String, Property>(switchConfig.getNodeProperties());
+            if (!nodeProperties.containsKey(propertyName.toLowerCase())) {
+                String msg = "Property " + propertyName + " does not exist or not "
+                        + "configured for switch " + nodeId;
+                throw new ResourceNotFoundException(msg);
+            } else {
+                return nodeProperties.get(propertyName.toLowerCase());
+            }
+        }
+    }
+
     /**
      *
      * Retrieve a list of all the nodeconnectors and their properties in a given