Merge "REST API to fetch Node Property"
authorAndrew Kim <andrekim@cisco.com>
Tue, 13 May 2014 18:30:11 +0000 (18:30 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 13 May 2014 18:30:11 +0000 (18:30 +0000)
29 files changed:
opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/03-toaster-sample.xml
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java
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/md-sal/samples/toaster-consumer/pom.xml
opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/config/yang/config/kitchen_service/impl/KitchenServiceModule.java [new file with mode: 0644]
opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/config/yang/config/kitchen_service/impl/KitchenServiceModuleFactory.java [moved from opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/config/yang/config/toaster_consumer/impl/ToasterConsumerModuleFactory.java with 56% similarity]
opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/config/yang/config/toaster_consumer/impl/ToasterConsumerModule.java [deleted file]
opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/api/EggsType.java [new file with mode: 0644]
opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/api/KitchenService.java [new file with mode: 0644]
opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/impl/KitchenServiceImpl.java [new file with mode: 0644]
opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/toaster/provider/api/ToastConsumer.java [deleted file]
opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/toaster/provider/impl/ToastConsumerImpl.java [deleted file]
opendaylight/md-sal/samples/toaster-consumer/src/main/yang/kitchen-service-impl.yang [moved from opendaylight/md-sal/samples/toaster-consumer/src/main/yang/toaster-consumer-impl.yang with 55% similarity]
opendaylight/md-sal/samples/toaster-consumer/src/main/yang/toaster-consumer.yang [deleted file]
opendaylight/md-sal/samples/toaster-it/src/test/java/org/opendaylight/controller/sample/toaster/it/ToasterTest.java
opendaylight/md-sal/samples/toaster-it/src/test/resources/controller.xml
opendaylight/md-sal/samples/toaster-provider/src/main/java/org/opendaylight/controller/config/yang/config/toaster_provider/impl/ToasterProviderModule.java
opendaylight/md-sal/samples/toaster-provider/src/main/java/org/opendaylight/controller/sample/toaster/provider/OpendaylightToaster.java
opendaylight/md-sal/samples/toaster-provider/src/main/yang/toaster-provider-impl.yang
opendaylight/md-sal/samples/toaster-provider/src/main/yang/toaster-provider.yang [deleted file]
opendaylight/md-sal/samples/toaster/src/main/yang/toaster.yang

index 6d5d0eb..502bdeb 100644 (file)
@@ -12,8 +12,8 @@
         <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
             <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
                 <module>
-                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
-                        prefix:toaster-provider-impl
+                    <type xmlns:toaster="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
+                        toaster:toaster-provider-impl
                     </type>
                     <name>toaster-provider-impl</name>
 
                         <name>binding-rpc-broker</name>
                     </rpc-registry>
 
+                    <data-broker>
+                        <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+                        <name>binding-data-broker</name>
+                    </data-broker>
+                    
                     <notification-service>
                         <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
                             binding:binding-notification-service
                 </module>
 
                 <module>
-                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl">
-                        prefix:toaster-consumer-impl
+                    <type xmlns:kitchen="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
+                        kitchen:kitchen-service-impl
                     </type>
-                    <name>toaster-consumer-impl</name>
+                    <name>kitchen-service-impl</name>
 
                     <rpc-registry>
                         <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
                     </notification-service>
                 </module>
             </modules>
-
+            
             <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
                 <service>
-                    <type xmlns:toaster="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider">toaster:toaster-provider</type>
-                    <instance>
-                        <name>toaster-provider</name>
-                        <provider>/modules/module[type='toaster-provider-impl'][name='toaster-provider-impl']</provider>
-                    </instance>
-                </service>
-                <service>
-                    <type xmlns:toaster="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer">toaster:toaster-consumer</type>
+                    <type xmlns:kitchen="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
+                        kitchen:kitchen-service
+                    </type>
                     <instance>
-                        <name>toaster-consumer</name>
-                        <provider>/modules/module[type='toaster-consumer-impl'][name='toaster-consumer-impl']</provider>
+                        <name>kitchen-service</name>
+                        <provider>/modules/module[type='kitchen-service-impl'][name='kitchen-service-impl']</provider>
                     </instance>
                 </service>
             </services>
     </configuration>
 
     <required-capabilities>
-        <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&amp;revision=2013-10-28</capability>
-        <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer?module=toaster-consumer&amp;revision=2014-01-31</capability>
-        <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl?module=toaster-consumer-impl&amp;revision=2014-01-31</capability>
-        <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider?module=toaster-provider&amp;revision=2014-01-31</capability>
+        <capability>urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl?module=kitchen-service-impl&amp;revision=2014-01-31</capability>
         <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl?module=toaster-provider-impl&amp;revision=2014-01-31</capability>
     </required-capabilities>
 
index e74ba95..44d5016 100644 (file)
@@ -327,12 +327,12 @@ public class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeList
             final Collection<Node> listeners, final NormalizedNode<?, ?> beforeData,
             final NormalizedNode<?, ?> afterData) {
 
-        if (beforeData instanceof NormalizedNodeContainer<?, ?, ?> && !listeners.isEmpty()) {
+        if (beforeData instanceof NormalizedNodeContainer<?, ?, ?>) {
             // Node is container (contains child) and we have interested
             // listeners registered for it, that means we need to do
             // resolution of changes on children level and can not
             // shortcut resolution.
-
+            LOG.trace("Resolving subtree replace event for {} before {}, after {}",path,beforeData,afterData);
             @SuppressWarnings("unchecked")
             NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> beforeCont = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) beforeData;
             @SuppressWarnings("unchecked")
@@ -342,7 +342,7 @@ public class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeList
             // Node is either of Leaf type (does not contain child nodes)
             // or we do not have listeners, so normal equals method is
             // sufficient for determining change.
-
+            LOG.trace("Resolving leaf replace event for {} , before {}, after {}",path,beforeData,afterData);
             DOMImmutableDataChangeEvent event = builder(DataChangeScope.BASE).setBefore(beforeData).setAfter(afterData)
                     .addUpdated(path, beforeData, afterData).build();
             addPartialTask(listeners, event);
@@ -447,12 +447,13 @@ public class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeList
     private DOMImmutableDataChangeEvent resolveSameEventRecursivelly(final InstanceIdentifier path,
             final Collection<Node> listeners, final NormalizedNode<PathArgument, ?> node,
             final SimpleEventFactory eventFactory) {
-
         final DOMImmutableDataChangeEvent event = eventFactory.create(path, node);
         DOMImmutableDataChangeEvent propagateEvent = event;
-            // We have listeners for this node or it's children, so we will try
+        // We have listeners for this node or it's children, so we will try
             // to do additional processing
         if (node instanceof NormalizedNodeContainer<?, ?, ?>) {
+            LOG.trace("Resolving subtree recursive event for {}, type {}", path, eventFactory);
+
             Builder eventBuilder = builder(DataChangeScope.BASE);
             eventBuilder.merge(event);
             eventBuilder.setBefore(event.getOriginalSubtree());
@@ -464,6 +465,7 @@ public class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeList
             NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> container = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) node;
             for (NormalizedNode<PathArgument, ?> child : container.getValue()) {
                 PathArgument childId = child.getIdentifier();
+                LOG.trace("Resolving event for child {}", childId);
                 Collection<Node> childListeners = getListenerChildrenWildcarded(listeners, childId);
                 eventBuilder.merge(resolveSameEventRecursivelly(append(path, childId), childListeners, child, eventFactory));
             }
@@ -473,7 +475,7 @@ public class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeList
             propagateEvent = builder(DataChangeScope.BASE).build();
         }
         if (!listeners.isEmpty()) {
-            addPartialTask(listeners, event);
+            addPartialTask(listeners, propagateEvent);
         }
         return propagateEvent;
     }
@@ -521,9 +523,9 @@ public class ResolveDataChangeEventsTask implements Callable<Iterable<ChangeList
 
     private DOMImmutableDataChangeEvent addPartialTask(final Collection<ListenerTree.Node> listeners,
             final DOMImmutableDataChangeEvent event) {
-
         for (ListenerTree.Node listenerNode : listeners) {
             if (!listenerNode.getListeners().isEmpty()) {
+                LOG.trace("Adding event {} for listeners {}",event,listenerNode);
                 events.put(listenerNode, event);
             }
         }
index d6ec2fd..fe00ab1 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 b08126b..007fb8e 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 79a9ea3..f6a8306 100644 (file)
       <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
-        <configuration>
-          <instructions>
-            <Export-Package>org.opendaylight.controller.sample.toaster.provider.api,
-                            org.opendaylight.controller.config.yang.toaster-consumer,</Export-Package>
-            <Import-Package>*</Import-Package>
-          </instructions>
-        </configuration>
       </plugin>
       <plugin>
         <groupId>org.opendaylight.yangtools</groupId>
diff --git a/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/config/yang/config/kitchen_service/impl/KitchenServiceModule.java b/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/config/yang/config/kitchen_service/impl/KitchenServiceModule.java
new file mode 100644 (file)
index 0000000..4dc3645
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+* Generated file
+
+* Generated from: yang module name: toaster-consumer-impl  yang module local name: toaster-consumer-impl
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Feb 05 11:31:30 CET 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.kitchen_service.impl;
+
+import org.opendaylight.controller.config.yang.config.kitchen_service.impl.AbstractKitchenServiceModule;
+import org.opendaylight.controller.sample.kitchen.api.EggsType;
+import org.opendaylight.controller.sample.kitchen.api.KitchenService;
+import org.opendaylight.controller.sample.kitchen.impl.KitchenServiceImpl;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastType;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+*
+*/
+public final class KitchenServiceModule extends AbstractKitchenServiceModule {
+    private static final Logger log = LoggerFactory.getLogger(KitchenServiceModule.class);
+
+    public KitchenServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public KitchenServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            KitchenServiceModule oldModule, java.lang.AutoCloseable oldInstance) {
+
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    protected void customValidation(){
+        // No need to validate dependencies, since all dependencies have mandatory true flag in yang
+        // config-subsystem will perform the validation
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        ToasterService toasterService = getRpcRegistryDependency().getRpcService(ToasterService.class);
+
+        final KitchenServiceImpl kitchenService = new KitchenServiceImpl(toasterService);
+
+        final Registration<NotificationListener> toasterListenerReg =
+                getNotificationServiceDependency().registerNotificationListener( kitchenService );
+
+        final KitchenServiceRuntimeRegistration runtimeReg =
+                getRootRuntimeBeanRegistratorWrapper().register( kitchenService );
+
+        final class AutoCloseableKitchenService implements KitchenService, AutoCloseable {
+
+            @Override
+            public void close() throws Exception {
+                toasterListenerReg.close();
+                runtimeReg.close();
+                log.info("Toaster consumer (instance {}) torn down.", this);
+            }
+
+            @Override
+            public boolean makeBreakfast( EggsType eggs, Class<? extends ToastType> toast, int toastDoneness ) {
+                return kitchenService.makeBreakfast( eggs, toast, toastDoneness );
+            }
+        }
+
+        AutoCloseable ret = new AutoCloseableKitchenService();
+        log.info("KitchenService (instance {}) initialized.", ret );
+        return ret;
+    }
+}
@@ -7,12 +7,14 @@
 *
 * Do not modify this file unless it is present under src/main directory
 */
-package org.opendaylight.controller.config.yang.config.toaster_consumer.impl;
+package org.opendaylight.controller.config.yang.config.kitchen_service.impl;
+
+import org.opendaylight.controller.config.yang.config.kitchen_service.impl.AbstractKitchenServiceModuleFactory;
 
 /**
 *
 */
-public class ToasterConsumerModuleFactory extends org.opendaylight.controller.config.yang.config.toaster_consumer.impl.AbstractToasterConsumerModuleFactory
+public class KitchenServiceModuleFactory extends AbstractKitchenServiceModuleFactory
 {
 
 
diff --git a/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/config/yang/config/toaster_consumer/impl/ToasterConsumerModule.java b/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/config/yang/config/toaster_consumer/impl/ToasterConsumerModule.java
deleted file mode 100644 (file)
index 486cdcf..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
-* Generated file
-
-* Generated from: yang module name: toaster-consumer-impl  yang module local name: toaster-consumer-impl
-* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
-* Generated at: Wed Feb 05 11:31:30 CET 2014
-*
-* Do not modify this file unless it is present under src/main directory
-*/
-package org.opendaylight.controller.config.yang.config.toaster_consumer.impl;
-
-import org.opendaylight.controller.sal.binding.api.NotificationListener;
-import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
-import org.opendaylight.controller.sample.toaster.provider.impl.ToastConsumerImpl;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastDone;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastType;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
-import org.opendaylight.yangtools.concepts.Registration;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
-*
-*/
-public final class ToasterConsumerModule extends org.opendaylight.controller.config.yang.config.toaster_consumer.impl.AbstractToasterConsumerModule
- {
-    private static final Logger log = LoggerFactory.getLogger(ToasterConsumerModule.class);
-
-    public ToasterConsumerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
-        super(identifier, dependencyResolver);
-    }
-
-    public ToasterConsumerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
-            ToasterConsumerModule oldModule, java.lang.AutoCloseable oldInstance) {
-
-        super(identifier, dependencyResolver, oldModule, oldInstance);
-    }
-
-    @Override
-    protected void customValidation(){
-        // No need to validate dependencies, since all dependencies have mandatory true flag in yang
-        // config-subsystem will perform the validation
-    }
-
-    @Override
-    public java.lang.AutoCloseable createInstance() {
-        ToasterService toasterService = getRpcRegistryDependency().getRpcService(ToasterService.class);
-
-        final ToastConsumerImpl consumer = new ToastConsumerImpl(toasterService);
-        final Registration<NotificationListener<ToastDone>> notificationRegistration = getNotificationServiceDependency()
-                .registerNotificationListener(ToastDone.class, consumer);
-
-        final ToasterConsumerRuntimeRegistration runtimeRegistration = getRootRuntimeBeanRegistratorWrapper().register(consumer);
-
-        final class AutoCloseableToastConsumer implements AutoCloseable, ToastConsumer {
-
-            @Override
-            public void close() throws Exception {
-                runtimeRegistration.close();
-                notificationRegistration.close();
-                log.info("Toaster consumer (instance {}) torn down.", this);
-            }
-
-            @Override
-            public boolean createToast(Class<? extends ToastType> type, int doneness) {
-                return consumer.createToast(type, doneness);
-            }
-        }
-
-        AutoCloseable ret = new AutoCloseableToastConsumer();
-        log.info("Toaster consumer (instance {}) initialized.", ret);
-        return ret;
-    }
-}
diff --git a/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/api/EggsType.java b/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/api/EggsType.java
new file mode 100644 (file)
index 0000000..d9c7f45
--- /dev/null
@@ -0,0 +1,7 @@
+package org.opendaylight.controller.sample.kitchen.api;
+
+public enum EggsType {
+    SCRAMBLED,
+    OVER_EASY,
+    POACHED
+}
diff --git a/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/api/KitchenService.java b/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/api/KitchenService.java
new file mode 100644 (file)
index 0000000..ef9c122
--- /dev/null
@@ -0,0 +1,7 @@
+package org.opendaylight.controller.sample.kitchen.api;
+
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastType;
+
+public interface KitchenService {
+    boolean makeBreakfast( EggsType eggs, Class<? extends ToastType> toast, int toastDoneness );
+}
diff --git a/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/impl/KitchenServiceImpl.java b/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/impl/KitchenServiceImpl.java
new file mode 100644 (file)
index 0000000..911a8c8
--- /dev/null
@@ -0,0 +1,84 @@
+package org.opendaylight.controller.sample.kitchen.impl;
+
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.config.yang.config.kitchen_service.impl.KitchenServiceRuntimeMXBean;
+import org.opendaylight.controller.sample.kitchen.api.EggsType;
+import org.opendaylight.controller.sample.kitchen.api.KitchenService;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInputBuilder;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastType;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterListener;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterOutOfBread;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterRestocked;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.WheatBread;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class KitchenServiceImpl implements KitchenService, KitchenServiceRuntimeMXBean, ToasterListener {
+
+    private static final Logger log = LoggerFactory.getLogger( KitchenServiceImpl.class );
+
+    private final ToasterService toaster;
+
+    private volatile boolean toasterOutOfBread;
+
+    public KitchenServiceImpl(ToasterService toaster) {
+        this.toaster = toaster;
+    }
+
+    @Override
+    public boolean makeBreakfast( EggsType eggs, Class<? extends ToastType> toast, int toastDoneness ) {
+
+        if( toasterOutOfBread )
+        {
+            log.info( "We're out of toast but we can make eggs" );
+            return true;
+        }
+
+        // Access the ToasterService to make the toast.
+        // We don't actually make the eggs for this example - sorry.
+        MakeToastInputBuilder toastInput = new MakeToastInputBuilder();
+        toastInput.setToasterDoneness( (long) toastDoneness);
+        toastInput.setToasterToastType( toast );
+
+        try {
+            RpcResult<Void> result = toaster.makeToast( toastInput.build() ).get();
+
+            if( result.isSuccessful() ) {
+                log.info( "makeToast succeeded" );
+            } else {
+                log.warn( "makeToast failed: " + result.getErrors() );
+            }
+
+            return result.isSuccessful();
+        } catch( InterruptedException | ExecutionException e ) {
+            log.warn( "Error occurred during toast creation" );
+        }
+        return false;
+    }
+
+    @Override
+    public Boolean makeScrambledWithWheat() {
+        return makeBreakfast( EggsType.SCRAMBLED, WheatBread.class, 2 );
+    }
+
+    /**
+     * Implemented from the ToasterListener interface.
+     */
+    @Override
+    public void onToasterOutOfBread( ToasterOutOfBread notification ) {
+        log.info( "ToasterOutOfBread notification" );
+        toasterOutOfBread = true;
+    }
+
+    /**
+     * Implemented from the ToasterListener interface.
+     */
+    @Override
+    public void onToasterRestocked( ToasterRestocked notification ) {
+        log.info( "ToasterRestocked notification - amountOfBread: " + notification.getAmountOfBread() );
+        toasterOutOfBread = false;
+    }
+}
diff --git a/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/toaster/provider/api/ToastConsumer.java b/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/toaster/provider/api/ToastConsumer.java
deleted file mode 100644 (file)
index afc972b..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (c) 2013 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.sample.toaster.provider.api;
-
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastType;
-
-public interface ToastConsumer {
-       
-       boolean createToast(Class<? extends ToastType> type,int doneness);
-
-}
diff --git a/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/toaster/provider/impl/ToastConsumerImpl.java b/opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/toaster/provider/impl/ToastConsumerImpl.java
deleted file mode 100644 (file)
index 5a4b45c..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2013 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.sample.toaster.provider.impl;
-
-import java.util.concurrent.ExecutionException;
-
-import org.opendaylight.controller.config.yang.config.toaster_consumer.impl.ToasterConsumerRuntimeMXBean;
-import org.opendaylight.controller.sal.binding.api.NotificationListener;
-import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.*;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ToastConsumerImpl implements
-        ToastConsumer,
-        NotificationListener<ToastDone>,ToasterConsumerRuntimeMXBean {
-
-    private static final Logger log = LoggerFactory.getLogger(ToastConsumerImpl.class);
-
-    private ToasterService toaster;
-
-    public ToastConsumerImpl(ToasterService toaster) {
-        this.toaster = toaster;
-    }
-
-    @Override
-    public boolean createToast(Class<? extends ToastType> type, int doneness) {
-        MakeToastInputBuilder toastInput = new MakeToastInputBuilder();
-        toastInput.setToasterDoneness((long) doneness);
-        toastInput.setToasterToastType(type);
-
-        try {
-            RpcResult<Void> result = toaster.makeToast(toastInput.build()).get();
-
-            if (result.isSuccessful()) {
-                log.trace("Toast was successfully finished");
-            } else {
-                log.warn("Toast was not successfully finished");
-            }
-            return result.isSuccessful();
-        } catch (InterruptedException | ExecutionException e) {
-            log.warn("Error occurred during toast creation");
-        }
-        return false;
-
-    }
-
-    @Override
-    public void onNotification(ToastDone notification) {
-        log.trace("ToastDone Notification Received: {} ",notification.getToastStatus());
-    }
-
-    @Override
-    public Boolean makeHashBrownToast(Integer doneness) {
-        return createToast(HashBrown.class, doneness);
-    }
-}
@@ -1,35 +1,40 @@
 // vi: set smarttab et sw=4 tabstop=4:
-module toaster-consumer-impl {
+module kitchen-service-impl {
 
     yang-version 1;
-    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl";
-    prefix "toaster-consumer-impl";
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl";
+    prefix "kitchen-service-impl";
 
     import config { prefix config; revision-date 2013-04-05; }
     import rpc-context { prefix rpcx; revision-date 2013-06-17; }
 
-    import toaster-consumer { prefix toaster-consumer; revision-date 2014-01-31; }
     import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; }
 
     description
         "This module contains the base YANG definitions for
-        toaster-consumer impl implementation.";
+        kitchen-service impl implementation.";
 
     revision "2014-01-31" {
         description
             "Initial revision.";
     }
 
-    // This is the definition of a service implementation
-    identity toaster-consumer-impl {
+    // This is the definition of kitchen service interface identity.
+    identity kitchen-service {
+        base "config:service-type";
+        config:java-class "org.opendaylight.controller.sample.kitchen.api.KitchenService";
+    }
+
+    // This is the definition of kitchen service implementation module identity. 
+    identity kitchen-service-impl {
             base config:module-type;
-            config:provided-service toaster-consumer:toaster-consumer;
-            config:java-name-prefix ToasterConsumer;
+            config:provided-service kitchen-service;
+            config:java-name-prefix KitchenService;
     }
 
     augment "/config:modules/config:module/config:configuration" {
-        case toaster-consumer-impl {
-            when "/config:modules/config:module/config:type = 'toaster-consumer-impl'";
+        case kitchen-service-impl {
+            when "/config:modules/config:module/config:type = 'kitchen-service-impl'";
 
             container rpc-registry {
                 uses config:service-ref {
@@ -48,30 +53,31 @@ module toaster-consumer-impl {
                     }
                 }
             }
-
         }
     }
-
+    
     augment "/config:modules/config:module/config:state" {
-        case toaster-consumer-impl {
-            when "/config:modules/config:module/config:type = 'toaster-consumer-impl'";
-            rpcx:rpc-context-instance "make-hash-brown-toast-rpc";
+        case kitchen-service-impl {
+            when "/config:modules/config:module/config:type = 'kitchen-service-impl'";
+            
+            rpcx:rpc-context-instance "make-scrambled-with-wheat-rpc";
         }
     }
 
-    identity make-hash-brown-toast-rpc;
+    identity make-scrambled-with-wheat-rpc;
 
-    rpc make-hash-brown-toast {
+    rpc make-scrambled-with-wheat  {
+        description
+          "Shortcut JMX call to make breakfast with scrambled eggs and wheat toast for testing.";
+          
         input {
             uses rpcx:rpc-context-ref {
                 refine context-instance {
-                    rpcx:rpc-context-instance make-hash-brown-toast-rpc;
+                    rpcx:rpc-context-instance make-scrambled-with-wheat-rpc;
                 }
             }
-            leaf doneness {
-                type uint16;
-            }
         }
+        
         output {
             leaf result {
                 type boolean;
diff --git a/opendaylight/md-sal/samples/toaster-consumer/src/main/yang/toaster-consumer.yang b/opendaylight/md-sal/samples/toaster-consumer/src/main/yang/toaster-consumer.yang
deleted file mode 100644 (file)
index c050ee8..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-// vi: set smarttab et sw=4 tabstop=4:
-module toaster-consumer {
-
-    yang-version 1;
-    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer";
-    prefix "toaster-consumer";
-
-    import config { prefix config; revision-date 2013-04-05; }
-
-    description
-        "This module contains the base YANG definitions for
-        toaster-consumer services.";
-
-    revision "2014-01-31" {
-        description
-            "Initial revision.";
-    }
-
-    // This is the definition of a service
-    identity toaster-consumer {
-
-        base "config:service-type";
-
-        config:java-class "org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer";
-    }
-}
\ No newline at end of file
index eb82605..907b354 100644 (file)
@@ -10,7 +10,8 @@ package org.opendaylight.controller.sample.toaster.it;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.opendaylight.controller.sample.toaster.provider.api.ToastConsumer;
+import org.opendaylight.controller.sample.kitchen.api.EggsType;
+import org.opendaylight.controller.sample.kitchen.api.KitchenService;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.HashBrown;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.WhiteBread;
 import org.ops4j.pax.exam.Configuration;
@@ -23,6 +24,7 @@ import org.ops4j.pax.exam.util.PathUtils;
 import javax.inject.Inject;
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
+
 import java.lang.management.ManagementFactory;
 
 import static org.junit.Assert.assertEquals;
@@ -42,7 +44,7 @@ public class ToasterTest {
 
     @Inject
     @Filter(timeout=60*1000)
-    ToastConsumer toastConsumer;
+    KitchenService kitchenService;
 
     @Configuration
     public Option[] config() {
@@ -86,7 +88,6 @@ public class ToasterTest {
     public void testToaster() throws Exception {
 
         MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
-        ObjectName consumerOn = new ObjectName("org.opendaylight.controller:instanceName=toaster-consumer-impl,type=RuntimeBean,moduleFactoryName=toaster-consumer-impl");
         ObjectName providerOn = new ObjectName("org.opendaylight.controller:instanceName=toaster-provider-impl,type=RuntimeBean,moduleFactoryName=toaster-provider-impl");
 
         long toastsMade = (long) platformMBeanServer.getAttribute(providerOn, "ToastsMade");
@@ -95,17 +96,14 @@ public class ToasterTest {
         boolean toasts = true;
 
         // Make toasts using OSGi service
-        toasts &= toastConsumer.createToast(HashBrown.class, 4);
-        toasts &= toastConsumer.createToast(WhiteBread.class, 8);
-
-        // Make toast using JMX/config-subsystem
-        toasts &= (Boolean)platformMBeanServer.invoke(consumerOn, "makeHashBrownToast", new Object[]{4}, new String[]{Integer.class.getName()});
+        toasts &= kitchenService.makeBreakfast( EggsType.SCRAMBLED, HashBrown.class, 4);
+        toasts &= kitchenService.makeBreakfast( EggsType.POACHED, WhiteBread.class, 8 );
 
-        Assert.assertTrue("Not all toasts done by " + toastConsumer, toasts);
+        Assert.assertTrue("Not all toasts done by " + kitchenService, toasts);
 
         // Verify toasts made count on provider via JMX/config-subsystem
         toastsMade = (long) platformMBeanServer.getAttribute(providerOn, "ToastsMade");
-        assertEquals(3, toastsMade);
+        assertEquals(2, toastsMade);
     }
 
 }
index 7a282db..c5a2a0d 100644 (file)
@@ -3,7 +3,7 @@
     <snapshots>
         <snapshot>
             <required-capabilities>
-                <capability>urn:opendaylight:l2:types?module=opendaylight-l2-types&amp;revision=2013-08-27</capability>
+                <!-- <capability>urn:opendaylight:l2:types?module=opendaylight-l2-types&amp;revision=2013-08-27</capability>-->
                 <capability>
                     urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&amp;revision=2013-10-28
                 </capability>
                     urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&amp;revision=2013-10-28
                 </capability>
                 <capability>urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&amp;revision=2013-07-16</capability>
-                <capability>urn:opendaylight:yang:extension:yang-ext?module=yang-ext&amp;revision=2013-07-09
-                </capability>
+                <!-- <capability>urn:opendaylight:yang:extension:yang-ext?module=yang-ext&amp;revision=2013-07-09</capability>-->
                 <capability>
                     urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&amp;revision=2013-10-28
                 </capability>
                 <capability>http://netconfcentral.org/ns/toaster?module=toaster&amp;revision=2009-11-20</capability>
-                <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer?module=toaster-consumer&amp;revision=2014-01-31</capability>
-                <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl?module=toaster-consumer-impl&amp;revision=2014-01-31</capability>
-                <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider?module=toaster-provider&amp;revision=2014-01-31</capability>
+                <capability>urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl?module=kitchen-service-impl&amp;revision=2014-01-31</capability>
                 <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl?module=toaster-provider-impl&amp;revision=2014-01-31</capability>
 
             </required-capabilities>
                                 <name>binding-rpc-broker</name>
                             </rpc-registry>
 
+                            <data-broker>
+                              <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+                              <name>ref_binding-data-broker</name>
+                            </data-broker>
+                    
                             <notification-service>
                                 <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
                                     binding:binding-notification-service
                         </module>
 
                         <module>
-                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-consumer:impl">
-                                prefix:toaster-consumer-impl
+                            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
+                                prefix:kitchen-service-impl
                             </type>
-                            <name>toaster-consumer-impl</name>
+                            <name>kitchen-service-impl</name>
 
                             <rpc-registry>
                                 <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-rpc-registry</type>
                     </modules>
 
                     <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+                        <service>
+                          <type xmlns:kitchen="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
+                            kitchen:kitchen-service
+                          </type>
+                          <instance>
+                            <name>kitchen-service</name>
+                            <provider>/modules/module[type='kitchen-service-impl'][name='kitchen-service-impl']</provider>
+                          </instance>
+                        </service>
                         <service>
                             <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
                                 dom:schema-service
index d9bb36e..bd8e89f 100644 (file)
 package org.opendaylight.controller.config.yang.config.toaster_provider.impl;
 
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
 import org.opendaylight.controller.sample.toaster.provider.OpendaylightToaster;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.Toaster;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterData;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -46,7 +47,13 @@ public final class ToasterProviderModule extends org.opendaylight.controller.con
 
         // Register to md-sal
         opendaylightToaster.setNotificationProvider(getNotificationServiceDependency());
-        opendaylightToaster.setDataProvider(getDataBrokerDependency());
+
+        DataProviderService dataBrokerService = getDataBrokerDependency();
+        opendaylightToaster.setDataProvider(dataBrokerService);
+
+        final ListenerRegistration<DataChangeListener> dataChangeListenerRegistration =
+                dataBrokerService.registerDataChangeListener( OpendaylightToaster.TOASTER_IID, opendaylightToaster );
+
         final BindingAwareBroker.RpcRegistration<ToasterService> rpcRegistration = getRpcRegistryDependency()
                 .addRpcImplementation(ToasterService.class, opendaylightToaster);
 
@@ -56,20 +63,16 @@ public final class ToasterProviderModule extends org.opendaylight.controller.con
 
         // Wrap toaster as AutoCloseable and close registrations to md-sal at
         // close()
-        final class AutoCloseableToaster implements AutoCloseable, ToasterData {
+        final class AutoCloseableToaster implements AutoCloseable {
 
             @Override
             public void close() throws Exception {
+                dataChangeListenerRegistration.close();
                 rpcRegistration.close();
                 runtimeReg.close();
                 opendaylightToaster.close();
                 log.info("Toaster provider (instance {}) torn down.", this);
             }
-
-            @Override
-            public Toaster getToaster() {
-                return opendaylightToaster.getToaster();
-            }
         }
 
         AutoCloseable ret = new AutoCloseableToaster();
index 2dab924..b4da5a3 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.controller.sample.toaster.provider;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
@@ -16,19 +17,24 @@ import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicLong;
 
 import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
+import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
 import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.common.util.RpcErrors;
 import org.opendaylight.controller.sal.common.util.Rpcs;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.DisplayString;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInput;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastDone.ToastStatus;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastDoneBuilder;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.Toaster;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.Toaster.ToasterStatus;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.RestockToasterInput;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterBuilder;
-import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterData;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterOutOfBreadBuilder;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterRestocked;
+import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterRestockedBuilder;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResult;
@@ -37,84 +43,179 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.util.concurrent.Futures;
 
-public class OpendaylightToaster implements ToasterData, ToasterService, ToasterProviderRuntimeMXBean, AutoCloseable {
+public class OpendaylightToaster implements ToasterService, ToasterProviderRuntimeMXBean,
+                                            DataChangeListener, AutoCloseable {
 
-    private static final Logger log = LoggerFactory.getLogger(OpendaylightToaster.class);
-    private static final InstanceIdentifier<Toaster>  toasterIID = InstanceIdentifier.builder(Toaster.class).build();
+    private static final Logger LOG = LoggerFactory.getLogger(OpendaylightToaster.class);
 
-    private static final DisplayString toasterManufacturer = new DisplayString("Opendaylight");
-    private static final DisplayString toasterModelNumber = new DisplayString("Model 1 - Binding Aware");
+    public static final InstanceIdentifier<Toaster> TOASTER_IID = InstanceIdentifier.builder(Toaster.class).build();
+
+    private static final DisplayString TOASTER_MANUFACTURER = new DisplayString("Opendaylight");
+    private static final DisplayString TOASTER_MODEL_NUMBER = new DisplayString("Model 1 - Binding Aware");
 
     private NotificationProviderService notificationProvider;
     private DataBrokerService dataProvider;
+
     private final ExecutorService executor;
 
-    private Future<RpcResult<Void>> currentTask;
+    // As you will see we are using multiple threads here. Therefore we need to be careful about concurrency.
+    // In this case we use the taskLock to provide synchronization for the current task.
+    private volatile Future<RpcResult<Void>> currentTask;
+    private final Object taskLock = new Object();
+
+    private final AtomicLong amountOfBreadInStock = new AtomicLong( 100 );
+
+    private final AtomicLong toastsMade = new AtomicLong(0);
+
+    // Thread safe holder for our darkness multiplier.
+    private final AtomicLong darknessFactor = new AtomicLong( 1000 );
 
     public OpendaylightToaster() {
         executor = Executors.newFixedThreadPool(1);
     }
 
+    public void setNotificationProvider(NotificationProviderService salService) {
+        this.notificationProvider = salService;
+    }
+
+    public void setDataProvider(DataBrokerService salDataProvider) {
+        this.dataProvider = salDataProvider;
+        updateStatus();
+    }
+
+    /**
+     * Implemented from the AutoCloseable interface.
+     */
     @Override
-    public synchronized Toaster getToaster() {
-        ToasterBuilder tb = new ToasterBuilder();
-        tb //
-        .setToasterManufacturer(toasterManufacturer) //
-        .setToasterModelNumber(toasterModelNumber) //
-        .setToasterStatus(currentTask == null ? ToasterStatus.Up : ToasterStatus.Down);
+    public void close() throws ExecutionException, InterruptedException {
+        // When we close this service we need to shutdown our executor!
+        executor.shutdown();
 
+        if (dataProvider != null) {
+            final DataModificationTransaction t = dataProvider.beginTransaction();
+            t.removeOperationalData(TOASTER_IID);
+            t.commit().get();
+        }
+    }
+
+    private Toaster buildToaster() {
+        // We don't need to synchronize on currentTask here b/c it's declared volatile and
+        // we're just doing a read.
+        boolean isUp = currentTask == null;
+
+        // note - we are simulating a device whose manufacture and model are
+        // fixed (embedded) into the hardware.
+        // This is why the manufacture and model number are hardcoded.
+        ToasterBuilder tb = new ToasterBuilder();
+        tb.setToasterManufacturer(TOASTER_MANUFACTURER).setToasterModelNumber(TOASTER_MODEL_NUMBER)
+                .setToasterStatus(isUp ? ToasterStatus.Up : ToasterStatus.Down);
         return tb.build();
     }
 
+    /**
+     * Implemented from the DataChangeListener interface.
+     */
     @Override
-    public synchronized Future<RpcResult<Void>> cancelToast() {
-        if (currentTask != null) {
-            cancelToastImpl();
+    public void onDataChanged( DataChangeEvent<InstanceIdentifier<?>, DataObject> change ) {
+        DataObject dataObject = change.getUpdatedConfigurationData().get( TOASTER_IID );
+        if( dataObject instanceof Toaster )
+        {
+            Toaster toaster = (Toaster) dataObject;
+            Long darkness = toaster.getDarknessFactor();
+            if( darkness != null )
+            {
+                darknessFactor.set( darkness );
+            }
         }
-        return null;
     }
 
+    /**
+     * RestConf RPC call implemented from the ToasterService interface.
+     */
     @Override
-    public synchronized Future<RpcResult<Void>> makeToast(MakeToastInput input) {
-        log.debug("makeToast - Received input for toast");
-        logToastInput(input);
-        if (currentTask != null) {
-            return inProgressError();
+    public Future<RpcResult<Void>> cancelToast() {
+        synchronized (taskLock) {
+            if (currentTask != null) {
+                currentTask.cancel(true);
+                currentTask = null;
+            }
         }
-        currentTask = executor.submit(new MakeToastTask(input));
-        updateStatus();
-        return currentTask;
+        // Always return success from the cancel toast call.
+        return Futures.immediateFuture(Rpcs.<Void> getRpcResult(true, Collections.<RpcError> emptySet()));
     }
 
-    private Future<RpcResult<Void>> inProgressError() {
-        RpcResult<Void> result = Rpcs.<Void> getRpcResult(false, null, Collections.<RpcError> emptySet());
-        return Futures.immediateFuture(result);
-    }
+    /**
+     * RestConf RPC call implemented from the ToasterService interface.
+     */
+    @Override
+    public Future<RpcResult<Void>> makeToast(MakeToastInput input) {
+        LOG.info("makeToast: " + input);
 
-    private void cancelToastImpl() {
-        currentTask.cancel(true);
-        ToastDoneBuilder toastDone = new ToastDoneBuilder();
-        toastDone.setToastStatus(ToastStatus.Cancelled);
-        notificationProvider.publish(toastDone.build());
-    }
+        synchronized (taskLock) {
+            if (currentTask != null) {
+                // return an error since we are already toasting some toast.
+                LOG.info( "Toaster is already making toast" );
 
-    public void setNotificationProvider(NotificationProviderService salService) {
-        this.notificationProvider = salService;
-    }
+                RpcResult<Void> result = Rpcs.<Void> getRpcResult(false, null, Arrays.asList(
+                        RpcErrors.getRpcError( null, null, null, null,
+                                               "Toaster is busy", null, null ) ) );
+                return Futures.immediateFuture(result);
+            }
+            else if( outOfBread() ) {
+                RpcResult<Void> result = Rpcs.<Void> getRpcResult(false, null, Arrays.asList(
+                        RpcErrors.getRpcError( null, null, null, null,
+                                               "Toaster is out of bread", null, null ) ) );
+                return Futures.immediateFuture(result);
+            }
+            else {
+                // Notice that we are moving the actual call to another thread,
+                // allowing this thread to return immediately.
+                // The MD-SAL design encourages asynchronus programming. If the
+                // caller needs to block until the call is
+                // complete then they can leverage the blocking methods on the
+                // Future interface.
+                currentTask = executor.submit(new MakeToastTask(input));
+            }
+        }
 
-    public void setDataProvider(DataBrokerService salDataProvider) {
-        this.dataProvider = salDataProvider;
         updateStatus();
+        return currentTask;
     }
 
-    private void logToastInput(MakeToastInput input) {
-        String toastType = input.getToasterToastType().getName();
-        String toastDoneness = input.getToasterDoneness().toString();
-        log.trace("Toast: {} doneness: {}", toastType, toastDoneness);
+    /**
+     * RestConf RPC call implemented from the ToasterService interface.
+     * Restocks the bread for the toaster, resets the toastsMade counter to 0, and sends a
+     * ToasterRestocked notification.
+     */
+    @Override
+    public Future<RpcResult<java.lang.Void>> restockToaster(RestockToasterInput input) {
+        LOG.info( "restockToaster: " + input );
+
+        synchronized( taskLock ) {
+            amountOfBreadInStock.set( input.getAmountOfBreadToStock() );
+
+            if( amountOfBreadInStock.get() > 0 ) {
+                ToasterRestocked reStockedNotification =
+                    new ToasterRestockedBuilder().setAmountOfBread( input.getAmountOfBreadToStock() ).build();
+                notificationProvider.publish( reStockedNotification );
+            }
+        }
+
+        return Futures.immediateFuture(Rpcs.<Void> getRpcResult(true, Collections.<RpcError> emptySet()));
     }
 
-    private final AtomicLong toastsMade = new AtomicLong(0);
+    /**
+     * JMX RPC call implemented from the ToasterProviderRuntimeMXBean interface.
+     */
+    @Override
+    public void clearToastsMade() {
+        LOG.info( "clearToastsMade" );
+        toastsMade.set( 0 );
+    }
 
+    /**
+     * Accesssor method implemented from the ToasterProviderRuntimeMXBean interface.
+     */
     @Override
     public Long getToastsMade() {
         return toastsMade.get();
@@ -123,26 +224,22 @@ public class OpendaylightToaster implements ToasterData, ToasterService, Toaster
     private void updateStatus() {
         if (dataProvider != null) {
             final DataModificationTransaction t = dataProvider.beginTransaction();
-            t.removeOperationalData(toasterIID);
-            t.putOperationalData(toasterIID, getToaster());
+            t.removeOperationalData(TOASTER_IID);
+            t.putOperationalData(TOASTER_IID, buildToaster());
 
             try {
                 t.commit().get();
             } catch (InterruptedException | ExecutionException e) {
-                log.warn("Failed to update toaster status, operational otherwise", e);
+                LOG.warn("Failed to update toaster status, operational otherwise", e);
             }
         } else {
-            log.trace("No data provider configured, not updating status");
+            LOG.trace("No data provider configured, not updating status");
         }
     }
 
-    @Override
-    public void close() throws ExecutionException, InterruptedException {
-        if (dataProvider != null) {
-            final DataModificationTransaction t = dataProvider.beginTransaction();
-            t.removeOperationalData(toasterIID);
-            t.commit().get();
-        }
+    private boolean outOfBread()
+    {
+        return amountOfBreadInStock.get() == 0;
     }
 
     private class MakeToastTask implements Callable<RpcResult<Void>> {
@@ -154,19 +251,35 @@ public class OpendaylightToaster implements ToasterData, ToasterService, Toaster
         }
 
         @Override
-        public RpcResult<Void> call() throws InterruptedException {
-            Thread.sleep(1000 * toastRequest.getToasterDoneness());
+        public RpcResult<Void> call() {
+            try
+            {
+                // make toast just sleeps for n secondn per doneness level.
+                long darknessFactor = OpendaylightToaster.this.darknessFactor.get();
+                Thread.sleep(darknessFactor * toastRequest.getToasterDoneness());
 
-            ToastDoneBuilder notifyBuilder = new ToastDoneBuilder();
-            notifyBuilder.setToastStatus(ToastStatus.Done);
-            notificationProvider.publish(notifyBuilder.build());
-            log.debug("Toast Done");
-            logToastInput(toastRequest);
+            }
+            catch( InterruptedException e ) {
+                LOG.info( "Interrupted while making the toast" );
+            }
 
-            currentTask = null;
             toastsMade.incrementAndGet();
+
+            amountOfBreadInStock.getAndDecrement();
+            if( outOfBread() ) {
+                LOG.info( "Toaster is out of bread!" );
+
+                notificationProvider.publish( new ToasterOutOfBreadBuilder().build() );
+            }
+
+            synchronized (taskLock) {
+                currentTask = null;
+            }
+
             updateStatus();
 
+            LOG.debug("Toast done");
+
             return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
         }
     }
index 17b0c8d..d6de5cf 100644 (file)
@@ -6,7 +6,7 @@ module toaster-provider-impl {
     prefix "toaster-provider-impl";
 
     import config { prefix config; revision-date 2013-04-05; }
-    import toaster-provider { prefix toaster-provider; revision-date 2014-01-31; }
+    import rpc-context { prefix rpcx; revision-date 2013-06-17; }
     import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; }
 
     description
@@ -18,13 +18,15 @@ module toaster-provider-impl {
             "Initial revision.";
     }
 
-    // This is the definition of a service implementation
+    // This is the definition of the service implementation as a module identity.
     identity toaster-provider-impl {
             base config:module-type;
-            config:provided-service toaster-provider:toaster-provider;
+            
+            // Specifies the prefix for generated java classes.
             config:java-name-prefix ToasterProvider;
     }
 
+    // Augments the 'configuration' choice node under modules/module.
     augment "/config:modules/config:module/config:configuration" {
         case toaster-provider-impl {
             when "/config:modules/config:module/config:type = 'toaster-provider-impl'";
@@ -61,11 +63,27 @@ module toaster-provider-impl {
     augment "/config:modules/config:module/config:state" {
         case toaster-provider-impl {
             when "/config:modules/config:module/config:type = 'toaster-provider-impl'";
-
+            
             leaf toasts-made {
                 type uint32;
             }
+            
+            rpcx:rpc-context-instance "clear-toasts-made-rpc";
+        }
+    }
+
+    identity clear-toasts-made-rpc;
 
+    rpc clear-toasts-made  {
+        description
+          "JMX call to clear the toasts-made counter.";
+          
+        input {
+            uses rpcx:rpc-context-ref {
+                refine context-instance {
+                    rpcx:rpc-context-instance clear-toasts-made-rpc;
+                }
+            }
         }
     }
 }
diff --git a/opendaylight/md-sal/samples/toaster-provider/src/main/yang/toaster-provider.yang b/opendaylight/md-sal/samples/toaster-provider/src/main/yang/toaster-provider.yang
deleted file mode 100644 (file)
index a5fba07..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-// vi: set smarttab et sw=4 tabstop=4:
-module toaster-provider {
-
-    yang-version 1;
-    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider";
-    prefix "toaster-provider";
-
-    import config { prefix config; revision-date 2013-04-05; }
-
-    description
-        "This module contains the base YANG definitions for
-        toaster-provider services.";
-
-    revision "2014-01-31" {
-        description
-            "Initial revision.";
-    }
-
-    // This is the definition of a service
-    identity toaster-provider {
-
-        base "config:service-type";
-
-        config:java-class "org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterData";
-    }
-}
\ No newline at end of file
index 15c0ac8..11b746f 100644 (file)
           "This variable indicates the current state of 
                the toaster.";
       }
+      
+      leaf darknessFactor {
+        type uint32;
+        config true;
+        default 1000;
+        description
+          "The darkness factor. Basically, the number of ms to multiple the doneness value by.";
+      }
     }  // container toaster
 
     rpc make-toast {
            if the toaster service is disabled.";
     }  // rpc cancel-toast
 
-    notification toastDone {
-      description
-        "Indicates that the toast in progress has completed.";
-      leaf toastStatus {
-        type enumeration {
-          enum "done" {
-            value 0;
-            description "The toast is done.";
-          }
-          enum "cancelled" {
-            value 1;
-            description
-              "The toast was cancelled.";
-          }
-          enum "error" {
-            value 2;
-            description
-              "The toaster service was disabled or
-                     the toaster is broken.";
-          }
+    rpc restock-toaster {
+        description
+          "Restocks the toaster with the amount of bread specified.";
+          
+        input {
+            leaf amountOfBreadToStock {
+                type uint32;
+                description
+                  "Indicates the amount of bread to re-stock";
+            }
         }
+    }
+    
+    notification toasterOutOfBread {
+      description
+        "Indicates that the toaster has run of out bread.";
+    }  // notification toasterOutOfStock
+    
+    notification toasterRestocked {
+      description
+        "Indicates that the toaster has run of out bread.";
+      leaf amountOfBread {
+        type uint32;
         description
-          "Indicates the final toast status";
+          "Indicates the amount of bread that was re-stocked";
       }
-    }  // notification toastDone
+    }  // notification toasterOutOfStock
+    
   }  // module toaster

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.