Merge "Deprecate model-flow-management as it was never used and is confusing people."
authorDevin Avery <devin.avery@brocade.com>
Wed, 9 Jul 2014 20:36:32 +0000 (20:36 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 9 Jul 2014 20:36:32 +0000 (20:36 +0000)
29 files changed:
features/extras/pom.xml [new file with mode: 0644]
features/extras/src/main/resources/features.xml [new file with mode: 0644]
opendaylight/commons/opendaylight/pom.xml
opendaylight/config/config-module-archetype/src/main/resources/archetype-resources/pom.xml
opendaylight/config/config-module-archetype/src/main/resources/archetype-resources/src/main/yang/__module-name__-impl.yang
opendaylight/config/config-module-archetype/src/main/resources/archetype-resources/src/main/yang/__module-name__.yang
opendaylight/config/config-util/pom.xml
opendaylight/config/feature/src/main/resources/features.xml
opendaylight/config/pom.xml
opendaylight/distribution/opendaylight-karaf/pom.xml
opendaylight/distribution/opendaylight/pom.xml
opendaylight/md-sal/model/model-flow-base/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/flow/types/port/rev130925/PortNumberBuilder.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareConsumer.java
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareProvider.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentConnector.java
opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToNotificationTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/test/resources/notification-payload.xml [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/user-notification.yang [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
opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/api/KitchenService.java
opendaylight/md-sal/samples/toaster-consumer/src/main/java/org/opendaylight/controller/sample/kitchen/impl/KitchenServiceImpl.java
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/logback.xml
opendaylight/md-sal/samples/toaster-provider/src/main/java/org/opendaylight/controller/sample/toaster/provider/OpendaylightToaster.java
opendaylight/netconf/config-netconf-connector/pom.xml
pom.xml

diff --git a/features/extras/pom.xml b/features/extras/pom.xml
new file mode 100644 (file)
index 0000000..4563190
--- /dev/null
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>commons.opendaylight</artifactId>
+    <version>1.4.2-SNAPSHOT</version>
+    <relativePath>../../opendaylight/commons/opendaylight</relativePath>
+  </parent>
+  <artifactId>extras-features</artifactId>
+  <packaging>kar</packaging>
+  <name>${project.artifactId}</name>
+  <description>Base Features POM</description>
+  <properties>
+    <features.file>features.xml</features.file>
+    <netty3.version>3.9.2.Final</netty3.version>
+  </properties>
+  <build>
+    <resources>
+      <resource>
+        <filtering>true</filtering>
+        <directory>src/main/resources</directory>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.karaf.tooling</groupId>
+        <artifactId>karaf-maven-plugin</artifactId>
+        <version>${karaf.version}</version>
+        <extensions>true</extensions>
+        <executions>
+          <execution>
+            <id>features-create-kar</id>
+            <goals>
+              <goal>features-create-kar</goal>
+            </goals>
+            <configuration>
+              <featuresFile>${project.build.directory}/classes/${features.file}</featuresFile>
+            </configuration>
+          </execution>
+        </executions>
+        <!-- There is no useful configuration for the kar mojo. The features-generate-descriptor mojo configuration may be useful -->
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>filter</id>
+            <goals>
+              <goal>resources</goal>
+            </goals>
+            <phase>generate-resources</phase>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-artifacts</id>
+            <goals>
+              <goal>attach-artifact</goal>
+            </goals>
+            <phase>package</phase>
+            <configuration>
+              <artifacts>
+                <artifact>
+                  <file>${project.build.directory}/classes/${features.file}</file>
+                  <type>xml</type>
+                  <classifier>features</classifier>
+                </artifact>
+              </artifacts>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/features/extras/src/main/resources/features.xml b/features/extras/src/main/resources/features.xml
new file mode 100644 (file)
index 0000000..3be66d9
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<features name="extras-features-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.0.0">
+
+   <feature name="osgi-compendium" description="OSGi compendium feature" version="${osgi.version}" resolver="(obr)">
+      <bundle start-level="10">mvn:org.osgi/org.osgi.compendium/${osgi.compendium.version}</bundle>
+   </feature>
+
+   <feature name="odl-extras-scala" description="Scala Runtime for OpenDaylight" version="${scala.version}" resolver="(obr)" start-level="10">
+      <bundle>mvn:org.scala-lang/scala-library/${scala.version}.${scala.micro.version}</bundle>
+      <bundle>mvn:org.scala-lang/scala-reflect/${scala.version}.${scala.micro.version}</bundle>
+   </feature>
+
+   <feature name="odl-extras-akka-system" description="Akka Actor Framework System Bundles" version="${akka.version}" resolver="(obr)" start-level="15">
+      <feature version="${scala.version}">odl-extras-scala</feature>
+      <bundle dependency="true">mvn:com.typesafe/config/${typesafe.config.version}</bundle>
+      <bundle dependency="true">mvn:com.typesafe.akka/akka-actor_${scala.version}/${akka.version}</bundle>
+      <bundle dependency="true">mvn:com.typesafe.akka/akka-slf4j_${scala.version}/${akka.version}</bundle>
+      <bundle>mvn:com.typesafe.akka/akka-osgi_${scala.version}/${akka.version}</bundle>
+   </feature>
+
+   <feature name="odl-extras-akka-clustering" description="Akka Clustering Support" version="${akka.version}" resolver="(obr)" start-level="20">
+      <feature version="${akka.version}">odl-extras-akka-system</feature>
+      <bundle dependency="true">wrap:mvn:org.uncommons.maths/uncommons-maths/${uncommons.maths.version}</bundle>
+      <bundle dependency="true">mvn:com.google.protobuf/protobuf-java/${protobuf.version}</bundle>
+      <bundle dependency="true">wrap:mvn:io.netty/netty/${netty3.version}</bundle>
+      <bundle>mvn:com.typesafe.akka/akka-remote_${scala.version}/${akka.version}</bundle>
+      <bundle>mvn:com.typesafe.akka/akka-cluster_${scala.version}/${akka.version}</bundle>
+   </feature>
+
+   <feature name='odl-extras-leveldb' description='LevelDB feature' version='0.7' resolver='(obr)'>
+      <bundle start-level="20">wrap:mvn:org.iq80.leveldb/leveldb/${leveldb.version}</bundle>
+      <bundle start-level="20">wrap:mvn:org.fusesource.leveldbjni/leveldbjni-all/${leveldbjni.version}</bundle>
+   </feature>
+</features>
index 61d7f87..bf84183 100644 (file)
@@ -18,7 +18,7 @@
 
   <properties>
 
-    <akka.version>2.3.2</akka.version>
+    <akka.version>2.3.4</akka.version>
     <aopalliance.version>1.0.0</aopalliance.version>
     <appauth.version>0.4.2-SNAPSHOT</appauth.version>
     <archetype-app-northbound>0.0.1-SNAPSHOT</archetype-app-northbound>
@@ -62,6 +62,7 @@
     <compiler.version>2.3.2</compiler.version>
     <commons.httpclient.version>0.1.2-SNAPSHOT</commons.httpclient.version>
     <concepts.version>0.5.2-SNAPSHOT</concepts.version>
+    <concurrentlinkedhashmap.version>1.4</concurrentlinkedhashmap.version>
     <config.version>0.2.5-SNAPSHOT</config.version>
     <configuration.implementation.version>0.4.3-SNAPSHOT</configuration.implementation.version>
     <configuration.version>0.4.3-SNAPSHOT</configuration.version>
     <karaf.branding.version>1.0.0-SNAPSHOT</karaf.branding.version>
     <karaf.shell.version>3.0.0</karaf.shell.version>
     <karaf.version>3.0.1</karaf.version>
+    <leveldb.version>0.7</leveldb.version>
+    <leveldbjni.version>1.8</leveldbjni.version>
     <lifecycle.mapping.version>1.0.0</lifecycle.mapping.version>
     <logback.version>1.0.9</logback.version>
     <logging.bridge.version>0.4.2-SNAPSHOT</logging.bridge.version>
     <maven.plugin.api.version>3.0.5</maven.plugin.api.version>
+    <mimepull.version>1.9.4</mimepull.version>
     <mdsal.version>1.1-SNAPSHOT</mdsal.version>
     <netconf.version>0.2.5-SNAPSHOT</netconf.version>
     <networkconfig.bridgedomain.northbound.version>0.0.3-SNAPSHOT</networkconfig.bridgedomain.northbound.version>
     <northbound.jolokia.version>1.4.2-SNAPSHOT</northbound.jolokia.version>
     <opendaylight-l2-types.version>2013.08.27.4-SNAPSHOT</opendaylight-l2-types.version>
     <osgi-brandfragment.web.version>0.0.2-SNAPSHOT</osgi-brandfragment.web.version>
+    <parboiled.version>1.1.6</parboiled.version>
+    <parboiled.scala.version>1.1.6</parboiled.scala.version>
     <propertymavenplugin.version>1.0-alpha-2</propertymavenplugin.version>
+    <protobuf.version>2.5.0</protobuf.version>
     <protocol-framework.version>0.5.0-SNAPSHOT</protocol-framework.version>
     <protocol_plugins.openflow.version>0.4.2-SNAPSHOT</protocol_plugins.openflow.version>
     <protocol_plugins.stub.version>0.4.2-SNAPSHOT</protocol_plugins.stub.version>
     <samples.loadbalancer.northbound.version>0.4.2-SNAPSHOT</samples.loadbalancer.northbound.version>
     <samples.simpleforwarding.version>0.4.2-SNAPSHOT</samples.simpleforwarding.version>
     <sanitytest.version>0.4.2-SNAPSHOT</sanitytest.version>
-    <scala.version>2.11</scala.version>
+    <scala.version>2.10</scala.version>
+    <scala.micro.version>4</scala.micro.version>
     <security.version>0.4.2-SNAPSHOT</security.version>
+    <shapeless.version>1.2.4</shapeless.version>
     <sitedeploy>dav:http://nexus.opendaylight.org/content/sites/site</sitedeploy>
     <sonar.branch>${user.name}-private-view</sonar.branch>
     <sonar.host.url>https://sonar.opendaylight.org/</sonar.host.url>
     <spring-security-karaf.version>3.1.4.RELEASE</spring-security-karaf.version>
     <spring-security.version>3.1.3.RELEASE</spring-security.version>
     <spring.version>3.1.3.RELEASE</spring.version>
+    <sonar.skippedModules>org.openflow.openflowj,net.sf.jung2</sonar.skippedModules>
     <statistics.northbound.version>0.4.2-SNAPSHOT</statistics.northbound.version>
     <statisticsmanager.implementation.version>0.4.2-SNAPSHOT</statisticsmanager.implementation.version>
     <statisticsmanager.version>0.5.1-SNAPSHOT</statisticsmanager.version>
     <topology.web.version>0.4.2-SNAPSHOT</topology.web.version>
     <topologymanager.version>0.4.2-SNAPSHOT</topologymanager.version>
     <troubleshoot.web.version>0.4.2-SNAPSHOT</troubleshoot.web.version>
+    <typesafe.config.version>1.2.0</typesafe.config.version>
+    <uncommons.maths.version>1.2.2</uncommons.maths.version>
     <usermanager.implementation.version>0.4.2-SNAPSHOT</usermanager.implementation.version>
     <usermanager.northbound.version>0.0.2-SNAPSHOT</usermanager.northbound.version>
     <usermanager.version>0.4.2-SNAPSHOT</usermanager.version>
index 6ddb130..ae041f4 100644 (file)
@@ -20,6 +20,7 @@
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
             <artifactId>config-api</artifactId>
+            <version>${config.version}</version>
         </dependency>
     </dependencies>
 
index b6e2efc..7429f4d 100644 (file)
@@ -8,7 +8,7 @@
     <relativePath>..</relativePath>
   </parent>
   <artifactId>config-util</artifactId>
-  <packaging>jar</packaging>
+  <packaging>bundle</packaging>
   <name>${project.artifactId}</name>
 
   <dependencies>
           <threadCount>1</threadCount>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            <Export-Package>org.opendaylight.controller.config.util</Export-Package>
+          </instructions>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
 
index 81972c3..a84a743 100644 (file)
@@ -1,27 +1,28 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <features name="config-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
-   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-   xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
-    <feature name='config-all' version='${project.version}'>
-        <feature version='${project.version}'>odl-config-subsystem</feature>
-    </feature>
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+  <feature name='config-all' version='${project.version}'>
+    <feature version='${project.version}'>odl-config-subsystem</feature>
+  </feature>
 
-    <feature name='odl-config-subsystem' version='${project.version}'>
-        <feature version='${yangtools.version}'>yangtools-concepts</feature>
-        <feature version='${yangtools.version}'>yangtools-binding</feature>
-        <feature version='${yangtools.version}'>yangtools-binding-generator</feature>
-        <feature version='${mdsal.version}'>odl-mdsal-commons</feature>
-        <bundle>mvn:org.opendaylight.controller/config-api/${project.version}</bundle>
-        <bundle>mvn:org.opendaylight.controller/config-manager/${project.version}</bundle>
-        <bundle>mvn:org.opendaylight.controller/yang-jmx-generator/${project.version}</bundle>
-        <bundle>mvn:org.opendaylight.controller/config-persister-api/${project.version}</bundle>
-        <bundle>mvn:org.opendaylight.controller/config-persister-file-xml-adapter/${project.version}</bundle>
-        <bundle>mvn:org.opendaylight.controller/config-persister-directory-xml-adapter/${project.version}</bundle>
-        <bundle>mvn:org.opendaylight.controller/shutdown-api/${project.version}</bundle>
-        <bundle>mvn:org.opendaylight.controller/shutdown-impl/${project.version}</bundle>
-        <bundle>mvn:org.osgi/org.osgi.core/${osgi.core.version}</bundle>
-        <bundle>wrap:mvn:com.google.guava/guava/${guava.version}</bundle>
-        <bundle>mvn:org.javassist/javassist/${javassist.version}</bundle>
-    </feature>
+  <feature name='odl-config-subsystem' version='${project.version}'>
+    <feature version='${yangtools.version}'>yangtools-concepts</feature>
+    <feature version='${yangtools.version}'>yangtools-binding</feature>
+    <feature version='${yangtools.version}'>yangtools-binding-generator</feature>
+    <feature version='${mdsal.version}'>odl-mdsal-commons</feature>
+    <bundle>mvn:org.opendaylight.controller/config-api/${project.version}</bundle>
+    <bundle>mvn:org.opendaylight.controller/config-util/${project.version}</bundle>
+    <bundle>mvn:org.opendaylight.controller/config-manager/${project.version}</bundle>
+    <bundle>mvn:org.opendaylight.controller/yang-jmx-generator/${project.version}</bundle>
+    <bundle>mvn:org.opendaylight.controller/config-persister-api/${project.version}</bundle>
+    <bundle>mvn:org.opendaylight.controller/config-persister-file-xml-adapter/${project.version}</bundle>
+    <bundle>mvn:org.opendaylight.controller/config-persister-directory-xml-adapter/${project.version}</bundle>
+    <bundle>mvn:org.opendaylight.controller/shutdown-api/${project.version}</bundle>
+    <bundle>mvn:org.opendaylight.controller/shutdown-impl/${project.version}</bundle>
+    <bundle>mvn:org.osgi/org.osgi.core/${osgi.core.version}</bundle>
+    <bundle>wrap:mvn:com.google.guava/guava/${guava.version}</bundle>
+    <bundle>mvn:org.javassist/javassist/${javassist.version}</bundle>
+  </feature>
 </features>
\ No newline at end of file
index 76fbd7f..a8a4c87 100644 (file)
@@ -1,5 +1,4 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- vi: set et smarttab sw=4 tabstop=4: -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
index d8f6ba1..221bfa7 100644 (file)
     </dependency>
     <!-- scope is compile so all features (there is only one) are installed
             into startup.properties and the feature repo itself is not installed -->
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>extras-features</artifactId>
+      <version>${project.version}</version>
+      <type>kar</type>
+      <scope>runtime</scope>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>config-features</artifactId>
index ae99697..d238ee7 100644 (file)
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>config-manager</artifactId>
         </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>config-util</artifactId>
+        </dependency>
         <dependency>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>config-netconf-connector</artifactId>
diff --git a/opendaylight/md-sal/model/model-flow-base/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/flow/types/port/rev130925/PortNumberBuilder.java b/opendaylight/md-sal/model/model-flow-base/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/flow/types/port/rev130925/PortNumberBuilder.java
new file mode 100644 (file)
index 0000000..ff78a74
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.CommonPort.PortNumber;
+
+
+public class PortNumberBuilder {
+
+    public static PortNumber getDefaultInstance(java.lang.String defaultValue) {
+        try {
+            long uint32 = Long.parseLong(defaultValue);
+            return new PortNumber(uint32);
+        } catch(NumberFormatException e){
+            return new PortNumber(defaultValue);
+        }
+    }
+
+}
index 4327451..bcbd687 100644 (file)
@@ -10,17 +10,64 @@ package org.opendaylight.controller.sal.binding.api;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
 
 /**
- *
- * Defines the component of controller and supplies additional metadata. A
- * component of the controller or application supplies a concrete implementation
- * of this interface.
- *
- * A user-implemented component (application) which facilitates the SAL and SAL
- * services to access infrastructure services or providers' functionality.
- *
- *
- *
- */
+*
+* A developer implemented component that gets registered with the Broker.
+*
+* Semantically, a consumer may:
+*
+* <ol>
+*   <li>Subscribe for Notifications </li>
+*   <li>Invoke RPCs</li>
+*   <li>Read from either the operational or config data tree</li>
+*   <li>Write to the config data tree</li>
+* </ol>
+* If you need to:
+* <ol>
+*   <li> Emit Notifications</li>
+*   <li> Provide the implementation of RPCs </li>
+*   <li> Write to the operational data tree </li>
+* </ol>
+*
+* Consider using a BindingAwareProvider
+*
+* Examples:
+*
+* To get a NotificationService:
+*
+* {code
+* public void onSessionInitiated(ProviderContext session) {
+*      NotificationProviderService notificationService = session.getSALService(NotificationProviderService.class);
+*      notificationService.publish(notification)
+* }
+* where notification is an instance of a modeled Notification.
+* For more information on sending notifications via the NotificationProviderService
+* @see org.opendaylight.controller.sal.binding.api.NotificationProviderService
+*
+*
+* A consumer can *invoke* and RPC ( ie, call foo(fooArgs)) but it cannot register an RPC
+* implementation with the MD-SAL that others can invoke(call).
+* To get an invokable RPC:
+*
+* {code
+* public void onSessionInitiated(ProviderContext session) {
+*    MyService rpcFlowSalService = session.getRpcService(MyService.class);
+* }
+*
+* Where MyService.class is a Service interface generated from a yang model with RPCs modeled in it.  The returned
+* rpcFlowSalService can be used like any other object by invoking its methods.  Note, nothing special needs to be done
+* for RoutedRPCs.  They just work.
+*
+* To get a DataBroker to allow access to the data tree:
+*
+* {code
+* public void onSessionInitiated(final ProviderContext session) {
+*      DataBroker databroker = session.getSALService(BindingDataBroker.class);
+* }
+* }
+* @see org.opendaylight.controller.md.sal.common.api.data.BindingDataBroker
+* for more info on using the DataBroker.
+*
+*/
 public interface BindingAwareConsumer {
 
     /**
index 0812e5f..cb26cad 100644 (file)
@@ -15,37 +15,131 @@ import org.opendaylight.yangtools.yang.binding.RpcService;
 
 /**
  *
- * Defines the component of controller and supplies additional metadata. A
- * component of the controller or application supplies a concrete implementation
- * of this interface.
+ * A developer implemented component that gets registered with the Broker.
  *
+ * Semantically, a provider may:
+ *
+ * <ol>
+ *   <li> Emit Notifications</li>
+ *   <li> Provide the implementation of RPCs </li>
+ *   <li> Write to the operational data tree </li>
+ * </ol>
+ *
+ * If a class is not doing at least one of those three, consider using
+ * a BindingAwareConsumer instead:
+ * @see org.opendaylight.controller.sal.binding.api.BindingAwareConsumer
+ *
+ * <p>
+ *
+ *In addition, a BindingAwareProvider can in pursuit of its goals:
+ *
+ * <ol>
+ *   <li>Subscribe for Notifications </li>
+ *   <li>Invoke RPCs</li>
+ *   <li>Read from either the operational or config data tree</li>
+ *   <li>Write to the config data tree</li>
+ * </ol>
+ * (All of the above are things a Consumer can also do).
+ *
+ *<p>
+ *
+ * Examples:
+ *
+ *<p>
+ *
+ * To get a NotificationService:
+ *
+ * {code
+ * public void onSessionInitiated(ProviderContext session) {
+ *      NotificationProviderService notificationService = session.getSALService(NotificationProviderService.class);
+ * }
+ * For more information on sending notifications via the NotificationProviderService
+ * @see org.opendaylight.controller.sal.binding.api.NotificationProviderService
+ *
+ * To register an RPC implementation:
+ *
+ * {code
+ * public void onSessionInitiated(ProviderContext session) {
+ *    RpcRegistration<MyService> registration = session.addRpcImplementation(MyService.class, myImplementationInstance);
+ * }
+ *
+ * <p>
+ *
+ * Where MyService.class is a Service interface generated from a yang model with RPCs modeled in it and myImplementationInstance
+ * is an instance of a class that implements MyService.
+ *
+ * To register a Routed RPC Implementation:
+ * {code
+ * public void onSessionInitiated(ProviderContext session) {
+ *   RoutedRpcRegistration<SalFlowService> flowRegistration = session.addRoutedRpcImplementation(SalFlowService.class, salFlowServiceImplementationInstance);
+     flowRegistration.registerPath(NodeContext.class, nodeInstanceId);
+ * }
+ * }
+ *
+ * Where SalFlowService.class is a Service interface generated from a yang model with RPCs modeled in it and salFlowServiceImplementationInstance is an instance
+ * of a class that implements SalFlowService.
  * <p>
- * A user-implemented component (application) which facilitates the SAL and SAL
- * services to access infrastructure services and to provide functionality to
- * {@link Consumer}s and other providers.
+ * The line:
+ * {code
+ * flowRegistration.registerPath(NodeContext.class, nodeInstanceId);
+ * }
+ * Is indicating that the RPC implementation is registered to handle RPC invocations that have their NodeContext pointing to the node with instance id nodeInstanceId.
+ * This bears a bit of further explanation.  RoutedRPCs can be 'routed' to an implementation based upon 'context'.  'context' is a pointer (instanceId) to some place
+ * in the data tree.  In this example, the 'context' is a pointer to a Node.  In this way, a provider can register its ability to provide a service for a particular
+ * Node, but not *all* Nodes.  The Broker routes the RPC by 'context' to the correct implementation, without the caller having to do extra work.  Because of this when
+ * a RoutedRPC is registered, it needs to also be able to indicate for which 'contexts' it is providing an implementation.
+ *
+ * An example of a Routed RPC would be an updateFlow(node, flow) that would be routed based on node to the provider which had registered to provide
+ * it *for that node*.
+ *
+ *<p>
  *
+ * To get a DataBroker to allow access to the data tree:
+ *
+ * {code
+ * public void onSessionInitiated(final ProviderContext session) {
+ *      DataBroker databroker = session.getSALService(BindingDataBroker.class);
+ * }
+ * }
+ * @see org.opendaylight.controller.md.sal.common.api.data.BindingDataBroker
+ * for more info on using the DataBroker.
  *
  */
 public interface BindingAwareProvider {
 
     /**
-     * Returns a set of provided implementations of YANG modules and their rpcs.
+     * @deprecated
      *
+     * This interface was originally intended to solve problems of how to get Implementations
+     * of functionality from a provider, but that is no longer necessary because the Provider
+     * Registers RPCs in onSessionInitiated.
      *
-     * @return Set of provided implementation of YANG modules and their Rpcs
+     * Recommend:
+     * {code
+     * public Collection<? extends RpcService> getImplementations() {
+     *   return Collections.emptySet();
+     * }
+     * }
      */
+    @Deprecated
     Collection<? extends RpcService> getImplementations();
 
     /**
-     * Gets a set of implementations of provider functionality to be registered
-     * into system during the provider registration to the SAL.
+     * @deprecated
      *
-     * <p>
-     * This method is invoked by {@link Broker#registerProvider(Provider)} to
-     * learn the initial provided functionality
+     * This interface was originally intended to solve problems of how to get Functionality
+     *  a provider could provide, but that is no longer necessary because the Provider
+     * Registers RPCs in onSessionInitiated.
+     *
+     * Recommend:
+     * {code
+     * public Collection<? extends ProviderFunctionality> getFunctionality() {
+     *   return Collections.emptySet();
+     * }
+     * }
      *
-     * @return Set of provider's functionality.
      */
+    @Deprecated
     Collection<? extends ProviderFunctionality> getFunctionality();
 
     /**
@@ -58,12 +152,38 @@ public interface BindingAwareProvider {
      *
      *
      */
+    @Deprecated
     public interface ProviderFunctionality {
 
     }
-
+    /**
+     * Callback signaling initialization of the consumer session to the SAL.
+     *
+     * The consumer MUST use the session for all communication with SAL or
+     * retrieving SAL infrastructure services.
+     *
+     * This method is invoked by
+     * {@link BindingAwareBroker#registerProvider(BindingAwareProvider)}
+     *
+     * @param session Unique session between consumer and SAL.
+     */
     void onSessionInitiated(ProviderContext session);
 
+    /*
+     * @deprecated
+     *
+     * A provider was at one point considered an extension of a consumer, thus this
+     * call.  It is deprecated and the @see org.opendaylight.controller.sal.binding.api.BindingAwareConsumer#onSessionInitiated
+     * used, or you should simply use {@link #onSessionInitiated(ProviderContext)}
+     *
+     * Recommend:
+     * {code
+     * public final void onSessionInitialized(ConsumerContext session) {
+     *   // NOOP - as method is deprecated
+     * }
+     * }
+     */
+    @Deprecated
     void onSessionInitialized(ConsumerContext session);
 
 }
index fe8c4a1..6e4b2d8 100644 (file)
@@ -602,7 +602,7 @@ public class BindingIndependentConnector implements //
             final Optional<Class<? extends RpcService>> rpcInterface = mappingService.getRpcServiceClassFor(
                     name.getNamespace().toString(), name.getFormattedRevision());
             if (rpcInterface.isPresent()) {
-                getRpcForwarder(rpcInterface.get(), null).registerToBidningBroker();
+                getRpcForwarder(rpcInterface.get(), null).registerToBindingBroker();
             }
         }
 
@@ -621,6 +621,7 @@ public class BindingIndependentConnector implements //
         private final WeakHashMap<Method, RpcInvocationStrategy> strategiesByMethod = new WeakHashMap<>();
         private final RpcService proxy;
         private ObjectRegistration<?> forwarderRegistration;
+        private boolean registrationInProgress = false;
 
         public DomToBindingRpcForwarder(final Class<? extends RpcService> service) {
             this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
@@ -680,7 +681,8 @@ public class BindingIndependentConnector implements //
          *
          */
         public void registerToDOMBroker() {
-            if(forwarderRegistration == null) {
+            if(!registrationInProgress && forwarderRegistration == null) {
+                registrationInProgress = true;
                 CompositeObjectRegistrationBuilder<DomToBindingRpcForwarder> builder = CompositeObjectRegistration.builderFor(this);
                 try {
                     for (QName rpc : supportedRpcs) {
@@ -690,6 +692,7 @@ public class BindingIndependentConnector implements //
                     LOG.error("Could not forward Rpcs of type {}", rpcServiceType.get(), e);
                 }
                 this.forwarderRegistration = builder.toInstance();
+                registrationInProgress = false;
             }
         }
 
@@ -801,12 +804,15 @@ public class BindingIndependentConnector implements //
          * creating forwarding loop.
          *
          */
-        public void registerToBidningBroker() {
-            if(forwarderRegistration == null) {
+        public void registerToBindingBroker() {
+            if(!registrationInProgress && forwarderRegistration == null) {
                try {
+                   registrationInProgress = true;
                    this.forwarderRegistration = baRpcRegistry.addRpcImplementation((Class)rpcServiceType.get(), proxy);
                } catch (Exception e) {
                    LOG.error("Unable to forward RPCs for {}",rpcServiceType.get(),e);
+               } finally {
+                   registrationInProgress = false;
                }
             }
         }
index 5ede600..c389618 100644 (file)
@@ -54,6 +54,7 @@ public class TestHelper {
                 mavenBundle("io.netty", "netty-transport").versionAsInProject(), //
 
                 mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), // //
+                mavenBundle(CONTROLLER, "config-util").versionAsInProject(), // //
                 mavenBundle("commons-io", "commons-io").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "yang-jmx-generator").versionAsInProject(), //
index 21f9447..f9e6239 100644 (file)
@@ -55,7 +55,7 @@ public class NetconfMessageTransformer implements MessageTransformer<NetconfMess
     private static CompositeNode toNotification(final NetconfMessage message, final SchemaContext ctx) {
         final Set<NotificationDefinition> notifications = ctx.getNotifications();
         final Document document = message.getDocument();
-        return XmlDocumentUtils.notificationToDomNodes(document, Optional.fromNullable(notifications));
+        return XmlDocumentUtils.notificationToDomNodes(document, Optional.fromNullable(notifications), ctx);
     }
 
     @Override
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToNotificationTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfToNotificationTest.java
new file mode 100644 (file)
index 0000000..521a55d
--- /dev/null
@@ -0,0 +1,64 @@
+package org.opendaylight.controller.sal.connect.netconf;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.w3c.dom.Document;
+
+/**
+ * @author Lukas Sedlak <lsedlak@cisco.com>
+ */
+public class NetconfToNotificationTest {
+
+    NetconfMessageTransformer messageTransformer;
+
+    NetconfMessage userNotification;
+
+    @Before
+    public void setup() throws Exception {
+        final List<InputStream> modelsToParse = Collections.singletonList(getClass().getResourceAsStream("/schemas/user-notification.yang"));
+        final YangContextParser parser = new YangParserImpl();
+        final Set<Module> modules = parser.parseYangModelsFromStreams(modelsToParse);
+        assertTrue(!modules.isEmpty());
+        final SchemaContext schemaContext = parser.resolveSchemaContext(modules);
+        assertNotNull(schemaContext);
+
+        messageTransformer = new NetconfMessageTransformer();
+        messageTransformer.onGlobalContextUpdated(schemaContext);
+
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        InputStream notifyPayloadStream = getClass().getResourceAsStream("/notification-payload.xml");
+        assertNotNull(notifyPayloadStream);
+
+        final Document doc = XmlUtil.readXmlToDocument(notifyPayloadStream);
+        assertNotNull(doc);
+        userNotification = new NetconfMessage(doc);
+    }
+
+    @Test
+    public void test() throws Exception {
+        final CompositeNode root = messageTransformer.toNotification(userNotification);
+
+        assertNotNull(root);
+        assertEquals(6, root.size());
+        assertEquals("user-visited-page", root.getKey().getLocalName());
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/resources/notification-payload.xml b/opendaylight/md-sal/sal-netconf-connector/src/test/resources/notification-payload.xml
new file mode 100644 (file)
index 0000000..837fcc1
--- /dev/null
@@ -0,0 +1,14 @@
+<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+<eventTime>2014-07-08T11:20:48UTC</eventTime>
+<user-visited-page xmlns="org:opendaylight:notification:test:ns:yang:user-notification">
+  <ui:incoming-user xmlns:ui="org:opendaylight:notification:test:ns:yang:user-notification">ui:public-user</ui:incoming-user>
+  <ip-address>172.23.29.104</ip-address>
+  <mac>00:11:00:ff:dd:02</mac>
+  <browser-id>Chrome 35.0.1916.153 m</browser-id>
+  <region>
+    <name>Slovakia</name>
+    <time-zone>UTC/GMT+2</time-zone>
+  </region>
+  <visiting-date>2014-07-08 11:20:48</visiting-date>
+</user-visited-page>
+</notification>
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/user-notification.yang b/opendaylight/md-sal/sal-netconf-connector/src/test/resources/schemas/user-notification.yang
new file mode 100644 (file)
index 0000000..50d2206
--- /dev/null
@@ -0,0 +1,56 @@
+module user-notification {
+    yang-version 1;
+    namespace "org:opendaylight:notification:test:ns:yang:user-notification";
+    prefix "user";
+
+    organization "Cisco Systems";
+    contact "Lukas Sedlak";
+    description "Test model for testing notifications";
+
+    revision "2014-07-08" {
+        description "Initial revision";
+    }
+
+    identity user-identity {
+        description "Identity of user incoming to Web Page";
+    }
+
+    identity public-user {
+        base user-identity;
+        description "Identity of random public non-registered user";
+    }
+
+    notification user-visited-page {
+        leaf incoming-user {
+            type identityref {
+                base "user-identity";
+            }
+        }
+
+        leaf ip-address {
+            type string;
+        }
+
+        leaf mac {
+            type string;
+        }
+
+        leaf browser-id {
+            type string;
+        }
+
+        container region {
+            leaf name {
+                type string;
+            }
+
+            leaf time-zone {
+                type string;
+            }
+        }
+
+        leaf visiting-date {
+            type string;
+        }
+    }
+}
\ No newline at end of file
index f6a8306..6e72029 100644 (file)
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>config-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-util</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>sal-binding-api</artifactId>
index a23def6..88281bd 100644 (file)
@@ -9,6 +9,8 @@
  */
 package org.opendaylight.controller.config.yang.config.kitchen_service.impl;
 
+import java.util.concurrent.Future;
+
 import org.opendaylight.controller.sample.kitchen.api.EggsType;
 import org.opendaylight.controller.sample.kitchen.api.KitchenService;
 import org.opendaylight.controller.sample.kitchen.impl.KitchenServiceImpl;
@@ -16,6 +18,7 @@ import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120
 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.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -63,7 +66,9 @@ public final class KitchenServiceModule extends AbstractKitchenServiceModule {
             }
 
             @Override
-            public boolean makeBreakfast( final EggsType eggs, final Class<? extends ToastType> toast, final int toastDoneness ) {
+            public Future<RpcResult<Void>> makeBreakfast( final EggsType eggs,
+                                                          final Class<? extends ToastType> toast,
+                                                          final int toastDoneness ) {
                 return kitchenService.makeBreakfast( eggs, toast, toastDoneness );
             }
         }
index ef9c122..7e7f342 100644 (file)
@@ -1,7 +1,11 @@
 package org.opendaylight.controller.sample.kitchen.api;
 
+import java.util.concurrent.Future;
+
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToastType;
+import org.opendaylight.yangtools.yang.common.RpcResult;
 
 public interface KitchenService {
-    boolean makeBreakfast( EggsType eggs, Class<? extends ToastType> toast, int toastDoneness );
+    Future<RpcResult<Void>> makeBreakfast( EggsType eggs, Class<? extends ToastType> toast,
+                                           int toastDoneness );
 }
index 911a8c8..50ae8fd 100644 (file)
@@ -1,10 +1,18 @@
 package org.opendaylight.controller.sample.kitchen.impl;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
-
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import org.opendaylight.controller.config.yang.config.kitchen_service.impl.KitchenServiceRuntimeMXBean;
+import org.opendaylight.controller.sal.common.util.RpcErrors;
+import org.opendaylight.controller.sal.common.util.Rpcs;
 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.MakeToastInput;
 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;
@@ -12,16 +20,31 @@ import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120
 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.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
 public class KitchenServiceImpl implements KitchenService, KitchenServiceRuntimeMXBean, ToasterListener {
 
     private static final Logger log = LoggerFactory.getLogger( KitchenServiceImpl.class );
 
     private final ToasterService toaster;
 
+    private final ListeningExecutorService executor =
+                                   MoreExecutors.listeningDecorator( Executors.newCachedThreadPool() );
+
     private volatile boolean toasterOutOfBread;
 
     public KitchenServiceImpl(ToasterService toaster) {
@@ -29,39 +52,102 @@ public class KitchenServiceImpl implements KitchenService, KitchenServiceRuntime
     }
 
     @Override
-    public boolean makeBreakfast( EggsType eggs, Class<? extends ToastType> toast, int toastDoneness ) {
+    public Future<RpcResult<Void>> makeBreakfast( EggsType eggsType, Class<? extends ToastType> toastType,
+                                                  int toastDoneness ) {
+
+        // Call makeToast and use JdkFutureAdapters to convert the Future to a ListenableFuture,
+        // The OpendaylightToaster impl already returns a ListenableFuture so the conversion is
+        // actually a no-op.
+
+        ListenableFuture<RpcResult<Void>> makeToastFuture = JdkFutureAdapters.listenInPoolThread(
+                makeToast( toastType, toastDoneness ), executor );
+
+        ListenableFuture<RpcResult<Void>> makeEggsFuture = makeEggs( eggsType );
+
+        // Combine the 2 ListenableFutures into 1 containing a list of RpcResults.
+
+        ListenableFuture<List<RpcResult<Void>>> combinedFutures =
+                Futures.allAsList( ImmutableList.of( makeToastFuture, makeEggsFuture ) );
+
+        // Then transform the RpcResults into 1.
+
+        return Futures.transform( combinedFutures,
+            new AsyncFunction<List<RpcResult<Void>>,RpcResult<Void>>() {
+                @Override
+                public ListenableFuture<RpcResult<Void>> apply( List<RpcResult<Void>> results )
+                                                                                 throws Exception {
+                    boolean atLeastOneSucceeded = false;
+                    Builder<RpcError> errorList = ImmutableList.builder();
+                    for( RpcResult<Void> result: results ) {
+                        if( result.isSuccessful() ) {
+                            atLeastOneSucceeded = true;
+                        }
+
+                        if( result.getErrors() != null ) {
+                            errorList.addAll( result.getErrors() );
+                        }
+                    }
+
+                    return Futures.immediateFuture(
+                              Rpcs.<Void> getRpcResult( atLeastOneSucceeded, errorList.build() ) );
+                }
+        } );
+    }
+
+    private ListenableFuture<RpcResult<Void>> makeEggs( EggsType eggsType ) {
+
+        return executor.submit( new Callable<RpcResult<Void>>() {
+
+            @Override
+            public RpcResult<Void> call() throws Exception {
+
+                // We don't actually do anything here - just return a successful result.
+                return Rpcs.<Void> getRpcResult( true, Collections.<RpcError>emptyList() );
+            }
+        } );
+    }
+
+    private Future<RpcResult<Void>> makeToast( Class<? extends ToastType> toastType,
+                                               int toastDoneness ) {
 
         if( toasterOutOfBread )
         {
             log.info( "We're out of toast but we can make eggs" );
-            return true;
+            return Futures.immediateFuture( Rpcs.<Void> getRpcResult( true,
+                       Arrays.asList( RpcErrors.getRpcError( "", "partial-operation", null,
+                                          ErrorSeverity.WARNING,
+                                          "Toaster is out of bread but we can make you eggs",
+                                          ErrorType.APPLICATION, null ) ) ) );
         }
 
         // 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();
+        MakeToastInput toastInput = new MakeToastInputBuilder()
+            .setToasterDoneness( (long) toastDoneness )
+            .setToasterToastType( toastType )
+            .build();
+
+        return toaster.makeToast( toastInput );
+    }
 
+    @Override
+    public Boolean makeScrambledWithWheat() {
+        try {
+            // This call has to block since we must return a result to the JMX client.
+            RpcResult<Void> result = makeBreakfast( EggsType.SCRAMBLED, WheatBread.class, 2 ).get();
             if( result.isSuccessful() ) {
-                log.info( "makeToast succeeded" );
+                log.info( "makeBreakfast succeeded" );
             } else {
-                log.warn( "makeToast failed: " + result.getErrors() );
+                log.warn( "makeBreakfast failed: " + result.getErrors() );
             }
 
             return result.isSuccessful();
+
         } catch( InterruptedException | ExecutionException e ) {
-            log.warn( "Error occurred during toast creation" );
+            log.warn( "An error occurred while maing breakfast: " + e );
         }
-        return false;
-    }
 
-    @Override
-    public Boolean makeScrambledWithWheat() {
-        return makeBreakfast( EggsType.SCRAMBLED, WheatBread.class, 2 );
+        return Boolean.FALSE;
     }
 
     /**
index 907b354..30f1762 100644 (file)
@@ -93,13 +93,13 @@ public class ToasterTest {
         long toastsMade = (long) platformMBeanServer.getAttribute(providerOn, "ToastsMade");
         assertEquals(0, toastsMade);
 
-        boolean toasts = true;
+        boolean success = true;
 
         // Make toasts using OSGi service
-        toasts &= kitchenService.makeBreakfast( EggsType.SCRAMBLED, HashBrown.class, 4);
-        toasts &= kitchenService.makeBreakfast( EggsType.POACHED, WhiteBread.class, 8 );
+        success &= kitchenService.makeBreakfast( EggsType.SCRAMBLED, HashBrown.class, 4).get().isSuccessful();
+        success &= kitchenService.makeBreakfast( EggsType.POACHED, WhiteBread.class, 8 ).get().isSuccessful();
 
-        Assert.assertTrue("Not all toasts done by " + kitchenService, toasts);
+        Assert.assertTrue("Not all breakfasts succeeded", success);
 
         // Verify toasts made count on provider via JMX/config-subsystem
         toastsMade = (long) platformMBeanServer.getAttribute(providerOn, "ToastsMade");
index f904f97..d76c2d3 100644 (file)
@@ -10,7 +10,9 @@
 
   <logger name="org.opendaylight.yangtools.yang.parser" level="ERROR"/>
 
-  <root level="debug">
+  <logger name="org.opendaylight.controller.sample.toaster.provider" level="DEBUG"/>
+
+  <root level="error">
     <appender-ref ref="STDOUT" />
   </root>
 </configuration>
index ec352e8..d2b0f90 100644 (file)
@@ -9,19 +9,24 @@ package org.opendaylight.controller.sample.toaster.provider;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
 import org.opendaylight.controller.sal.common.util.RpcErrors;
 import org.opendaylight.controller.sal.common.util.Rpcs;
@@ -44,7 +49,13 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
 
 public class OpendaylightToaster implements ToasterService, ToasterProviderRuntimeMXBean,
                                             DataChangeListener, AutoCloseable {
@@ -61,10 +72,9 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
 
     private final ExecutorService executor;
 
-    // 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();
+    // The following holds the Future for the current make toast task.
+    // This is used to cancel the current toast.
+    private final AtomicReference<Future<?>> currentMakeToastTask = new AtomicReference<>();
 
     private final AtomicLong amountOfBreadInStock = new AtomicLong( 100 );
 
@@ -83,7 +93,7 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
 
     public void setDataProvider(final DataBroker salDataProvider) {
         this.dataProvider = salDataProvider;
-        updateStatus();
+        setToasterStatusUp( null );
     }
 
     /**
@@ -97,22 +107,30 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
         if (dataProvider != null) {
             WriteTransaction t = dataProvider.newWriteOnlyTransaction();
             t.delete(LogicalDatastoreType.OPERATIONAL,TOASTER_IID);
-            t.commit().get(); // FIXME: This call should not be blocking.
+            ListenableFuture<RpcResult<TransactionStatus>> future = t.commit();
+            Futures.addCallback( future, new FutureCallback<RpcResult<TransactionStatus>>() {
+                @Override
+                public void onSuccess( RpcResult<TransactionStatus> result ) {
+                    LOG.debug( "Delete Toaster commit result: " + result );
+                }
+
+                @Override
+                public void onFailure( Throwable t ) {
+                    LOG.error( "Delete of Toaster failed", t );
+                }
+            } );
         }
     }
 
-    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;
+    private Toaster buildToaster( ToasterStatus status ) {
 
         // 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();
+        return new ToasterBuilder().setToasterManufacturer( TOASTER_MANUFACTURER )
+                                   .setToasterModelNumber( TOASTER_MODEL_NUMBER )
+                                   .setToasterStatus( status )
+                                   .build();
     }
 
     /**
@@ -133,57 +151,148 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
     }
 
     /**
-     * RestConf RPC call implemented from the ToasterService interface.
+     * RPC call implemented from the ToasterService interface that cancels the current
+     * toast, if any.
      */
     @Override
     public Future<RpcResult<Void>> cancelToast() {
-        synchronized (taskLock) {
-            if (currentTask != null) {
-                currentTask.cancel(true);
-                currentTask = null;
-            }
+
+        Future<?> current = currentMakeToastTask.getAndSet( null );
+        if( current != null ) {
+            current.cancel( true );
         }
+
         // Always return success from the cancel toast call.
-        return Futures.immediateFuture(Rpcs.<Void> getRpcResult(true, Collections.<RpcError> emptySet()));
+        return Futures.immediateFuture( Rpcs.<Void> getRpcResult( true,
+                                        Collections.<RpcError>emptyList() ) );
     }
 
     /**
-     * RestConf RPC call implemented from the ToasterService interface.
+     * RPC call implemented from the ToasterService interface that attempts to make toast.
      */
     @Override
     public Future<RpcResult<Void>> makeToast(final MakeToastInput input) {
         LOG.info("makeToast: " + input);
 
-        synchronized (taskLock) {
-            if (currentTask != null) {
-                // return an error since we are already toasting some toast.
-                LOG.info( "Toaster is already making toast" );
+        final SettableFuture<RpcResult<Void>> futureResult = SettableFuture.create();
 
-                RpcResult<Void> result = Rpcs.<Void> getRpcResult(false, null, Arrays.asList(
-                        RpcErrors.getRpcError( "", "in-use", null, ErrorSeverity.WARNING,
-                                               "Toaster is busy", ErrorType.APPLICATION, null ) ) );
-                return Futures.immediateFuture(result);
-            }
-            else if( outOfBread() ) {
-                RpcResult<Void> result = Rpcs.<Void> getRpcResult(false, null, Arrays.asList(
-                        RpcErrors.getRpcError( "out-of-stock", "resource-denied", null, null,
-                                               "Toaster is out of bread",
-                                               ErrorType.APPLICATION, 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));
+        checkStatusAndMakeToast( input, futureResult );
+
+        return futureResult;
+    }
+
+    private List<RpcError> makeToasterOutOfBreadError() {
+        return Arrays.asList(
+                RpcErrors.getRpcError( "out-of-stock", "resource-denied", null, null,
+                                       "Toaster is out of bread",
+                                       ErrorType.APPLICATION, null ) );
+    }
+
+    private List<RpcError> makeToasterInUseError() {
+        return Arrays.asList(
+            RpcErrors.getRpcError( "", "in-use", null, ErrorSeverity.WARNING,
+                                   "Toaster is busy", ErrorType.APPLICATION, null ) );
+    }
+
+    private void checkStatusAndMakeToast( final MakeToastInput input,
+                                          final SettableFuture<RpcResult<Void>> futureResult ) {
+
+        // Read the ToasterStatus and, if currently Up, try to write the status to Down.
+        // If that succeeds, then we essentially have an exclusive lock and can proceed
+        // to make toast.
+
+        final ReadWriteTransaction tx = dataProvider.newReadWriteTransaction();
+        ListenableFuture<Optional<DataObject>> readFuture =
+                                          tx.read( LogicalDatastoreType.OPERATIONAL, TOASTER_IID );
+
+        final ListenableFuture<RpcResult<TransactionStatus>> commitFuture =
+            Futures.transform( readFuture, new AsyncFunction<Optional<DataObject>,
+                                                                   RpcResult<TransactionStatus>>() {
+
+                @Override
+                public ListenableFuture<RpcResult<TransactionStatus>> apply(
+                        Optional<DataObject> toasterData ) throws Exception {
+
+                    ToasterStatus toasterStatus = ToasterStatus.Up;
+                    if( toasterData.isPresent() ) {
+                        toasterStatus = ((Toaster)toasterData.get()).getToasterStatus();
+                    }
+
+                    LOG.debug( "Read toaster status: {}", toasterStatus );
+
+                    if( toasterStatus == ToasterStatus.Up ) {
+
+                        if( outOfBread() ) {
+                            LOG.debug( "Toaster is out of bread" );
+
+                            return Futures.immediateFuture( Rpcs.<TransactionStatus>getRpcResult(
+                                       false, null, makeToasterOutOfBreadError() ) );
+                        }
+
+                        LOG.debug( "Setting Toaster status to Down" );
+
+                        // We're not currently making toast - try to update the status to Down
+                        // to indicate we're going to make toast. This acts as a lock to prevent
+                        // concurrent toasting.
+                        tx.put( LogicalDatastoreType.OPERATIONAL, TOASTER_IID,
+                                buildToaster( ToasterStatus.Down ) );
+                        return tx.commit();
+                    }
+
+                    LOG.debug( "Oops - already making toast!" );
+
+                    // Return an error since we are already making toast. This will get
+                    // propagated to the commitFuture below which will interpret the null
+                    // TransactionStatus in the RpcResult as an error condition.
+                    return Futures.immediateFuture( Rpcs.<TransactionStatus>getRpcResult(
+                            false, null, makeToasterInUseError() ) );
+                }
+        } );
+
+        Futures.addCallback( commitFuture, new FutureCallback<RpcResult<TransactionStatus>>() {
+            @Override
+            public void onSuccess( RpcResult<TransactionStatus> result ) {
+                if( result.getResult() == TransactionStatus.COMMITED  ) {
+
+                    // OK to make toast
+                    currentMakeToastTask.set( executor.submit(
+                                                    new MakeToastTask( input, futureResult ) ) );
+                } else {
+
+                    LOG.debug( "Setting error result" );
+
+                    // Either the transaction failed to commit for some reason or, more likely,
+                    // the read above returned ToasterStatus.Down. Either way, fail the
+                    // futureResult and copy the errors.
+
+                    futureResult.set( Rpcs.<Void>getRpcResult( false, null, result.getErrors() ) );
+                }
             }
-        }
 
-        updateStatus();
-        return currentTask;
+            @Override
+            public void onFailure( Throwable ex ) {
+                if( ex instanceof OptimisticLockFailedException ) {
+
+                    // Another thread is likely trying to make toast simultaneously and updated the
+                    // status before us. Try reading the status again - if another make toast is
+                    // now in progress, we should get ToasterStatus.Down and fail.
+
+                    LOG.debug( "Got OptimisticLockFailedException - trying again" );
+
+                    checkStatusAndMakeToast( input, futureResult );
+
+                } else {
+
+                    LOG.error( "Failed to commit Toaster status", ex );
+
+                    // Got some unexpected error so fail.
+                    futureResult.set( Rpcs.<Void> getRpcResult( false, null, Arrays.asList(
+                        RpcErrors.getRpcError( null, null, null, ErrorSeverity.ERROR,
+                                               ex.getMessage(),
+                                               ErrorType.APPLICATION, ex ) ) ) );
+                }
+            }
+        } );
     }
 
     /**
@@ -195,17 +304,15 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
     public Future<RpcResult<java.lang.Void>> restockToaster(final RestockToasterInput input) {
         LOG.info( "restockToaster: " + input );
 
-        synchronized( taskLock ) {
-            amountOfBreadInStock.set( input.getAmountOfBreadToStock() );
+        amountOfBreadInStock.set( input.getAmountOfBreadToStock() );
 
-            if( amountOfBreadInStock.get() > 0 ) {
-                ToasterRestocked reStockedNotification =
-                    new ToasterRestockedBuilder().setAmountOfBread( input.getAmountOfBreadToStock() ).build();
-                notificationProvider.publish( reStockedNotification );
-            }
+        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()));
+        return Futures.immediateFuture(Rpcs.<Void> getRpcResult(true, Collections.<RpcError>emptyList()));
     }
 
     /**
@@ -225,19 +332,38 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
         return toastsMade.get();
     }
 
-    private void updateStatus() {
-        if (dataProvider != null) {
-            WriteTransaction tx = dataProvider.newWriteOnlyTransaction();
-            tx.put(LogicalDatastoreType.OPERATIONAL,TOASTER_IID, buildToaster());
+    private void setToasterStatusUp( final Function<Boolean,Void> resultCallback ) {
 
-            try {
-                tx.commit().get();
-            } catch (InterruptedException | ExecutionException e) {
-                LOG.warn("Failed to update toaster status, operational otherwise", e);
+        WriteTransaction tx = dataProvider.newWriteOnlyTransaction();
+        tx.put( LogicalDatastoreType.OPERATIONAL,TOASTER_IID, buildToaster( ToasterStatus.Up ) );
+
+        ListenableFuture<RpcResult<TransactionStatus>> commitFuture = tx.commit();
+
+        Futures.addCallback( commitFuture, new FutureCallback<RpcResult<TransactionStatus>>() {
+            @Override
+            public void onSuccess( RpcResult<TransactionStatus> result ) {
+                if( result.getResult() != TransactionStatus.COMMITED ) {
+                    LOG.error( "Failed to update toaster status: " + result.getErrors() );
+                }
+
+                notifyCallback( result.getResult() == TransactionStatus.COMMITED );
             }
-        } else {
-            LOG.trace("No data provider configured, not updating status");
-        }
+
+            @Override
+            public void onFailure( Throwable t ) {
+                // We shouldn't get an OptimisticLockFailedException (or any ex) as no
+                // other component should be updating the operational state.
+                LOG.error( "Failed to update toaster status", t );
+
+                notifyCallback( false );
+            }
+
+            void notifyCallback( boolean result ) {
+                if( resultCallback != null ) {
+                    resultCallback.apply( result );
+                }
+            }
+        } );
     }
 
     private boolean outOfBread()
@@ -245,19 +371,22 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
         return amountOfBreadInStock.get() == 0;
     }
 
-    private class MakeToastTask implements Callable<RpcResult<Void>> {
+    private class MakeToastTask implements Callable<Void> {
 
         final MakeToastInput toastRequest;
+        final SettableFuture<RpcResult<Void>> futureResult;
 
-        public MakeToastTask(final MakeToastInput toast) {
-            toastRequest = toast;
+        public MakeToastTask( final MakeToastInput toastRequest,
+                              final SettableFuture<RpcResult<Void>> futureResult ) {
+            this.toastRequest = toastRequest;
+            this.futureResult = futureResult;
         }
 
         @Override
-        public RpcResult<Void> call() {
+        public Void call() {
             try
             {
-                // make toast just sleeps for n secondn per doneness level.
+                // make toast just sleeps for n seconds per doneness level.
                 long darknessFactor = OpendaylightToaster.this.darknessFactor.get();
                 Thread.sleep(darknessFactor * toastRequest.getToasterDoneness());
 
@@ -275,15 +404,26 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
                 notificationProvider.publish( new ToasterOutOfBreadBuilder().build() );
             }
 
-            synchronized (taskLock) {
-                currentTask = null;
-            }
+            // Set the Toaster status back to up - this essentially releases the toasting lock.
+            // We can't clear the current toast task nor set the Future result until the
+            // update has been committed so we pass a callback to be notified on completion.
+
+            setToasterStatusUp( new Function<Boolean,Void>() {
+                @Override
+                public Void apply( Boolean result ) {
+
+                    currentMakeToastTask.set( null );
+
+                    LOG.debug("Toast done");
 
-            updateStatus();
+                    futureResult.set( Rpcs.<Void>getRpcResult( true, null,
+                                                          Collections.<RpcError>emptyList() ) );
 
-            LOG.debug("Toast done");
+                    return null;
+                }
+            } );
 
-            return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
+            return null;
         }
     }
 }
index c10f0a9..8a29139 100644 (file)
                             org.opendaylight.controller.netconf.confignetconfconnector.transactions,
                             org.opendaylight.controller.netconf.confignetconfconnector.util,
                             org.opendaylight.controller.netconf.confignetconfconnector.osgi,
-                            org.opendaylight.controller.config.util,
                             org.opendaylight.controller.netconf.confignetconfconnector.exception,</Private-Package>
             <Import-Package>com.google.common.base,
                             com.google.common.collect,
                             com.google.common.io,
                             org.opendaylight.yangtools.yang.model.api.type,
                             org.opendaylight.yangtools.sal.binding.generator.spi,
-                            org.opendaylight.yangtools.sal.binding.yang.types,</Import-Package>
+                            org.opendaylight.yangtools.sal.binding.yang.types,
+                            org.opendaylight.controller.config.util
+              </Import-Package>
             <Export-Package></Export-Package>
           </instructions>
         </configuration>
diff --git a/pom.xml b/pom.xml
index 012d939..af84002 100644 (file)
--- a/pom.xml
+++ b/pom.xml
     <module>features/base</module>
     <module>features/controller</module>
     <module>features/adsal</module>
+    <module>features/extras</module>
     <module>opendaylight/dummy-console</module>
     <module>opendaylight/karaf-branding</module>
     <module>opendaylight/distribution/opendaylight-karaf</module>

©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.