Merge "Ganymed ChannelManager hack for subsystem command, functionality for subsystem...
authorEd Warnicke <eaw@cisco.com>
Wed, 20 Nov 2013 23:54:14 +0000 (23:54 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 20 Nov 2013 23:54:14 +0000 (23:54 +0000)
51 files changed:
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ConfigRegistryImpl.java
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/ModuleInternalTransactionalInfo.java
opendaylight/config/netty-config-api/pom.xml [new file with mode: 0644]
opendaylight/config/netty-config-api/src/main/yang/netty.yang [new file with mode: 0644]
opendaylight/config/netty-event-executor-config/pom.xml
opendaylight/config/netty-event-executor-config/src/main/java/org/opendaylight/controller/config/yang/netty/eventexecutor/GlobalEventExecutorModule.java
opendaylight/config/netty-event-executor-config/src/main/yang/netty-event-executor.yang
opendaylight/config/netty-threadgroup-config/pom.xml
opendaylight/config/netty-threadgroup-config/src/main/java/org/opendaylight/controller/config/yang/netty/threadgroup/NettyThreadgroupModule.java
opendaylight/config/netty-threadgroup-config/src/main/yang/netty-threadgroup.yang [moved from opendaylight/config/netty-threadgroup-config/src/main/yang/nsos-netty-threadgroup.yang with 83% similarity]
opendaylight/config/netty-timer-config/pom.xml [new file with mode: 0644]
opendaylight/config/netty-timer-config/src/main/java/org/opendaylight/controller/config/yang/netty/timer/HashedWheelTimerModule.java [new file with mode: 0644]
opendaylight/config/netty-timer-config/src/main/java/org/opendaylight/controller/config/yang/netty/timer/HashedWheelTimerModuleFactory.java [new file with mode: 0644]
opendaylight/config/netty-timer-config/src/main/yang/netty-timer.yang [new file with mode: 0644]
opendaylight/config/netty-timer-config/src/test/org/opendaylight/controller/config/yang/netty/timer/HashedWheelTimerModuleTest.java [new file with mode: 0644]
opendaylight/config/pom.xml
opendaylight/config/threadpool-config-api/pom.xml
opendaylight/config/threadpool-config-api/src/main/yang/threadpool.yang
opendaylight/config/yang-store-api/pom.xml
opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreListenerRegistration.java [deleted file]
opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreService.java
opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/spi/YangStoreListener.java [deleted file]
opendaylight/config/yang-store-impl/pom.xml
opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/ExtenderYangTracker.java [moved from opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/ExtenderYangTrackerCustomizer.java with 51% similarity]
opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/MbeParser.java
opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/YangStoreActivator.java
opendaylight/config/yang-store-impl/src/main/java/org/opendaylight/controller/config/yang/store/impl/YangStoreSnapshotImpl.java
opendaylight/config/yang-store-impl/src/test/java/org/opendaylight/controller/config/yang/store/impl/ExtenderYangTrackerCustomizerTest.java
opendaylight/config/yang-store-impl/src/test/java/org/opendaylight/controller/config/yang/store/impl/HardcodedYangStoreService.java
opendaylight/distribution/opendaylight/pom.xml
opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfServiceLegacy.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonReader.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.xtend
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyFuture.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyRpcResult.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromJsonToCompositeNodeTest.java [moved from opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromJsonToCompositeNode.java with 71% similarity]
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromXmlToCompositeNodeTest.java [moved from opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/FromXmlToCompositeNode.java with 97% similarity]
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/TestUtils.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/XmlProvidersTest.java
opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/array-with-null.json [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/multiple-leaflist-items.json [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/simple-container-yang/simple-container.yang
opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/simple-list-yang/simple-list1.yang [moved from opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/simple-list-yang/simple-list.yang with 68% similarity]
opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/simple-list-yang/simple-list2.yang [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java

index f93409f99ed21d980316df9c85a2bf144f061f3f..84c2c6dd4dd46b57372d1fbf14a46f43ed225181 100644 (file)
@@ -275,6 +275,7 @@ public class ConfigRegistryImpl implements AutoCloseable, ConfigRegistryImplMXBe
             ModuleJMXRegistrator newModuleJMXRegistrator = baseJMXRegistrator
                     .createModuleJMXRegistrator();
 
+            OsgiRegistration osgiRegistration = null;
             if (entry.hasOldModule()) {
                 ModuleInternalInfo oldInternalInfo = entry.getOldInternalInfo();
                 DynamicReadableWrapper oldReadableConfigBean = oldInternalInfo
@@ -282,19 +283,21 @@ public class ConfigRegistryImpl implements AutoCloseable, ConfigRegistryImplMXBe
                 currentConfig.remove(entry.getName());
 
                 // test if old instance == new instance
-                if (oldReadableConfigBean.getInstance().equals(
-                        module.getInstance())) {
+                if (oldReadableConfigBean.getInstance().equals(module.getInstance())) {
                     // reused old instance:
                     // wrap in readable dynamic mbean
                     reusedInstances.add(primaryReadOnlyON);
+                    osgiRegistration = oldInternalInfo.getOsgiRegistration();
                 } else {
                     // recreated instance:
                     // it is responsibility of module to call the old instance -
                     // we just need to unregister configbean
                     recreatedInstances.add(primaryReadOnlyON);
+
+                    // close old osgi registration
+                    oldInternalInfo.getOsgiRegistration().close();
                 }
-                // close old osgi registration in any case
-                oldInternalInfo.getOsgiRegistration().close();
+
                 // close old module jmx registrator
                 oldInternalInfo.getModuleJMXRegistrator().close();
             } else {
@@ -316,10 +319,10 @@ public class ConfigRegistryImpl implements AutoCloseable, ConfigRegistryImplMXBe
             }
 
             // register to OSGi
-            OsgiRegistration osgiRegistration = beanToOsgiServiceManager
-                    .registerToOsgi(module.getClass(),
-                            newReadableConfigBean.getInstance(),
-                            entry.getName());
+            if (osgiRegistration == null) {
+                osgiRegistration = beanToOsgiServiceManager.registerToOsgi(module.getClass(),
+                        newReadableConfigBean.getInstance(), entry.getName());
+            }
 
             RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator = runtimeRegistrators
                     .get(entry.getName());
index e71aef4c04cd3b90d5bc4cf8002aa52b8a4d2da9..c4f40fbeeb42a6c05e08a6213fc2d928d2a912da 100644 (file)
@@ -36,6 +36,11 @@ public class ModuleInternalTransactionalInfo implements Identifiable<ModuleIdent
         this.transactionModuleJMXRegistration = transactionModuleJMXRegistration;
     }
 
+
+    /**
+     * Use {@link #getIdentifier()} instead.
+     */
+    @Deprecated
     public ModuleIdentifier getName() {
         return name;
     }
@@ -56,7 +61,7 @@ public class ModuleInternalTransactionalInfo implements Identifiable<ModuleIdent
                 maybeOldInternalInfo.getOrderingIdx());
     }
 
-    @Deprecated
+
     public Module getModule() {
         return module;
     }
diff --git a/opendaylight/config/netty-config-api/pom.xml b/opendaylight/config/netty-config-api/pom.xml
new file mode 100644 (file)
index 0000000..9a2fe37
--- /dev/null
@@ -0,0 +1,54 @@
+<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">
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>config-subsystem</artifactId>
+    <version>0.2.3-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>netty-config-api</artifactId>
+     <name>${project.artifactId}</name>
+   <packaging>bundle</packaging>
+   <prerequisites>
+      <maven>3.0.4</maven>
+   </prerequisites>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>config-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-transport</artifactId>
+        </dependency>
+    </dependencies>
+   <build>
+      <plugins>
+         <plugin>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>maven-bundle-plugin</artifactId>
+            <configuration>
+               <instructions>
+                  <Import-Package>
+                     org.opendaylight.controller.config.api.*,
+                     io.netty.channel,
+                     io.netty.util,
+                     io.netty.util.concurrent
+                  </Import-Package>
+                  <Export-Package>
+                     org.opendaylight.controller.config.yang.netty
+                  </Export-Package>
+               </instructions>
+            </configuration>
+         </plugin>
+         <plugin>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-maven-plugin</artifactId>
+         </plugin>
+      </plugins>
+   </build>
+</project>
\ No newline at end of file
diff --git a/opendaylight/config/netty-config-api/src/main/yang/netty.yang b/opendaylight/config/netty-config-api/src/main/yang/netty.yang
new file mode 100644 (file)
index 0000000..7f7a3ff
--- /dev/null
@@ -0,0 +1,52 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module netty {
+       yang-version 1;
+       namespace "urn:opendaylight:params:xml:ns:yang:controller:netty";
+       prefix "netty";
+
+       import config { prefix config; revision-date 2013-04-05; }
+
+       organization "Cisco Systems, Inc.";
+
+       contact "Milos Fabian <milfabia@cisco.com>";
+
+    description
+        "This module contains the base YANG definitions for
+         netty services.
+
+        Copyright (c)2013 Cisco Systems, Inc. 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";
+
+    revision "2013-11-19" {
+        description
+            "Initial revision.";
+    }
+    
+    identity netty-threadgroup {
+        description
+            "Configuration wrapper around netty's threadgroup";
+
+        base "config:service-type";
+        config:java-class "io.netty.channel.EventLoopGroup";
+    }
+    
+    identity netty-event-executor {
+        description
+            "Configuration wrapper around netty's event executor";
+
+        base "config:service-type";
+        config:java-class "io.netty.util.concurrent.EventExecutor";
+    }
+    
+    identity netty-timer {
+        description
+            "Configuration wrapper around netty's timer";
+
+        base "config:service-type";
+        config:java-class "io.netty.util.Timer";       
+    }
+}
\ No newline at end of file
index a2ce94f340f073cfc447a46c5e971776bbe44fe9..3d5384d171774d053ca01ee39ca1a9e3c784401f 100644 (file)
       </dependency>
       <dependency>
          <groupId>org.opendaylight.controller</groupId>
-         <artifactId>threadpool-config-api</artifactId>
+         <artifactId>netty-config-api</artifactId>
          <version>${project.version}</version>
       </dependency>
       <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
       </dependency>
-      <dependency>
-         <groupId>com.google.guava</groupId>
-         <artifactId>guava</artifactId>
-      </dependency>
 
       <!--test dependencies -->
       <dependency>
@@ -80,7 +76,7 @@
                   </Export-Package>
                   <Import-Package>
                      com.google.common.base,
-                     org.opendaylight.controller.config.yang.threadpool,
+                     org.opendaylight.controller.config.yang.netty,
                      io.netty.util.concurrent,
                      org.opendaylight.controller.config.api,
                      org.opendaylight.controller.config.api.annotations,
index d45eccded8f7fd6b5c20c3f68f67898e559815a5..16e5c07356df97c6af8baf2f6e7eb80ac7d123f4 100644 (file)
@@ -2,18 +2,18 @@
 module netty-event-executor {
     yang-version 1;
        namespace "urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor";
-    prefix "netty-t";
+    prefix "netty-ee";
 
     import config { prefix config; revision-date 2013-04-05; }
-    import threadpool { prefix th; revision-date 2013-04-09; }
+    import netty { prefix netty; revision-date 2013-11-19; }
 
     organization "Cisco Systems, Inc.";
 
     contact "Milos Fabian <milfabia@cisco.com>";
 
     description
-        "This module contains the base YANG definitions for NS-OS
-         thread-related services.
+        "This module contains the base YANG definitions for
+         netty event executor implementation.
 
         Copyright (c)2013 Cisco Systems, Inc. All rights reserved.;
         
@@ -29,7 +29,7 @@ module netty-event-executor {
     
     identity netty-global-event-executor {
         base config:module-type;
-        config:provided-service th:netty-event-executor;
+        config:provided-service netty:netty-event-executor;
         config:java-name-prefix GlobalEventExecutor;
     }
 
@@ -39,6 +39,4 @@ module netty-event-executor {
 
         }
     }
-
-
 }
index ef63fce2ce66126f329d72eeb94aa0e69d33eb23..8dc989e7283751c1a91b4e57d789046724b1f5b3 100644 (file)
         </dependency>
         <dependency>
             <groupId>org.opendaylight.controller</groupId>
-            <artifactId>threadpool-config-api</artifactId>
+            <artifactId>netty-config-api</artifactId>
             <version>${project.version}</version>
         </dependency>
-
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
 
-
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-        </dependency>
-
         <!--test dependencies -->
         <dependency>
             <groupId>junit</groupId>
@@ -82,7 +75,6 @@
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
-                <version>2.3.7</version>
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
@@ -92,7 +84,7 @@
                         <Import-Package>
                             com.google.common.base,
                             io.netty.channel.nio,
-                            org.opendaylight.controller.config.yang.threadpool,
+                            org.opendaylight.controller.config.yang.netty,
                             io.netty.util.concurrent,
                             org.opendaylight.controller.config.api,
                             org.opendaylight.controller.config.api.annotations,
index fd6b216f53fa50c9cb956710d565140383bbf515..9ceef3116ac5603219c019eb2fd21d340e23dcca 100644 (file)
@@ -9,9 +9,10 @@
 */
 package org.opendaylight.controller.config.yang.netty.threadgroup;
 
-import com.google.common.base.Preconditions;
 import io.netty.channel.nio.NioEventLoopGroup;
 
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+
 /**
 *
 */
@@ -28,7 +29,8 @@ public final class NettyThreadgroupModule extends org.opendaylight.controller.co
     @Override
     public void validate(){
         if(getThreadCount()!=null) {
-            Preconditions.checkArgument(getThreadCount() > 0, "Thread count cannot be < 0");
+            JmxAttributeValidationException.checkCondition(getThreadCount() > 0, "value must be greater than 0",
+                    threadCountJmxAttribute);
         }
     }
 
similarity index 83%
rename from opendaylight/config/netty-threadgroup-config/src/main/yang/nsos-netty-threadgroup.yang
rename to opendaylight/config/netty-threadgroup-config/src/main/yang/netty-threadgroup.yang
index f13cf391bf3dbd042853dc65df2ef6a652150435..e648c5328a71349f9261efa82f8c8ef02a798a58 100644 (file)
@@ -1,19 +1,19 @@
 // vi: set smarttab et sw=4 tabstop=4:
-module nsos-threadpool {
+module threadgroup {
     yang-version 1;
        namespace "urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup";
-    prefix "netty-t";
+    prefix "netty-th";
 
     import config { prefix config; revision-date 2013-04-05; }
-    import threadpool { prefix th; revision-date 2013-04-09; }
+    import netty { prefix netty; revision-date 2013-11-19; }
 
     organization "Cisco Systems, Inc.";
 
     contact "Robert Varga <rovarga@cisco.com>";
 
     description
-        "This module contains the base YANG definitions for NS-OS
-         thread-related services.
+        "This module contains the base YANG definitions for
+         netty threadgroup implementation.
 
         Copyright (c)2013 Cisco Systems, Inc. All rights reserved.";
 
@@ -24,7 +24,7 @@ module nsos-threadpool {
 
     identity netty-threadgroup-fixed {
         base config:module-type;
-        config:provided-service th:netty-threadgroup;
+        config:provided-service netty:netty-threadgroup;
         config:java-name-prefix NettyThreadgroup;
     }
 
diff --git a/opendaylight/config/netty-timer-config/pom.xml b/opendaylight/config/netty-timer-config/pom.xml
new file mode 100644 (file)
index 0000000..095e71f
--- /dev/null
@@ -0,0 +1,114 @@
+<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">
+   <parent>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>config-subsystem</artifactId>
+      <version>0.2.3-SNAPSHOT</version>
+   </parent>
+   <modelVersion>4.0.0</modelVersion>
+   <artifactId>netty-timer-config</artifactId>
+   <description>Configuration Wrapper around netty's timer</description>
+   <packaging>bundle</packaging>
+   <name>${project.artifactId}</name>
+   <prerequisites>
+      <maven>3.0.4</maven>
+   </prerequisites>
+
+   <dependencies>
+      <dependency>
+         <groupId>org.opendaylight.controller</groupId>
+         <artifactId>config-api</artifactId>
+      </dependency>
+      <dependency>
+         <groupId>org.opendaylight.controller</groupId>
+         <artifactId>netty-config-api</artifactId>
+         <version>${project.version}</version>
+      </dependency>
+      <dependency>
+         <groupId>org.opendaylight.controller</groupId>
+         <artifactId>threadpool-config-api</artifactId>
+         <version>${project.version}</version>
+      </dependency>
+      <dependency>
+         <groupId>org.slf4j</groupId>
+         <artifactId>slf4j-api</artifactId>
+      </dependency>
+
+      <!--test dependencies -->
+      <dependency>
+         <groupId>junit</groupId>
+         <artifactId>junit</artifactId>
+         <scope>test</scope>
+      </dependency>
+      <dependency>
+         <groupId>org.opendaylight.controller</groupId>
+         <artifactId>config-manager</artifactId>
+         <scope>test</scope>
+         <type>test-jar</type>
+      </dependency>
+      <dependency>
+         <groupId>org.opendaylight.controller</groupId>
+         <artifactId>config-manager</artifactId>
+         <scope>test</scope>
+      </dependency>
+      <dependency>
+         <groupId>org.opendaylight.controller</groupId>
+         <artifactId>config-util</artifactId>
+         <scope>test</scope>
+      </dependency>
+      <dependency>
+         <groupId>org.opendaylight.bgpcep</groupId>
+         <artifactId>mockito-configuration</artifactId>
+         <scope>test</scope>
+      </dependency>
+      <dependency>
+         <groupId>org.opendaylight.controller</groupId>
+         <artifactId>threadpool-config-impl</artifactId>
+         <version>${project.version}</version>
+         <scope>test</scope>
+      </dependency>
+
+   </dependencies>
+
+   <build>
+      <plugins>
+         <plugin>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-maven-plugin</artifactId>
+         </plugin>
+         <plugin>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>maven-bundle-plugin</artifactId>
+            <extensions>true</extensions>
+            <configuration>
+               <instructions>
+                  <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+                  <Export-Package>
+                  </Export-Package>
+                  <Import-Package>
+                     javax.management,
+                     com.google.common.base,
+                     org.opendaylight.controller.config.yang.netty,
+                     org.opendaylight.controller.config.yang.threadpool,
+                     io.netty.util,
+                     org.opendaylight.controller.config.api,
+                     org.opendaylight.controller.config.api.annotations,
+                     org.opendaylight.controller.config.api.runtime,
+                     org.opendaylight.controller.config.spi,
+                     org.slf4j,
+                     org.osgi.framework
+                  </Import-Package>
+               </instructions>
+            </configuration>
+         </plugin>
+      </plugins>
+   </build>
+
+   <distributionManagement>
+      <site>
+         <id>${project.artifactId}</id>
+         <name>NETTY-TIMER-CONFIG Module site</name>
+         <url>${basedir}/target/site/${project.artifactId}</url>
+      </site>
+   </distributionManagement>
+</project>
\ No newline at end of file
diff --git a/opendaylight/config/netty-timer-config/src/main/java/org/opendaylight/controller/config/yang/netty/timer/HashedWheelTimerModule.java b/opendaylight/config/netty-timer-config/src/main/java/org/opendaylight/controller/config/yang/netty/timer/HashedWheelTimerModule.java
new file mode 100644 (file)
index 0000000..cc78124
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: netty-event-executor  yang module local name: netty-hashed-wheel-timer
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Tue Nov 19 12:49:59 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.netty.timer;
+
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timeout;
+import io.netty.util.Timer;
+import io.netty.util.TimerTask;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+
+/**
+*
+*/
+public final class HashedWheelTimerModule extends
+        org.opendaylight.controller.config.yang.netty.timer.AbstractHashedWheelTimerModule {
+
+    public HashedWheelTimerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+        super(identifier, dependencyResolver);
+    }
+
+    public HashedWheelTimerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+            org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+            HashedWheelTimerModule oldModule, java.lang.AutoCloseable oldInstance) {
+        super(identifier, dependencyResolver, oldModule, oldInstance);
+    }
+
+    @Override
+    public void validate() {
+        super.validate();
+        if (getTickDuration() != null) {
+            JmxAttributeValidationException.checkCondition(getTickDuration() > 0, "value must be greater than 0",
+                    tickDurationJmxAttribute);
+        }
+        if (getTicksPerWheel() != null) {
+            JmxAttributeValidationException.checkCondition(getTicksPerWheel() > 0, "value must be greater than 0",
+                    ticksPerWheelJmxAttribute);
+        }
+    }
+
+    @Override
+    public java.lang.AutoCloseable createInstance() {
+        TimeUnit unit = TimeUnit.MILLISECONDS;
+        if (getTickDuration() != null && getThreadFactoryDependency() == null && getTicksPerWheel() == null) {
+            return new HashedWheelTimerCloseable(new HashedWheelTimer(getTickDuration(), unit));
+        }
+        if (getTickDuration() != null && getThreadFactoryDependency() == null && getTicksPerWheel() != null) {
+            return new HashedWheelTimerCloseable(new HashedWheelTimer(getTickDuration(), unit, getTicksPerWheel()));
+        }
+        if (getTickDuration() == null && getThreadFactoryDependency() != null && getTicksPerWheel() == null) {
+            return new HashedWheelTimerCloseable(new HashedWheelTimer(getThreadFactoryDependency()));
+        }
+        if (getTickDuration() != null && getThreadFactoryDependency() != null && getTicksPerWheel() == null) {
+            return new HashedWheelTimerCloseable(new HashedWheelTimer(getThreadFactoryDependency(), getTickDuration(),
+                    unit));
+        }
+        if (getTickDuration() != null && getThreadFactoryDependency() != null && getTicksPerWheel() != null) {
+            return new HashedWheelTimerCloseable(new HashedWheelTimer(getThreadFactoryDependency(), getTickDuration(),
+                    unit, getTicksPerWheel()));
+        }
+        return new HashedWheelTimerCloseable(new HashedWheelTimer());
+    }
+
+    static final private class HashedWheelTimerCloseable implements AutoCloseable, Timer {
+
+        private final Timer timer;
+
+        public HashedWheelTimerCloseable(Timer timer) {
+            this.timer = timer;
+        }
+
+        @Override
+        public void close() throws Exception {
+            stop();
+        }
+
+        @Override
+        public Timeout newTimeout(TimerTask task, long delay, TimeUnit unit) {
+            return this.timer.newTimeout(task, delay, unit);
+        }
+
+        @Override
+        public Set<Timeout> stop() {
+            return this.timer.stop();
+        }
+
+    }
+}
diff --git a/opendaylight/config/netty-timer-config/src/main/java/org/opendaylight/controller/config/yang/netty/timer/HashedWheelTimerModuleFactory.java b/opendaylight/config/netty-timer-config/src/main/java/org/opendaylight/controller/config/yang/netty/timer/HashedWheelTimerModuleFactory.java
new file mode 100644 (file)
index 0000000..e291ab5
--- /dev/null
@@ -0,0 +1,18 @@
+/**
+ * Generated file
+
+ * Generated from: yang module name: netty-event-executor  yang module local name: netty-hashed-wheel-timer
+ * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+ * Generated at: Tue Nov 19 12:49:59 CET 2013
+ *
+ * Do not modify this file unless it is present under src/main directory
+ */
+package org.opendaylight.controller.config.yang.netty.timer;
+
+/**
+*
+*/
+public class HashedWheelTimerModuleFactory extends
+        org.opendaylight.controller.config.yang.netty.timer.AbstractHashedWheelTimerModuleFactory {
+
+}
diff --git a/opendaylight/config/netty-timer-config/src/main/yang/netty-timer.yang b/opendaylight/config/netty-timer-config/src/main/yang/netty-timer.yang
new file mode 100644 (file)
index 0000000..b53b13f
--- /dev/null
@@ -0,0 +1,59 @@
+// vi: set smarttab et sw=4 tabstop=4:
+module netty-timer {
+    yang-version 1;
+       namespace "urn:opendaylight:params:xml:ns:yang:controller:netty:timer";
+    prefix "netty-timer";
+
+    import config { prefix config; revision-date 2013-04-05; }
+    import netty { prefix netty; revision-date 2013-11-19; }
+    import threadpool { prefix th; revision-date 2013-04-09; }
+
+    organization "Cisco Systems, Inc.";
+
+    contact "Milos Fabian <milfabia@cisco.com>";
+
+    description
+        "This module contains the base YANG definitions for
+         netty timer implementation.
+
+        Copyright (c)2013 Cisco Systems, Inc. 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";
+
+    revision "2013-11-19" {
+        description
+            "Initial revision";
+    }
+    
+    identity netty-hashed-wheel-timer {
+        base config:module-type;
+        config:provided-service netty:netty-timer;
+        config:java-name-prefix HashedWheelTimer;
+    }
+
+    augment "/config:modules/config:module/config:configuration" {
+        case netty-hashed-wheel-timer {
+            when "/config:modules/config:module/config:type = 'netty-hashed-wheel-timer'";
+            
+            leaf tick-duration {
+                type uint32;
+            }
+            
+            leaf ticks-per-wheel {
+                type uint16;
+            }
+            
+                       container thread-factory {
+                               uses config:service-ref {
+                                       refine type {
+                                               mandatory false;
+                                               config:required-identity th:threadfactory;
+                                       }
+                               }
+                       }
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/config/netty-timer-config/src/test/org/opendaylight/controller/config/yang/netty/timer/HashedWheelTimerModuleTest.java b/opendaylight/config/netty-timer-config/src/test/org/opendaylight/controller/config/yang/netty/timer/HashedWheelTimerModuleTest.java
new file mode 100644 (file)
index 0000000..8bc4d95
--- /dev/null
@@ -0,0 +1,131 @@
+package org.opendaylight.controller.config.yang.netty.timer;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.threadpool.impl.NamingThreadFactoryModuleFactory;
+import org.opendaylight.controller.config.yang.threadpool.impl.NamingThreadFactoryModuleMXBean;
+
+public class HashedWheelTimerModuleTest extends AbstractConfigTest {
+
+    private HashedWheelTimerModuleFactory factory;
+    private NamingThreadFactoryModuleFactory threadFactory;
+    private final String instanceName = "hashed-wheel-timer1";
+
+    @Before
+    public void setUp() {
+        factory = new HashedWheelTimerModuleFactory();
+        threadFactory = new NamingThreadFactoryModuleFactory();
+        super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(factory, threadFactory));
+    }
+
+    public void testValidationExceptionTickDuration() throws InstanceAlreadyExistsException {
+        ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        try {
+            createInstance(transaction, instanceName, 0L, 10, true);
+            transaction.validateConfig();
+            Assert.fail();
+        } catch (ValidationException e) {
+            Assert.assertTrue(e.getMessage().contains("TickDuration value must be greater than 0"));
+        }
+    }
+
+    public void testValidationExceptionTicksPerWheel() throws InstanceAlreadyExistsException {
+        ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        try {
+            createInstance(transaction, instanceName, 500L, 0, true);
+            transaction.validateConfig();
+            Assert.fail();
+        } catch (ValidationException e) {
+            Assert.assertTrue(e.getMessage().contains("TicksPerWheel value must be greater than 0"));
+        }
+    }
+
+    @Test
+    public void testCreateBean() throws InstanceAlreadyExistsException, ValidationException,
+            ConflictingVersionException {
+        ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+
+        createInstance(transaction, instanceName, 500L, 10, true);
+        createInstance(transaction, instanceName + 1, null, null, false);
+        createInstance(transaction, instanceName + 2, 500L, 10, false);
+        createInstance(transaction, instanceName + 3, 500L, null, false);
+        transaction.validateConfig();
+        CommitStatus status = transaction.commit();
+
+        assertBeanCount(4, factory.getImplementationName());
+        assertStatus(status, 5, 0, 0);
+    }
+
+    @Test
+    public void testReusingOldInstance() throws InstanceAlreadyExistsException, ConflictingVersionException,
+            ValidationException {
+
+        ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        createInstance(transaction, instanceName, 500L, 10, true);
+
+        transaction.commit();
+
+        transaction = configRegistryClient.createTransaction();
+        assertBeanCount(1, factory.getImplementationName());
+        CommitStatus status = transaction.commit();
+
+        assertBeanCount(1, factory.getImplementationName());
+        assertStatus(status, 0, 0, 2);
+    }
+
+    @Test
+    public void testReconfigure() throws InstanceAlreadyExistsException, ConflictingVersionException,
+            ValidationException, InstanceNotFoundException {
+
+        ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+        createInstance(transaction, instanceName, 500L, 10, true);
+        transaction.commit();
+
+        transaction = configRegistryClient.createTransaction();
+        assertBeanCount(1, factory.getImplementationName());
+        HashedWheelTimerModuleMXBean mxBean = transaction.newMBeanProxy(
+                transaction.lookupConfigBean(factory.getImplementationName(), instanceName),
+                HashedWheelTimerModuleMXBean.class);
+        mxBean.setTicksPerWheel(20);
+        CommitStatus status = transaction.commit();
+
+        assertBeanCount(1, factory.getImplementationName());
+        assertStatus(status, 0, 1, 1);
+    }
+
+    private ObjectName createInstance(ConfigTransactionJMXClient transaction, String instanceName,
+            final Long tickDuration, final Integer ticksPerWheel, final boolean hasThreadfactory)
+            throws InstanceAlreadyExistsException {
+        ObjectName nameCreated = transaction.createModule(factory.getImplementationName(), instanceName);
+        HashedWheelTimerModuleMXBean mxBean = transaction
+                .newMBeanProxy(nameCreated, HashedWheelTimerModuleMXBean.class);
+        mxBean.setTickDuration(tickDuration);
+        mxBean.setTicksPerWheel(ticksPerWheel);
+        if (hasThreadfactory) {
+            mxBean.setThreadFactory(createThreadfactoryInstance(transaction, "thread-factory1", "th"));
+        }
+        return nameCreated;
+    }
+
+    private ObjectName createThreadfactoryInstance(ConfigTransactionJMXClient transaction, String instanceName,
+            final String namePrefix) throws InstanceAlreadyExistsException {
+        ObjectName nameCreated = transaction.createModule(threadFactory.getImplementationName(), instanceName);
+        NamingThreadFactoryModuleMXBean mxBean = transaction.newMBeanProxy(nameCreated,
+                NamingThreadFactoryModuleMXBean.class);
+        mxBean.setNamePrefix(namePrefix);
+        return nameCreated;
+    }
+
+}
index 3f27ff1055882d1352e27f7208d4571919f06e71..22be6f162b23239cf4952ec6283ae0094d3e0e74 100755 (executable)
         <module>yang-test</module>
         <module>logback-config</module>
         <module>threadpool-config-api</module>
+        <module>netty-config-api</module>
         <module>threadpool-config-impl</module>
         <module>netty-threadgroup-config</module>
         <module>netty-event-executor-config</module>
+        <module>netty-timer-config</module>
     </modules>
 
     <profiles>
index cddfb6483099d3e8e4e5127484866d102ef86e2c..5c70ac7958fdd4a9af2803879bb598c2d2bee11f 100644 (file)
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
-        <dependency>
-            <groupId>io.netty</groupId>
-            <artifactId>netty-transport</artifactId>
-        </dependency>
     </dependencies>
 
    <build>
@@ -38,8 +34,6 @@
                   <Import-Package>
                      org.opendaylight.controller.config.api.*,
                      com.google.common.eventbus,
-                     io.netty.channel,
-                     io.netty.util.concurrent
                   </Import-Package>
                   <Export-Package>
                      org.opendaylight.controller.config.threadpool,
index 9c73711c17bf98e0547b87674be96fd6451a64f1..8f3064822be319dfee6fd7c7061c8bee14db268f 100644 (file)
@@ -73,23 +73,4 @@ module threadpool {
                base "threadpool";
         config:java-class "org.opendaylight.controller.config.threadpool.ScheduledThreadPool";
        }
-
-
-    identity netty-threadgroup {
-        description
-            "Configuration wrapper around netty's threadgroup";
-
-        base "config:service-type";
-        config:java-class "io.netty.channel.EventLoopGroup";
-    }
-    
-    identity netty-event-executor {
-        description
-            "Configuration wrapper around netty's event executor";
-
-        base "config:service-type";
-        config:java-class "io.netty.util.concurrent.EventExecutor";
-    }
-
-
 }
index 9b103df8d7f8900e7e474bf0e777ec30805670f4..382dced3e754b08e8f4c593b3fca2ae9bcb11eaf 100644 (file)
@@ -13,7 +13,7 @@
 
     <dependencies>
         <dependency>
-            <groupId>org.opendaylight.controller</groupId>
+            <groupId>${project.groupId}</groupId>
             <artifactId>yang-jmx-generator</artifactId>
         </dependency>
     </dependencies>
@@ -31,7 +31,6 @@
                         </Import-Package>
                         <Export-Package>
                             org.opendaylight.controller.config.yang.store.api,
-                            org.opendaylight.controller.config.yang.store.spi
                         </Export-Package>
                     </instructions>
                 </configuration>
diff --git a/opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreListenerRegistration.java b/opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/api/YangStoreListenerRegistration.java
deleted file mode 100644 (file)
index 8101595..0000000
+++ /dev/null
@@ -1,14 +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.config.yang.store.api;
-
-public interface YangStoreListenerRegistration extends AutoCloseable {
-
-    @Override
-    void close();
-}
index 3ac4b84fdb2e1b203d9508dfb01f16b0db76af08..15619a88cc41b3ac88e59469b3e72563de0e1b5c 100644 (file)
@@ -7,8 +7,6 @@
  */
 package org.opendaylight.controller.config.yang.store.api;
 
-import org.opendaylight.controller.config.yang.store.spi.YangStoreListener;
-
 /**
  * Yang store OSGi service
  */
@@ -21,10 +19,4 @@ public interface YangStoreService {
      */
     YangStoreSnapshot getYangStoreSnapshot() throws YangStoreException;
 
-
-    /**
-     * Allows for registering for change notifications.
-     */
-    YangStoreListenerRegistration registerListener(YangStoreListener listener);
-
 }
diff --git a/opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/spi/YangStoreListener.java b/opendaylight/config/yang-store-api/src/main/java/org/opendaylight/controller/config/yang/store/spi/YangStoreListener.java
deleted file mode 100644 (file)
index 72c3d34..0000000
+++ /dev/null
@@ -1,23 +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.config.yang.store.spi;
-
-import java.net.URL;
-import java.util.Collection;
-
-/**
- * Implementation of this interface gets notified when bundle containing yang files in META-INF/yang has been
- * added or removed. One notification is sent per one bundle.
- */
-public interface YangStoreListener {
-
-    void onAddedYangURL(Collection<URL> url);
-
-    void onRemovedYangURL(Collection<URL> url);
-
-}
index ae59dde26ca461ee844df5486ae50fd6367a76b9..7b79c831f84477253bdcf647303631ebdc8e2f13 100644 (file)
             <artifactId>mockito-configuration</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.google.code.findbugs</groupId>
+            <artifactId>jsr305</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
                 <artifactId>maven-bundle-plugin</artifactId>
                 <configuration>
                     <instructions>
-                        <Bundle-Activator>org.opendaylight.controller.config.yang.store.impl.YangStoreActivator
-                        </Bundle-Activator>
-                        <Private-Package>
-                            org.opendaylight.controller.config.yang.store.impl,
-                        </Private-Package>
-
+                        <Bundle-Activator>org.opendaylight.controller.config.yang.store.impl.YangStoreActivator</Bundle-Activator>
                         <Import-Package>
                             org.opendaylight.controller.config.yang.store.api,
-                            org.opendaylight.controller.config.yang.store.spi,
                             org.opendaylight.controller.config.yangjmxgenerator,
                             com.google.common.base,
                             com.google.common.collect,
@@ -76,7 +74,7 @@
                             org.opendaylight.yangtools.yang.common,
                             org.opendaylight.yangtools.yang.model.api,
                             org.opendaylight.yangtools.sal.binding.generator.spi,
-                            org.opendaylight.yangtools.yang.parser.impl
+                            org.opendaylight.yangtools.yang.parser.impl,
                         </Import-Package>
                         <Export-Package>
                         </Export-Package>
@@ -16,24 +16,27 @@ import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
 import org.opendaylight.controller.config.yang.store.api.YangStoreException;
-import org.opendaylight.controller.config.yang.store.api.YangStoreListenerRegistration;
 import org.opendaylight.controller.config.yang.store.api.YangStoreService;
 import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
-import org.opendaylight.controller.config.yang.store.spi.YangStoreListener;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
-import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.BundleTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.annotation.concurrent.GuardedBy;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Note on consistency:
@@ -41,10 +44,9 @@ import java.util.Set;
  * is not preserved. We thus maintain two maps, one containing consistent snapshot, other inconsistent. The
  * container should eventually send all events and thus making the inconsistent map redundant.
  */
-public class ExtenderYangTrackerCustomizer implements BundleTrackerCustomizer<Object>, YangStoreService {
+public class ExtenderYangTracker extends BundleTracker<Object> implements YangStoreService, AutoCloseable {
 
-    private static final Logger logger = LoggerFactory
-            .getLogger(ExtenderYangTrackerCustomizer.class);
+    private static final Logger logger = LoggerFactory.getLogger(ExtenderYangTracker.class);
 
     private final Multimap<Bundle, URL> consistentBundlesToYangURLs = HashMultimap.create();
 
@@ -55,20 +57,25 @@ public class ExtenderYangTrackerCustomizer implements BundleTrackerCustomizer<Ob
 
     private final YangStoreCache cache = new YangStoreCache();
     private final MbeParser mbeParser;
-    private final List<YangStoreListener> listeners = new ArrayList<>();
 
-    public ExtenderYangTrackerCustomizer() {
-        this(new MbeParser());
 
+    public ExtenderYangTracker(Optional<Pattern> maybeBlacklist, BundleContext bundleContext) {
+        this(new MbeParser(), maybeBlacklist, bundleContext);
     }
 
+    @GuardedBy("this")
+    private Optional<Pattern> maybeBlacklist;
+
     @VisibleForTesting
-    ExtenderYangTrackerCustomizer(MbeParser mbeParser) {
+    ExtenderYangTracker(MbeParser mbeParser, Optional<Pattern> maybeBlacklist, BundleContext bundleContext) {
+        super(bundleContext, BundleEvent.RESOLVED | BundleEvent.UNRESOLVED, null);
         this.mbeParser = mbeParser;
+        this.maybeBlacklist = maybeBlacklist;
+        open();
     }
 
     @Override
-    public Object addingBundle(Bundle bundle, BundleEvent event) {
+    public synchronized Object addingBundle(Bundle bundle, BundleEvent event) {
 
         // Ignore system bundle:
         // system bundle might have config-api on classpath &&
@@ -77,6 +84,14 @@ public class ExtenderYangTrackerCustomizer implements BundleTrackerCustomizer<Ob
         if (bundle.getBundleId() == 0)
             return bundle;
 
+        if (maybeBlacklist.isPresent()) {
+            Matcher m = maybeBlacklist.get().matcher(bundle.getSymbolicName());
+            if (m.matches()) {
+                logger.debug("Ignoring {} because it is in blacklist {}", bundle, maybeBlacklist);
+                return bundle;
+            }
+        }
+
         Enumeration<URL> enumeration = bundle.findEntries("META-INF/yang", "*.yang", false);
         if (enumeration != null && enumeration.hasMoreElements()) {
             synchronized (this) {
@@ -90,8 +105,32 @@ public class ExtenderYangTrackerCustomizer implements BundleTrackerCustomizer<Ob
                 Multimap<Bundle, URL> proposedNewState = HashMultimap.create(consistentBundlesToYangURLs);
                 proposedNewState.putAll(inconsistentBundlesToYangURLs);
                 proposedNewState.putAll(bundle, addedURLs);
-                boolean adding = true;
-                if (tryToUpdateState(addedURLs, proposedNewState, adding) == false) {
+
+                Preconditions.checkArgument(addedURLs.size() > 0, "No change can occur when no URLs are changed");
+                boolean success;
+                String failureReason = null;
+                try(YangStoreSnapshotImpl snapshot = createSnapshot(mbeParser, proposedNewState)) {
+                    updateCache(snapshot);
+                    success = true;
+                } catch(YangStoreException e) {
+                    failureReason = e.toString();
+                    success = false;
+                }
+                if (success){
+                    // consistent state
+                    // merge into
+                    consistentBundlesToYangURLs.clear();
+                    consistentBundlesToYangURLs.putAll(proposedNewState);
+                    inconsistentBundlesToYangURLs.clear();
+
+                    logger.info("Yang store updated to new consistent state containing {} yang files", consistentBundlesToYangURLs.size());
+                    logger.trace("Yang store updated to new consistent state containing {}", consistentBundlesToYangURLs);
+                } else {
+                    // inconsistent state
+                    logger.debug("Yang store is falling back on last consistent state containing {}, inconsistent yang files {}, reason {}",
+                            consistentBundlesToYangURLs, inconsistentBundlesToYangURLs, failureReason);
+                    logger.warn("Yang store is falling back on last consistent state containing {} files, inconsistent yang files size is {}, reason {}",
+                            consistentBundlesToYangURLs.size(), inconsistentBundlesToYangURLs.size(), failureReason);
                     inconsistentBundlesToYangURLs.putAll(bundle, addedURLs);
                 }
             }
@@ -99,30 +138,7 @@ public class ExtenderYangTrackerCustomizer implements BundleTrackerCustomizer<Ob
         return bundle;
     }
 
-    private synchronized boolean tryToUpdateState(Collection<URL> changedURLs, Multimap<Bundle, URL> proposedNewState, boolean adding) {
-        Preconditions.checkArgument(changedURLs.size() > 0, "No change can occur when no URLs are changed");
-        try(YangStoreSnapshot snapshot = createSnapshot(mbeParser, proposedNewState)) {
-            // consistent state
-            // merge into
-            consistentBundlesToYangURLs.clear();
-            consistentBundlesToYangURLs.putAll(proposedNewState);
-            inconsistentBundlesToYangURLs.clear();
-            // update cache
-            updateCache(snapshot);
-            logger.info("Yang store updated to new consistent state");
-            logger.trace("Yang store updated to new consistent state containing {}", consistentBundlesToYangURLs);
-
-            notifyListeners(changedURLs, adding);
-            return true;
-        } catch(YangStoreException e) {
-            // inconsistent state
-            logger.debug("Yang store is falling back on last consistent state containing {}, inconsistent yang files {}, reason {}",
-                    consistentBundlesToYangURLs, inconsistentBundlesToYangURLs, e.toString());
-            return false;
-        }
-    }
-
-    private void updateCache(YangStoreSnapshot snapshot) {
+    private void updateCache(YangStoreSnapshotImpl snapshot) {
         cache.cacheYangStore(consistentBundlesToYangURLs, snapshot);
     }
 
@@ -131,31 +147,6 @@ public class ExtenderYangTrackerCustomizer implements BundleTrackerCustomizer<Ob
         logger.debug("Modified bundle {} {} {}", bundle, event, object);
     }
 
-    /**
-     * Notifiers get only notified when consistent snapshot has changed.
-     */
-    private void notifyListeners(Collection<URL> changedURLs, boolean adding) {
-        Preconditions.checkArgument(changedURLs.size() > 0, "Cannot notify when no URLs changed");
-        if (changedURLs.size() > 0) {
-            RuntimeException potential = new RuntimeException("Error while notifying listeners");
-            for (YangStoreListener listener : listeners) {
-                try {
-                    if (adding) {
-                        listener.onAddedYangURL(changedURLs);
-                    } else {
-                        listener.onRemovedYangURL(changedURLs);
-                    }
-                } catch(RuntimeException e) {
-                    potential.addSuppressed(e);
-                }
-            }
-            if (potential.getSuppressed().length > 0) {
-                throw potential;
-            }
-        }
-    }
-
-
     /**
      * If removing YANG files makes yang store inconsistent, method {@link #getYangStoreSnapshot()}
      * will throw exception. There is no rollback.
@@ -163,31 +154,25 @@ public class ExtenderYangTrackerCustomizer implements BundleTrackerCustomizer<Ob
     @Override
     public synchronized void removedBundle(Bundle bundle, BundleEvent event, Object object) {
         inconsistentBundlesToYangURLs.removeAll(bundle);
-        Collection<URL> consistentURLsToBeRemoved = consistentBundlesToYangURLs.removeAll(bundle);
-
-        if (consistentURLsToBeRemoved.isEmpty()){
-            return; // no change
-        }
-        boolean adding = false;
-        notifyListeners(consistentURLsToBeRemoved, adding);
+        consistentBundlesToYangURLs.removeAll(bundle);
     }
 
     @Override
     public synchronized YangStoreSnapshot getYangStoreSnapshot()
             throws YangStoreException {
-        Optional<YangStoreSnapshot> yangStoreOpt = cache.getCachedYangStore(consistentBundlesToYangURLs);
+        Optional<YangStoreSnapshot> yangStoreOpt = cache.getSnapshotIfPossible(consistentBundlesToYangURLs);
         if (yangStoreOpt.isPresent()) {
             logger.trace("Returning cached yang store {}", yangStoreOpt.get());
             return yangStoreOpt.get();
         }
-        YangStoreSnapshot snapshot = createSnapshot(mbeParser, consistentBundlesToYangURLs);
+        YangStoreSnapshotImpl snapshot = createSnapshot(mbeParser, consistentBundlesToYangURLs);
         updateCache(snapshot);
         return snapshot;
     }
 
-    private static YangStoreSnapshot createSnapshot(MbeParser mbeParser, Multimap<Bundle, URL> multimap) throws YangStoreException {
+    private static YangStoreSnapshotImpl createSnapshot(MbeParser mbeParser, Multimap<Bundle, URL> multimap) throws YangStoreException {
         try {
-            YangStoreSnapshot yangStoreSnapshot = mbeParser.parseYangFiles(fromUrlsToInputStreams(multimap));
+            YangStoreSnapshotImpl yangStoreSnapshot = mbeParser.parseYangFiles(fromUrlsToInputStreams(multimap));
             logger.trace("{} module entries parsed successfully from {} yang files",
                     yangStoreSnapshot.countModuleMXBeanEntries(), multimap.values().size());
             return yangStoreSnapshot;
@@ -213,44 +198,46 @@ public class ExtenderYangTrackerCustomizer implements BundleTrackerCustomizer<Ob
                 });
     }
 
-    @Override
-    public synchronized YangStoreListenerRegistration registerListener(final YangStoreListener listener) {
-        listeners.add(listener);
-        return new YangStoreListenerRegistration() {
-            @Override
-            public void close() {
-                listeners.remove(listener);
-            }
-        };
+    public synchronized void setMaybeBlacklist(Optional<Pattern> maybeBlacklistPattern) {
+        maybeBlacklist = maybeBlacklistPattern;
+        cache.invalidate();
     }
+}
 
-    private static final class YangStoreCache {
-
-        Set<URL> cachedUrls;
-        YangStoreSnapshot cachedYangStoreSnapshot;
-
-        Optional<YangStoreSnapshot> getCachedYangStore(
-                Multimap<Bundle, URL> bundlesToYangURLs) {
-            Set<URL> urls = setFromMultimapValues(bundlesToYangURLs);
-            if (cachedUrls != null && cachedUrls.equals(urls)) {
-                Preconditions.checkState(cachedYangStoreSnapshot != null);
-                return Optional.of(cachedYangStoreSnapshot);
-            }
-            return Optional.absent();
+class YangStoreCache {
+    @GuardedBy("this")
+    private Set<URL> cachedUrls = Collections.emptySet();
+    @GuardedBy("this")
+    private Optional<YangStoreSnapshotImpl> cachedYangStoreSnapshot = Optional.absent();
+
+    synchronized Optional<YangStoreSnapshot> getSnapshotIfPossible(Multimap<Bundle, URL> bundlesToYangURLs) {
+        Set<URL> urls = setFromMultimapValues(bundlesToYangURLs);
+        if (cachedUrls != null && cachedUrls.equals(urls)) {
+            Preconditions.checkState(cachedYangStoreSnapshot.isPresent());
+            YangStoreSnapshot freshSnapshot = new YangStoreSnapshotImpl(cachedYangStoreSnapshot.get());
+            return Optional.of(freshSnapshot);
         }
+        return Optional.absent();
+    }
 
-        private static Set<URL> setFromMultimapValues(
-                Multimap<Bundle, URL> bundlesToYangURLs) {
-            Set<URL> urls = Sets.newHashSet(bundlesToYangURLs.values());
-            Preconditions.checkState(bundlesToYangURLs.size() == urls.size());
-            return urls;
-        }
+    private static Set<URL> setFromMultimapValues(
+            Multimap<Bundle, URL> bundlesToYangURLs) {
+        Set<URL> urls = Sets.newHashSet(bundlesToYangURLs.values());
+        Preconditions.checkState(bundlesToYangURLs.size() == urls.size());
+        return urls;
+    }
 
-        void cacheYangStore(Multimap<Bundle, URL> urls,
-                YangStoreSnapshot yangStoreSnapshot) {
-            this.cachedUrls = setFromMultimapValues(urls);
-            this.cachedYangStoreSnapshot = yangStoreSnapshot;
-        }
+    synchronized void cacheYangStore(Multimap<Bundle, URL> urls,
+                        YangStoreSnapshotImpl yangStoreSnapshot) {
+        this.cachedUrls = setFromMultimapValues(urls);
+        this.cachedYangStoreSnapshot = Optional.of(yangStoreSnapshot);
+    }
 
+    synchronized void invalidate() {
+        cachedUrls.clear();
+        if (cachedYangStoreSnapshot.isPresent()){
+            cachedYangStoreSnapshot.get().close();
+            cachedYangStoreSnapshot = Optional.absent();
+        }
     }
 }
index fc895eb51dcb25ba4e381ef3cac7d7b7d49d0479..211da6bfefdc62df10906eff27b30655e90eb790 100644 (file)
@@ -7,19 +7,11 @@
  */
 package org.opendaylight.controller.config.yang.store.impl;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import org.apache.commons.io.IOUtils;
 import org.opendaylight.controller.config.yang.store.api.YangStoreException;
-import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
 import org.opendaylight.controller.config.yangjmxgenerator.PackageTranslator;
 import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
@@ -30,13 +22,19 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
 public class MbeParser {
 
-    public YangStoreSnapshot parseYangFiles(
+    public YangStoreSnapshotImpl parseYangFiles(
             Collection<? extends InputStream> allInput)
             throws YangStoreException {
         YangParserImpl parser = new YangParserImpl();
index a358e5f7c1e0bc7080fd674effe18d24ffc9703d..a25b05ab8aa92225df6d1ec1f7782a70efef640b 100644 (file)
@@ -7,50 +7,40 @@
  */
 package org.opendaylight.controller.config.yang.store.impl;
 
-import java.util.Dictionary;
-import java.util.Hashtable;
-
+import com.google.common.base.Optional;
 import org.opendaylight.controller.config.yang.store.api.YangStoreService;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.util.tracker.BundleTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class YangStoreActivator implements BundleActivator {
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.regex.Pattern;
 
-    private BundleTracker bundleTracker;
-    private ServiceRegistration<YangStoreService> registration;
-    private static final Logger logger = LoggerFactory
-            .getLogger(YangStoreActivator.class);
+public class YangStoreActivator implements BundleActivator {
+    private static final Logger logger = LoggerFactory.getLogger(YangStoreActivator.class);
 
     @Override
     public void start(BundleContext context) throws Exception {
-        ExtenderYangTrackerCustomizer customizerAndService = new ExtenderYangTrackerCustomizer();
-        bundleTracker = new BundleTracker(context, BundleEvent.RESOLVED | BundleEvent.UNRESOLVED, customizerAndService);
-        bundleTracker.open();
-
+        // get blacklist
+        Optional<Pattern> maybeBlacklistPattern = Optional.absent();
+        String blacklist = context.getProperty("yangstore.blacklist");
+        if (blacklist != null) {
+            try {
+                maybeBlacklistPattern = Optional.of(Pattern.compile(blacklist));
+            } catch (RuntimeException e) {
+                logger.error("Cannot parse blacklist regex " + blacklist, e);
+                throw e;
+            }
+        }
+        ExtenderYangTracker extenderYangTracker = new ExtenderYangTracker(maybeBlacklistPattern, context);
         Dictionary<String, ?> properties = new Hashtable<>();
-        registration = context.registerService(YangStoreService.class,
-                customizerAndService, properties);
+        context.registerService(YangStoreService.class, extenderYangTracker, properties);
     }
 
     @Override
     public void stop(BundleContext context) throws Exception {
-        try {
-            bundleTracker.close();
-        } catch (Exception e) {
-            logger.warn("Exception while closing bundleTracker", e);
-        }
-        if (registration != null) {
-            try {
-                registration.unregister();
-            } catch (Exception e) {
-                logger.warn("Exception while unregistring yang store service",
-                        e);
-            }
-        }
+
     }
 }
index d5169eac38b2d97982ef6a9227933662450687c3..7a5ca7debe8c3d4379e1c03812afcabac3ee1390 100644 (file)
@@ -7,32 +7,31 @@
  */
 package org.opendaylight.controller.config.yang.store.impl;
 
-import java.util.Map;
-import java.util.Map.Entry;
-
 import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
 import org.opendaylight.yangtools.yang.model.api.Module;
 
+import java.util.Collections;
+import java.util.Map;
+import java.util.Map.Entry;
+
 public class YangStoreSnapshotImpl implements YangStoreSnapshot {
 
-    private final Map<String /* Namespace from yang file */, Map<String /*
-                                                                         * Name
-                                                                         * of
-                                                                         * module
-                                                                         * entry
-                                                                         * from
-                                                                         * yang
-                                                                         * file
-                                                                         */, ModuleMXBeanEntry>> moduleMXBeanEntryMap;
+    private final Map<String /* Namespace from yang file */,
+            Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> moduleMXBeanEntryMap;
 
     private final Map<String, Entry<Module, String>> moduleMap;
 
     public YangStoreSnapshotImpl(
             Map<String, Map<String, ModuleMXBeanEntry>> moduleMXBeanEntryMap,
             Map<String, Entry<Module, String>> moduleMap) {
-        this.moduleMXBeanEntryMap = moduleMXBeanEntryMap;
-        this.moduleMap = moduleMap;
+        this.moduleMXBeanEntryMap = Collections.unmodifiableMap(moduleMXBeanEntryMap);
+        this.moduleMap = Collections.unmodifiableMap(moduleMap);
+    }
+
+    public YangStoreSnapshotImpl(YangStoreSnapshotImpl yangStoreSnapshot) {
+        this.moduleMXBeanEntryMap = yangStoreSnapshot.moduleMXBeanEntryMap;
+        this.moduleMap = yangStoreSnapshot.moduleMap;
     }
 
     @Override
index 427da1f634cd6c3ac657bf71b6ef4926f3b5b023..c4c523992f271391b33f6220b9f49fc28a4fc4ca 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.controller.config.yang.store.impl;
 
+import com.google.common.base.Optional;
 import com.google.common.collect.Lists;
 import org.junit.Before;
 import org.junit.Test;
@@ -15,14 +16,19 @@ import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.config.yang.store.api.YangStoreException;
 import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
 import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleListener;
 
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
+import java.util.regex.Pattern;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyCollectionOf;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -34,22 +40,26 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
 public class ExtenderYangTrackerCustomizerTest {
 
 
-    private ExtenderYangTrackerCustomizer tested;
+    private ExtenderYangTracker tested;
     @Mock
     private MbeParser parser;
     @Mock
-    private YangStoreSnapshot yangStoreSnapshot;
+    private YangStoreSnapshotImpl yangStoreSnapshot;
+    @Mock
+    private BundleContext bundleContext;
 
     @Before
     public void setUp() throws YangStoreException {
         MockitoAnnotations.initMocks(this);
-
-        tested = new ExtenderYangTrackerCustomizer(parser);
+        doNothing().when(bundleContext).addBundleListener(any(BundleListener.class));
+        doReturn(new Bundle[0]).when(bundleContext).getBundles();
+        tested = new ExtenderYangTracker(parser, Optional.<Pattern>absent(), bundleContext);
         doReturn(yangStoreSnapshot).when(parser).parseYangFiles(
                 anyCollectionOf(InputStream.class));
         doReturn(22).when(yangStoreSnapshot).countModuleMXBeanEntries();
         doReturn("mock yang store").when(yangStoreSnapshot).toString();
         doNothing().when(yangStoreSnapshot).close();
+        doReturn(Collections.emptyMap()).when(yangStoreSnapshot).getModuleMap();
     }
 
     @Test
@@ -73,17 +83,15 @@ public class ExtenderYangTrackerCustomizerTest {
         bundle = getMockedBundle(10, false);
         tested.addingBundle(bundle, null);
 
-        for(int i = 0; i< 10; i++){
+        for(int i = 0; i< 20; i++){
             tested.getYangStoreSnapshot();
         }
 
-        verify(parser, times(5)).parseYangFiles(
-                anyCollectionOf(InputStream.class));
+        verify(parser, times(7)).parseYangFiles(anyCollectionOf(InputStream.class));
 
         returnedStore = tested.getYangStoreSnapshot();
 
         verifyNoMoreInteractions(parser);
-        assertEquals(yangStoreSnapshot, returnedStore);
     }
 
     int bundleCounter = 1;
@@ -106,6 +114,7 @@ public class ExtenderYangTrackerCustomizerTest {
             doReturn(1L).when(mock).getBundleId();
 
         doReturn("mockedBundle").when(mock).toString();
+        doReturn("mockedBundle").when(mock).getSymbolicName();
 
         return mock;
     }
index 844f682b7a6348a2e86cecceb35e010772e5074e..6221682147e86eea464e3f25abb066d0bfa4d3aa 100644 (file)
@@ -7,7 +7,10 @@
  */
 package org.opendaylight.controller.config.yang.store.impl;
 
-import static org.junit.Assert.assertNotNull;
+import org.apache.commons.io.IOUtils;
+import org.opendaylight.controller.config.yang.store.api.YangStoreException;
+import org.opendaylight.controller.config.yang.store.api.YangStoreService;
+import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -15,12 +18,7 @@ import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collection;
 
-import org.apache.commons.io.IOUtils;
-import org.opendaylight.controller.config.yang.store.api.YangStoreException;
-import org.opendaylight.controller.config.yang.store.api.YangStoreListenerRegistration;
-import org.opendaylight.controller.config.yang.store.api.YangStoreService;
-import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
-import org.opendaylight.controller.config.yang.store.spi.YangStoreListener;
+import static org.junit.Assert.assertNotNull;
 
 public class HardcodedYangStoreService implements YangStoreService {
 
@@ -50,9 +48,4 @@ public class HardcodedYangStoreService implements YangStoreService {
         }
         return new MbeParser().parseYangFiles(byteArrayInputStreams);
     }
-
-    @Override
-    public YangStoreListenerRegistration registerListener(YangStoreListener listener){
-        throw new UnsupportedOperationException("Cannot register for changes on this service");
-    }
 }
index 8e22c6ea6ce0380211f1031707b4c56850704337..74a133f9139261d9741183337034b4c9c791b4f5 100644 (file)
             <version>1.2.0</version>
         </dependency>
 
+         <!-- threadpool -->
+          <dependency>
+              <groupId>org.opendaylight.controller</groupId>
+              <artifactId>threadpool-config-api</artifactId>
+              <version>${config.version}</version>
+          </dependency>
+          <dependency>
+              <groupId>org.opendaylight.controller</groupId>
+              <artifactId>netty-config-api</artifactId>
+              <version>${config.version}</version>
+          </dependency>
+          <dependency>
+              <groupId>org.opendaylight.controller</groupId>
+              <artifactId>threadpool-config-impl</artifactId>
+              <version>${config.version}</version>
+          </dependency>
+          <dependency>
+              <groupId>org.opendaylight.controller</groupId>
+              <artifactId>netty-threadgroup-config</artifactId>
+              <version>${config.version}</version>
+          </dependency>
+          <dependency>
+              <groupId>org.opendaylight.controller</groupId>
+              <artifactId>netty-event-executor-config</artifactId>
+              <version>${config.version}</version>
+          </dependency>
+          <dependency>
+              <groupId>org.opendaylight.controller</groupId>
+              <artifactId>netty-timer-config</artifactId>
+              <version>${config.version}</version>
+          </dependency>
+
+
           <!-- toaster example I'm pretty sure we should trim -->
          <dependency>
           <groupId>org.opendaylight.controller.samples</groupId>
index caf9d095914413477ef2888501e0dd53e473559e..dacdd2546ef235fda3453c10d522294b297348a0 100644 (file)
@@ -14,7 +14,7 @@ osgi.bundles=\
     reference\:file\:../lib/jersey-server-1.17.jar@2:start
 
 # Netconf startup configuration
-netconf.tcp.address=127.0.0.1
+netconf.tcp.address=0.0.0.0
 netconf.tcp.port=8383
 
 #netconf.tls.address=127.0.0.1
@@ -23,6 +23,7 @@ netconf.tcp.port=8383
 #netconf.tls.keystore.password=
 
 netconf.config.persister.storageAdapterClass=org.opendaylight.controller.netconf.persist.impl.NoOpStorageAdapter
+yangstore.blacklist=.*controller.model.*
 
 # Set Default start level for framework
 osgi.bundles.defaultStartLevel=4
index c36a79c5d901093ad3bf79d1a4a354ebcf161209..a22ea623975049cd3bd37c8af3103fecce4317e4 100644 (file)
@@ -15,10 +15,9 @@ import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
 
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
-import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 
 /**
@@ -57,7 +56,6 @@ public interface RestconfService extends RestconfServiceLegacy {
     @GET
     public Object getRoot();
 
-
     @GET
     @Path("/modules")
     @Produces({API+JSON,API+XML})
@@ -68,23 +66,20 @@ public interface RestconfService extends RestconfServiceLegacy {
     @Produces({Draft02.MediaTypes.API+JSON,Draft02.MediaTypes.API+XML,API+JSON,API+XML})
     public StructuredData invokeRpc(@PathParam("identifier") String identifier, CompositeNode payload);
     
-    
     @GET
     @Path("/config/{identifier:.+}")
     @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML})
     public StructuredData readConfigurationData(@PathParam("identifier") String identifier);
-
-    
     
     @PUT
     @Path("/config/{identifier:.+}")
     @Produces({API+JSON,API+XML})
-    public RpcResult<TransactionStatus> createConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
+    public Response createConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
 
     @POST
     @Path("/config/{identifier:.+}")
     @Produces({API+JSON,API+XML})
-    public RpcResult<TransactionStatus> updateConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
+    public Response updateConfigurationData(@PathParam("identifier") String identifier, CompositeNode payload);
 
     @GET
     @Path("/operational/{identifier:.+}")
@@ -94,12 +89,11 @@ public interface RestconfService extends RestconfServiceLegacy {
     @PUT
     @Path("/operational/{identifier:.+}")
     @Produces({API+JSON,API+XML})
-    public RpcResult<TransactionStatus> createOperationalData(@PathParam("identifier") String identifier, CompositeNode payload);
+    public Response createOperationalData(@PathParam("identifier") String identifier, CompositeNode payload);
 
     @POST
     @Path("/operational/{identifier:.+}")
     @Produces({API+JSON,API+XML})
-    public RpcResult<TransactionStatus> updateOperationalData(@PathParam("identifier") String identifier, CompositeNode payload);
+    public Response updateOperationalData(@PathParam("identifier") String identifier, CompositeNode payload);
 
-    
 }
index 6683fd1835942f2af4bd23d42dc16220f3bb6872..242e7f3150d4b775ce56d957ae62ba947a080eba 100644 (file)
@@ -8,10 +8,9 @@ import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
 
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.sal.restconf.impl.StructuredData;
-import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 
 public interface RestconfServiceLegacy {
@@ -35,12 +34,12 @@ public interface RestconfServiceLegacy {
     @PUT
     @Path("/datastore/{identifier:.+}")
     @Produces({API+JSON,API+XML})
-    public RpcResult<TransactionStatus> createConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
+    public Response createConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
 
     @Deprecated
     @POST
     @Path("/datastore/{identifier:.+}")
     @Produces({API+JSON,API+XML})
-    public RpcResult<TransactionStatus> updateConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
+    public Response updateConfigurationDataLegacy(@PathParam("identifier") String identifier, CompositeNode payload);
 
 }
index a0acaf156facb47a74f9e672f1514277f78fc4a4..a2ae1c9f7f248e81218f9862e4247376ae47dc75 100644 (file)
@@ -19,12 +19,12 @@ class JsonReader {
 
     public CompositeNodeWrapper read(InputStream entityStream) throws UnsupportedFormatException {
         JsonParser parser = new JsonParser();
-        
+
         JsonElement rootElement = parser.parse(new InputStreamReader(entityStream));
         if (!rootElement.isJsonObject()) {
             throw new UnsupportedFormatException("Root element of Json has to be Object");
         }
-        
+
         Set<Entry<String, JsonElement>> entrySetsOfRootJsonObject = rootElement.getAsJsonObject().entrySet();
         if (entrySetsOfRootJsonObject.size() != 1) {
             throw new UnsupportedFormatException("Json Object should contain one element");
@@ -41,13 +41,15 @@ class JsonReader {
                     if (firstElementInArray.isJsonObject()) {
                         return createStructureWithRoot(firstElementName, firstElementInArray.getAsJsonObject());
                     }
-                    throw new UnsupportedFormatException("Array as the first element in Json Object can have only Object element");
+                    throw new UnsupportedFormatException(
+                            "Array as the first element in Json Object can have only Object element");
                 }
             }
-            throw new UnsupportedFormatException("First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet.");
+            throw new UnsupportedFormatException(
+                    "First element in Json Object has to be \"Object\" or \"Array with one Object element\". Other scenarios are not supported yet.");
         }
     }
-    
+
     private CompositeNodeWrapper createStructureWithRoot(String rootObjectName, JsonObject rootObject) {
         CompositeNodeWrapper firstNode = new CompositeNodeWrapper(getNamespaceFrom(rootObjectName),
                 getLocalNameFrom(rootObjectName));
@@ -56,7 +58,7 @@ class JsonReader {
         }
         return firstNode;
     }
-    
+
     private void addChildToParent(String childName, JsonElement childType, CompositeNodeWrapper parent) {
         if (childType.isJsonObject()) {
             CompositeNodeWrapper child = new CompositeNodeWrapper(getNamespaceFrom(childName),
@@ -66,19 +68,18 @@ class JsonReader {
                 addChildToParent(childOfChild.getKey(), childOfChild.getValue(), child);
             }
         } else if (childType.isJsonArray()) {
-            for (JsonElement childOfChildType : childType.getAsJsonArray()) {
-                addChildToParent(childName, childOfChildType, parent);
+            if (childType.getAsJsonArray().size() == 1 && childType.getAsJsonArray().get(0).isJsonNull()) {
+                parent.addValue(new SimpleNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName), null));
+
+            } else {
+                for (JsonElement childOfChildType : childType.getAsJsonArray()) {
+                    addChildToParent(childName, childOfChildType, parent);
+                }
             }
         } else if (childType.isJsonPrimitive()) {
             JsonPrimitive childPrimitive = childType.getAsJsonPrimitive();
             String value = childPrimitive.getAsString();
-            SimpleNodeWrapper child = null;
-            if (value.equals("[null]")) {
-                child = new SimpleNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName), null);
-            } else {
-                child = new SimpleNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName), value);
-            }
-            parent.addValue(child);
+            parent.addValue(new SimpleNodeWrapper(getNamespaceFrom(childName), getLocalNameFrom(childName), value));
         }
     }
 
index a41a48287df87f887dd37c12b0fd88f4539f7597..d9ac53589fa5415bcba3e59467576fcbb98d069e 100644 (file)
@@ -1,10 +1,12 @@
 package org.opendaylight.controller.sal.restconf.impl
 
 import java.util.List
+import javax.ws.rs.core.Response
 import org.opendaylight.controller.sal.rest.api.RestconfService
 import org.opendaylight.yangtools.yang.data.api.CompositeNode
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus
 
 class RestconfImpl implements RestconfService {
     
@@ -48,13 +50,21 @@ class RestconfImpl implements RestconfService {
     override createConfigurationData(String identifier, CompositeNode payload) {
         val identifierWithSchemaNode = identifier.toInstanceIdentifier
         val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
-        return broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+        val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+        switch status.result {
+            case TransactionStatus.COMMITED: Response.status(Response.Status.OK).build
+            default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
+        }
     }
 
     override updateConfigurationData(String identifier, CompositeNode payload) {
         val identifierWithSchemaNode = identifier.toInstanceIdentifier
         val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
-        return broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+        val status = broker.commitConfigurationDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+        switch status.result {
+            case TransactionStatus.COMMITED: Response.status(Response.Status.NO_CONTENT).build
+            default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
+        }
     }
 
     override invokeRpc(String identifier, CompositeNode payload) {
@@ -88,13 +98,21 @@ class RestconfImpl implements RestconfService {
     override createOperationalData(String identifier, CompositeNode payload) {
         val identifierWithSchemaNode = identifier.toInstanceIdentifier
         val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
-        return broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+        val status = broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+        switch status.result {
+            case TransactionStatus.COMMITED: Response.status(Response.Status.OK).build
+            default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
+        }
     }
     
     override updateOperationalData(String identifier, CompositeNode payload) {
         val identifierWithSchemaNode = identifier.toInstanceIdentifier
         val value = resolveNodeNamespaceBySchema(payload, identifierWithSchemaNode.schemaNode)
-        return broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+        val status = broker.commitOperationalDataPut(identifierWithSchemaNode.instanceIdentifier,value).get();
+        switch status.result {
+            case TransactionStatus.COMMITED: Response.status(Response.Status.NO_CONTENT).build
+            default: Response.status(Response.Status.INTERNAL_SERVER_ERROR).build
+        }
     }
     
     private def CompositeNode resolveNodeNamespaceBySchema(CompositeNode node, DataSchemaNode schema) {
index a32a3479bada1b7a5b63d5c8f39e7c86731dc12f..251b212513c80f617740bb6562f1159f43aa6b24 100644 (file)
@@ -6,30 +6,85 @@ import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 
 public class DummyFuture implements Future<RpcResult<TransactionStatus>> {
+    
+    private final boolean cancel;
+    private final boolean isCancelled;
+    private final boolean isDone;
+    private final RpcResult<TransactionStatus> result;
+    
+    public DummyFuture() {
+        cancel = false;
+        isCancelled = false;
+        isDone = false;
+        result = null;
+    }
+    
+    private DummyFuture(Builder builder) {
+        cancel = builder.cancel;
+        isCancelled = builder.isCancelled;
+        isDone = builder.isDone;
+        result = builder.result;
+    }
+    
+    public static Builder builder() {
+        return new DummyFuture.Builder();
+    }
 
     @Override
     public boolean cancel(boolean mayInterruptIfRunning) {
-        return false;
+        return cancel;
     }
 
     @Override
     public boolean isCancelled() {
-        return false;
+        return isCancelled;
     }
 
     @Override
     public boolean isDone() {
-        return false;
+        return isDone;
     }
 
     @Override
     public RpcResult<TransactionStatus> get() throws InterruptedException, ExecutionException {
-        return null;
+        return result;
     }
 
     @Override
     public RpcResult<TransactionStatus> get(long timeout, TimeUnit unit) throws InterruptedException,
             ExecutionException, TimeoutException {
-        return null;
+        return result;
+    }
+    
+    public static class Builder {
+        
+        private boolean cancel;
+        private boolean isCancelled;
+        private boolean isDone;
+        private RpcResult<TransactionStatus> result;
+
+        public Builder cancel(boolean cancel) {
+            this.cancel = cancel;
+            return this;
+        }
+        
+        public Builder isCancelled(boolean isCancelled) {
+            this.isCancelled = isCancelled;
+            return this;
+        }
+        
+        public Builder isDone(boolean isDone) {
+            this.isDone = isDone;
+            return this;
+        }
+        
+        public Builder rpcResult(RpcResult<TransactionStatus> result) {
+            this.result = result;
+            return this;
+        }
+        
+        public Future<RpcResult<TransactionStatus>> build() {
+            return new DummyFuture(this);
+        }
     }
 }
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyRpcResult.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/DummyRpcResult.java
new file mode 100644 (file)
index 0000000..5ab4f99
--- /dev/null
@@ -0,0 +1,72 @@
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public class DummyRpcResult implements RpcResult<TransactionStatus> {
+    
+    private final boolean isSuccessful;
+    private final TransactionStatus result;
+    private final Collection<RpcError> errors;
+    
+    public DummyRpcResult() {
+        isSuccessful = false;
+        result = null;
+        errors = null;
+    }
+    
+    private DummyRpcResult(Builder builder) {
+        isSuccessful = builder.isSuccessful;
+        result = builder.result;
+        errors = builder.errors;
+    }
+    
+    public static Builder builder() {
+        return new DummyRpcResult.Builder();
+    }
+
+    @Override
+    public boolean isSuccessful() {
+        return isSuccessful;
+    }
+
+    @Override
+    public TransactionStatus getResult() {
+        return result;
+    }
+
+    @Override
+    public Collection<RpcError> getErrors() {
+        return errors;
+    }
+    
+    public static class Builder {
+        private boolean isSuccessful;
+        private TransactionStatus result;
+        private Collection<RpcError> errors;
+        
+        public Builder isSuccessful(boolean isSuccessful) {
+            this.isSuccessful = isSuccessful;
+            return this;
+        }
+        
+        public Builder result(TransactionStatus result) {
+            this.result = result;
+            return this;
+        }
+        
+        public Builder errors(Collection<RpcError> errors) {
+            this.errors = errors;
+            return this;
+        }
+        
+        public RpcResult<TransactionStatus> build() {
+            return new DummyRpcResult(this);
+        }
+        
+    }
+
+}
@@ -25,20 +25,53 @@ import org.opendaylight.yangtools.yang.model.api.*;
 
 import com.google.gson.JsonSyntaxException;
 
-public class FromJsonToCompositeNode {
+public class FromJsonToCompositeNodeTest {
 
-    private static Logger LOG = LoggerFactory.getLogger(FromJsonToCompositeNode.class);
+    private static final Logger LOG = LoggerFactory.getLogger(FromJsonToCompositeNodeTest.class);
 
     @Test
     public void simpleListTest() {
         simpleTest("/json-to-composite-node/simple-list.json", "/json-to-composite-node/simple-list-yang", "lst",
-                "simple:data:types");
+                "simple:list:yang1", "simple-list-yang1");
     }
 
     @Test
     public void simpleContainerTest() {
         simpleTest("/json-to-composite-node/simple-container.json", "/json-to-composite-node/simple-container-yang",
-                "cont", "simple:data:types");
+                "cont", "simple:container:yang", "simple-container-yang");
+    }
+
+    /**
+     * test if for every leaf list item is simple node instance created
+     */
+    @Test
+    public void multipleItemsInLeafList() {
+        CompositeNode compositeNode = compositeContainerFromJson(
+                "/json-to-composite-node/multiple-leaflist-items.json", true);
+        assertNotNull(compositeNode);
+        assertEquals(3, compositeNode.getChildren().size());
+
+        boolean lflst1_1 = false;
+        boolean lflst1_2 = false;
+        boolean lflst1_3 = false;
+
+        for (Node<?> node : compositeNode.getChildren()) {
+            assertEquals("lflst1", node.getNodeType().getLocalName());
+            assertTrue(node instanceof SimpleNode<?>);
+            SimpleNode<?> simpleNode = (SimpleNode<?>) node;
+            if (simpleNode.getValue().equals("45")) {
+                lflst1_1 = true;
+            } else if (simpleNode.getValue().equals("55")) {
+                lflst1_2 = true;
+            } else if (simpleNode.getValue().equals("66")) {
+                lflst1_3 = true;
+            }
+        }
+
+        assertTrue(lflst1_1);
+        assertTrue(lflst1_2);
+        assertTrue(lflst1_3);
+
     }
 
     /**
@@ -56,6 +89,21 @@ public class FromJsonToCompositeNode {
         verityMultipleItemsInList(compositeNode);
     }
 
+    @Test
+    public void nullArrayToCompositeNodeWithNullValueTest() {
+        CompositeNode compositeNode = compositeContainerFromJson("/json-to-composite-node/array-with-null.json", true);
+        assertNotNull(compositeNode);
+        assertEquals("cont", compositeNode.getNodeType().getLocalName());
+
+        assertNotNull(compositeNode.getChildren());
+        assertEquals(1, compositeNode.getChildren().size());
+        Node<?> lfNode = compositeNode.getChildren().iterator().next();
+
+        assertTrue(lfNode instanceof SimpleNode<?>);
+        assertEquals(null, ((SimpleNode<?>) lfNode).getValue());
+
+    }
+
     @Test
     public void incorrectTopLevelElementsTest() {
         Throwable cause1 = null;
@@ -124,13 +172,57 @@ public class FromJsonToCompositeNode {
 
     }
 
-    private void simpleTest(String jsonPath, String yangPath, String topLevelElementName, String namespace) {
+    /**
+     * Tests whether namespace <b>stay unchanged</b> if concrete values are
+     * present in composite or simple node and if the method for update is
+     * called.
+     * 
+     */
+    @Test
+    public void notSupplyNamespaceIfAlreadySupplied() {
+
+        CompositeNode compositeNode = compositeContainerFromJson("/json-to-composite-node/simple-list.json");
+        assertNotNull(compositeNode);
+
+        DataSchemaNode dataSchemaNode1 = null;
+        DataSchemaNode dataSchemaNode2 = null;
+        try {
+            dataSchemaNode1 = TestUtils.obtainSchemaFromYang("/json-to-composite-node/simple-list-yang",
+                    "simple-list-yang1");
+            dataSchemaNode2 = TestUtils.obtainSchemaFromYang("/json-to-composite-node/simple-list-yang",
+                    "simple-list-yang2");
+        } catch (FileNotFoundException e) {
+            LOG.error(e.getMessage());
+            assertTrue(false);
+        }
+        assertNotNull(dataSchemaNode1);
+        assertNotNull(dataSchemaNode2);
+
+        // supplement namespaces according to first data schema -
+        // "simple:data:types1"
+        TestUtils.supplementNamespace(dataSchemaNode1, compositeNode);
+
+        assertTrue(compositeNode instanceof CompositeNodeWrapper);
+        CompositeNode compNode = ((CompositeNodeWrapper) compositeNode).unwrap(null);
+
+        assertEquals("lst", compNode.getNodeType().getLocalName());
+        verifyCompositeNode(compNode, "simple:list:yang1");
+
+        // dataSchemaNode2 should't be taken into account, because compNode
+        // isn't CompositeNodeWrapper
+        TestUtils.supplementNamespace(dataSchemaNode2, compNode);
+        verifyCompositeNode(compNode, "simple:list:yang1");
+
+    }
+
+    private void simpleTest(String jsonPath, String yangPath, String topLevelElementName, String namespace,
+            String moduleName) {
         CompositeNode compositeNode = compositeContainerFromJson(jsonPath);
         assertNotNull(compositeNode);
 
         DataSchemaNode dataSchemaNode = null;
         try {
-            dataSchemaNode = TestUtils.obtainSchemaFromYang(yangPath);
+            dataSchemaNode = TestUtils.obtainSchemaFromYang(yangPath, moduleName);
         } catch (FileNotFoundException e) {
             LOG.error(e.getMessage());
             assertTrue(false);
@@ -234,7 +326,7 @@ public class FromJsonToCompositeNode {
             throws WebApplicationException {
 
         JsonToCompositeNodeProvider jsonToCompositeNodeProvider = JsonToCompositeNodeProvider.INSTANCE;
-        InputStream jsonStream = FromJsonToCompositeNode.class.getResourceAsStream(jsonPath);
+        InputStream jsonStream = FromJsonToCompositeNodeTest.class.getResourceAsStream(jsonPath);
         try {
             CompositeNode compositeNode = jsonToCompositeNodeProvider
                     .readFrom(null, null, null, null, null, jsonStream);
@@ -16,8 +16,8 @@ import org.opendaylight.yangtools.yang.data.api.*;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.slf4j.*;
 
-public class FromXmlToCompositeNode {
-    private static Logger LOG = LoggerFactory.getLogger(FromXmlToCompositeNode.class);
+public class FromXmlToCompositeNodeTest {
+    private static final Logger LOG = LoggerFactory.getLogger(FromXmlToCompositeNodeTest.class);
 
     /**
      * top level element represents container. second level element is list with
@@ -230,7 +230,7 @@ public class FromXmlToCompositeNode {
     private CompositeNode compositeContainerFromXml(String xmlPath, boolean dummyNamespaces) {
         XmlToCompositeNodeProvider xmlToCompositeNodeProvider = XmlToCompositeNodeProvider.INSTANCE;
         try {
-            InputStream xmlStream = FromXmlToCompositeNode.class.getResourceAsStream(xmlPath);
+            InputStream xmlStream = FromXmlToCompositeNodeTest.class.getResourceAsStream(xmlPath);
             CompositeNode compositeNode = xmlToCompositeNodeProvider.readFrom(null, null, null, null, null, xmlStream);
             if (dummyNamespaces) {
                 try {
index 3d06e4a759985f1eb938576b7bac10e9be9b8aae..1d8d7495f9dfabae21bdddb737401032af34724b 100644 (file)
@@ -10,16 +10,22 @@ import java.io.*;
 import java.net.*;
 import java.sql.Date;
 import java.util.*;
+import java.util.concurrent.Future;
 
 import javax.ws.rs.WebApplicationException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.transform.*;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
 
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
 import org.opendaylight.controller.sal.restconf.impl.*;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.*;
 import org.opendaylight.yangtools.yang.data.impl.XmlTreeBuilder;
 import org.opendaylight.yangtools.yang.model.api.*;
@@ -27,6 +33,9 @@ import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 import org.slf4j.*;
 import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import com.google.common.base.Preconditions;
 
 final class TestUtils {
 
@@ -90,8 +99,20 @@ final class TestUtils {
         }
         return (CompositeNode) dataTree;
     }
+    
+    public static Document loadDocumentFrom(InputStream inputStream) {
+        try {
+            DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
+            DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
+            return docBuilder.parse(inputStream);
+        } catch (SAXException | IOException | ParserConfigurationException e) {
+            logger.error("Error during loading Document from XML", e);
+            return null;
+        }
+    }
 
     public static String getDocumentInPrintableForm(Document doc) {
+        Preconditions.checkNotNull(doc);
         try {
             ByteArrayOutputStream out = new ByteArrayOutputStream();
             TransformerFactory tf = TransformerFactory.newInstance();
@@ -272,9 +293,10 @@ final class TestUtils {
         ControllerContext controllerContext = mock(ControllerContext.class);
         BrokerFacade broker = mock(BrokerFacade.class);
 
+        RpcResult<TransactionStatus> rpcResult = DummyRpcResult.builder().result(TransactionStatus.COMMITED).build();
+        Future<RpcResult<TransactionStatus>> future = DummyFuture.builder().rpcResult(rpcResult).build();
         when(controllerContext.toInstanceIdentifier(any(String.class))).thenReturn(instIdAndSchema);
-        when(broker.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(
-                new DummyFuture());
+        when(broker.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(future);
 
         restconf.setControllerContext(controllerContext);
         restconf.setBroker(broker);
index baf226712ffbb255550948c3c5f36160d4c30262..7b63c5fd9422e5bb3abd0060be1ba57dc9643389 100644 (file)
@@ -1,22 +1,19 @@
 package org.opendaylight.controller.sal.restconf.impl.test;
 
-import static org.mockito.Mockito.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.io.FileNotFoundException;
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URLEncoder;
-import java.util.Collection;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
 
@@ -24,11 +21,7 @@ import javax.ws.rs.client.Entity;
 import javax.ws.rs.core.Application;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
 
-import org.glassfish.jersey.client.ClientConfig;
 import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
 import org.glassfish.jersey.test.TestProperties;
@@ -43,15 +36,11 @@ import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
 import org.opendaylight.controller.sal.restconf.impl.MediaTypes;
 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
 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.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
 
 import com.google.common.base.Charsets;
 
@@ -60,15 +49,11 @@ public class XmlProvidersTest extends JerseyTest {
     private static ControllerContext controllerContext;
     private static BrokerFacade brokerFacade;
     private static RestconfImpl restconfImpl;
+    private static final MediaType MEDIA_TYPE = new MediaType("application", "vnd.yang.api+xml");
 
     @BeforeClass
-    public static void init() {
-        Set<Module> allModules = null;
-        try {
-            allModules = TestUtils.loadModules(RestconfImplTest.class.getResource("/full-versions/yangs").getPath());
-        } catch (FileNotFoundException e) {
-            e.printStackTrace();
-        }
+    public static void init() throws FileNotFoundException {
+        Set<Module> allModules = TestUtils.loadModules(RestconfImplTest.class.getResource("/full-versions/yangs").getPath());
         SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
         controllerContext = ControllerContext.getInstance();
         controllerContext.setSchemas(schemaContext);
@@ -87,96 +72,100 @@ public class XmlProvidersTest extends JerseyTest {
     }
 
     @Test
-    public void testStructuredDataToXmlProvider() throws FileNotFoundException {
-        URI uri = null;
-        try {
-            uri = new URI("/datastore/" + URLEncoder.encode("ietf-interfaces:interfaces/interface/eth0", Charsets.US_ASCII.name()).toString());
-        } catch (UnsupportedEncodingException | URISyntaxException e) {
-            e.printStackTrace();
-        }
+    public void testStructuredDataToXmlProvider() throws FileNotFoundException, UnsupportedEncodingException {
+        String uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
         
         InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
         CompositeNode loadedCompositeNode = TestUtils.loadCompositeNode(xmlStream);
         when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(loadedCompositeNode);
         
-        Response response = target(uri.toASCIIString()).request(MediaTypes.API+RestconfService.XML).get();
+        Response response = target(uri).request(MEDIA_TYPE).get();
         assertEquals(200, response.getStatus());
     }
 
     @Test
-    public void testXmlToCompositeNodeProvider() throws ParserConfigurationException, SAXException, IOException {
-        URI uri = null;
-        try {
-            uri = new URI("/config/" + URLEncoder.encode("ietf-interfaces:interfaces/interface/eth0", Charsets.US_ASCII.name()).toString());
-        } catch (UnsupportedEncodingException | URISyntaxException e) {
-            e.printStackTrace();
-        }
-        InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
-        final CompositeNode loadedCompositeNode = TestUtils.loadCompositeNode(xmlStream);
-        when(brokerFacade.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(new Future<RpcResult<TransactionStatus>>() {
-            @Override
-            public boolean cancel(boolean mayInterruptIfRunning) {
-                return false;
-            }
-            @Override
-            public boolean isCancelled() {
-                return false;
-            }
-            @Override
-            public boolean isDone() {
-                return false;
-            }
-            @Override
-            public RpcResult<TransactionStatus> get() throws InterruptedException, ExecutionException {
-                return null;
-            }
-            @Override
-            public RpcResult<TransactionStatus> get(long timeout, TimeUnit unit) throws InterruptedException,
-                    ExecutionException, TimeoutException {
-                return null;
-            }
-        });
+    public void testBadFormatXmlToCompositeNodeProvider() throws UnsupportedEncodingException, URISyntaxException {
+        String uri = createUri("/operations/", "ietf-interfaces:interfaces/interface/eth0");
         
-        DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
-        DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
-        xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
-        Document doc = docBuilder.parse(xmlStream);
+        Response response = target(uri).request(MediaTypes.API + RestconfService.XML).post(
+                Entity.entity("<SimpleNode/>", MEDIA_TYPE));
+        assertEquals(400, response.getStatus());
         
-        Response response = target(uri.toASCIIString()).request(MediaTypes.API+RestconfService.XML).post(Entity.entity(TestUtils.getDocumentInPrintableForm(doc), new MediaType("application","vnd.yang.api+xml")));
-        assertEquals(204, response.getStatus());
+        response = target(uri).request(MediaTypes.API + RestconfService.XML).post(
+                Entity.entity("<SimpleNode>", MEDIA_TYPE));
+        assertEquals(400, response.getStatus());
     }
     
     @Test
-    public void testXmlToCompositeNodeProviderExceptions() {
-        URI uri = null;
-        try {
-            uri = new URI("/operations/" + URLEncoder.encode("ietf-interfaces:interfaces/interface/eth0", Charsets.US_ASCII.name()).toString());
-        } catch (UnsupportedEncodingException | URISyntaxException e) {
-            e.printStackTrace();
-        }
+    public void testXmlToCompositeNode404NotFound() throws UnsupportedEncodingException, URISyntaxException {
+        String uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
         
-        Response response = target(uri.toASCIIString()).request(MediaTypes.API + RestconfService.XML).post(
-                Entity.entity("<SimpleNode/>", new MediaType("application", "vnd.yang.api+xml")));
-        assertEquals(400, response.getStatus());
+        when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(null);
         
-        response = target(uri.toASCIIString()).request(MediaTypes.API + RestconfService.XML).post(
-                Entity.entity("<SimpleNode>", new MediaType("application", "vnd.yang.api+xml")));
-        assertEquals(400, response.getStatus());
+        Response response = target(uri).request(MediaTypes.API+RestconfService.XML).get();
+        assertEquals(404, response.getStatus());
     }
     
     @Test
-    public void testXmlToCompositeNode404NotFound() {
-        URI uri = null;
-        try {
-            uri = new URI("/datastore/" + URLEncoder.encode("ietf-interfaces:interfaces/interface/eth0", Charsets.US_ASCII.name()).toString());
-        } catch (UnsupportedEncodingException | URISyntaxException e) {
-            e.printStackTrace();
-        }
+    public void testRpcResultCommitedToStatusCodes() throws UnsupportedEncodingException {
+        InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
+        String xml = TestUtils.getDocumentInPrintableForm(TestUtils.loadDocumentFrom(xmlStream));
+        Entity<String> entity = Entity.entity(xml, MEDIA_TYPE);
+        RpcResult<TransactionStatus> rpcResult = DummyRpcResult.builder().result(TransactionStatus.COMMITED).build();
+        Future<RpcResult<TransactionStatus>> dummyFuture = DummyFuture.builder().rpcResult(rpcResult).build();
+        when(brokerFacade.commitOperationalDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(dummyFuture);
+        when(brokerFacade.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(dummyFuture);
         
-        when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(null);
+        String uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0");
+        Response response = target(uri).request(MEDIA_TYPE).put(entity);
+        assertEquals(200, response.getStatus());
+        response = target(uri).request(MEDIA_TYPE).post(entity);
+        assertEquals(204, response.getStatus());
         
-        Response response = target(uri.toASCIIString()).request(MediaTypes.API+RestconfService.XML).get();
-        assertEquals(404, response.getStatus());
+        uri = createUri("/operational/", "ietf-interfaces:interfaces/interface/eth0");
+        response = target(uri).request(MEDIA_TYPE).put(entity);
+        assertEquals(200, response.getStatus());
+        response = target(uri).request(MEDIA_TYPE).post(entity);
+        assertEquals(204, response.getStatus());
+        
+        uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
+        response = target(uri).request(MEDIA_TYPE).put(entity);
+        assertEquals(200, response.getStatus());
+        response = target(uri).request(MEDIA_TYPE).post(entity);
+        assertEquals(204, response.getStatus());
+    }
+    
+    @Test
+    public void testRpcResultOtherToStatusCodes() throws UnsupportedEncodingException {
+        InputStream xmlStream = RestconfImplTest.class.getResourceAsStream("/parts/ietf-interfaces_interfaces.xml");
+        String xml = TestUtils.getDocumentInPrintableForm(TestUtils.loadDocumentFrom(xmlStream));
+        Entity<String> entity = Entity.entity(xml, MEDIA_TYPE);
+        RpcResult<TransactionStatus> rpcResult = DummyRpcResult.builder().result(TransactionStatus.FAILED).build();
+        Future<RpcResult<TransactionStatus>> dummyFuture = DummyFuture.builder().rpcResult(rpcResult).build();
+        when(brokerFacade.commitOperationalDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(dummyFuture);
+        when(brokerFacade.commitConfigurationDataPut(any(InstanceIdentifier.class), any(CompositeNode.class))).thenReturn(dummyFuture);
+        
+        String uri = createUri("/config/", "ietf-interfaces:interfaces/interface/eth0");
+        Response response = target(uri).request(MEDIA_TYPE).put(entity);
+        assertEquals(500, response.getStatus());
+        response = target(uri).request(MEDIA_TYPE).post(entity);
+        assertEquals(500, response.getStatus());
+        
+        uri = createUri("/operational/", "ietf-interfaces:interfaces/interface/eth0");
+        response = target(uri).request(MEDIA_TYPE).put(entity);
+        assertEquals(500, response.getStatus());
+        response = target(uri).request(MEDIA_TYPE).post(entity);
+        assertEquals(500, response.getStatus());
+        
+        uri = createUri("/datastore/", "ietf-interfaces:interfaces/interface/eth0");
+        response = target(uri).request(MEDIA_TYPE).put(entity);
+        assertEquals(500, response.getStatus());
+        response = target(uri).request(MEDIA_TYPE).post(entity);
+        assertEquals(500, response.getStatus());
+    }
+    
+    private String createUri(String prefix, String encodedPart) throws UnsupportedEncodingException {
+        return URI.create(prefix + URLEncoder.encode(encodedPart, Charsets.US_ASCII.name()).toString()).toASCIIString();
     }
 
     @Override
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/array-with-null.json b/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/array-with-null.json
new file mode 100644 (file)
index 0000000..a19d948
--- /dev/null
@@ -0,0 +1,5 @@
+{
+       "cont": {
+               "lf":[null]
+       }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/multiple-leaflist-items.json b/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/multiple-leaflist-items.json
new file mode 100644 (file)
index 0000000..b61a8a8
--- /dev/null
@@ -0,0 +1,5 @@
+{
+       "cont": {
+               "lflst1":[45,55,66]
+       }
+}
\ No newline at end of file
index ddd67f7f8074dc03503d9426058a72ac2c9b7ca8..493101ced14f938845488f5ee70ecacb89e047e6 100644 (file)
@@ -1,5 +1,5 @@
-module simple-data-types {
-  namespace "simple:data:types";  
+module simple-container-yang {
+  namespace "simple:container:yang";  
 
   prefix "smpdtp";
   revision 2013-11-12 {    
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/simple-list-yang/simple-list2.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/json-to-composite-node/simple-list-yang/simple-list2.yang
new file mode 100644 (file)
index 0000000..0872a47
--- /dev/null
@@ -0,0 +1,20 @@
+module simple-list-yang2 {
+  namespace "simple:list:yang2";  
+
+  prefix "smplstyg";
+  revision 2013-11-12 {    
+  }
+  
+  list lst {
+       container cont1 {
+       }
+       list lst1 {
+       }
+       leaf-list lflst1 {
+               type string;
+       }
+       leaf lf1 {
+               type string;
+       }
+  }
+}
\ No newline at end of file
index b3483a737a5d66653b0441faba5592909ddd519f..b8113a090313d270c4b1a391777d0d6e2e7d759e 100644 (file)
@@ -41,12 +41,12 @@ public class TransactionProvider implements AutoCloseable {
     @Override
     public synchronized void close() {
         for (ObjectName tx : allOpenedTransactions) {
-            if (isStillOpenTransaction(tx)) {
-                try {
+            try {
+                if (isStillOpenTransaction(tx)) {
                     configRegistryClient.getConfigTransactionClient(tx).abortConfig();
-                } catch (Exception e) {
-                    logger.debug("Ignoring {} while closing transaction {}", e.toString(), tx, e);
                 }
+            } catch (Exception e) {
+                logger.debug("Ignoring exception while closing transaction {}", tx, e);
             }
         }
         allOpenedTransactions.clear();
index a20e00bcffcc114ab9849b821898280bc743b4b2..0d68e25f67071b1505b2ab4a54e836e6cfe78e2d 100644 (file)
@@ -54,9 +54,9 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
     private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class);
 
     private final InetSocketAddress address;
-    private final NetconfClientDispatcher dispatcher;
     private final EventLoopGroup nettyThreadgroup;
 
+    private NetconfClientDispatcher netconfClientDispatcher;
     private NetconfClient netconfClient;
 
     private final Persister persister;
@@ -81,7 +81,6 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
         this.timeout = timeout;
 
         this.nettyThreadgroup = new NioEventLoopGroup();
-        this.dispatcher = new NetconfClientDispatcher(Optional.<SSLContext>absent(), nettyThreadgroup, nettyThreadgroup);
     }
 
     public void init() throws InterruptedException {
@@ -125,11 +124,12 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
         while (true) {
             attempt++;
 
+            netconfClientDispatcher = new NetconfClientDispatcher(Optional.<SSLContext>absent(), nettyThreadgroup, nettyThreadgroup);
             try {
-                netconfClient = new NetconfClient(this.toString(), address, delay, dispatcher);
-                // TODO is this correct ex to catch ?
+                netconfClient = new NetconfClient(this.toString(), address, delay, netconfClientDispatcher);
             } catch (IllegalStateException e) {
                 logger.debug("Netconf {} was not initialized or is not stable, attempt {}", address, attempt, e);
+                netconfClientDispatcher.close();
                 Thread.sleep(delay);
                 continue;
             }
@@ -148,11 +148,7 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
 
             logger.debug("Polling hello from netconf, attempt {}, capabilities {}", attempt, currentCapabilities);
 
-            try {
-                netconfClient.close();
-            } catch (IOException e) {
-                throw new RuntimeException("Error closing temporary client " + netconfClient);
-            }
+            closeClientAndDispatcher(netconfClient, netconfClientDispatcher);
 
             Thread.sleep(delay);
         }
@@ -162,6 +158,25 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
 
     }
 
+    private static void closeClientAndDispatcher(Closeable client, Closeable dispatcher) {
+        Exception fromClient = null;
+        try {
+            client.close();
+        } catch (Exception e) {
+            fromClient = e;
+        } finally {
+            try {
+                dispatcher.close();
+            } catch (Exception e) {
+                if (fromClient != null) {
+                    e.addSuppressed(fromClient);
+                }
+
+                throw new RuntimeException("Error closing temporary client ", e);
+            }
+        }
+    }
+
     private boolean isSubset(Set<String> currentCapabilities, Set<String> expectedCaps) {
         for (String exCap : expectedCaps) {
             if (currentCapabilities.contains(exCap) == false)
@@ -318,10 +333,18 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
             }
         }
 
+        if (netconfClientDispatcher != null) {
+            try {
+                netconfClientDispatcher.close();
+            } catch (Exception e) {
+                logger.warn("Unable to close connection to netconf {}", netconfClientDispatcher, e);
+            }
+        }
+
         try {
             nettyThreadgroup.shutdownGracefully();
         } catch (Exception e) {
-            logger.warn("Unable to close netconf client thread group {}", dispatcher, e);
+            logger.warn("Unable to close netconf client thread group {}", netconfClientDispatcher, e);
         }
 
         // unregister from JMX
index 6fc4da026f38acc3add7538f3c172e2c6ee01a0f..62c2113056afa1611300fce0c363ce293314fb49 100644 (file)
@@ -23,20 +23,27 @@ import org.opendaylight.protocol.framework.AbstractDispatcher;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
 import org.opendaylight.protocol.framework.SessionListener;
 import org.opendaylight.protocol.framework.SessionListenerFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
+import java.io.Closeable;
 import java.net.InetSocketAddress;
 
-public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSession, NetconfClientSessionListener> {
+public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSession, NetconfClientSessionListener> implements Closeable {
+
+    private static final Logger logger = LoggerFactory.getLogger(NetconfClient.class);
 
     private final Optional<SSLContext> maybeContext;
     private final NetconfClientSessionNegotiatorFactory negotatorFactory;
+    private final HashedWheelTimer timer;
 
     public NetconfClientDispatcher(final Optional<SSLContext> maybeContext, EventLoopGroup bossGroup, EventLoopGroup workerGroup) {
         super(bossGroup, workerGroup);
         this.maybeContext = Preconditions.checkNotNull(maybeContext);
-        this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(new HashedWheelTimer());
+        timer = new HashedWheelTimer();
+        this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(timer);
     }
 
     public Future<NetconfClientSession> createClient(InetSocketAddress address,
@@ -83,4 +90,12 @@ public class NetconfClientDispatcher extends AbstractDispatcher<NetconfClientSes
         }
     }
 
+    @Override
+    public void close() {
+        try {
+            timer.stop();
+        } catch (Exception e) {
+            logger.debug("Ignoring exception while closing {}", timer, e);
+        }
+    }
 }
index 1a4888ba93b69081ab0d584a1730c7b74d84ed37..890bbe728804e469f0d2989253e6815ec1b85d06 100644 (file)
@@ -37,6 +37,7 @@ public class NetconfImplActivator implements BundleActivator {
     private DefaultCommitNotificationProducer commitNot;
     private NetconfServerDispatcher dispatch;
     private NioEventLoopGroup eventLoopGroup;
+    private HashedWheelTimer timer;
 
     @Override
     public void start(final BundleContext context) throws Exception {
@@ -50,8 +51,9 @@ public class NetconfImplActivator implements BundleActivator {
         factoriesTracker.open();
 
         SessionIdProvider idProvider = new SessionIdProvider();
+        timer = new HashedWheelTimer();
         NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
-                new HashedWheelTimer(), factoriesListener, idProvider);
+                timer, factoriesListener, idProvider);
 
         commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
 
@@ -88,5 +90,6 @@ public class NetconfImplActivator implements BundleActivator {
 
         commitNot.close();
         eventLoopGroup.shutdownGracefully();
+        timer.stop();
     }
 }
index 403ba3d0fc090ea06e9ce73c4ff4acc2b7fc32c5..5f6e046929b4a5183f3e4cce1932db9f61c538bd 100644 (file)
@@ -134,6 +134,7 @@ public class NetconfITTest extends AbstractConfigTest {
     public void tearDown() throws Exception {
         commitNot.close();
         nettyThreadgroup.shutdownGracefully();
+        clientDispatcher.close();
     }
 
     private void loadMessages() throws IOException, SAXException, ParserConfigurationException {