Merge "BUG 2221 : Add metering to ShardTransaction actor"
authorMoiz Raja <moraja@cisco.com>
Wed, 29 Oct 2014 03:05:09 +0000 (03:05 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 29 Oct 2014 03:05:10 +0000 (03:05 +0000)
112 files changed:
features/base/pom.xml
features/base/src/main/resources/features.xml
features/netconf-connector/pom.xml
features/netconf/pom.xml
features/netconf/src/main/resources/features.xml
opendaylight/commons/opendaylight/pom.xml
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectPromise.java
opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ServerTest.java
opendaylight/config/config-api/src/main/yang/config.yang
opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownModuleFactory.java
opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownServiceImpl.java
opendaylight/distribution/opendaylight/pom.xml
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/InventoryAndReadAdapter.java
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/example/ExampleActor.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehavior.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Candidate.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Follower.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/RaftActorBehavior.java
opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorTest.java
opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehaviorTest.java
opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java
opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java
opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java
opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/NodeToNormalizedNodeBuilder.java [deleted file]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/NodeValueCodec.java [deleted file]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/NormalizedNodeToProtocolBufferNode.java [deleted file]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/NodeIdentifierFactory.java
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NodeTypes.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeInputStreamReader.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeOutputStreamWriter.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeStreamReader.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeStreamWriter.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/PathArgumentTypes.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypes.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/util/EncoderDecoderUtil.java [deleted file]
opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeStreamReaderWriterTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeWriter.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/SampleNormalizedNodeSerializable.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/util/NormalizedNodeXmlConverterTest.java [deleted file]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ClusterWrapper.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ClusterWrapperImpl.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListener.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxy.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/NoOpCohort.java [deleted file]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/NoOpDataChangeListenerRegistration.java [deleted file]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadTransaction.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardTransaction.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/exceptions/LocalShardNotFoundException.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/FindLocalShard.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/FindPrimary.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/ForwardedReadyTransaction.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DataChangeListenerRegistrationProxyTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreIntegrationTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java [deleted file]
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardManagerTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTestKit.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockClusterWrapper.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockDataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/md/sal/dom/broker/spi/rpc/RpcRoutingStrategy.java
opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLAdapter.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/BrokerFacade.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java
opendaylight/md-sal/samples/toaster-provider/src/main/java/org/opendaylight/controller/sample/toaster/provider/OpendaylightToaster.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/InstanceRuntime.java
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java
opendaylight/netconf/netconf-artifacts/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/SubtreeFilter.java
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/SubtreeFilterTest.java
opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/post-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/pre-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITSecureTest.java
opendaylight/netconf/netconf-netty-util/pom.xml
opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandler.java
opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandlerReader.java [moved from opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHanderReader.java with 66% similarity]
opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandlerWriter.java
opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/AsyncSshHandlerTest.java
opendaylight/netconf/netconf-ssh/pom.xml
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java [deleted file]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/RemoteNetconfCommand.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyClientHandler.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyServer.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java [deleted file]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/AuthProviderTracker.java [new file with mode: 0644]
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/NetconfSSHActivator.java
opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java [deleted file]
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/ssh/authentication/SSHServerTest.java
opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlElement.java
opendaylight/netconf/pom.xml
pom.xml
third-party/ganymed/pom.xml [deleted file]
third-party/ganymed/src/main/java/ch/ethz/ssh2/Connection.java [deleted file]
third-party/ganymed/src/main/java/ch/ethz/ssh2/channel/ChannelManager.java [deleted file]
third-party/ganymed/src/main/java/ch/ethz/ssh2/transport/TransportManager.java [deleted file]

index b7ab3ca75774c0c4e8875893adf418c619267b55..cd84eeaf3395df9718dad12e9e9c006d82d39d04 100644 (file)
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>karaf-tomcat-security</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>ganymed</artifactId>
-    </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
index c324f6cea65cc4d9fb9e6883f43ff93d854c03c8..d6802acd0e909615a5009acbfc859bc8924b5404 100644 (file)
@@ -36,7 +36,6 @@
       <bundle>wrap:mvn:io.netty/netty-common/${netty.version}</bundle>
       <bundle>wrap:mvn:io.netty/netty-handler/${netty.version}</bundle>
       <bundle>wrap:mvn:io.netty/netty-codec-http/${netty.version}</bundle>
-      <bundle>mvn:org.opendaylight.controller.thirdparty/ganymed/1.2.0-SNAPSHOT</bundle>
    </feature>
    <feature name="odl-base-jersey" description="Jersey" version="${jersey.version}">
       <feature>odl-base-gemini-web</feature>
@@ -80,6 +79,7 @@
    <feature name="odl-base-eclipselink-persistence" description="EclipseLink Persistence API" version="2.0.4.v201112161009">
       <bundle start="true">mvn:eclipselink/javax.persistence/2.0.4.v201112161009</bundle>
       <bundle start="true">mvn:eclipselink/javax.resource/1.5.0.v200906010428</bundle>
+      <bundle start="true">mvn:org.eclipse.persistence/org.eclipse.persistence.antlr/2.5.0</bundle>
       <bundle start="true">mvn:org.eclipse.persistence/org.eclipse.persistence.moxy/2.5.0</bundle>
       <bundle start="true">mvn:org.eclipse.persistence/org.eclipse.persistence.core/2.5.0</bundle>
    </feature>
index 03d6fed6055d74ba4845a93be457fd3761720aa9..c69ee197be24dfa8db753ff96a99e13d37e186c3 100644 (file)
       Optional TODO: Remove TODO comments.
     -->
     <!-- test to validate features.xml -->
+   <!--FIXME BUG-2195 When running single feature tests for netconf connector, features including ssh proxy server always fail (this behavior does not appear when running karaf distro directly)-->
     <dependency>
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>features-test</artifactId>
index a944bb4dec86f3175ccb2ff6f501d8afb947af81..028c16b02f9058ea7fd1ecb289c133735e3bdee3 100644 (file)
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>netconf-netty-util</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>ganymed</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.apache.sshd</groupId>
       <artifactId>sshd-core</artifactId>
index 444f20865b565d48d1dd2e55d6b3362db051cf76..fb668ae15adb788c1d5c718fb70968cc46f05196 100644 (file)
@@ -57,7 +57,6 @@
     <feature version='${project.version}'>odl-netconf-mapping-api</feature>
     <feature version='${project.version}'>odl-netconf-util</feature>
     <bundle>mvn:org.opendaylight.controller/netconf-netty-util/${project.version}</bundle>
-    <bundle>mvn:org.opendaylight.controller.thirdparty/ganymed/${ganymed.version}</bundle>
     <bundle>mvn:org.apache.sshd/sshd-core/${sshd-core.version}</bundle>
     <bundle>mvn:org.openexi/nagasena/${exi.nagasena.version}</bundle>
     <bundle>mvn:io.netty/netty-codec/${netty.version}</bundle>
index ffb9ef746da4118982b6ed61a715024491a00a72..198d17a79a66931ebb8e19a55d97372c09485b14 100644 (file)
     <forwarding.staticrouting.northbound.version>0.5.0-SNAPSHOT</forwarding.staticrouting.northbound.version>
     <forwardingrulesmanager.implementation.version>0.5.0-SNAPSHOT</forwardingrulesmanager.implementation.version>
     <forwardingrulesmanager.version>0.7.0-SNAPSHOT</forwardingrulesmanager.version>
-    <ganymed.version>1.2.0-SNAPSHOT</ganymed.version>
     <hosttracker.api.version>0.6.0-SNAPSHOT</hosttracker.api.version>
     <hosttracker.implementation.version>0.6.0-SNAPSHOT</hosttracker.implementation.version>
     <hosttracker.northbound.version>0.5.0-SNAPSHOT</hosttracker.northbound.version>
     <sonar.language>java</sonar.language>
     <sonar.jacoco.reportPath>target/code-coverage/jacoco.exec</sonar.jacoco.reportPath>
     <sonar.jacoco.itReportPath>target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPath>
-    <sonar.skippedModules>org.openflow.openflowj,net.sf.jung2,org.opendaylight.controller.protobuff.messages,ch.ethz.ssh2</sonar.skippedModules>
+    <sonar.skippedModules>org.openflow.openflowj,net.sf.jung2,org.opendaylight.controller.protobuff.messages</sonar.skippedModules>
     <sonar.profile>Sonar way with Findbugs</sonar.profile>
     <spifly.version>1.0.0</spifly.version>
     <spring-osgi.version>1.2.1</spring-osgi.version>
 
   <dependencyManagement>
     <dependencies>
+
        <!-- project specific dependencies -->
       <dependency>
-        <groupId>${project.groupId}</groupId>
-        <artifactId>ietf-netconf-monitoring</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>${project.groupId}</groupId>
-        <artifactId>ietf-netconf-monitoring-extension</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>${project.groupId}</groupId>
-        <artifactId>netconf-netty-util</artifactId>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>netconf-artifacts</artifactId>
         <version>${netconf.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
       </dependency>
       <dependency>
         <groupId>org.apache.sshd</groupId>
         <artifactId>config-manager</artifactId>
         <version>${config.version}</version>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>config-netconf-connector</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>config-persister-api</artifactId>
         <artifactId>config-persister-feature-adapter</artifactId>
         <version>${config.version}</version>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>config-persister-impl</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
 
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <version>${dummy-console.version}</version>
       </dependency>
 
-      <!-- Netconf -->
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-api</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-client</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-client</artifactId>
-        <version>${netconf.version}</version>
-        <type>test-jar</type>
-      </dependency>
-
-      <!--Netconf config-->
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-config-dispatcher</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-impl</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-impl</artifactId>
-        <version>${netconf.version}</version>
-        <type>test-jar</type>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-mapping-api</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-monitoring</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-netty-util</artifactId>
-        <version>${netconf.version}</version>
-        <type>test-jar</type>
-      </dependency>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>netconf-auth</artifactId>
-            <version>${netconf.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.opendaylight.controller</groupId>
-            <artifactId>netconf-usermanager</artifactId>
-            <version>${netconf.version}</version>
-        </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-ssh</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-ssh</artifactId>
-        <version>${netconf.version}</version>
-        <type>test-jar</type>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-tcp</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-util</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-util</artifactId>
-        <version>${netconf.version}</version>
-        <type>test-jar</type>
-      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>netty-config-api</artifactId>
         <artifactId>md-sal-config</artifactId>
         <version>${mdsal.version}</version>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-config</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>netconf-connector-config</artifactId>
-        <version>${netconf.version}</version>
-      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>sal-rest-docgen</artifactId>
         <artifactId>com.sun.jersey.jersey-servlet</artifactId>
         <version>${jersey-servlet.version}</version>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller.thirdparty</groupId>
-        <artifactId>ganymed</artifactId>
-        <version>${ganymed.version}</version>
-      </dependency>
+
       <!-- Third parties from opendaylight released -->
       <dependency>
         <groupId>org.opendaylight.controller.thirdparty</groupId>
         <artifactId>org.openflow.openflowj</artifactId>
         <version>1.0.2</version>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>binding-generator-impl</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>binding-data-codec</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>binding-generator-spi</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>binding-generator-util</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>binding-type-provider</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>concepts</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>object-cache-api</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>object-cache-guava</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>restconf-client-api</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>restconf-client-impl</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>util</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-data-composite-node</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-data-codec-gson</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
 
-      <!-- yangtools dependencies -->
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-binding</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-common</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-data-api</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-data-impl</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
+      <!-- yangtools artifacts -->
       <dependency>
         <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-data-util</artifactId>
+        <artifactId>yangtools-artifacts</artifactId>
         <version>${yangtools.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-maven-plugin-spi</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-model-api</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-model-util</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-parser-api</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>yang-parser-impl</artifactId>
-        <version>${yangtools.version}</version>
-      </dependency>
-      <!-- yang model dependencies -->
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-inet-types</artifactId>
-        <version>${ietf-inet-types.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-restconf</artifactId>
-        <version>${ietf-restconf.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-topology</artifactId>
-        <version>${ietf-topology.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-topology-l3-unicast-igp</artifactId>
-        <version>${ietf-topology.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-yang-types</artifactId>
-        <version>${ietf-yang-types.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>ietf-yang-types-20130715</artifactId>
-        <version>2013.07.15.7-SNAPSHOT</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>opendaylight-l2-types</artifactId>
-        <version>${opendaylight-l2-types.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.model</groupId>
-        <artifactId>yang-ext</artifactId>
-        <version>${yang-ext.version}</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.thirdparty</groupId>
-        <artifactId>antlr4-runtime-osgi-nohead</artifactId>
-        <version>4.0</version>
-      </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools.thirdparty</groupId>
-        <artifactId>xtend-lib-osgi</artifactId>
-        <version>${xtend.version}</version>
-      </dependency>
+
       <dependency>
         <groupId>org.openexi</groupId>
         <artifactId>nagasena</artifactId>
         <version>${mdsal.version}</version>
         <scope>test</scope>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.yangtools</groupId>
-        <artifactId>mockito-configuration</artifactId>
-        <version>${yangtools.version}</version>
-        <scope>test</scope>
-      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>features-config</artifactId>
         <type>xml</type>
         <scope>runtime</scope>
       </dependency>
-      <dependency>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>features-netconf</artifactId>
-        <version>${netconf.version}</version>
-        <classifier>features</classifier>
-        <type>xml</type>
-        <scope>runtime</scope>
-      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller</groupId>
         <artifactId>features-config-persister</artifactId>
index ea87afa48dedddfe20ef5430aa60d25ecc594c1a..98a2c2cca16cbc8c085e530af206aa497c9e5d49 100644 (file)
@@ -98,10 +98,8 @@ final class ReconnectPromise<S extends ProtocolSession<?>, L extends SessionList
                 return;
             }
 
-            // Check if initial connection was fully finished. If the session was dropped during negotiation, reconnect will not happen.
-            // Session can be dropped during negotiation on purpose by the client side and would make no sense to initiate reconnect
             if (promise.isInitialConnectFinished() == false) {
-                return;
+                LOG.debug("Connection to {} was dropped during negotiation, reattempting", promise.address);
             }
 
             LOG.debug("Reconnecting after connection to {} was dropped", promise.address);
index 63026e384c498ce12dca810c94886f9a562215d8..fc38888de30af0158d9b4d20fc7e6387d73767eb 100644 (file)
@@ -250,52 +250,6 @@ public class ServerTest {
         assertFalse(session.isSuccess());
     }
 
-    @Test
-    public void testNegotiationFailedNoReconnect() throws Exception {
-        final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
-
-        this.dispatcher = getServerDispatcher(p);
-
-        this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory<SimpleSessionListener>() {
-            @Override
-            public SimpleSessionListener getSessionListener() {
-                return new SimpleSessionListener();
-            }
-        });
-
-        this.server.get();
-
-        this.clientDispatcher = new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
-            @Override
-            public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
-                                                                         final Channel channel, final Promise<SimpleSession> promise) {
-
-                return new SimpleSessionNegotiator(promise, channel) {
-                    @Override
-                    protected void startNegotiation() throws Exception {
-                        negotiationFailed(new IllegalStateException("Negotiation failed"));
-                    }
-                };
-            }
-        }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
-
-        final ReconnectStrategyFactory reconnectStrategyFactory = mock(ReconnectStrategyFactory.class);
-        final ReconnectStrategy reconnectStrategy = getMockedReconnectStrategy();
-        doReturn(reconnectStrategy).when(reconnectStrategyFactory).createReconnectStrategy();
-
-        this.clientDispatcher.createReconnectingClient(this.serverAddress,
-                reconnectStrategyFactory, new SessionListenerFactory<SimpleSessionListener>() {
-                    @Override
-                    public SimpleSessionListener getSessionListener() {
-                        return new SimpleSessionListener();
-                    }
-                });
-
-
-        // Only one strategy should be created for initial connect, no more = no reconnects
-        verify(reconnectStrategyFactory, times(1)).createReconnectStrategy();
-    }
-
     private SimpleDispatcher getClientDispatcher() {
         return new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
             @Override
index 5d6c11fbeead51a0652ce151265ca3e50313f7d0..e46d327ece9aff4e89ecf7ab0088561acb17c5aa 100644 (file)
@@ -140,7 +140,7 @@ module config {
             "Top level container encapsulating configuration of all modules.";
 
         list module {
-            key "name";
+            key "type name";
             leaf name {
                 description "Unique module instance name";
                 type string;
index 4df9b036f1a995a2aba3bd13dc611836fd8d50dd..1994e21a6de9680eb9ac8ad851d8e5fa9893581c 100644 (file)
@@ -5,26 +5,16 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-/**
- * Generated file
-
- * Generated from: yang module name: shutdown-impl  yang module local name: shutdown
- * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
- * Generated at: Wed Dec 18 14:02:06 CET 2013
- *
- * Do not modify this file unless it is present under src/main directory
- */
 package org.opendaylight.controller.config.yang.shutdown.impl;
 
+import java.util.Arrays;
+import java.util.Set;
 import org.opendaylight.controller.config.api.DependencyResolver;
 import org.opendaylight.controller.config.api.DependencyResolverFactory;
 import org.opendaylight.controller.config.api.ModuleIdentifier;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 
-import java.util.Arrays;
-import java.util.Set;
-
 public class ShutdownModuleFactory extends AbstractShutdownModuleFactory {
 
     public ShutdownModule instantiateModule(String instanceName, DependencyResolver dependencyResolver,
index 4abbd3b36f761b71bc521c9a769f091e8afa3b44..7d97fcd9643bf049289770fb4a5d40d001919065 100644 (file)
@@ -8,15 +8,14 @@
 package org.opendaylight.controller.config.yang.shutdown.impl;
 
 import com.google.common.base.Optional;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
 import org.opendaylight.controller.config.shutdown.ShutdownService;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.lang.management.ManagementFactory;
-import java.lang.management.ThreadInfo;
-
 public class ShutdownServiceImpl implements ShutdownService, AutoCloseable {
     private final ShutdownService impl;
     private final ShutdownRuntimeRegistration registration;
@@ -42,7 +41,7 @@ public class ShutdownServiceImpl implements ShutdownService, AutoCloseable {
 }
 
 class Impl implements ShutdownService {
-    private static final Logger logger = LoggerFactory.getLogger(Impl.class);
+    private static final Logger LOG = LoggerFactory.getLogger(Impl.class);
     private final String secret;
     private final Bundle systemBundle;
 
@@ -53,27 +52,27 @@ class Impl implements ShutdownService {
 
     @Override
     public void shutdown(String inputSecret, Long maxWaitTime, Optional<String> reason) {
-        logger.warn("Shutdown issued with secret {} and reason {}", inputSecret, reason);
+        LOG.warn("Shutdown issued with secret {} and reason {}", inputSecret, reason);
         try {
             Thread.sleep(1000); // prevent brute force attack
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
-            logger.warn("Shutdown process interrupted", e);
+            LOG.warn("Shutdown process interrupted", e);
         }
         if (this.secret.equals(inputSecret)) {
-            logger.info("Server is shutting down");
+            LOG.info("Server is shutting down");
 
             // actual work:
             Thread stopSystemBundleThread = new StopSystemBundleThread(systemBundle);
             stopSystemBundleThread.start();
             if (maxWaitTime != null && maxWaitTime > 0) {
                 Thread systemExitThread = new CallSystemExitThread(maxWaitTime);
-                logger.debug("Scheduling {}", systemExitThread);
+                LOG.debug("Scheduling {}", systemExitThread);
                 systemExitThread.start();
             }
             // end
         } else {
-            logger.warn("Unauthorized attempt to shut down server");
+            LOG.warn("Unauthorized attempt to shut down server");
             throw new IllegalArgumentException("Invalid secret");
         }
     }
@@ -81,7 +80,7 @@ class Impl implements ShutdownService {
 }
 
 class StopSystemBundleThread extends Thread {
-    private static final Logger logger = LoggerFactory.getLogger(StopSystemBundleThread.class);
+    private static final Logger LOG = LoggerFactory.getLogger(StopSystemBundleThread.class);
     private final Bundle systemBundle;
 
     StopSystemBundleThread(Bundle systemBundle) {
@@ -94,18 +93,18 @@ class StopSystemBundleThread extends Thread {
         try {
             // wait so that JMX response is received
             Thread.sleep(1000);
-            logger.debug("Stopping system bundle");
+            LOG.debug("Stopping system bundle");
             systemBundle.stop();
         } catch (BundleException e) {
-            logger.warn("Can not stop OSGi server", e);
+            LOG.warn("Can not stop OSGi server", e);
         } catch (InterruptedException e) {
-            logger.warn("Shutdown process interrupted", e);
+            LOG.warn("Shutdown process interrupted", e);
         }
     }
 }
 
 class CallSystemExitThread extends Thread {
-    private static final Logger logger = LoggerFactory.getLogger(CallSystemExitThread.class);
+    private static final Logger LOG = LoggerFactory.getLogger(CallSystemExitThread.class);
     private final long maxWaitTime;
     CallSystemExitThread(long maxWaitTime) {
         super("call-system-exit-daemon");
@@ -128,7 +127,7 @@ class CallSystemExitThread extends Thread {
         try {
             // wait specified time
             Thread.sleep(maxWaitTime);
-            logger.error("Since some threads are still running, server is going to shut down via System.exit(1) !");
+            LOG.error("Since some threads are still running, server is going to shut down via System.exit(1) !");
             // do a thread dump
             ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
             StringBuffer sb = new StringBuffer();
@@ -136,10 +135,10 @@ class CallSystemExitThread extends Thread {
                 sb.append(info);
                 sb.append("\n");
             }
-            logger.warn("Thread dump:{}", sb);
+            LOG.warn("Thread dump:{}", sb);
             System.exit(1);
         } catch (InterruptedException e) {
-            logger.warn("Interrupted, not going to call System.exit(1)");
+            LOG.warn("Interrupted, not going to call System.exit(1)");
         }
     }
 }
index e30ff05bf0fde541c3c55210bed47c5190c1b376..f6ecb44fa172f02fd4cd5f34824325ad30629ef8 100644 (file)
           <artifactId>sample-toaster-provider</artifactId>
           <version>${mdsal.version}</version>
         </dependency>
-        <dependency>
-          <groupId>org.opendaylight.controller.thirdparty</groupId>
-          <artifactId>ganymed</artifactId>
-        </dependency>
         <dependency>
           <groupId>org.apache.sshd</groupId>
           <artifactId>sshd-core</artifactId>
         <dependency>
           <groupId>org.opendaylight.yangtools</groupId>
           <artifactId>binding-generator-api</artifactId>
-          <version>${yangtools.version}</version>
         </dependency>
         <dependency>
           <groupId>org.opendaylight.yangtools</groupId>
         <dependency>
           <groupId>org.opendaylight.yangtools</groupId>
           <artifactId>binding-model-api</artifactId>
-          <version>${yangtools.version}</version>
         </dependency>
         <dependency>
           <groupId>org.opendaylight.yangtools</groupId>
index bbb6673a8e04cf25fa2b43a7fe8ba686b8e36690..560d8a1d3f379f13b7d4dedce3f45077caa6d1ad 100644 (file)
@@ -7,16 +7,10 @@
  */
 package org.opendaylight.controller.sal.compatibility;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.CopyOnWriteArrayList;
+import com.google.common.base.Optional;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.Iterables;
 
 import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader;
 import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
@@ -46,6 +40,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.ta
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.AggregateFlowStatisticsUpdate;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowStatisticsFromFlowTableInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowStatisticsFromFlowTableInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetAllFlowStatisticsFromFlowTableOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.GetFlowStatisticsFromFlowTableInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsListener;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.OpendaylightFlowStatisticsService;
@@ -59,6 +56,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev13
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.statistics.FlowTableStatistics;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.FlowTopologyDiscoveryService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.Link;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev131103.TransactionAware;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev131103.TransactionId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved;
@@ -81,21 +80,36 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.N
 import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsListener;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.OpendaylightPortStatisticsService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableId;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 
 public class InventoryAndReadAdapter implements IPluginInReadService, IPluginInInventoryService, OpendaylightFlowStatisticsListener, OpendaylightFlowTableStatisticsListener, OpendaylightPortStatisticsListener {
     private static final Logger LOG = LoggerFactory.getLogger(InventoryAndReadAdapter.class);
     private static final short OPENFLOWV10_TABLE_ID = 0;
+    private static final int SLEEP_FOR_NOTIFICATIONS_MILLIS = 500;
 
     private final InventoryNotificationProvider inventoryNotificationProvider = new InventoryNotificationProvider();
     private final Map<PathArgument,List<PathArgument>> nodeToNodeConnectorsMap = new ConcurrentHashMap<>();
     private List<IPluginOutInventoryService> inventoryPublisher = new CopyOnWriteArrayList<>();
     private List<IPluginOutReadService> statisticsPublisher = new CopyOnWriteArrayList<>();
+    private Cache<String, TransactionNotificationList<? extends TransactionAware>> txCache;
 
     private OpendaylightFlowTableStatisticsService flowTableStatisticsService;
     private OpendaylightPortStatisticsService nodeConnectorStatisticsService;
@@ -171,6 +185,7 @@ public class InventoryAndReadAdapter implements IPluginInReadService, IPluginInI
     public void startAdapter() {
         inventoryNotificationProvider.setDataProviderService(getDataProviderService());
         inventoryNotificationProvider.setInventoryPublisher(getInventoryPublisher());
+        txCache = CacheBuilder.newBuilder().expireAfterWrite(60L, TimeUnit.SECONDS).maximumSize(10000).build();
         // inventoryNotificationProvider.start();
     }
 
@@ -251,22 +266,97 @@ public class InventoryAndReadAdapter implements IPluginInReadService, IPluginInI
 
     @Override
     public List<FlowOnNode> readAllFlow(final Node node, final boolean cached) {
-        final ArrayList<FlowOnNode> output = new ArrayList<>();
-        final Table table = readOperationalTable(node, OPENFLOWV10_TABLE_ID);
-        if (table != null) {
-            final List<Flow> flows = table.getFlow();
-            LOG.trace("Number of flows installed in table 0 of node {} : {}", node, flows.size());
+        final ArrayList<FlowOnNode> ret= new ArrayList<>();
+        if (cached) {
+            final Table table = readOperationalTable(node, OPENFLOWV10_TABLE_ID);
+            if (table != null) {
+                final List<Flow> flows = table.getFlow();
+                LOG.trace("Number of flows installed in table 0 of node {} : {}", node, flows.size());
+
+                for (final Flow flow : flows) {
+                    final FlowStatisticsData statsFromDataStore = flow.getAugmentation(FlowStatisticsData.class);
+                    if (statsFromDataStore != null) {
+                        final FlowOnNode it = new FlowOnNode(ToSalConversionsUtils.toFlow(flow, node));
+                        ret.add(addFlowStats(it, statsFromDataStore.getFlowStatistics()));
+                    }
+                }
+            }
+        } else {
+            LOG.debug("readAllFlow cached:{}", cached);
+            GetAllFlowStatisticsFromFlowTableInput input =
+                new GetAllFlowStatisticsFromFlowTableInputBuilder()
+                    .setNode(NodeMapping.toNodeRef(node))
+                    .setTableId(new TableId(OPENFLOWV10_TABLE_ID))
+                    .build();
+
+            Future<RpcResult<GetAllFlowStatisticsFromFlowTableOutput>> future =
+                getFlowStatisticsService().getAllFlowStatisticsFromFlowTable(input);
 
-            for (final Flow flow : flows) {
-                final FlowStatisticsData statsFromDataStore = flow.getAugmentation(FlowStatisticsData.class);
-                if (statsFromDataStore != null) {
-                    final FlowOnNode it = new FlowOnNode(ToSalConversionsUtils.toFlow(flow, node));
-                    output.add(addFlowStats(it, statsFromDataStore.getFlowStatistics()));
+            RpcResult<GetAllFlowStatisticsFromFlowTableOutput> result = null;
+            try {
+                // having a blocking call is fine here, as we need to join
+                // the notifications and return the result
+                result = future.get();
+            } catch (Exception e) {
+               LOG.error("Exception in getAllFlowStatisticsFromFlowTable ", e);
+               return ret;
+            }
+
+            GetAllFlowStatisticsFromFlowTableOutput output = result.getResult();
+            if (output == null) {
+                return ret;
+            }
+
+            TransactionId transactionId = output.getTransactionId();
+            String cacheKey = buildCacheKey(transactionId, NodeMapping.toNodeId(node));
+            LOG.info("readAllFlow transactionId:{} cacheKey:{}", transactionId, cacheKey);
+
+            // insert an entry in tempcache, will get updated when notification is received
+            txCache.put(cacheKey, new TransactionNotificationList<FlowsStatisticsUpdate>(
+                transactionId, node.getNodeIDString()));
+
+            TransactionNotificationList<FlowsStatisticsUpdate> txnList =
+                (TransactionNotificationList<FlowsStatisticsUpdate>) txCache.getIfPresent(cacheKey);
+
+            // this loop would not be infinite as the cache will remove an entry
+            // after defined time if not written to
+            while (txnList != null && !txnList.areAllNotificationsGathered()) {
+                LOG.debug("readAllFlow waiting for notification...");
+                waitForNotification();
+                txnList = (TransactionNotificationList<FlowsStatisticsUpdate>) txCache.getIfPresent(cacheKey);
+            }
+
+            if (txnList == null) {
+                return ret;
+            }
+
+            List<FlowsStatisticsUpdate> notifications = txnList.getNotifications();
+            for (FlowsStatisticsUpdate flowsStatisticsUpdate : notifications) {
+                List<FlowAndStatisticsMapList> flowAndStatisticsMapList = flowsStatisticsUpdate.getFlowAndStatisticsMapList();
+                if (flowAndStatisticsMapList != null) {
+                    for (FlowAndStatisticsMapList flowAndStatistics : flowAndStatisticsMapList) {
+                        final FlowOnNode it = new FlowOnNode(ToSalConversionsUtils.toFlow(flowAndStatistics, node));
+                        ret.add(addFlowStats(it, flowAndStatistics));
+                    }
                 }
             }
         }
+        return ret;
+    }
+
+    private String buildCacheKey(final TransactionId id, final NodeId nodeId) {
+        return String.valueOf(id.getValue()) + "-" + nodeId.getValue();
+    }
 
-        return output;
+    private void waitForNotification() {
+        try {
+            // going for a simple sleep approach,as wait-notify on a monitor would require
+            // us to maintain monitors per txn-node combo
+            Thread.sleep(SLEEP_FOR_NOTIFICATIONS_MILLIS);
+            LOG.trace("statCollector is waking up from a wait stat Response sleep");
+        } catch (final InterruptedException e) {
+            LOG.warn("statCollector has been interrupted waiting stat Response sleep", e);
+        }
     }
 
     @Override
@@ -623,6 +713,8 @@ public class InventoryAndReadAdapter implements IPluginInReadService, IPluginInI
         for (final IPluginOutReadService statsPublisher : getStatisticsPublisher()) {
             statsPublisher.nodeFlowStatisticsUpdated(aDNode, adsalFlowsStatistics);
         }
+
+        updateTransactionCache(notification, notification.getId(), !notification.isMoreReplies());
     }
 
     /**
@@ -778,4 +870,48 @@ public class InventoryAndReadAdapter implements IPluginInReadService, IPluginInI
     private List<PathArgument> removeNodeConnectors(final InstanceIdentifier<? extends Object> nodeIdentifier) {
         return this.nodeToNodeConnectorsMap.remove(Iterables.get(nodeIdentifier.getPathArguments(), 1));
     }
+
+    private <T extends TransactionAware> void updateTransactionCache(T notification, NodeId nodeId, boolean lastNotification) {
+
+        String cacheKey = buildCacheKey(notification.getTransactionId(), nodeId);
+        TransactionNotificationList<T> txnList = (TransactionNotificationList<T>) txCache.getIfPresent(cacheKey);
+        final Optional<TransactionNotificationList<T>> optional = Optional.<TransactionNotificationList<T>>fromNullable(txnList);
+        if (optional.isPresent()) {
+            LOG.info("updateTransactionCache cacheKey:{}, lastNotification:{}, txnList-present:{}", cacheKey, lastNotification, optional.isPresent());
+            TransactionNotificationList<T> txn = optional.get();
+            txn.addNotification(notification);
+            txn.setAllNotificationsGathered(lastNotification);
+        }
+    }
+
+    private class TransactionNotificationList<T extends TransactionAware> {
+        private TransactionId id;
+        private String nId;
+        private List<T> notifications;
+        private boolean allNotificationsGathered;
+
+        public TransactionNotificationList(TransactionId id, String nId) {
+            this.nId = nId;
+            this.id = id;
+            notifications = new ArrayList<T>();
+        }
+
+        public void addNotification(T notification) {
+            notifications.add(notification);
+        }
+
+        public void setAllNotificationsGathered(boolean allNotificationsGathered) {
+            this.allNotificationsGathered = allNotificationsGathered;
+        }
+
+        public boolean areAllNotificationsGathered() {
+            return allNotificationsGathered;
+        }
+
+        public List<T> getNotifications() {
+            return notifications;
+        }
+
+    }
+
 }
index bcb2367e7a35685b8a660bf9b7d2a00c9effcb1b..2bc3e603096bc6c7ef75affef7b8214caf239b70 100644 (file)
@@ -10,11 +10,6 @@ package org.opendaylight.controller.sal.compatibility;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
-import java.math.BigInteger;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.regex.Pattern;
 import org.opendaylight.controller.sal.common.util.Arguments;
 import org.opendaylight.controller.sal.core.AdvertisedBandwidth;
 import org.opendaylight.controller.sal.core.Bandwidth;
@@ -65,6 +60,12 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.regex.Pattern;
+
 public final class NodeMapping {
 
     private static final Logger LOG = LoggerFactory
@@ -167,7 +168,7 @@ public final class NodeMapping {
      * @param aDNode
      * @return
      */
-    private static NodeId toNodeId(org.opendaylight.controller.sal.core.Node aDNode) {
+    public static NodeId toNodeId(org.opendaylight.controller.sal.core.Node aDNode) {
         String targetPrefix = null;
         if (NodeIDType.OPENFLOW.equals(aDNode.getType())) {
                 targetPrefix = OPENFLOW_ID_PREFIX;
index 04df7785ad8d27c69e77f9e0cd85a5cbb64208b0..97b912ef74dcb1a78073999a06a8957c63c8eb47 100644 (file)
@@ -100,7 +100,7 @@ public class ExampleActor extends RaftActor {
         try {
             bs = fromObject(state);
         } catch (Exception e) {
-            LOG.error("Exception in creating snapshot", e);
+            LOG.error(e, "Exception in creating snapshot");
         }
         getSelf().tell(new CaptureSnapshotReply(bs), null);
     }
@@ -110,10 +110,10 @@ public class ExampleActor extends RaftActor {
         try {
             state.putAll((HashMap) toObject(snapshot));
         } catch (Exception e) {
-           LOG.error("Exception in applying snapshot", e);
+           LOG.error(e, "Exception in applying snapshot");
         }
         if(LOG.isDebugEnabled()) {
-            LOG.debug("Snapshot applied to state :" + ((HashMap) state).size());
+            LOG.debug("Snapshot applied to state : {}", ((HashMap) state).size());
         }
     }
 
index 64fa7496042466e58bd51cf0a488c265898866da..66a46ef3bde0ca8a8d1fa8fe1056ccb463a594d6 100644 (file)
@@ -29,9 +29,7 @@ import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot;
 import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshotReply;
 import org.opendaylight.controller.cluster.raft.base.messages.Replicate;
 import org.opendaylight.controller.cluster.raft.base.messages.SendHeartBeat;
-import org.opendaylight.controller.cluster.raft.behaviors.Candidate;
 import org.opendaylight.controller.cluster.raft.behaviors.Follower;
-import org.opendaylight.controller.cluster.raft.behaviors.Leader;
 import org.opendaylight.controller.cluster.raft.behaviors.RaftActorBehavior;
 import org.opendaylight.controller.cluster.raft.client.messages.AddRaftPeer;
 import org.opendaylight.controller.cluster.raft.client.messages.FindLeader;
@@ -159,7 +157,9 @@ public abstract class RaftActor extends UntypedPersistentActor {
     }
 
     private void onRecoveredSnapshot(SnapshotOffer offer) {
-        LOG.debug("SnapshotOffer called..");
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("SnapshotOffer called..");
+        }
 
         initRecoveryTimer();
 
@@ -250,7 +250,7 @@ public abstract class RaftActor extends UntypedPersistentActor {
             replicatedLog.lastIndex(), replicatedLog.snapshotIndex,
             replicatedLog.snapshotTerm, replicatedLog.size());
 
-        currentBehavior = switchBehavior(RaftState.Follower);
+        currentBehavior = new Follower(context);
         onStateChanged();
     }
 
@@ -355,14 +355,13 @@ public abstract class RaftActor extends UntypedPersistentActor {
             if (!(message instanceof AppendEntriesMessages.AppendEntries)
                 && !(message instanceof AppendEntriesReply) && !(message instanceof SendHeartBeat)) {
                 if(LOG.isDebugEnabled()) {
-                    LOG.debug("onReceiveCommand: message:" + message.getClass());
+                    LOG.debug("onReceiveCommand: message: {}", message.getClass());
                 }
             }
 
-            RaftState state =
-                currentBehavior.handleMessage(getSender(), message);
             RaftActorBehavior oldBehavior = currentBehavior;
-            currentBehavior = switchBehavior(state);
+            currentBehavior = currentBehavior.handleMessage(getSender(), message);
+
             if(oldBehavior != currentBehavior){
                 onStateChanged();
             }
@@ -569,38 +568,6 @@ public abstract class RaftActor extends UntypedPersistentActor {
 
     protected void onLeaderChanged(String oldLeader, String newLeader){};
 
-    private RaftActorBehavior switchBehavior(RaftState state) {
-        if (currentBehavior != null) {
-            if (currentBehavior.state() == state) {
-                return currentBehavior;
-            }
-            LOG.info("Switching from state " + currentBehavior.state() + " to "
-                + state);
-
-            try {
-                currentBehavior.close();
-            } catch (Exception e) {
-                LOG.error(e,
-                    "Failed to close behavior : " + currentBehavior.state());
-            }
-
-        } else {
-            LOG.info("Switching behavior to " + state);
-        }
-        RaftActorBehavior behavior = null;
-        if (state == RaftState.Candidate) {
-            behavior = new Candidate(context);
-        } else if (state == RaftState.Follower) {
-            behavior = new Follower(context);
-        } else {
-            behavior = new Leader(context);
-        }
-
-
-
-        return behavior;
-    }
-
     private void trimPersistentData(long sequenceNumber) {
         // Trim akka snapshots
         // FIXME : Not sure how exactly the SnapshotSelectionCriteria is applied
@@ -622,8 +589,8 @@ public abstract class RaftActor extends UntypedPersistentActor {
         }
         String peerAddress = context.getPeerAddress(leaderId);
         if(LOG.isDebugEnabled()) {
-            LOG.debug("getLeaderAddress leaderId = " + leaderId + " peerAddress = "
-                + peerAddress);
+            LOG.debug("getLeaderAddress leaderId = {} peerAddress = {}",
+                    leaderId, peerAddress);
         }
 
         return peerAddress;
@@ -697,8 +664,11 @@ public abstract class RaftActor extends UntypedPersistentActor {
         public void appendAndPersist(final ActorRef clientActor,
             final String identifier,
             final ReplicatedLogEntry replicatedLogEntry) {
-            context.getLogger().debug(
-                "Append log entry and persist {} ", replicatedLogEntry);
+
+            if(LOG.isDebugEnabled()) {
+                LOG.debug("Append log entry and persist {} ", replicatedLogEntry);
+            }
+
             // FIXME : By adding the replicated log entry to the in-memory journal we are not truly ensuring durability of the logs
             journal.add(replicatedLogEntry);
 
index b1560a5648b283e028ae0dc96e4267d92c6f438f..eed74bba82fbf8a4f8e4c1b5431887451c49d59d 100644 (file)
@@ -10,9 +10,9 @@ package org.opendaylight.controller.cluster.raft.behaviors;
 
 import akka.actor.ActorRef;
 import akka.actor.Cancellable;
+import akka.event.LoggingAdapter;
 import org.opendaylight.controller.cluster.raft.ClientRequestTracker;
 import org.opendaylight.controller.cluster.raft.RaftActorContext;
-import org.opendaylight.controller.cluster.raft.RaftState;
 import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
 import org.opendaylight.controller.cluster.raft.SerializationUtils;
 import org.opendaylight.controller.cluster.raft.base.messages.ApplyLogEntries;
@@ -44,6 +44,11 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior {
      */
     protected final RaftActorContext context;
 
+    /**
+     *
+     */
+    protected final LoggingAdapter LOG;
+
     /**
      *
      */
@@ -57,6 +62,7 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior {
 
     protected AbstractRaftActorBehavior(RaftActorContext context) {
         this.context = context;
+        this.LOG = context.getLogger();
     }
 
     /**
@@ -71,7 +77,7 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior {
      * @param appendEntries  The AppendEntries message
      * @return
      */
-    protected abstract RaftState handleAppendEntries(ActorRef sender,
+    protected abstract RaftActorBehavior handleAppendEntries(ActorRef sender,
         AppendEntries appendEntries);
 
 
@@ -83,19 +89,21 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior {
      * @param appendEntries
      * @return
      */
-    protected RaftState appendEntries(ActorRef sender,
+    protected RaftActorBehavior appendEntries(ActorRef sender,
         AppendEntries appendEntries) {
 
         // 1. Reply false if term < currentTerm (§5.1)
         if (appendEntries.getTerm() < currentTerm()) {
-            context.getLogger().debug(
-                "Cannot append entries because sender term " + appendEntries
-                    .getTerm() + " is less than " + currentTerm());
+            if(LOG.isDebugEnabled()) {
+                LOG.debug("Cannot append entries because sender term {} is less than {}",
+                        appendEntries.getTerm(), currentTerm());
+            }
+
             sender.tell(
                 new AppendEntriesReply(context.getId(), currentTerm(), false,
                     lastIndex(), lastTerm()), actor()
             );
-            return state();
+            return this;
         }
 
 
@@ -114,7 +122,7 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior {
      * @param appendEntriesReply The AppendEntriesReply message
      * @return
      */
-    protected abstract RaftState handleAppendEntriesReply(ActorRef sender,
+    protected abstract RaftActorBehavior handleAppendEntriesReply(ActorRef sender,
         AppendEntriesReply appendEntriesReply);
 
     /**
@@ -125,11 +133,12 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior {
      * @param requestVote
      * @return
      */
-    protected RaftState requestVote(ActorRef sender,
+    protected RaftActorBehavior requestVote(ActorRef sender,
         RequestVote requestVote) {
 
-
-        context.getLogger().debug(requestVote.toString());
+        if(LOG.isDebugEnabled()) {
+            LOG.debug(requestVote.toString());
+        }
 
         boolean grantVote = false;
 
@@ -167,7 +176,7 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior {
 
         sender.tell(new RequestVoteReply(currentTerm(), grantVote), actor());
 
-        return state();
+        return this;
     }
 
     /**
@@ -182,7 +191,7 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior {
      * @param requestVoteReply The RequestVoteReply message
      * @return
      */
-    protected abstract RaftState handleRequestVoteReply(ActorRef sender,
+    protected abstract RaftActorBehavior handleRequestVoteReply(ActorRef sender,
         RequestVoteReply requestVoteReply);
 
     /**
@@ -341,12 +350,14 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior {
             } else {
                 //if one index is not present in the log, no point in looping
                 // around as the rest wont be present either
-                context.getLogger().warning(
-                    "Missing index {} from log. Cannot apply state. Ignoring {} to {}", i, i, index );
+                LOG.warning(
+                        "Missing index {} from log. Cannot apply state. Ignoring {} to {}", i, i, index);
                 break;
             }
         }
-        context.getLogger().debug("Setting last applied to {}", newLastApplied);
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("Setting last applied to {}", newLastApplied);
+        }
         context.setLastApplied(newLastApplied);
 
         // send a message to persist a ApplyLogEntries marker message into akka's persistent journal
@@ -361,7 +372,7 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior {
     }
 
     @Override
-    public RaftState handleMessage(ActorRef sender, Object message) {
+    public RaftActorBehavior handleMessage(ActorRef sender, Object message) {
         if (message instanceof AppendEntries) {
             return appendEntries(sender, (AppendEntries) message);
         } else if (message instanceof AppendEntriesReply) {
@@ -371,10 +382,21 @@ public abstract class AbstractRaftActorBehavior implements RaftActorBehavior {
         } else if (message instanceof RequestVoteReply) {
             return handleRequestVoteReply(sender, (RequestVoteReply) message);
         }
-        return state();
+        return this;
     }
 
     @Override public String getLeaderId() {
         return leaderId;
     }
+
+    protected RaftActorBehavior switchBehavior(RaftActorBehavior behavior) {
+        LOG.info("Switching from behavior {} to {}", this.state(), behavior.state());
+        try {
+            close();
+        } catch (Exception e) {
+            LOG.error(e, "Failed to close behavior : {}", this.state());
+        }
+
+        return behavior;
+    }
 }
index bb1927ef231949bd320d0cf060d6a9a8018829bb..4a3e2c5d664406844edaddee6308abf112b0f79c 100644 (file)
@@ -52,7 +52,9 @@ public class Candidate extends AbstractRaftActorBehavior {
 
         peers = context.getPeerAddresses().keySet();
 
-        context.getLogger().debug("Election:Candidate has following peers:"+ peers);
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("Election:Candidate has following peers: {}", peers);
+        }
 
         if(peers.size() > 0) {
             // Votes are required from a majority of the peers including self.
@@ -78,21 +80,23 @@ public class Candidate extends AbstractRaftActorBehavior {
         scheduleElection(electionDuration());
     }
 
-    @Override protected RaftState handleAppendEntries(ActorRef sender,
+    @Override protected RaftActorBehavior handleAppendEntries(ActorRef sender,
         AppendEntries appendEntries) {
 
-        context.getLogger().debug(appendEntries.toString());
+        if(LOG.isDebugEnabled()) {
+            LOG.debug(appendEntries.toString());
+        }
 
-        return state();
+        return this;
     }
 
-    @Override protected RaftState handleAppendEntriesReply(ActorRef sender,
+    @Override protected RaftActorBehavior handleAppendEntriesReply(ActorRef sender,
         AppendEntriesReply appendEntriesReply) {
 
-        return state();
+        return this;
     }
 
-    @Override protected RaftState handleRequestVoteReply(ActorRef sender,
+    @Override protected RaftActorBehavior handleRequestVoteReply(ActorRef sender,
         RequestVoteReply requestVoteReply) {
 
         if (requestVoteReply.isVoteGranted()) {
@@ -100,10 +104,10 @@ public class Candidate extends AbstractRaftActorBehavior {
         }
 
         if (voteCount >= votesRequired) {
-            return RaftState.Leader;
+            return switchBehavior(new Leader(context));
         }
 
-        return state();
+        return this;
     }
 
     @Override public RaftState state() {
@@ -111,7 +115,7 @@ public class Candidate extends AbstractRaftActorBehavior {
     }
 
     @Override
-    public RaftState handleMessage(ActorRef sender, Object originalMessage) {
+    public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) {
 
         Object message = fromSerializableMessage(originalMessage);
 
@@ -119,14 +123,17 @@ public class Candidate extends AbstractRaftActorBehavior {
 
             RaftRPC rpc = (RaftRPC) message;
 
-            context.getLogger().debug("RaftRPC message received {} my term is {}", rpc.toString(), context.getTermInformation().getCurrentTerm());
+            if(LOG.isDebugEnabled()) {
+                LOG.debug("RaftRPC message received {} my term is {}", rpc, context.getTermInformation().getCurrentTerm());
+            }
 
             // If RPC request or response contains term T > currentTerm:
             // set currentTerm = T, convert to follower (§5.1)
             // This applies to all RPC messages and responses
             if (rpc.getTerm() > context.getTermInformation().getCurrentTerm()) {
                 context.getTermInformation().updateAndPersist(rpc.getTerm(), null);
-                return RaftState.Follower;
+
+                return switchBehavior(new Follower(context));
             }
         }
 
@@ -137,11 +144,12 @@ public class Candidate extends AbstractRaftActorBehavior {
                 // ourselves the leader. This gives enough time for a leader
                 // who we do not know about (as a peer)
                 // to send a message to the candidate
-                return RaftState.Leader;
+
+                return switchBehavior(new Leader(context));
             }
             startNewTerm();
             scheduleElection(electionDuration());
-            return state();
+            return this;
         }
 
         return super.handleMessage(sender, message);
@@ -159,7 +167,9 @@ public class Candidate extends AbstractRaftActorBehavior {
         context.getTermInformation().updateAndPersist(currentTerm + 1,
             context.getId());
 
-        context.getLogger().debug("Starting new term " + (currentTerm + 1));
+        if(LOG.isDebugEnabled()) {
+            LOG.debug("Starting new term {}", (currentTerm + 1));
+        }
 
         // Request for a vote
         // TODO: Retry request for vote if replies do not arrive in a reasonable
index 1cfdf9dba8b912a5bc23f78b85c447621298d908..7ada8b31c54f75c9c8f8dc9ce3456260d77af80a 100644 (file)
@@ -9,7 +9,6 @@
 package org.opendaylight.controller.cluster.raft.behaviors;
 
 import akka.actor.ActorRef;
-import akka.event.LoggingAdapter;
 import com.google.protobuf.ByteString;
 import org.opendaylight.controller.cluster.raft.RaftActorContext;
 import org.opendaylight.controller.cluster.raft.RaftState;
@@ -39,17 +38,13 @@ import java.util.ArrayList;
 public class Follower extends AbstractRaftActorBehavior {
     private ByteString snapshotChunksCollected = ByteString.EMPTY;
 
-    private final LoggingAdapter LOG;
-
     public Follower(RaftActorContext context) {
         super(context);
 
-        LOG = context.getLogger();
-
         scheduleElection(electionDuration());
     }
 
-    @Override protected RaftState handleAppendEntries(ActorRef sender,
+    @Override protected RaftActorBehavior handleAppendEntries(ActorRef sender,
         AppendEntries appendEntries) {
 
         if(appendEntries.getEntries() != null && appendEntries.getEntries().size() > 0) {
@@ -133,15 +128,14 @@ public class Follower extends AbstractRaftActorBehavior {
                 new AppendEntriesReply(context.getId(), currentTerm(), false,
                     lastIndex(), lastTerm()), actor()
             );
-            return state();
+            return this;
         }
 
         if (appendEntries.getEntries() != null
             && appendEntries.getEntries().size() > 0) {
             if(LOG.isDebugEnabled()) {
                 LOG.debug(
-                    "Number of entries to be appended = " + appendEntries
-                        .getEntries().size()
+                    "Number of entries to be appended = {}", appendEntries.getEntries().size()
                 );
             }
 
@@ -168,8 +162,7 @@ public class Follower extends AbstractRaftActorBehavior {
 
                     if(LOG.isDebugEnabled()) {
                         LOG.debug(
-                            "Removing entries from log starting at "
-                                + matchEntry.getIndex()
+                            "Removing entries from log starting at {}", matchEntry.getIndex()
                         );
                     }
 
@@ -181,9 +174,7 @@ public class Follower extends AbstractRaftActorBehavior {
             }
 
             if(LOG.isDebugEnabled()) {
-                context.getLogger().debug(
-                    "After cleanup entries to be added from = " + (addEntriesFrom
-                        + lastIndex())
+                LOG.debug("After cleanup entries to be added from = {}", (addEntriesFrom + lastIndex())
                 );
             }
 
@@ -191,17 +182,14 @@ public class Follower extends AbstractRaftActorBehavior {
             for (int i = addEntriesFrom;
                  i < appendEntries.getEntries().size(); i++) {
 
-                context.getLogger().info(
-                    "Append entry to log " + appendEntries.getEntries().get(
-                        i).getData()
-                        .toString()
-                );
-                context.getReplicatedLog()
-                    .appendAndPersist(appendEntries.getEntries().get(i));
+                if(LOG.isDebugEnabled()) {
+                    LOG.debug("Append entry to log {}", appendEntries.getEntries().get(i).getData());
+                }
+                context.getReplicatedLog().appendAndPersist(appendEntries.getEntries().get(i));
             }
 
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Log size is now " + context.getReplicatedLog().size());
+                LOG.debug("Log size is now {}", context.getReplicatedLog().size());
             }
         }
 
@@ -216,7 +204,7 @@ public class Follower extends AbstractRaftActorBehavior {
 
         if (prevCommitIndex != context.getCommitIndex()) {
             if(LOG.isDebugEnabled()) {
-                LOG.debug("Commit index set to " + context.getCommitIndex());
+                LOG.debug("Commit index set to {}", context.getCommitIndex());
             }
         }
 
@@ -239,24 +227,24 @@ public class Follower extends AbstractRaftActorBehavior {
         sender.tell(new AppendEntriesReply(context.getId(), currentTerm(), true,
             lastIndex(), lastTerm()), actor());
 
-        return state();
+        return this;
     }
 
-    @Override protected RaftState handleAppendEntriesReply(ActorRef sender,
+    @Override protected RaftActorBehavior handleAppendEntriesReply(ActorRef sender,
         AppendEntriesReply appendEntriesReply) {
-        return state();
+        return this;
     }
 
-    @Override protected RaftState handleRequestVoteReply(ActorRef sender,
+    @Override protected RaftActorBehavior handleRequestVoteReply(ActorRef sender,
         RequestVoteReply requestVoteReply) {
-        return state();
+        return this;
     }
 
     @Override public RaftState state() {
         return RaftState.Follower;
     }
 
-    @Override public RaftState handleMessage(ActorRef sender, Object originalMessage) {
+    @Override public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) {
 
         Object message = fromSerializableMessage(originalMessage);
 
@@ -271,7 +259,7 @@ public class Follower extends AbstractRaftActorBehavior {
         }
 
         if (message instanceof ElectionTimeout) {
-            return RaftState.Candidate;
+            return switchBehavior(new Candidate(context));
 
         } else if (message instanceof InstallSnapshot) {
             InstallSnapshot installSnapshot = (InstallSnapshot) message;
@@ -297,8 +285,10 @@ public class Follower extends AbstractRaftActorBehavior {
                 // this is the last chunk, create a snapshot object and apply
 
                 snapshotChunksCollected = snapshotChunksCollected.concat(installSnapshot.getData());
-                context.getLogger().debug("Last chunk received: snapshotChunksCollected.size:{}",
-                    snapshotChunksCollected.size());
+                if(LOG.isDebugEnabled()) {
+                    LOG.debug("Last chunk received: snapshotChunksCollected.size:{}",
+                            snapshotChunksCollected.size());
+                }
 
                 Snapshot snapshot = Snapshot.create(snapshotChunksCollected.toByteArray(),
                     new ArrayList<ReplicatedLogEntry>(),
@@ -324,7 +314,7 @@ public class Follower extends AbstractRaftActorBehavior {
                 true), actor());
 
         } catch (Exception e) {
-            context.getLogger().error("Exception in InstallSnapshot of follower", e);
+            LOG.error(e, "Exception in InstallSnapshot of follower:");
             //send reply with success as false. The chunk will be sent again on failure
             sender.tell(new InstallSnapshotReply(currentTerm(), context.getId(),
                 installSnapshot.getChunkIndex(), false), actor());
index ff8a2256d3cb05a7f29a99f6741aa4948935145b..9edba85865e87a0351ca14154f4665fe935f75d2 100644 (file)
@@ -11,7 +11,6 @@ package org.opendaylight.controller.cluster.raft.behaviors;
 import akka.actor.ActorRef;
 import akka.actor.ActorSelection;
 import akka.actor.Cancellable;
-import akka.event.LoggingAdapter;
 import com.google.common.base.Preconditions;
 import com.google.protobuf.ByteString;
 import org.opendaylight.controller.cluster.raft.ClientRequestTracker;
@@ -81,13 +80,9 @@ public class Leader extends AbstractRaftActorBehavior {
 
     private final int minReplicationCount;
 
-    private final LoggingAdapter LOG;
-
     public Leader(RaftActorContext context) {
         super(context);
 
-        LOG = context.getLogger();
-
         followers = context.getPeerAddresses().keySet();
 
         for (String followerId : followers) {
@@ -100,7 +95,7 @@ public class Leader extends AbstractRaftActorBehavior {
         }
 
         if(LOG.isDebugEnabled()) {
-            LOG.debug("Election:Leader has following peers:" + followers);
+            LOG.debug("Election:Leader has following peers: {}", followers);
         }
 
         if (followers.size() > 0) {
@@ -123,17 +118,17 @@ public class Leader extends AbstractRaftActorBehavior {
 
     }
 
-    @Override protected RaftState handleAppendEntries(ActorRef sender,
+    @Override protected RaftActorBehavior handleAppendEntries(ActorRef sender,
         AppendEntries appendEntries) {
 
         if(LOG.isDebugEnabled()) {
             LOG.debug(appendEntries.toString());
         }
 
-        return state();
+        return this;
     }
 
-    @Override protected RaftState handleAppendEntriesReply(ActorRef sender,
+    @Override protected RaftActorBehavior handleAppendEntriesReply(ActorRef sender,
         AppendEntriesReply appendEntriesReply) {
 
         if(! appendEntriesReply.isSuccess()) {
@@ -149,7 +144,7 @@ public class Leader extends AbstractRaftActorBehavior {
 
         if(followerLogInformation == null){
             LOG.error("Unknown follower {}", followerId);
-            return state();
+            return this;
         }
 
         if (appendEntriesReply.isSuccess()) {
@@ -199,7 +194,7 @@ public class Leader extends AbstractRaftActorBehavior {
             applyLogToStateMachine(context.getCommitIndex());
         }
 
-        return state();
+        return this;
     }
 
     protected ClientRequestTracker removeClientRequestTracker(long logIndex) {
@@ -222,16 +217,16 @@ public class Leader extends AbstractRaftActorBehavior {
         return null;
     }
 
-    @Override protected RaftState handleRequestVoteReply(ActorRef sender,
+    @Override protected RaftActorBehavior handleRequestVoteReply(ActorRef sender,
         RequestVoteReply requestVoteReply) {
-        return state();
+        return this;
     }
 
     @Override public RaftState state() {
         return RaftState.Leader;
     }
 
-    @Override public RaftState handleMessage(ActorRef sender, Object originalMessage) {
+    @Override public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) {
         Preconditions.checkNotNull(sender, "sender should not be null");
 
         Object message = fromSerializableMessage(originalMessage);
@@ -243,13 +238,15 @@ public class Leader extends AbstractRaftActorBehavior {
             // This applies to all RPC messages and responses
             if (rpc.getTerm() > context.getTermInformation().getCurrentTerm()) {
                 context.getTermInformation().updateAndPersist(rpc.getTerm(), null);
-                return RaftState.Follower;
+
+                return switchBehavior(new Follower(context));
             }
         }
 
         try {
             if (message instanceof SendHeartBeat) {
-                return sendHeartBeat();
+                sendHeartBeat();
+                return this;
             } else if(message instanceof SendInstallSnapshot) {
                 installSnapshotIfNeeded();
             } else if (message instanceof Replicate) {
@@ -321,7 +318,7 @@ public class Leader extends AbstractRaftActorBehavior {
         long logIndex = replicate.getReplicatedLogEntry().getIndex();
 
         if(LOG.isDebugEnabled()) {
-            LOG.debug("Replicate message " + logIndex);
+            LOG.debug("Replicate message {}", logIndex);
         }
 
         // Create a tracker entry we will use this later to notify the
@@ -445,7 +442,7 @@ public class Leader extends AbstractRaftActorBehavior {
                 followerActor.path(), mapFollowerToSnapshot.get(followerId).getChunkIndex(),
                 mapFollowerToSnapshot.get(followerId).getTotalChunks());
         } catch (IOException e) {
-            LOG.error("InstallSnapshot failed for Leader.", e);
+            LOG.error(e, "InstallSnapshot failed for Leader.");
         }
     }
 
@@ -467,11 +464,10 @@ public class Leader extends AbstractRaftActorBehavior {
         return nextChunk;
     }
 
-    private RaftState sendHeartBeat() {
+    private void sendHeartBeat() {
         if (followers.size() > 0) {
             sendAppendEntries();
         }
-        return state();
     }
 
     private void stopHeartBeat() {
index ca2d916ecf619ae61b8b627c2e10181d075a187a..064cd8b88c8b68152f04cdc25378c792eb263a7b 100644 (file)
@@ -25,17 +25,18 @@ import org.opendaylight.controller.cluster.raft.RaftState;
  * differently.
  */
 public interface RaftActorBehavior extends AutoCloseable{
+
     /**
      * Handle a message. If the processing of the message warrants a state
-     * change then a new state should be returned otherwise this method should
-     * return the state for the current behavior.
+     * change then a new behavior should be returned otherwise this method should
+     * return the current behavior.
      *
      * @param sender The sender of the message
      * @param message A message that needs to be processed
      *
-     * @return The new state or self (this)
+     * @return The new behavior or current behavior
      */
-    RaftState handleMessage(ActorRef sender, Object message);
+    RaftActorBehavior handleMessage(ActorRef sender, Object message);
 
     /**
      * The state associated with a given behavior
index 22f374319cfdbca12115fad320949c7b277a45a5..c15c9198bd17c2a3da0fa54b017379a5e8971b13 100644 (file)
@@ -181,7 +181,7 @@ public class RaftActorTest extends AbstractActorTest {
                         return true;
                     }
                 }.from(raftActor.path().toString())
-                    .message("Switching from state Candidate to Leader")
+                    .message("Switching from behavior Candidate to Leader")
                     .occurrences(1).exec();
 
 
index 8068dfbcff48970ad7203c43aae378eac4803851..38930180082fdcb80958c1b9adbe04f5062769bf 100644 (file)
@@ -7,7 +7,6 @@ import org.junit.Test;
 import org.opendaylight.controller.cluster.raft.AbstractActorTest;
 import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
 import org.opendaylight.controller.cluster.raft.RaftActorContext;
-import org.opendaylight.controller.cluster.raft.RaftState;
 import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
 import org.opendaylight.controller.cluster.raft.SerializationUtils;
 import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
@@ -22,6 +21,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest {
 
@@ -79,12 +79,12 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest {
             RaftActorBehavior behavior = createBehavior(context);
 
             // Send an unknown message so that the state of the RaftActor remains unchanged
-            RaftState expected = behavior.handleMessage(getRef(), "unknown");
+            RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
 
-            RaftState raftState =
+            RaftActorBehavior raftBehavior =
                 behavior.handleMessage(getRef(), appendEntries);
 
-            assertEquals(expected, raftState);
+            assertEquals(expected, raftBehavior);
 
             // Also expect an AppendEntriesReply to be sent where success is false
             final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
@@ -145,12 +145,12 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest {
                 }
 
                 // Send an unknown message so that the state of the RaftActor remains unchanged
-                RaftState expected = behavior.handleMessage(getRef(), "unknown");
+                RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
 
-                RaftState raftState =
+                RaftActorBehavior raftBehavior =
                     behavior.handleMessage(getRef(), appendEntries);
 
-                assertEquals(expected, raftState);
+                assertEquals(expected, raftBehavior);
 
                 assertEquals(1, log.size());
 
@@ -174,11 +174,11 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest {
                     RaftActorBehavior behavior = createBehavior(
                         createActorContext(behaviorActor));
 
-                    RaftState raftState = behavior.handleMessage(getTestActor(),
+                    RaftActorBehavior raftBehavior = behavior.handleMessage(getTestActor(),
                         new RequestVote(1000, "test", 10000, 999));
 
-                    if(behavior.state() != RaftState.Follower){
-                        assertEquals(RaftState.Follower, raftState);
+                    if(!(behavior instanceof Follower)){
+                        assertTrue(raftBehavior instanceof Follower);
                     } else {
 
                         final Boolean out =
@@ -228,11 +228,11 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest {
 
                     RaftActorBehavior behavior = createBehavior(actorContext);
 
-                    RaftState raftState = behavior.handleMessage(getTestActor(),
+                    RaftActorBehavior raftBehavior = behavior.handleMessage(getTestActor(),
                         new RequestVote(1000, "test", 10000, 999));
 
-                    if(behavior.state() != RaftState.Follower){
-                        assertEquals(RaftState.Follower, raftState);
+                    if(!(behavior instanceof Follower)){
+                        assertTrue(raftBehavior instanceof Follower);
                     } else {
                         final Boolean out =
                             new ExpectMsg<Boolean>(duration("1 seconds"),
@@ -309,10 +309,10 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest {
         setLastLogEntry(
             (MockRaftActorContext) actorContext, 0, 0, p);
 
-        RaftState raftState = createBehavior(actorContext)
+        RaftActorBehavior raftBehavior = createBehavior(actorContext)
             .handleMessage(actorRef, rpc);
 
-        assertEquals(RaftState.Follower, raftState);
+        assertTrue(raftBehavior instanceof Follower);
     }
 
     protected MockRaftActorContext.SimpleReplicatedLog setLastLogEntry(
index d478b175550ba223a778fcf4121f36b9b41fbc1f..a8d47e2c60239a9a7f796a5afe93f7df0ef57d96 100644 (file)
@@ -9,7 +9,6 @@ import org.junit.Test;
 import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
 import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
 import org.opendaylight.controller.cluster.raft.RaftActorContext;
-import org.opendaylight.controller.cluster.raft.RaftState;
 import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
 import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
 import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
@@ -109,10 +108,10 @@ public class CandidateTest extends AbstractRaftActorBehaviorTest {
         Candidate candidate =
             new Candidate(raftActorContext);
 
-        RaftState raftState =
+        RaftActorBehavior raftBehavior =
             candidate.handleMessage(candidateActor, new ElectionTimeout());
 
-        Assert.assertEquals(RaftState.Leader, raftState);
+        Assert.assertTrue(raftBehavior instanceof Leader);
     }
 
     @Test
@@ -123,10 +122,10 @@ public class CandidateTest extends AbstractRaftActorBehaviorTest {
         Candidate candidate =
             new Candidate(raftActorContext);
 
-        RaftState raftState =
+        RaftActorBehavior raftBehavior =
             candidate.handleMessage(candidateActor, new ElectionTimeout());
 
-        Assert.assertEquals(RaftState.Candidate, raftState);
+        Assert.assertTrue(raftBehavior instanceof Candidate);
     }
 
     @Test
@@ -137,9 +136,9 @@ public class CandidateTest extends AbstractRaftActorBehaviorTest {
         Candidate candidate =
             new Candidate(raftActorContext);
 
-        RaftState stateOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
+        RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
 
-        Assert.assertEquals(RaftState.Leader, stateOnFirstVote);
+        Assert.assertTrue(behaviorOnFirstVote instanceof Leader);
 
     }
 
@@ -151,12 +150,12 @@ public class CandidateTest extends AbstractRaftActorBehaviorTest {
         Candidate candidate =
             new Candidate(raftActorContext);
 
-        RaftState stateOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
+        RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
 
-        RaftState stateOnSecondVote = candidate.handleMessage(peerActor2, new RequestVoteReply(0, true));
+        RaftActorBehavior behaviorOnSecondVote = candidate.handleMessage(peerActor2, new RequestVoteReply(0, true));
 
-        Assert.assertEquals(RaftState.Candidate, stateOnFirstVote);
-        Assert.assertEquals(RaftState.Leader, stateOnSecondVote);
+        Assert.assertTrue(behaviorOnFirstVote instanceof Candidate);
+        Assert.assertTrue(behaviorOnSecondVote instanceof Leader);
 
     }
 
index a72a7c43324922748cad1837df047000cb1a4013..edeab11e2a6421cfdbafc7661ddca1e5b059ac7c 100644 (file)
@@ -9,7 +9,6 @@ import org.junit.Test;
 import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
 import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
 import org.opendaylight.controller.cluster.raft.RaftActorContext;
-import org.opendaylight.controller.cluster.raft.RaftState;
 import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
 import org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot;
 import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
@@ -85,10 +84,10 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest {
         Follower follower =
             new Follower(raftActorContext);
 
-        RaftState raftState =
+        RaftActorBehavior raftBehavior =
             follower.handleMessage(followerActor, new ElectionTimeout());
 
-        Assert.assertEquals(RaftState.Candidate, raftState);
+        Assert.assertTrue(raftBehavior instanceof Candidate);
     }
 
     @Test
@@ -187,7 +186,7 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest {
             AppendEntries appendEntries =
                 new AppendEntries(2, "leader-1", 100, 1, entries, 101);
 
-            RaftState raftState =
+            RaftActorBehavior raftBehavior =
                 createBehavior(context).handleMessage(getRef(), appendEntries);
 
             assertEquals(101L, context.getLastApplied());
@@ -226,12 +225,12 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest {
             RaftActorBehavior behavior = createBehavior(context);
 
             // Send an unknown message so that the state of the RaftActor remains unchanged
-            RaftState expected = behavior.handleMessage(getRef(), "unknown");
+            RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
 
-            RaftState raftState =
+            RaftActorBehavior raftBehavior =
                 behavior.handleMessage(getRef(), appendEntries);
 
-            assertEquals(expected, raftState);
+            assertEquals(expected, raftBehavior);
 
             // Also expect an AppendEntriesReply to be sent where success is false
             final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"),
@@ -302,12 +301,12 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest {
             RaftActorBehavior behavior = createBehavior(context);
 
             // Send an unknown message so that the state of the RaftActor remains unchanged
-            RaftState expected = behavior.handleMessage(getRef(), "unknown");
+            RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
 
-            RaftState raftState =
+            RaftActorBehavior raftBehavior =
                 behavior.handleMessage(getRef(), appendEntries);
 
-            assertEquals(expected, raftState);
+            assertEquals(expected, raftBehavior);
             assertEquals(5, log.last().getIndex() + 1);
             assertNotNull(log.get(3));
             assertNotNull(log.get(4));
@@ -382,12 +381,12 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest {
             RaftActorBehavior behavior = createBehavior(context);
 
             // Send an unknown message so that the state of the RaftActor remains unchanged
-            RaftState expected = behavior.handleMessage(getRef(), "unknown");
+            RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown");
 
-            RaftState raftState =
+            RaftActorBehavior raftBehavior =
                 behavior.handleMessage(getRef(), appendEntries);
 
-            assertEquals(expected, raftState);
+            assertEquals(expected, raftBehavior);
 
             // The entry at index 2 will be found out-of-sync with the leader
             // and will be removed
index 19af64790ff7896f496f6585aa27939d76fa4f65..48543d7de2178e937a82d31e03b71282ffdcb88b 100644 (file)
@@ -12,7 +12,6 @@ import org.opendaylight.controller.cluster.raft.FollowerLogInformation;
 import org.opendaylight.controller.cluster.raft.FollowerLogInformationImpl;
 import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
 import org.opendaylight.controller.cluster.raft.RaftActorContext;
-import org.opendaylight.controller.cluster.raft.RaftState;
 import org.opendaylight.controller.cluster.raft.ReplicatedLogImplEntry;
 import org.opendaylight.controller.cluster.raft.SerializationUtils;
 import org.opendaylight.controller.cluster.raft.base.messages.ApplyState;
@@ -54,8 +53,8 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest {
 
             // handle message should return the Leader state when it receives an
             // unknown message
-            RaftState state = leader.handleMessage(senderActor, "foo");
-            Assert.assertEquals(RaftState.Leader, state);
+            RaftActorBehavior behavior = leader.handleMessage(senderActor, "foo");
+            Assert.assertTrue(behavior instanceof Leader);
         }};
     }
 
@@ -125,7 +124,7 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest {
                     actorContext.setPeerAddresses(peerAddresses);
 
                     Leader leader = new Leader(actorContext);
-                    RaftState raftState = leader
+                    RaftActorBehavior raftBehavior = leader
                         .handleMessage(senderActor, new Replicate(null, null,
                             new MockRaftActorContext.MockReplicatedLogEntry(1,
                                 100,
@@ -133,7 +132,7 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest {
                         ));
 
                     // State should not change
-                    assertEquals(RaftState.Leader, raftState);
+                    assertTrue(raftBehavior instanceof Leader);
 
                     final String out =
                         new ExpectMsg<String>(duration("1 seconds"), "match hint") {
@@ -179,11 +178,11 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest {
                             .build());
 
                     Leader leader = new Leader(actorContext);
-                    RaftState raftState = leader
+                    RaftActorBehavior raftBehavior = leader
                         .handleMessage(senderActor, new Replicate(null, "state-id",actorContext.getReplicatedLog().get(1)));
 
                     // State should not change
-                    assertEquals(RaftState.Leader, raftState);
+                    assertTrue(raftBehavior instanceof Leader);
 
                     assertEquals(1, actorContext.getCommitIndex());
 
@@ -258,10 +257,10 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest {
                             new MockRaftActorContext.MockPayload("D"));
 
                     // this should invoke a sendinstallsnapshot as followersLastIndex < snapshotIndex
-                    RaftState raftState = leader.handleMessage(
+                    RaftActorBehavior raftBehavior = leader.handleMessage(
                         senderActor, new Replicate(null, "state-id", entry));
 
-                    assertEquals(RaftState.Leader, raftState);
+                    assertTrue(raftBehavior instanceof Leader);
 
                     // we might receive some heartbeat messages, so wait till we SendInstallSnapshot
                     Boolean[] matches = new ReceiveWhile<Boolean>(Boolean.class, duration("2 seconds")) {
@@ -333,9 +332,9 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest {
                         new ReplicatedLogImplEntry(newEntryIndex, currentTerm,
                             new MockRaftActorContext.MockPayload("D"));
 
-                    RaftState raftState = leader.handleMessage(senderActor, new SendInstallSnapshot());
+                    RaftActorBehavior raftBehavior = leader.handleMessage(senderActor, new SendInstallSnapshot());
 
-                    assertEquals(RaftState.Leader, raftState);
+                    assertTrue(raftBehavior instanceof Leader);
 
                     // check if installsnapshot gets called with the correct values.
                     final String out =
@@ -419,11 +418,11 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest {
                     //clears leaders log
                     actorContext.getReplicatedLog().removeFrom(0);
 
-                    RaftState raftState = leader.handleMessage(senderActor,
+                    RaftActorBehavior raftBehavior = leader.handleMessage(senderActor,
                         new InstallSnapshotReply(currentTerm, followerActor.path().toString(),
                             leader.getFollowerToSnapshot().getChunkIndex(), true));
 
-                    assertEquals(RaftState.Leader, raftState);
+                    assertTrue(raftBehavior instanceof Leader);
 
                     assertEquals(leader.mapFollowerToSnapshot.size(), 0);
                     assertEquals(leader.followerToLog.size(), 1);
index efd35ccfa606d298834cb0eaa05a4d5d393a825a..f7313f4ce703cab84da3b23503615b3c4e2d86c3 100644 (file)
@@ -76,7 +76,6 @@ public class TestHelper {
                 mavenBundle("org.apache.sshd", "sshd-core").versionAsInProject(), //
                 mavenBundle("org.openexi", "nagasena").versionAsInProject(), //
                 mavenBundle("org.openexi", "nagasena-rta").versionAsInProject(), //
-                mavenBundle(CONTROLLER + ".thirdparty", "ganymed").versionAsInProject(), //
                 mavenBundle(CONTROLLER, "netconf-mapping-api").versionAsInProject(), //
 
                 mavenBundle(CONTROLLER, "config-persister-impl").versionAsInProject(), //
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/NodeToNormalizedNodeBuilder.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/NodeToNormalizedNodeBuilder.java
deleted file mode 100644 (file)
index 03d632b..0000000
+++ /dev/null
@@ -1,856 +0,0 @@
-/*
- *
- *  Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- *  This program and the accompanying materials are made available under the
- *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
- *  and is available at http://www.eclipse.org/legal/epl-v10.html
- *
- */
-
-package org.opendaylight.controller.cluster.datastore.node;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import org.opendaylight.controller.cluster.datastore.node.utils.NodeIdentifierFactory;
-import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node;
-import org.opendaylight.yangtools.concepts.Identifiable;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
-import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-/**
- * NormalizedNodeBuilder is a builder that walks through a tree like structure and constructs a
- * NormalizedNode from it.
- * <p/>
- * A large part of this code has been copied over from a similar class in sal-common-impl which was
- * originally supposed to convert a CompositeNode to NormalizedNode
- *
- * @param <T>
- */
-public abstract class NodeToNormalizedNodeBuilder<T extends PathArgument>
-    implements Identifiable<T> {
-
-    private final T identifier;
-
-    protected static final Logger logger = LoggerFactory
-        .getLogger(NodeToNormalizedNodeBuilder.class);
-
-    @Override
-    public T getIdentifier() {
-        return identifier;
-    }
-
-    ;
-
-    protected NodeToNormalizedNodeBuilder(final T identifier) {
-        super();
-        this.identifier = identifier;
-
-    }
-
-    /**
-     * @return Should return true if the node that this operation corresponds to is a mixin
-     */
-    public boolean isMixin() {
-        return false;
-    }
-
-
-    /**
-     * @return Should return true if the node that this operation corresponds to has a 'key'
-     * associated with it. This is typically true for a list-item or leaf-list entry in yang
-     */
-    public boolean isKeyedEntry() {
-        return false;
-    }
-
-    protected Set<QName> getQNameIdentifiers() {
-        return Collections.singleton(identifier.getNodeType());
-    }
-
-    public abstract NodeToNormalizedNodeBuilder<?> getChild(
-        final PathArgument child);
-
-    public abstract NodeToNormalizedNodeBuilder<?> getChild(QName child);
-
-    public abstract NormalizedNode<?, ?> normalize(QName nodeType, Node node);
-
-
-
-    private static abstract class SimpleTypeNormalization<T extends PathArgument>
-        extends NodeToNormalizedNodeBuilder<T> {
-
-        protected SimpleTypeNormalization(final T identifier) {
-            super(identifier);
-        }
-
-        @Override
-        public NormalizedNode<?, ?> normalize(final QName nodeType,
-            final Node node) {
-            checkArgument(node != null);
-            return normalizeImpl(nodeType, node);
-        }
-
-        protected abstract NormalizedNode<?, ?> normalizeImpl(QName nodeType,
-            Node node);
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(
-            final PathArgument child) {
-            return null;
-        }
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
-            return null;
-        }
-
-        @Override
-        public NormalizedNode<?, ?> createDefault(
-            final PathArgument currentArg) {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-    }
-
-
-    private static final class LeafNormalization extends
-        SimpleTypeNormalization<NodeIdentifier> {
-
-        private final LeafSchemaNode schema;
-
-        protected LeafNormalization(final LeafSchemaNode schema, final NodeIdentifier identifier) {
-            super(identifier);
-            this.schema = schema;
-        }
-
-        @Override
-        protected NormalizedNode<?, ?> normalizeImpl(final QName nodeType,
-            final Node node) {
-            Object value = NodeValueCodec.toTypeSafeValue(this.schema, this.schema.getType(), node);
-            return ImmutableNodes.leafNode(nodeType, value);
-
-        }
-
-    }
-
-
-    private static final class LeafListEntryNormalization extends
-        SimpleTypeNormalization<NodeWithValue> {
-
-        private final LeafListSchemaNode schema;
-
-        public LeafListEntryNormalization(final LeafListSchemaNode potential) {
-            super(new NodeWithValue(potential.getQName(), null));
-            this.schema = potential;
-        }
-
-        @Override
-        protected NormalizedNode<?, ?> normalizeImpl(final QName nodeType,
-            final Node node) {
-            final Object data = node.getValue();
-            if (data == null) {
-                Preconditions.checkArgument(false,
-                    "No data available in leaf list entry for " + nodeType);
-            }
-
-            Object value = NodeValueCodec.toTypeSafeValue(this.schema, this.schema.getType(), node);
-
-            NodeWithValue nodeId = new NodeWithValue(nodeType, value);
-            return Builders.leafSetEntryBuilder().withNodeIdentifier(nodeId)
-                .withValue(value).build();
-        }
-
-
-        @Override
-        public boolean isKeyedEntry() {
-            return true;
-        }
-    }
-
-
-    private static abstract class NodeToNormalizationNodeOperation<T extends PathArgument>
-        extends NodeToNormalizedNodeBuilder<T> {
-
-        protected NodeToNormalizationNodeOperation(final T identifier) {
-            super(identifier);
-        }
-
-        @SuppressWarnings({"rawtypes", "unchecked"})
-        @Override
-        public final NormalizedNodeContainer<?, ?, ?> normalize(
-            final QName nodeType, final Node node) {
-            checkArgument(node != null);
-
-            if (!node.getType().equals(AugmentationNode.class.getSimpleName())
-                && !node.getType().equals(ContainerNode.class.getSimpleName())
-                && !node.getType().equals(MapNode.class.getSimpleName())) {
-                checkArgument(nodeType != null);
-            }
-
-            NormalizedNodeContainerBuilder builder = createBuilder(node);
-
-            Set<NodeToNormalizedNodeBuilder<?>> usedMixins = new HashSet<>();
-
-            logNode(node);
-
-            if (node.getChildCount() == 0 && (
-                node.getType().equals(LeafSetEntryNode.class.getSimpleName())
-                    || node.getType().equals(LeafNode.class.getSimpleName()))) {
-                PathArgument childPathArgument =
-                    NodeIdentifierFactory.getArgument(node.getPath());
-
-                final NormalizedNode child;
-                if (childPathArgument instanceof NodeWithValue) {
-                    final NodeWithValue nodeWithValue =
-                        new NodeWithValue(childPathArgument.getNodeType(),
-                            node.getValue());
-                    child =
-                        Builders.leafSetEntryBuilder()
-                            .withNodeIdentifier(nodeWithValue)
-                            .withValue(node.getValue()).build();
-                } else {
-                    child =
-                        ImmutableNodes.leafNode(childPathArgument.getNodeType(),
-                            node.getValue());
-                }
-                builder.addChild(child);
-            }
-
-            final List<Node> children = node.getChildList();
-            for (Node nodeChild : children) {
-
-                PathArgument childPathArgument =
-                    NodeIdentifierFactory.getArgument(nodeChild.getPath());
-
-                QName childNodeType = null;
-                NodeToNormalizedNodeBuilder childOp = null;
-
-                if (childPathArgument instanceof AugmentationIdentifier) {
-                    childOp = getChild(childPathArgument);
-                    checkArgument(childOp instanceof AugmentationNormalization, childPathArgument);
-                } else {
-                    childNodeType = childPathArgument.getNodeType();
-                    childOp = getChild(childNodeType);
-                }
-                // We skip unknown nodes if this node is mixin since
-                // it's nodes and parent nodes are interleaved
-                if (childOp == null && isMixin()) {
-                    continue;
-                } else if (childOp == null) {
-                    logger.error(
-                        "childOp is null and this operation is not a mixin : this = {}",
-                        this.toString());
-                }
-
-                checkArgument(childOp != null,
-                    "Node %s is not allowed inside %s",
-                    childNodeType, getIdentifier());
-
-                if (childOp.isMixin()) {
-                    if (usedMixins.contains(childOp)) {
-                        // We already run / processed that mixin, so to avoid
-                        // duplicate we are
-                        // skipping next nodes.
-                        continue;
-                    }
-                    // builder.addChild(childOp.normalize(nodeType, treeCacheNode));
-                    final NormalizedNode childNode =
-                        childOp.normalize(childNodeType, nodeChild);
-                    if (childNode != null)
-                        builder.addChild(childNode);
-                    usedMixins.add(childOp);
-                } else {
-                    final NormalizedNode childNode =
-                        childOp.normalize(childNodeType, nodeChild);
-                    if (childNode != null)
-                        builder.addChild(childNode);
-                }
-            }
-
-
-            try {
-                return (NormalizedNodeContainer<?, ?, ?>) builder.build();
-            } catch (Exception e) {
-                return null;
-            }
-
-        }
-
-        private void logNode(Node node) {
-            //let us find out the type of the node
-            logger.debug("We got a {} , with identifier {} with {} children",
-                node.getType(), node.getPath(),
-                node.getChildList());
-        }
-
-        @SuppressWarnings("rawtypes")
-        protected abstract NormalizedNodeContainerBuilder createBuilder(
-            final Node node);
-
-    }
-
-
-    private static abstract class DataContainerNormalizationOperation<T extends PathArgument>
-        extends NodeToNormalizationNodeOperation<T> {
-
-        private final DataNodeContainer schema;
-        private final Map<QName, NodeToNormalizedNodeBuilder<?>> byQName;
-        private final Map<PathArgument, NodeToNormalizedNodeBuilder<?>> byArg;
-
-        protected DataContainerNormalizationOperation(final T identifier,
-            final DataNodeContainer schema) {
-            super(identifier);
-            this.schema = schema;
-            this.byArg = new ConcurrentHashMap<>();
-            this.byQName = new ConcurrentHashMap<>();
-        }
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(
-            final PathArgument child) {
-            NodeToNormalizedNodeBuilder<?> potential = byArg.get(child);
-            if (potential != null) {
-                return potential;
-            }
-            potential = fromSchema(schema, child);
-            return register(potential);
-        }
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
-            if (child == null) {
-                return null;
-            }
-
-            NodeToNormalizedNodeBuilder<?> potential = byQName.get(child);
-            if (potential != null) {
-                return potential;
-            }
-            potential = fromSchemaAndPathArgument(schema, child);
-            return register(potential);
-        }
-
-        private NodeToNormalizedNodeBuilder<?> register(
-            final NodeToNormalizedNodeBuilder<?> potential) {
-            if (potential != null) {
-                byArg.put(potential.getIdentifier(), potential);
-                for (QName qName : potential.getQNameIdentifiers()) {
-                    byQName.put(qName, potential);
-                }
-            }
-            return potential;
-        }
-
-    }
-
-
-    private static final class ListItemNormalization extends
-        DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
-
-        private final List<QName> keyDefinition;
-        private final ListSchemaNode schemaNode;
-
-        protected ListItemNormalization(
-            final NodeIdentifierWithPredicates identifier,
-            final ListSchemaNode schema) {
-            super(identifier, schema);
-            this.schemaNode = schema;
-            keyDefinition = schema.getKeyDefinition();
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder createBuilder(
-            final Node node) {
-            NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
-                (NodeIdentifierWithPredicates) NodeIdentifierFactory
-                    .createPathArgument(node
-                        .getPath(), schemaNode);
-            return Builders.mapEntryBuilder()
-                .withNodeIdentifier(
-                    nodeIdentifierWithPredicates
-            );
-        }
-
-        @Override
-        public NormalizedNode<?, ?> createDefault(
-            final PathArgument currentArg) {
-            DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode>
-                builder =
-                Builders.mapEntryBuilder().withNodeIdentifier(
-                    (NodeIdentifierWithPredicates) currentArg);
-            for (Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg)
-                .getKeyValues().entrySet()) {
-                if (keyValue.getValue() == null) {
-                    throw new NullPointerException(
-                        "Null value found for path : "
-                            + currentArg);
-                }
-                builder.addChild(Builders.leafBuilder()
-                    //
-                    .withNodeIdentifier(new NodeIdentifier(keyValue.getKey()))
-                    .withValue(keyValue.getValue()).build());
-            }
-            return builder.build();
-        }
-
-
-        @Override
-        public boolean isKeyedEntry() {
-            return true;
-        }
-    }
-
-
-    private static final class ContainerNormalization extends
-        DataContainerNormalizationOperation<NodeIdentifier> {
-
-        protected ContainerNormalization(final ContainerSchemaNode schema) {
-            super(new NodeIdentifier(schema.getQName()), schema);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder createBuilder(
-            final Node node) {
-            return Builders.containerBuilder()
-                .withNodeIdentifier(getIdentifier());
-        }
-
-        @Override
-        public NormalizedNode<?, ?> createDefault(
-            final PathArgument currentArg) {
-            return Builders.containerBuilder()
-                .withNodeIdentifier((NodeIdentifier) currentArg).build();
-        }
-
-    }
-
-
-    private static abstract class MixinNormalizationOp<T extends PathArgument>
-        extends NodeToNormalizationNodeOperation<T> {
-
-        protected MixinNormalizationOp(final T identifier) {
-            super(identifier);
-        }
-
-        @Override
-        public final boolean isMixin() {
-            return true;
-        }
-
-    }
-
-
-    private static final class LeafListMixinNormalization extends
-        MixinNormalizationOp<NodeIdentifier> {
-
-        private final NodeToNormalizedNodeBuilder<?> innerOp;
-
-        public LeafListMixinNormalization(final LeafListSchemaNode potential) {
-            super(new NodeIdentifier(potential.getQName()));
-            innerOp = new LeafListEntryNormalization(potential);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder createBuilder(
-            final Node node) {
-            return Builders.leafSetBuilder()
-                .withNodeIdentifier(getIdentifier());
-        }
-
-        @Override
-        public NormalizedNode<?, ?> createDefault(
-            final PathArgument currentArg) {
-            return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier())
-                .build();
-        }
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(
-            final PathArgument child) {
-            if (child instanceof NodeWithValue) {
-                return innerOp;
-            }
-            return null;
-        }
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
-            if (getIdentifier().getNodeType().equals(child)) {
-                return innerOp;
-            }
-            return null;
-        }
-
-    }
-
-
-    private static final class AugmentationNormalization extends
-        MixinNormalizationOp<AugmentationIdentifier> {
-
-        private final Map<QName, NodeToNormalizedNodeBuilder<?>> byQName;
-        private final Map<PathArgument, NodeToNormalizedNodeBuilder<?>> byArg;
-
-        public AugmentationNormalization(final AugmentationSchema augmentation,
-            final DataNodeContainer schema) {
-            super(augmentationIdentifierFrom(augmentation));
-
-            ImmutableMap.Builder<QName, NodeToNormalizedNodeBuilder<?>>
-                byQNameBuilder =
-                ImmutableMap.builder();
-            ImmutableMap.Builder<PathArgument, NodeToNormalizedNodeBuilder<?>>
-                byArgBuilder =
-                ImmutableMap.builder();
-
-            for (DataSchemaNode augNode : augmentation.getChildNodes()) {
-                DataSchemaNode resolvedNode =
-                    schema.getDataChildByName(augNode.getQName());
-                NodeToNormalizedNodeBuilder<?> resolvedOp =
-                    fromDataSchemaNode(resolvedNode);
-                byArgBuilder.put(resolvedOp.getIdentifier(), resolvedOp);
-                for (QName resQName : resolvedOp.getQNameIdentifiers()) {
-                    byQNameBuilder.put(resQName, resolvedOp);
-                }
-            }
-            byQName = byQNameBuilder.build();
-            byArg = byArgBuilder.build();
-
-        }
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(
-            final PathArgument child) {
-            return byArg.get(child);
-        }
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
-            return byQName.get(child);
-        }
-
-        @Override
-        protected Set<QName> getQNameIdentifiers() {
-            return getIdentifier().getPossibleChildNames();
-        }
-
-        @SuppressWarnings("rawtypes")
-        @Override
-        protected NormalizedNodeContainerBuilder createBuilder(
-            final Node node) {
-            return Builders.augmentationBuilder()
-                .withNodeIdentifier(getIdentifier());
-        }
-
-        @Override
-        public NormalizedNode<?, ?> createDefault(
-            final PathArgument currentArg) {
-            return Builders.augmentationBuilder()
-                .withNodeIdentifier(getIdentifier())
-                .build();
-        }
-
-    }
-
-
-    private static final class ListMixinNormalization extends
-        MixinNormalizationOp<NodeIdentifier> {
-
-        private final ListItemNormalization innerNode;
-
-        public ListMixinNormalization(final ListSchemaNode list) {
-            super(new NodeIdentifier(list.getQName()));
-            this.innerNode =
-                new ListItemNormalization(new NodeIdentifierWithPredicates(
-                    list.getQName(), Collections.<QName, Object>emptyMap()),
-                    list);
-        }
-
-        @SuppressWarnings("rawtypes")
-        @Override
-        protected NormalizedNodeContainerBuilder createBuilder(
-            final Node node) {
-            return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
-        }
-
-        @Override
-        public NormalizedNode<?, ?> createDefault(
-            final PathArgument currentArg) {
-            return Builders.mapBuilder().withNodeIdentifier(getIdentifier())
-                .build();
-        }
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(
-            final PathArgument child) {
-            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
-                return innerNode;
-            }
-            return null;
-        }
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
-            if (getIdentifier().getNodeType().equals(child)) {
-                return innerNode;
-            }
-            return null;
-        }
-
-    }
-
-
-    private static class ChoiceNodeNormalization extends
-        MixinNormalizationOp<NodeIdentifier> {
-
-        private final ImmutableMap<QName, NodeToNormalizedNodeBuilder<?>>
-            byQName;
-        private final ImmutableMap<PathArgument, NodeToNormalizedNodeBuilder<?>>
-            byArg;
-
-        protected ChoiceNodeNormalization(
-            final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
-            super(new NodeIdentifier(schema.getQName()));
-            ImmutableMap.Builder<QName, NodeToNormalizedNodeBuilder<?>>
-                byQNameBuilder =
-                ImmutableMap.builder();
-            ImmutableMap.Builder<PathArgument, NodeToNormalizedNodeBuilder<?>>
-                byArgBuilder =
-                ImmutableMap.builder();
-
-            for (ChoiceCaseNode caze : schema.getCases()) {
-                for (DataSchemaNode cazeChild : caze.getChildNodes()) {
-                    NodeToNormalizedNodeBuilder<?> childOp =
-                        fromDataSchemaNode(cazeChild);
-                    byArgBuilder.put(childOp.getIdentifier(), childOp);
-                    for (QName qname : childOp.getQNameIdentifiers()) {
-                        byQNameBuilder.put(qname, childOp);
-                    }
-                }
-            }
-            byQName = byQNameBuilder.build();
-            byArg = byArgBuilder.build();
-        }
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(
-            final PathArgument child) {
-            return byArg.get(child);
-        }
-
-        @Override
-        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
-            return byQName.get(child);
-        }
-
-        @Override
-        protected NormalizedNodeContainerBuilder createBuilder(
-            final Node node) {
-            return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
-        }
-
-        @Override
-        public NormalizedNode<?, ?> createDefault(
-            final PathArgument currentArg) {
-            return Builders.choiceBuilder().withNodeIdentifier(getIdentifier())
-                .build();
-        }
-    }
-
-    /**
-     * Find an appropriate NormalizedNodeBuilder using both the schema and the
-     * Path Argument
-     *
-     * @param schema
-     * @param child
-     * @return
-     */
-    public static NodeToNormalizedNodeBuilder<?> fromSchemaAndPathArgument(
-        final DataNodeContainer schema, final QName child) {
-        DataSchemaNode potential = schema.getDataChildByName(child);
-        if (potential == null) {
-            Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode>
-                choices =
-                FluentIterable.from(schema.getChildNodes()).filter(
-                    org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
-            potential = findChoice(choices, child);
-        }
-        if (potential == null) {
-            if (logger.isTraceEnabled()) {
-                logger.trace("BAD CHILD = {}", child.toString());
-            }
-        }
-
-        checkArgument(potential != null,
-            "Supplied QName %s is not valid according to schema %s", child,
-            schema);
-
-        // If the schema in an instance of DataSchemaNode and the potential
-        // is augmenting something then there is a chance that this may be
-        // and augmentation node
-        if ((schema instanceof DataSchemaNode)
-            && potential.isAugmenting()) {
-
-            AugmentationNormalization augmentation =
-                fromAugmentation(schema, (AugmentationTarget) schema,
-                    potential);
-
-            // If an augmentation normalization (builder) is not found then
-            // we fall through to the regular processing
-            if(augmentation != null){
-                return augmentation;
-            }
-        }
-        return fromDataSchemaNode(potential);
-    }
-
-    /**
-     * Given a bunch of choice nodes and a the name of child find a choice node for that child which
-     * has a non-null value
-     *
-     * @param choices
-     * @param child
-     * @return
-     */
-    private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
-        final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices,
-        final QName child) {
-        org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
-        choiceLoop:
-        for (org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
-            for (ChoiceCaseNode caze : choice.getCases()) {
-                if (caze.getDataChildByName(child) != null) {
-                    foundChoice = choice;
-                    break choiceLoop;
-                }
-            }
-        }
-        return foundChoice;
-    }
-
-
-    /**
-     * Create an AugmentationIdentifier based on the AugmentationSchema
-     *
-     * @param augmentation
-     * @return
-     */
-    public static AugmentationIdentifier augmentationIdentifierFrom(
-        final AugmentationSchema augmentation) {
-        ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
-        for (DataSchemaNode child : augmentation.getChildNodes()) {
-            potentialChildren.add(child.getQName());
-        }
-        return new AugmentationIdentifier(potentialChildren.build());
-    }
-
-    /**
-     * Create an AugmentationNormalization based on the schema of the DataContainer, the
-     * AugmentationTarget and the potential schema node
-     *
-     * @param schema
-     * @param augments
-     * @param potential
-     * @return
-     */
-    private static AugmentationNormalization fromAugmentation(
-        final DataNodeContainer schema, final AugmentationTarget augments,
-        final DataSchemaNode potential) {
-        AugmentationSchema augmentation = null;
-        for (AugmentationSchema aug : augments.getAvailableAugmentations()) {
-            DataSchemaNode child = aug.getDataChildByName(potential.getQName());
-            if (child != null) {
-                augmentation = aug;
-                break;
-            }
-
-        }
-        if (augmentation != null) {
-            return new AugmentationNormalization(augmentation, schema);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * @param schema
-     * @param child
-     * @return
-     */
-    private static NodeToNormalizedNodeBuilder<?> fromSchema(
-        final DataNodeContainer schema, final PathArgument child) {
-        if (child instanceof AugmentationIdentifier) {
-            QName childQName = ((AugmentationIdentifier) child)
-                .getPossibleChildNames().iterator().next();
-
-            return fromSchemaAndPathArgument(schema, childQName);
-        }
-        return fromSchemaAndPathArgument(schema, child.getNodeType());
-    }
-
-    public static NodeToNormalizedNodeBuilder<?> fromDataSchemaNode(
-        final DataSchemaNode potential) {
-        if (potential instanceof ContainerSchemaNode) {
-            return new ContainerNormalization((ContainerSchemaNode) potential);
-        } else if (potential instanceof ListSchemaNode) {
-            return new ListMixinNormalization((ListSchemaNode) potential);
-        } else if (potential instanceof LeafSchemaNode) {
-            return new LeafNormalization((LeafSchemaNode) potential,
-                new NodeIdentifier(potential.getQName()));
-        } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
-            return new ChoiceNodeNormalization(
-                (org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
-        } else if (potential instanceof LeafListSchemaNode) {
-            return new LeafListMixinNormalization(
-                (LeafListSchemaNode) potential);
-        }
-        return null;
-    }
-
-    public static NodeToNormalizedNodeBuilder<?> from(final SchemaContext ctx) {
-        return new ContainerNormalization(ctx);
-    }
-
-    public abstract NormalizedNode<?, ?> createDefault(PathArgument currentArg);
-
-}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/NodeValueCodec.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/NodeValueCodec.java
deleted file mode 100644 (file)
index b6dbefb..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *
- *  Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- *  This program and the accompanying materials are made available under the
- *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
- *  and is available at http://www.eclipse.org/legal/epl-v10.html
- *
- */
-
-package org.opendaylight.controller.cluster.datastore.node;
-
-import org.opendaylight.controller.cluster.datastore.node.utils.QNameFactory;
-import org.opendaylight.controller.cluster.datastore.util.InstanceIdentifierUtils;
-import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
-import org.opendaylight.yangtools.yang.data.api.codec.BitsCodec;
-import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
-import org.opendaylight.yangtools.yang.model.util.IdentityrefType;
-import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
-import org.opendaylight.yangtools.yang.model.util.Leafref;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class NodeValueCodec {
-    protected static final Logger logger = LoggerFactory
-        .getLogger(NodeValueCodec.class);
-
-    public static Object toTypeSafeValue(DataSchemaNode schema, TypeDefinition type, NormalizedNodeMessages.Node node){
-
-        String value = node.getValue();
-
-        if(schema != null && value != null){
-            TypeDefinition<?> baseType = type;
-
-            while (baseType.getBaseType() != null) {
-                baseType = baseType.getBaseType();
-            }
-
-            TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> codec =
-                TypeDefinitionAwareCodec.from(type);
-
-            if(codec instanceof BitsCodec){
-                if(value.contains("[]")){
-                    value = "";
-                } else {
-                    value = value.replace("[", "");
-                    value = value.replace("]", "");
-                    value = value.replace(",", " ");
-                }
-            }
-
-            if (codec != null) {
-                return codec.deserialize(value);
-            } else if(baseType instanceof Leafref) {
-                return value;
-            } else if(baseType instanceof IdentityrefType) {
-                return QNameFactory.create(value);
-            } else if(baseType instanceof InstanceIdentifierType) {
-                return InstanceIdentifierUtils.fromSerializable(node.getInstanceIdentifierValue());
-            } else {
-                logger.error("Could not figure out how to transform value " + value +  " for schemaType " + type);
-            }
-        }
-
-        return value;
-    }
-}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/NormalizedNodeToProtocolBufferNode.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/NormalizedNodeToProtocolBufferNode.java
deleted file mode 100644 (file)
index 68d3c59..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- *
- *  Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- *  This program and the accompanying materials are made available under the
- *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
- *  and is available at http://www.eclipse.org/legal/epl-v10.html
- *
- */
-
-package org.opendaylight.controller.cluster.datastore.node;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.cluster.datastore.util.InstanceIdentifierUtils;
-import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
-import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
-import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
-import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
-
-import java.util.Map;
-
-/**
- * NormalizedNodeToProtocolBufferNode walks the NormalizedNode tree converting it to the
- * NormalizedMessage.Node
- * <p/>
- * {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode } is a tree like structure that provides a generic structure for a yang data
- * model
- */
-public class NormalizedNodeToProtocolBufferNode {
-
-
-    private final Node.Builder builderRoot;
-    private NormalizedNodeMessages.Container container;
-
-    public NormalizedNodeToProtocolBufferNode() {
-
-        builderRoot = Node.newBuilder();
-    }
-
-    public void encode(String parentPath, NormalizedNode<?, ?> normalizedNode) {
-        if (parentPath == null) {
-            parentPath = "";
-        }
-
-        NormalizedNodeMessages.Container.Builder containerBuilder =
-            NormalizedNodeMessages.Container.newBuilder();
-
-        if (normalizedNode != null) {
-
-            navigateNormalizedNode(0, parentPath, normalizedNode, builderRoot);
-            // here we need to put back the Node Tree in Container
-
-            container =
-                containerBuilder.setParentPath(parentPath).setNormalizedNode(
-                    builderRoot.build()).build();
-        } else {
-            //this can happen when an attempt was made to read from datastore and normalized node was null.
-            container = containerBuilder.setParentPath(parentPath).build();
-
-        }
-
-    }
-
-
-    private void navigateDataContainerNode(int level, final String parentPath,
-        final DataContainerNode<?> dataContainerNode,
-        Node.Builder builderParent) {
-
-        String newParentPath =
-            parentPath + "/" + dataContainerNode.getIdentifier().toString();
-        String type = getDataContainerType(dataContainerNode).getSimpleName();
-        builderParent.setPath(dataContainerNode.getIdentifier().toString())
-            .setType(type);
-
-        final Iterable<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>>
-            value =
-            dataContainerNode.getValue();
-        for (NormalizedNode<?, ?> node : value) {
-            Node.Builder builderChild = Node.newBuilder();
-            if (node instanceof MixinNode
-                && node instanceof NormalizedNodeContainer) {
-
-                navigateNormalizedNodeContainerMixin(level, newParentPath,
-                    (NormalizedNodeContainer<?, ?, ?>) node, builderChild);
-            } else {
-                navigateNormalizedNode(level, newParentPath, node,
-                    builderChild);
-            }
-            builderParent.addChild(builderChild);
-        }
-    }
-
-    private Class getDataContainerType(
-        NormalizedNodeContainer<?, ?, ?> dataContainerNode) {
-        if (dataContainerNode instanceof ChoiceNode) {
-            return ChoiceNode.class;
-        } else if (dataContainerNode instanceof AugmentationNode) {
-            return AugmentationNode.class;
-        } else if (dataContainerNode instanceof ContainerNode) {
-            return ContainerNode.class;
-        } else if (dataContainerNode instanceof MapEntryNode) {
-            return MapEntryNode.class;
-        } else if (dataContainerNode instanceof UnkeyedListEntryNode) {
-            return UnkeyedListEntryNode.class;
-        } else if (dataContainerNode instanceof MapNode) {
-            return MapNode.class;
-        } else if (dataContainerNode instanceof LeafSetNode) {
-            return LeafSetNode.class;
-        }
-        throw new IllegalArgumentException(
-            "could not find the data container node type "
-                + dataContainerNode.toString()
-        );
-    }
-
-    private void navigateNormalizedNodeContainerMixin(int level,
-        final String parentPath,
-        NormalizedNodeContainer<?, ?, ?> node, Node.Builder builderParent) {
-        String newParentPath =
-            parentPath + "/" + node.getIdentifier().toString();
-
-        builderParent.setPath(node.getIdentifier().toString()).setType(
-            this.getDataContainerType(node).getSimpleName());
-        final Iterable<? extends NormalizedNode<?, ?>> value = node.getValue();
-        for (NormalizedNode normalizedNode : value) {
-            // child node builder
-            Node.Builder builderChild = Node.newBuilder();
-            if (normalizedNode instanceof MixinNode
-                && normalizedNode instanceof NormalizedNodeContainer) {
-                navigateNormalizedNodeContainerMixin(level + 1, newParentPath,
-                    (NormalizedNodeContainer) normalizedNode, builderChild);
-            } else {
-                navigateNormalizedNode(level, newParentPath, normalizedNode,
-                    builderChild);
-            }
-            builderParent.addChild(builderChild);
-
-        }
-
-
-
-    }
-
-
-    private void navigateNormalizedNode(int level,
-        String parentPath, NormalizedNode<?, ?> normalizedNode,
-        Node.Builder builderParent) {
-
-        if (normalizedNode instanceof DataContainerNode) {
-
-            final DataContainerNode<?> dataContainerNode =
-                (DataContainerNode) normalizedNode;
-
-            navigateDataContainerNode(level + 1, parentPath, dataContainerNode,
-                builderParent);
-        } else if (normalizedNode instanceof MixinNode
-            && normalizedNode instanceof NormalizedNodeContainer) {
-
-            navigateNormalizedNodeContainerMixin(level, parentPath,
-                (NormalizedNodeContainer<?, ?, ?>) normalizedNode,
-                builderParent);
-        } else {
-            if (normalizedNode instanceof LeafNode) {
-                buildLeafNode(parentPath, normalizedNode, builderParent);
-            } else if (normalizedNode instanceof LeafSetEntryNode) {
-                buildLeafSetEntryNode(parentPath, normalizedNode,
-                    builderParent);
-            }
-
-        }
-
-    }
-
-    private void buildLeafSetEntryNode(String parentPath,
-        NormalizedNode<?, ?> normalizedNode,
-        Node.Builder builderParent) {
-        String path =
-            parentPath + "/" + normalizedNode.getIdentifier().toString();
-        LeafSetEntryNode leafSetEntryNode = (LeafSetEntryNode) normalizedNode;
-        Map<QName, String> attributes = leafSetEntryNode.getAttributes();
-        if (!attributes.isEmpty()) {
-            NormalizedNodeMessages.Attribute.Builder builder = null;
-            for (Map.Entry<QName, String> attribute : attributes.entrySet()) {
-                builder = NormalizedNodeMessages.Attribute.newBuilder();
-
-                builder
-                    .setName(attribute.getKey().toString())
-                    .setValue(normalizedNode.getValue().toString());
-
-                builderParent.addAttributes(builder.build());
-            }
-        }
-        buildNodeValue(normalizedNode, builderParent);
-    }
-
-    private void buildLeafNode(String parentPath,
-        NormalizedNode<?, ?> normalizedNode,
-        Node.Builder builderParent) {
-        Preconditions.checkNotNull(parentPath);
-        Preconditions.checkNotNull(normalizedNode);
-        String path =
-            parentPath + "/" + normalizedNode.getIdentifier().toString();
-        LeafNode leafNode = (LeafNode) normalizedNode;
-        Map<QName, String> attributes = leafNode.getAttributes();
-        if (!attributes.isEmpty()) {
-            NormalizedNodeMessages.Attribute.Builder builder = null;
-            for (Map.Entry<QName, String> attribute : attributes.entrySet()) {
-                builder = NormalizedNodeMessages.Attribute.newBuilder();
-                builder
-                    .setName(attribute.getKey().toString())
-                    .setValue(attribute.getValue().toString());
-
-                builderParent.addAttributes(builder.build());
-            }
-        }
-
-        Object value = normalizedNode.getValue();
-        if (value == null) {
-            builderParent
-                .setPath(normalizedNode.getIdentifier().toString())
-                .setType(LeafNode.class.getSimpleName())
-                .setValueType(String.class.getSimpleName())
-                .setValue("");
-        } else {
-            buildNodeValue(normalizedNode, builderParent);
-        }
-    }
-
-    private void buildNodeValue(NormalizedNode<?, ?> normalizedNode,
-        Node.Builder builderParent) {
-
-        Object value = normalizedNode.getValue();
-
-        builderParent
-            .setPath(normalizedNode.getIdentifier().toString())
-            .setType(LeafNode.class.getSimpleName())
-            .setValueType((value.getClass().getSimpleName()))
-            .setValue(value.toString());
-
-        if(value.getClass().equals(YangInstanceIdentifier.class)){
-            builderParent.setInstanceIdentifierValue(
-                InstanceIdentifierUtils
-                    .toSerializable((YangInstanceIdentifier) value));
-        }
-    }
-
-    public NormalizedNodeMessages.Container getContainer() {
-        return container;
-    }
-}
index ea3986f4a0a35fd840a92d341a18f5ccb614a256..da61e6de7327c9b3adabf30bf9d621561dd7c998 100644 (file)
@@ -32,20 +32,6 @@ public class NodeIdentifierFactory {
         return value;
     }
 
-    public static YangInstanceIdentifier.PathArgument getArgument(String id, DataSchemaNode schemaNode){
-        YangInstanceIdentifier.PathArgument value = cache.get(id);
-        if(value == null){
-            synchronized (cache){
-                value = cache.get(id);
-                if(value == null) {
-                    value = createPathArgument(id, schemaNode);
-                    cache.put(id, value);
-                }
-            }
-        }
-        return value;
-    }
-
     public static YangInstanceIdentifier.PathArgument createPathArgument(String id, DataSchemaNode schemaNode){
         final NodeIdentifierWithPredicatesGenerator
             nodeIdentifierWithPredicatesGenerator = new NodeIdentifierWithPredicatesGenerator(id, schemaNode);
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NodeTypes.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NodeTypes.java
new file mode 100644 (file)
index 0000000..3ff6efb
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ *  Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ *  This program and the accompanying materials are made available under the
+ *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ *  and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ */
+
+package org.opendaylight.controller.cluster.datastore.node.utils.stream;
+
+public class NodeTypes {
+
+    public static final byte LEAF_NODE = 1;
+    public static final byte LEAF_SET = 2;
+    public static final byte LEAF_SET_ENTRY_NODE = 3;
+    public static final byte CONTAINER_NODE = 4;
+    public static final byte UNKEYED_LIST = 5;
+    public static final byte UNKEYED_LIST_ITEM = 6;
+    public static final byte MAP_NODE = 7;
+    public static final byte MAP_ENTRY_NODE = 8;
+    public static final byte ORDERED_MAP_NODE = 9;
+    public static final byte CHOICE_NODE = 10;
+    public static final byte AUGMENTATION_NODE = 11;
+    public static final byte ANY_XML_NODE = 12;
+    public static final byte END_NODE = 13;
+
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeInputStreamReader.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeInputStreamReader.java
new file mode 100644 (file)
index 0000000..bdc52bc
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ *
+ *  Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ *  This program and the accompanying materials are made available under the
+ *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ *  and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ */
+
+package org.opendaylight.controller.cluster.datastore.node.utils.stream;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.cluster.datastore.node.utils.QNameFactory;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * NormalizedNodeInputStreamReader reads the byte stream and constructs the normalized node including its children nodes.
+ * This process goes in recursive manner, where each NodeTypes object signifies the start of the object, except END_NODE.
+ * If a node can have children, then that node's end is calculated based on appearance of END_NODE.
+ *
+ */
+
+public class NormalizedNodeInputStreamReader implements NormalizedNodeStreamReader {
+
+    private DataInputStream reader;
+
+    private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodeInputStreamReader.class);
+
+    private Map<Integer, String> codedStringMap = new HashMap<>();
+    private static final String REVISION_ARG = "?revision=";
+
+    public NormalizedNodeInputStreamReader(InputStream stream) throws IOException {
+        Preconditions.checkNotNull(stream);
+        reader = new DataInputStream(stream);
+    }
+
+
+    public NormalizedNode<?, ?> readNormalizedNode() throws IOException {
+        NormalizedNode<?, ?> node = null;
+
+        // each node should start with a byte
+        byte nodeType = reader.readByte();
+
+        if(nodeType == NodeTypes.END_NODE) {
+            LOG.debug("End node reached. return");
+            return null;
+        }
+        else if(nodeType == NodeTypes.AUGMENTATION_NODE) {
+            LOG.debug("Reading augmentation node. will create augmentation identifier");
+
+            YangInstanceIdentifier.AugmentationIdentifier identifier =
+                new YangInstanceIdentifier.AugmentationIdentifier(readQNameSet());
+            DataContainerNodeBuilder<YangInstanceIdentifier.AugmentationIdentifier, AugmentationNode> augmentationBuilder =
+                Builders.augmentationBuilder().withNodeIdentifier(identifier);
+            augmentationBuilder = addDataContainerChildren(augmentationBuilder);
+            node = augmentationBuilder.build();
+
+        } else {
+            QName qName = readQName();
+
+            if(nodeType == NodeTypes.LEAF_SET_ENTRY_NODE) {
+                LOG.debug("Reading leaf set entry node. Will create NodeWithValue instance identifier");
+
+                // Read the object value
+                Object value = readObject();
+
+                YangInstanceIdentifier.NodeWithValue nodeWithValue = new YangInstanceIdentifier.NodeWithValue(qName, value);
+                node =  Builders.leafSetEntryBuilder().withNodeIdentifier(nodeWithValue).withValue(value).build();
+
+            } else if(nodeType == NodeTypes.MAP_ENTRY_NODE) {
+                LOG.debug("Reading map entry node. Will create node identifier with predicates.");
+
+                YangInstanceIdentifier.NodeIdentifierWithPredicates nodeIdentifier =
+                    new YangInstanceIdentifier.NodeIdentifierWithPredicates(qName, readKeyValueMap());
+                DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder
+                    = Builders.mapEntryBuilder().withNodeIdentifier(nodeIdentifier);
+
+                mapEntryBuilder = (DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates,
+                    MapEntryNode>)addDataContainerChildren(mapEntryBuilder);
+                node = mapEntryBuilder.build();
+
+            } else {
+                LOG.debug("Creating standard node identifier. ");
+                YangInstanceIdentifier.NodeIdentifier identifier = new YangInstanceIdentifier.NodeIdentifier(qName);
+                node = readNodeIdentifierDependentNode(nodeType, identifier);
+
+            }
+        }
+        return node;
+    }
+
+    private NormalizedNode<?, ?> readNodeIdentifierDependentNode(byte nodeType, YangInstanceIdentifier.NodeIdentifier identifier)
+        throws IOException {
+
+        switch(nodeType) {
+            case NodeTypes.LEAF_NODE :
+                LOG.debug("Read leaf node");
+                // Read the object value
+                NormalizedNodeAttrBuilder leafBuilder = Builders.leafBuilder();
+                return leafBuilder.withNodeIdentifier(identifier).withValue(readObject()).build();
+
+            case NodeTypes.ANY_XML_NODE :
+                LOG.debug("Read xml node");
+                Node value = (Node) readObject();
+                return Builders.anyXmlBuilder().withValue(value).build();
+
+            case NodeTypes.MAP_NODE :
+                LOG.debug("Read map node");
+                CollectionNodeBuilder<MapEntryNode, MapNode> mapBuilder = Builders.mapBuilder().withNodeIdentifier(identifier);
+                mapBuilder = addMapNodeChildren(mapBuilder);
+                return mapBuilder.build();
+
+            case NodeTypes.CHOICE_NODE :
+                LOG.debug("Read choice node");
+                DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ChoiceNode> choiceBuilder =
+                    Builders.choiceBuilder().withNodeIdentifier(identifier);
+                choiceBuilder = addDataContainerChildren(choiceBuilder);
+                return choiceBuilder.build();
+
+            case NodeTypes.ORDERED_MAP_NODE :
+                LOG.debug("Reading ordered map node");
+                CollectionNodeBuilder<MapEntryNode, OrderedMapNode> orderedMapBuilder =
+                    Builders.orderedMapBuilder().withNodeIdentifier(identifier);
+                orderedMapBuilder = addMapNodeChildren(orderedMapBuilder);
+                return orderedMapBuilder.build();
+
+            case NodeTypes.UNKEYED_LIST :
+                LOG.debug("Read unkeyed list node");
+                CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> unkeyedListBuilder =
+                    Builders.unkeyedListBuilder().withNodeIdentifier(identifier);
+                unkeyedListBuilder = addUnkeyedListChildren(unkeyedListBuilder);
+                return unkeyedListBuilder.build();
+
+            case NodeTypes.UNKEYED_LIST_ITEM :
+                LOG.debug("Read unkeyed list item node");
+                DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, UnkeyedListEntryNode> unkeyedListEntryBuilder
+                    = Builders.unkeyedListEntryBuilder().withNodeIdentifier(identifier);
+
+                unkeyedListEntryBuilder = (DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, UnkeyedListEntryNode>)
+                    addDataContainerChildren(unkeyedListEntryBuilder);
+                return unkeyedListEntryBuilder.build();
+
+            case NodeTypes.CONTAINER_NODE :
+                LOG.debug("Read container node");
+                DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> containerBuilder =
+                    Builders.containerBuilder().withNodeIdentifier(identifier);
+
+                containerBuilder = (DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode>)
+                    addDataContainerChildren(containerBuilder);
+                return containerBuilder.build();
+
+            case NodeTypes.LEAF_SET :
+                LOG.debug("Read leaf set node");
+                ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafSetBuilder =
+                    Builders.leafSetBuilder().withNodeIdentifier(identifier);
+                leafSetBuilder = addLeafSetChildren(leafSetBuilder);
+                return leafSetBuilder.build();
+
+            default :
+                return null;
+        }
+    }
+
+    private QName readQName() throws IOException {
+        // Read in the same sequence of writing
+        String localName = readCodedString();
+        String namespace = readCodedString();
+        String revision = readCodedString();
+        String qName;
+        // Not using stringbuilder as compiler optimizes string concatenation of +
+        if(revision != null){
+            qName = "(" + namespace+ REVISION_ARG + revision + ")" +localName;
+        } else {
+            qName = "(" + namespace + ")" +localName;
+        }
+
+        return QNameFactory.create(qName);
+    }
+
+
+    private String readCodedString() throws IOException {
+        boolean readFromMap = reader.readBoolean();
+        if(readFromMap) {
+            return codedStringMap.get(reader.readInt());
+        } else {
+            String value = reader.readUTF();
+            if(value != null) {
+                codedStringMap.put(Integer.valueOf(codedStringMap.size()), value);
+            }
+            return value;
+        }
+    }
+
+    private Set<QName> readQNameSet() throws IOException{
+        // Read the children count
+        int count = reader.readInt();
+        Set<QName> children = new HashSet<>(count);
+        for(int i = 0; i<count; i++) {
+            children.add(readQName());
+        }
+        return children;
+    }
+
+    private Map<QName, Object> readKeyValueMap() throws IOException {
+        int count = reader.readInt();
+        Map<QName, Object> keyValueMap = new HashMap<>(count);
+
+        for(int i = 0; i<count; i++) {
+            keyValueMap.put(readQName(), readObject());
+        }
+
+        return keyValueMap;
+    }
+
+    private Object readObject() throws IOException {
+        byte objectType = reader.readByte();
+        switch(objectType) {
+            case ValueTypes.BITS_TYPE:
+                return readObjSet();
+
+            case ValueTypes.BOOL_TYPE :
+                return reader.readBoolean();
+
+            case ValueTypes.BYTE_TYPE :
+                return reader.readByte();
+
+            case ValueTypes.INT_TYPE :
+                return reader.readInt();
+
+            case ValueTypes.LONG_TYPE :
+                return reader.readLong();
+
+            case ValueTypes.QNAME_TYPE :
+                return readQName();
+
+            case ValueTypes.SHORT_TYPE :
+                return reader.readShort();
+
+            case ValueTypes.STRING_TYPE :
+                return reader.readUTF();
+
+            case ValueTypes.BIG_DECIMAL_TYPE :
+                return new BigDecimal(reader.readUTF());
+
+            case ValueTypes.BIG_INTEGER_TYPE :
+                return new BigInteger(reader.readUTF());
+
+            case ValueTypes.YANG_IDENTIFIER_TYPE :
+                int size = reader.readInt();
+
+                List<YangInstanceIdentifier.PathArgument> pathArguments = new ArrayList<>(size);
+
+                for(int i=0; i<size; i++) {
+                    pathArguments.add(readPathArgument());
+                }
+                return YangInstanceIdentifier.create(pathArguments);
+
+            default :
+                return null;
+        }
+    }
+
+    private Set<String> readObjSet() throws IOException {
+        int count = reader.readInt();
+        Set<String> children = new HashSet<>(count);
+        for(int i = 0; i<count; i++) {
+            children.add(readCodedString());
+        }
+        return children;
+    }
+
+    private YangInstanceIdentifier.PathArgument readPathArgument() throws IOException {
+        // read Type
+        int type = reader.readByte();
+
+        switch(type) {
+
+            case PathArgumentTypes.AUGMENTATION_IDENTIFIER :
+                return new YangInstanceIdentifier.AugmentationIdentifier(readQNameSet());
+
+            case PathArgumentTypes.NODE_IDENTIFIER :
+            return new YangInstanceIdentifier.NodeIdentifier(readQName());
+
+            case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES :
+            return new YangInstanceIdentifier.NodeIdentifierWithPredicates(readQName(), readKeyValueMap());
+
+            case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE :
+            return new YangInstanceIdentifier.NodeWithValue(readQName(), readObject());
+
+            default :
+                return null;
+        }
+    }
+
+    private ListNodeBuilder<Object, LeafSetEntryNode<Object>> addLeafSetChildren(ListNodeBuilder<Object,
+        LeafSetEntryNode<Object>> builder)
+        throws IOException {
+
+        LOG.debug("Reading children of leaf set");
+        LeafSetEntryNode<Object> child = (LeafSetEntryNode<Object>)readNormalizedNode();
+
+        while(child != null) {
+            builder.withChild(child);
+            child = (LeafSetEntryNode<Object>)readNormalizedNode();
+        }
+        return builder;
+    }
+
+    private CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> addUnkeyedListChildren(
+        CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder)
+        throws IOException{
+
+        LOG.debug("Reading children of unkeyed list");
+        UnkeyedListEntryNode child = (UnkeyedListEntryNode)readNormalizedNode();
+
+        while(child != null) {
+            builder.withChild(child);
+            child = (UnkeyedListEntryNode)readNormalizedNode();
+        }
+        return builder;
+    }
+
+    private DataContainerNodeBuilder addDataContainerChildren(DataContainerNodeBuilder builder)
+        throws IOException {
+        LOG.debug("Reading data container (leaf nodes) nodes");
+
+        DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> child =
+            (DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>) readNormalizedNode();
+
+        while(child != null) {
+            builder.withChild(child);
+            child =
+                (DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>) readNormalizedNode();
+        }
+        return builder;
+    }
+
+
+    private CollectionNodeBuilder addMapNodeChildren(CollectionNodeBuilder builder)
+        throws IOException {
+        LOG.debug("Reading map node children");
+        MapEntryNode child = (MapEntryNode)readNormalizedNode();
+
+        while(child != null){
+            builder.withChild(child);
+            child = (MapEntryNode)readNormalizedNode();
+        }
+
+        return builder;
+    }
+
+
+    @Override
+    public void close() throws IOException {
+        reader.close();
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeOutputStreamWriter.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeOutputStreamWriter.java
new file mode 100644 (file)
index 0000000..05a47a0
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ *
+ *  Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ *  This program and the accompanying materials are made available under the
+ *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ *  and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ */
+
+package org.opendaylight.controller.cluster.datastore.node.utils.stream;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * NormalizedNodeOutputStreamWriter will be used by distributed datastore to send normalized node in
+ * a stream.
+ * A stream writer wrapper around this class will write node objects to stream in recursive manner.
+ * for example - If you have a ContainerNode which has a two LeafNode as children, then
+ * you will first call {@link #startContainerNode(YangInstanceIdentifier.NodeIdentifier, int)}, then will call
+ * {@link #leafNode(YangInstanceIdentifier.NodeIdentifier, Object)} twice and then, {@link #endNode()} to end
+ * container node.
+ *
+ * Based on the each node, the node type is also written to the stream, that helps in reconstructing the object,
+ * while reading.
+ *
+ *
+ */
+
+public class NormalizedNodeOutputStreamWriter implements NormalizedNodeStreamWriter{
+
+    private DataOutputStream writer;
+
+    private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodeOutputStreamWriter.class);
+
+    private Map<String, Integer> stringCodeMap = new HashMap<>();
+
+    public NormalizedNodeOutputStreamWriter(OutputStream stream) throws IOException {
+        Preconditions.checkNotNull(stream);
+        writer = new DataOutputStream(stream);
+    }
+
+    @Override
+    public void leafNode(YangInstanceIdentifier.NodeIdentifier name, Object value) throws IOException, IllegalArgumentException {
+        Preconditions.checkNotNull(name, "Node identifier should not be null");
+        LOG.debug("Writing a new leaf node");
+        startNode(name.getNodeType(), NodeTypes.LEAF_NODE);
+
+        writeObject(value);
+    }
+
+    @Override
+    public void startLeafSet(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException {
+        Preconditions.checkNotNull(name, "Node identifier should not be null");
+        LOG.debug("Starting a new leaf set");
+
+        startNode(name.getNodeType(), NodeTypes.LEAF_SET);
+    }
+
+    @Override
+    public void leafSetEntryNode(YangInstanceIdentifier.NodeWithValue name, Object value) throws IOException, IllegalArgumentException {
+        Preconditions.checkNotNull(name, "Node identifier should not be null");
+
+        LOG.debug("Writing a new leaf set entry node");
+        startNode(name.getNodeType(), NodeTypes.LEAF_SET_ENTRY_NODE);
+
+        writeObject(value);
+    }
+
+    @Override
+    public void startContainerNode(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException {
+        Preconditions.checkNotNull(name, "Node identifier should not be null");
+
+        LOG.debug("Starting a new container node");
+
+        startNode(name.getNodeType(), NodeTypes.CONTAINER_NODE);
+    }
+
+    @Override
+    public void startUnkeyedList(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException {
+        Preconditions.checkNotNull(name, "Node identifier should not be null");
+        LOG.debug("Starting a new unkeyed list");
+
+        startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST);
+    }
+
+    @Override
+    public void startUnkeyedListItem(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint) throws IOException, IllegalStateException {
+        Preconditions.checkNotNull(name, "Node identifier should not be null");
+        LOG.debug("Starting a new unkeyed list item");
+
+        startNode(name.getNodeType(), NodeTypes.UNKEYED_LIST_ITEM);
+    }
+
+    @Override
+    public void startMapNode(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException {
+        Preconditions.checkNotNull(name, "Node identifier should not be null");
+        LOG.debug("Starting a new map node");
+
+        startNode(name.getNodeType(), NodeTypes.MAP_NODE);
+    }
+
+    @Override
+    public void startMapEntryNode(YangInstanceIdentifier.NodeIdentifierWithPredicates identifier, int childSizeHint) throws IOException, IllegalArgumentException {
+        Preconditions.checkNotNull(identifier, "Node identifier should not be null");
+        LOG.debug("Starting a new map entry node");
+        startNode(identifier.getNodeType(), NodeTypes.MAP_ENTRY_NODE);
+
+        writeKeyValueMap(identifier.getKeyValues());
+
+    }
+
+    @Override
+    public void startOrderedMapNode(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException {
+        Preconditions.checkNotNull(name, "Node identifier should not be null");
+        LOG.debug("Starting a new ordered map node");
+
+        startNode(name.getNodeType(), NodeTypes.ORDERED_MAP_NODE);
+    }
+
+    @Override
+    public void startChoiceNode(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint) throws IOException, IllegalArgumentException {
+        Preconditions.checkNotNull(name, "Node identifier should not be null");
+        LOG.debug("Starting a new choice node");
+
+        startNode(name.getNodeType(), NodeTypes.CHOICE_NODE);
+    }
+
+    @Override
+    public void startAugmentationNode(YangInstanceIdentifier.AugmentationIdentifier identifier) throws IOException, IllegalArgumentException {
+        Preconditions.checkNotNull(identifier, "Node identifier should not be null");
+        LOG.debug("Starting a new augmentation node");
+
+        writer.writeByte(NodeTypes.AUGMENTATION_NODE);
+        writeQNameSet(identifier.getPossibleChildNames());
+    }
+
+    @Override
+    public void anyxmlNode(YangInstanceIdentifier.NodeIdentifier name, Object value) throws IOException, IllegalArgumentException {
+        Preconditions.checkNotNull(name, "Node identifier should not be null");
+        LOG.debug("Writing a new xml node");
+
+        startNode(name.getNodeType(), NodeTypes.ANY_XML_NODE);
+
+        writeObject(value);
+    }
+
+    @Override
+    public void endNode() throws IOException, IllegalStateException {
+        LOG.debug("Ending the node");
+
+        writer.writeByte(NodeTypes.END_NODE);
+    }
+
+    @Override
+    public void close() throws IOException {
+        writer.close();
+    }
+
+    @Override
+    public void flush() throws IOException {
+        writer.flush();
+    }
+
+    private void startNode(final QName qName, byte nodeType) throws IOException {
+
+        Preconditions.checkNotNull(qName, "QName of node identifier should not be null.");
+        // First write the type of node
+        writer.writeByte(nodeType);
+        // Write Start Tag
+        writeQName(qName);
+    }
+
+    private void writeQName(QName qName) throws IOException {
+
+        writeCodedString(qName.getLocalName());
+        writeCodedString(qName.getNamespace().toString());
+        writeCodedString(qName.getFormattedRevision());
+    }
+
+    private void writeCodedString(String key) throws IOException {
+        Integer value = stringCodeMap.get(key);
+
+        if(value != null) {
+            writer.writeBoolean(true);
+            writer.writeInt(value);
+        } else {
+            if(key != null) {
+                stringCodeMap.put(key, Integer.valueOf(stringCodeMap.size()));
+            }
+            writer.writeBoolean(false);
+            writer.writeUTF(key);
+        }
+    }
+
+    private void writeObjSet(Set set) throws IOException {
+        if(!set.isEmpty()){
+            writer.writeInt(set.size());
+            for(Object o : set){
+                if(o instanceof String){
+                    writeCodedString(o.toString());
+                } else {
+                    throw new IllegalArgumentException("Expected value type to be String but was : " +
+                        o.toString());
+                }
+            }
+        } else {
+            writer.writeInt(0);
+        }
+    }
+
+    private void writeYangInstanceIdentifier(YangInstanceIdentifier identifier) throws IOException {
+        Iterable<YangInstanceIdentifier.PathArgument> pathArguments = identifier.getPathArguments();
+        int size = Iterables.size(pathArguments);
+        writer.writeInt(size);
+
+        for(YangInstanceIdentifier.PathArgument pathArgument : pathArguments) {
+            writePathArgument(pathArgument);
+        }
+    }
+
+    private void writePathArgument(YangInstanceIdentifier.PathArgument pathArgument) throws IOException {
+
+        byte type = PathArgumentTypes.getSerializablePathArgumentType(pathArgument);
+
+        writer.writeByte(type);
+
+        switch(type) {
+            case PathArgumentTypes.NODE_IDENTIFIER :
+
+                YangInstanceIdentifier.NodeIdentifier nodeIdentifier =
+                    (YangInstanceIdentifier.NodeIdentifier) pathArgument;
+
+                writeQName(nodeIdentifier.getNodeType());
+                break;
+
+            case PathArgumentTypes.NODE_IDENTIFIER_WITH_PREDICATES:
+
+                YangInstanceIdentifier.NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
+                    (YangInstanceIdentifier.NodeIdentifierWithPredicates) pathArgument;
+                writeQName(nodeIdentifierWithPredicates.getNodeType());
+
+                writeKeyValueMap(nodeIdentifierWithPredicates.getKeyValues());
+                break;
+
+            case PathArgumentTypes.NODE_IDENTIFIER_WITH_VALUE :
+
+                YangInstanceIdentifier.NodeWithValue nodeWithValue =
+                    (YangInstanceIdentifier.NodeWithValue) pathArgument;
+
+                writeQName(nodeWithValue.getNodeType());
+                writeObject(nodeWithValue.getValue());
+                break;
+
+            case PathArgumentTypes.AUGMENTATION_IDENTIFIER :
+
+                YangInstanceIdentifier.AugmentationIdentifier augmentationIdentifier =
+                    (YangInstanceIdentifier.AugmentationIdentifier) pathArgument;
+
+                // No Qname in augmentation identifier
+                writeQNameSet(augmentationIdentifier.getPossibleChildNames());
+                break;
+            default :
+                throw new IllegalStateException("Unknown node identifier type is found : " + pathArgument.getClass().toString() );
+        }
+    }
+
+    private void writeKeyValueMap(Map<QName, Object> keyValueMap) throws IOException {
+        if(keyValueMap != null && !keyValueMap.isEmpty()) {
+            writer.writeInt(keyValueMap.size());
+            Set<QName> qNameSet = keyValueMap.keySet();
+
+            for(QName qName : qNameSet) {
+                writeQName(qName);
+                writeObject(keyValueMap.get(qName));
+            }
+        } else {
+            writer.writeInt(0);
+        }
+    }
+
+    private void writeQNameSet(Set<QName> children) throws IOException {
+        // Write each child's qname separately, if list is empty send count as 0
+        if(children != null && !children.isEmpty()) {
+            writer.writeInt(children.size());
+            for(QName qName : children) {
+                writeQName(qName);
+            }
+        } else {
+            LOG.debug("augmentation node does not have any child");
+            writer.writeInt(0);
+        }
+    }
+
+    private void writeObject(Object value) throws IOException {
+
+        byte type = ValueTypes.getSerializableType(value);
+        // Write object type first
+        writer.writeByte(type);
+
+        switch(type) {
+            case ValueTypes.BOOL_TYPE:
+                writer.writeBoolean((Boolean) value);
+                break;
+            case ValueTypes.QNAME_TYPE:
+                writeQName((QName) value);
+                break;
+            case ValueTypes.INT_TYPE:
+                writer.writeInt((Integer) value);
+                break;
+            case ValueTypes.BYTE_TYPE:
+                writer.writeByte((Byte) value);
+                break;
+            case ValueTypes.LONG_TYPE:
+                writer.writeLong((Long) value);
+                break;
+            case ValueTypes.SHORT_TYPE:
+                writer.writeShort((Short) value);
+                break;
+            case ValueTypes.BITS_TYPE:
+                writeObjSet((Set) value);
+                break;
+            case ValueTypes.YANG_IDENTIFIER_TYPE:
+                writeYangInstanceIdentifier((YangInstanceIdentifier) value);
+                break;
+            default:
+                writer.writeUTF(value.toString());
+                break;
+        }
+    }
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeStreamReader.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeStreamReader.java
new file mode 100644 (file)
index 0000000..c619afd
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ *
+ *  Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ *  This program and the accompanying materials are made available under the
+ *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ *  and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ */
+
+
+package org.opendaylight.controller.cluster.datastore.node.utils.stream;
+
+
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import java.io.IOException;
+
+
+public interface NormalizedNodeStreamReader extends AutoCloseable {
+
+    NormalizedNode<?, ?> readNormalizedNode() throws IOException;
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeStreamWriter.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..af95b61
--- /dev/null
@@ -0,0 +1,219 @@
+
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.node.utils.stream;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Event Stream Writer based on Normalized Node tree representation
+ *
+ * <h3>Writing Event Stream</h3>
+ *
+ * <ul>
+ * <li><code>container</code> - Container node representation, start event is
+ * emitted using {@link #startContainerNode(YangInstanceIdentifier.NodeIdentifier, int)}
+ * and node end event is
+ * emitted using {@link #endNode()}. Container node is implementing
+ * {@link org.opendaylight.yangtools.yang.binding.DataObject} interface.
+ *
+ * <li><code>list</code> - YANG list statement has two representation in event
+ * stream - unkeyed list and map. Unkeyed list is YANG list which did not
+ * specify key.</li>
+ *
+ * <ul>
+ * <li><code>Map</code> - Map start event is emitted using
+ * {@link #startMapNode(YangInstanceIdentifier.NodeIdentifier, int)}
+ * and is ended using {@link #endNode()}. Each map entry start is emitted using
+ * {@link #startMapEntryNode(YangInstanceIdentifier.NodeIdentifierWithPredicates, int)}
+ * with Map of keys
+ * and finished using {@link #endNode()}.</li>
+ *
+ * <li><code>UnkeyedList</code> - Unkeyed list represent list without keys,
+ * unkeyed list start is emitted using
+ * {@link #startUnkeyedList(YangInstanceIdentifier.NodeIdentifier, int)} list
+ * end is emitted using {@link #endNode()}. Each list item is emitted using
+ * {@link #startUnkeyedListItem(YangInstanceIdentifier.NodeIdentifier, int)}
+ * and ended using {@link #endNode()}.</li>
+ * </ul>
+ *
+ * <li><code>leaf</code> - Leaf node event is emitted using
+ * {@link #leafNode(YangInstanceIdentifier.NodeIdentifier, Object)}.
+ * {@link #endNode()} MUST NOT BE emitted for
+ * leaf node.</li>
+ *
+ * <li><code>leaf-list</code> - Leaf list start is emitted using
+ * {@link #startLeafSet(YangInstanceIdentifier.NodeIdentifier, int)}.
+ * Leaf list end is emitted using
+ * {@link #endNode()}. Leaf list entries are emitted using
+ * {@link #leafSetEntryNode(YangInstanceIdentifier.NodeWithValue name, Object).
+ *
+ * <li><code>anyxml - Anyxml node event is emitted using
+ * {@link #leafNode(YangInstanceIdentifier.NodeIdentifier, Object)}. {@link #endNode()} MUST NOT BE emitted
+ * for anyxml node.</code></li>
+ *
+ *
+ * <li><code>choice</code> Choice node event is emmited by
+ * {@link #startChoiceNode(YangInstanceIdentifier.NodeIdentifier, int)} event and
+ * finished by invoking {@link #endNode()}
+ * <li>
+ * <code>augment</code> - Represents augmentation, augmentation node is started
+ * by invoking {@link #startAugmentationNode(YangInstanceIdentifier.AugmentationIdentifier)} and
+ * finished by invoking {@link #endNode()}.</li>
+ *
+ * </ul>
+ *
+ * <h3>Implementation notes</h3>
+ *
+ * <p>
+ * Implementations of this interface must not hold user suppled objects
+ * and resources needlessly.
+ *
+ */
+
+public interface NormalizedNodeStreamWriter extends Closeable, Flushable {
+
+    public final int UNKNOWN_SIZE = -1;
+
+    /**
+     * Write the leaf node identifier and value to the stream.
+     * @param name
+     * @param value
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
+    void leafNode(YangInstanceIdentifier.NodeIdentifier name, Object value)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * Start writing leaf Set node. You must call {@link #endNode()} once you are done writing all of its children.
+     * @param name
+     * @param childSizeHint is the estimated children count. Usage is optional in implementation.
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
+    void startLeafSet(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * Write the leaf Set Entry Node object to the stream with identifier and value.
+     * @param name
+     * @param value
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
+    void leafSetEntryNode(YangInstanceIdentifier.NodeWithValue name, Object value)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * Start writing container node. You must call {@link #endNode()} once you are done writing all of its children.
+     * @param name
+     * @param childSizeHint is the estimated children count. Usage is optional in implementation.
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
+    void startContainerNode(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * Start writing unkeyed list node. You must call {@link #endNode()} once you are done writing all of its children.
+     * @param name
+     * @param childSizeHint is the estimated children count. Usage is optional in implementation.
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
+    void startUnkeyedList(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * Start writing unkeyed list item. You must call {@link #endNode()} once you are done writing all of its children.
+     * @param name
+     * @param childSizeHint is the estimated children count. Usage is optional in implementation.
+     * @throws IOException
+     * @throws IllegalStateException
+     */
+    void startUnkeyedListItem(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint)
+        throws IOException, IllegalStateException;
+
+    /**
+     * Start writing map node. You must call {@link #endNode()} once you are done writing all of its children.
+     * @param name
+     * @param childSizeHint is the estimated children count. Usage is optional in implementation.
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
+    void startMapNode(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * Start writing map entry node. You must call {@link #endNode()} once you are done writing all of its children.
+     * @param identifier
+     * @param childSizeHint is the estimated children count. Usage is optional in implementation.
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
+    void startMapEntryNode(YangInstanceIdentifier.NodeIdentifierWithPredicates identifier, int childSizeHint)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * Start writing ordered map node. You must call {@link #endNode()} once you are done writing all of its children.
+     * @param name
+     * @param childSizeHint is the estimated children count. Usage is optional in implementation.
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
+    void startOrderedMapNode(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * Start writing choice node. You must call {@link #endNode()} once you are done writing all of its children.
+     * @param name
+     * @param childSizeHint is the estimated children count. Usage is optional in implementation.
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
+    void startChoiceNode(YangInstanceIdentifier.NodeIdentifier name, int childSizeHint)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * Start writing augmentation node. You must call {@link #endNode()} once you are done writing all of its children.
+     * @param identifier
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
+    void startAugmentationNode(YangInstanceIdentifier.AugmentationIdentifier identifier)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * Write any xml node identifier and value to the stream
+     * @param name
+     * @param value
+     * @throws IOException
+     * @throws IllegalArgumentException
+     */
+    void anyxmlNode(YangInstanceIdentifier.NodeIdentifier name, Object value)
+        throws IOException, IllegalArgumentException;
+
+    /**
+     * This method should be used to add end symbol/identifier of node in the stream.
+     * @throws IOException
+     * @throws IllegalStateException
+     */
+    void endNode() throws IOException, IllegalStateException;
+
+    @Override
+    void close() throws IOException;
+
+    @Override
+    void flush() throws IOException;
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/PathArgumentTypes.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/PathArgumentTypes.java
new file mode 100644 (file)
index 0000000..b01beb8
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.node.utils.stream;
+
+import com.google.common.collect.ImmutableMap;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+import java.util.Map;
+
+public class PathArgumentTypes {
+    public static final byte AUGMENTATION_IDENTIFIER = 1;
+    public static final byte NODE_IDENTIFIER = 2;
+    public static final byte NODE_IDENTIFIER_WITH_VALUE = 3;
+    public static final byte NODE_IDENTIFIER_WITH_PREDICATES = 4;
+
+    private static Map<Class<?>, Byte> CLASS_TO_ENUM_MAP =
+            ImmutableMap.<Class<?>, Byte>builder().
+                put(YangInstanceIdentifier.AugmentationIdentifier.class, AUGMENTATION_IDENTIFIER).
+                put(YangInstanceIdentifier.NodeIdentifier.class, NODE_IDENTIFIER).
+                put(YangInstanceIdentifier.NodeIdentifierWithPredicates.class, NODE_IDENTIFIER_WITH_PREDICATES).
+                put(YangInstanceIdentifier.NodeWithValue.class, NODE_IDENTIFIER_WITH_VALUE).build();
+
+    public static byte getSerializablePathArgumentType(YangInstanceIdentifier.PathArgument pathArgument){
+
+        Byte type = CLASS_TO_ENUM_MAP.get(pathArgument.getClass());
+        if(type == null) {
+            throw new IllegalArgumentException("Unknown type of PathArgument = " + pathArgument);
+        }
+
+        return type;
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypes.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/ValueTypes.java
new file mode 100644 (file)
index 0000000..6035e3c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.node.utils.stream;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class ValueTypes {
+    public static final byte SHORT_TYPE = 1;
+    public static final byte BYTE_TYPE = 2;
+    public static final byte INT_TYPE = 3;
+    public static final byte LONG_TYPE = 4;
+    public static final byte BOOL_TYPE = 5;
+    public static final byte QNAME_TYPE = 6;
+    public static final byte BITS_TYPE = 7;
+    public static final byte YANG_IDENTIFIER_TYPE = 8;
+    public static final byte STRING_TYPE = 9;
+    public static final byte BIG_INTEGER_TYPE = 10;
+    public static final byte BIG_DECIMAL_TYPE = 11;
+
+    private static Map<Class, Byte> types = new HashMap<>();
+
+    static {
+        types.put(String.class, Byte.valueOf(STRING_TYPE));
+        types.put(Byte.class, Byte.valueOf(BYTE_TYPE));
+        types.put(Integer.class, Byte.valueOf(INT_TYPE));
+        types.put(Long.class, Byte.valueOf(LONG_TYPE));
+        types.put(Boolean.class, Byte.valueOf(BOOL_TYPE));
+        types.put(QName.class, Byte.valueOf(QNAME_TYPE));
+        types.put(Set.class, Byte.valueOf(BITS_TYPE));
+        types.put(YangInstanceIdentifier.class, Byte.valueOf(YANG_IDENTIFIER_TYPE));
+        types.put(Short.class, Byte.valueOf(SHORT_TYPE));
+        types.put(BigInteger.class, Byte.valueOf(BIG_INTEGER_TYPE));
+        types.put(BigDecimal.class, Byte.valueOf(BIG_DECIMAL_TYPE));
+    }
+
+    public static final byte getSerializableType(Object node){
+        Preconditions.checkNotNull(node, "node should not be null");
+
+        Byte type = types.get(node.getClass());
+        if(type != null) {
+            return type;
+        } else if(node instanceof Set){
+            return BITS_TYPE;
+        }
+
+        throw new IllegalArgumentException("Unknown value type " + node.getClass().getSimpleName());
+    }
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/util/EncoderDecoderUtil.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/util/EncoderDecoderUtil.java
deleted file mode 100644 (file)
index 90f5cf3..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- *
- *  Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- *  This program and the accompanying materials are made available under the
- *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
- *  and is available at http://www.eclipse.org/legal/epl-v10.html
- *
- */
-
-package org.opendaylight.controller.cluster.datastore.util;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-import org.opendaylight.controller.protobuff.messages.common.SimpleNormalizedNodeMessage;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer.DomFromNormalizedNodeSerializerFactory;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.TransformerFactoryConfigurationError;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import java.io.ByteArrayInputStream;
-import java.io.StringWriter;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-/*
- *
- * <code>EncoderDecoderUtil</code> helps in wrapping the NormalizedNode into a SimpleNormalizedNode
- * protobuf message containing the XML representation of the NormalizeNode
- *
- * @author: syedbahm
- */
-public class EncoderDecoderUtil {
-    static DocumentBuilderFactory factory;
-
-    private static DomFromNormalizedNodeSerializerFactory serializerFactory =
-        DomFromNormalizedNodeSerializerFactory
-            .getInstance(XmlDocumentUtils.getDocument(),
-                DomUtils.defaultValueCodecProvider());
-
-    private static DomToNormalizedNodeParserFactory parserFactory =
-        DomToNormalizedNodeParserFactory
-            .getInstance(DomUtils.defaultValueCodecProvider());
-
-    static {
-        factory = DocumentBuilderFactory.newInstance();
-        factory.setNamespaceAware(true);
-        factory.setCoalescing(true);
-        factory.setIgnoringElementContentWhitespace(true);
-        factory.setIgnoringComments(true);
-    }
-
-    private static DataSchemaNode findChildNode(Collection<DataSchemaNode> children,
-        String name) {
-        List<DataNodeContainer> containers = Lists.newArrayList();
-
-        for (DataSchemaNode dataSchemaNode : children) {
-            if (dataSchemaNode.getQName().getLocalName().equals(name))
-                return dataSchemaNode;
-            if (dataSchemaNode instanceof DataNodeContainer) {
-                containers.add((DataNodeContainer) dataSchemaNode);
-            } else if (dataSchemaNode instanceof ChoiceNode) {
-                containers.addAll(((ChoiceNode) dataSchemaNode).getCases());
-            }
-        }
-
-        for (DataNodeContainer container : containers) {
-            DataSchemaNode retVal =
-                findChildNode(container.getChildNodes(), name);
-            if (retVal != null) {
-                return retVal;
-            }
-        }
-
-        return null;
-    }
-
-    private static DataSchemaNode getSchemaNode(SchemaContext context,
-        QName qname) {
-
-        for (Module module : context
-            .findModuleByNamespace(qname.getNamespace())) {
-            // we will take the first child as the start of the
-            if (module.getChildNodes() != null || !module.getChildNodes()
-                .isEmpty()) {
-
-                DataSchemaNode found =
-                    findChildNode(module.getChildNodes(), qname.getLocalName());
-                return found;
-            }
-        }
-        return null;
-    }
-
-    private static String toString(Element xml) {
-        try {
-            Transformer transformer =
-                TransformerFactory.newInstance().newTransformer();
-            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-
-            StreamResult result = new StreamResult(new StringWriter());
-            DOMSource source = new DOMSource(xml);
-            transformer.transform(source, result);
-
-            return result.getWriter().toString();
-        } catch (IllegalArgumentException | TransformerFactoryConfigurationError
-            | TransformerException e) {
-            throw new RuntimeException("Unable to serialize xml element " + xml,
-                e);
-        }
-    }
-
-  private static String toString(Iterable<Element> xmlIterable) {
-    try {
-      Transformer transformer =
-          TransformerFactory.newInstance().newTransformer();
-      transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-
-      StreamResult result = new StreamResult(new StringWriter());
-      Iterator iterator = xmlIterable.iterator();
-      DOMSource source;
-      if(iterator.hasNext()) {
-        source = new DOMSource((org.w3c.dom.Node) iterator.next());
-        transformer.transform(source, result);
-        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
-      }
-
-      while(iterator.hasNext()) {
-        source = new DOMSource((org.w3c.dom.Node) iterator.next());
-        transformer.transform(source, result);
-      }
-      System.out.println(result.getWriter().toString());
-      return result.getWriter().toString();
-    } catch (IllegalArgumentException | TransformerFactoryConfigurationError
-        | TransformerException e) {
-      throw new RuntimeException("Unable to serialize xml element(s) " + xmlIterable.toString(),
-          e);
-    }
-  }
-
-    private static Iterable<Element> serialize(DataSchemaNode schemaNode, NormalizedNode normalizedNode){
-        if(schemaNode instanceof ContainerSchemaNode){      //1
-            return serializerFactory
-                .getContainerNodeSerializer()
-                .serialize((ContainerSchemaNode) schemaNode,
-                    (ContainerNode) normalizedNode);
-        } else if(schemaNode instanceof ChoiceNode){        //2
-            return serializerFactory
-                .getChoiceNodeSerializer()
-                .serialize((ChoiceNode) schemaNode,
-                    (org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode) normalizedNode);
-        } else if(schemaNode instanceof LeafSchemaNode){    //3
-            return serializerFactory
-                .getLeafNodeSerializer()
-                .serialize((LeafSchemaNode) schemaNode, (LeafNode) normalizedNode);
-        } else if(schemaNode instanceof ListSchemaNode){    //4
-            return serializerFactory
-                .getMapNodeSerializer()
-                .serialize((ListSchemaNode) schemaNode, (MapNode) normalizedNode);
-        } else if(schemaNode instanceof LeafListSchemaNode){    //5
-            return serializerFactory
-                .getLeafSetNodeSerializer()
-                .serialize((LeafListSchemaNode) schemaNode, (LeafSetNode) normalizedNode);
-        } else if(schemaNode instanceof AugmentationSchema){//6
-            return serializerFactory
-                .getAugmentationNodeSerializer()
-                .serialize((AugmentationSchema) schemaNode, (AugmentationNode) normalizedNode);
-        } else if(schemaNode instanceof ListSchemaNode && normalizedNode instanceof LeafSetEntryNode){    //7
-            return serializerFactory
-                .getLeafSetEntryNodeSerializer()
-                .serialize((LeafListSchemaNode) schemaNode, (LeafSetEntryNode) normalizedNode);
-        } else if(schemaNode instanceof ListSchemaNode){    //8
-            return serializerFactory
-                .getMapEntryNodeSerializer()
-                .serialize((ListSchemaNode) schemaNode, (MapEntryNode) normalizedNode);
-        }
-
-
-
-        throw new UnsupportedOperationException(schemaNode.getClass().toString());
-    }
-
-    private static NormalizedNode parse(Document doc, DataSchemaNode schemaNode){
-        if(schemaNode instanceof ContainerSchemaNode){
-            return parserFactory
-                .getContainerNodeParser()
-                .parse(Collections.singletonList(doc.getDocumentElement()),
-                    (ContainerSchemaNode) schemaNode);
-
-        } else if(schemaNode instanceof ChoiceNode){
-            return parserFactory
-                .getChoiceNodeParser()
-                .parse(Collections.singletonList(doc.getDocumentElement()),
-                    (ChoiceNode) schemaNode);
-        } else if(schemaNode instanceof LeafNode){
-            return parserFactory
-                .getLeafNodeParser()
-                .parse(Collections.singletonList(doc.getDocumentElement()),
-                    (LeafSchemaNode) schemaNode);
-        } else if(schemaNode instanceof ListSchemaNode){
-            return parserFactory
-                .getMapNodeParser()
-                .parse(Collections.singletonList(doc.getDocumentElement()),
-                    (ListSchemaNode) schemaNode);
-        } else if(schemaNode instanceof LeafListSchemaNode){
-            return parserFactory
-                .getLeafSetNodeParser()
-                .parse(Collections.singletonList(doc.getDocumentElement()),
-                    (LeafListSchemaNode) schemaNode);
-        } else if(schemaNode instanceof AugmentationSchema){
-            return parserFactory
-                .getAugmentationNodeParser()
-                .parse(Collections.singletonList(doc.getDocumentElement()),
-                    (AugmentationSchema) schemaNode);
-        } else if(schemaNode instanceof ListSchemaNode){
-            return parserFactory
-                .getMapEntryNodeParser()
-                .parse(Collections.singletonList(doc.getDocumentElement()),
-                    (ListSchemaNode) schemaNode);
-
-        }
-
-        throw new UnsupportedOperationException(schemaNode.getClass().toString());
-    }
-
-
-    /**
-     * Helps in generation of NormalizedNodeXml message for the supplied NormalizedNode
-     *
-     * @param sc             --SchemaContext
-     * @param normalizedNode -- Normalized Node to be encoded
-     * @return SimpleNormalizedNodeMessage.NormalizedNodeXml
-     */
-    public static SimpleNormalizedNodeMessage.NormalizedNodeXml encode(
-        SchemaContext sc, NormalizedNode<?, ?> normalizedNode) {
-
-        Preconditions.checkArgument(sc != null, "Schema context found null");
-
-        Preconditions.checkArgument(normalizedNode != null,
-            "normalized node found null");
-
-        DataSchemaNode schemaNode = getSchemaNode(sc,
-            normalizedNode.getIdentifier()
-                .getNodeType()
-        );
-
-        Preconditions.checkState(schemaNode != null,
-            "Couldn't find schema node for " + normalizedNode.getIdentifier());
-
-        Iterable<Element> els = serialize(schemaNode, normalizedNode);
-
-        String xmlString = toString(els.iterator().next());
-        SimpleNormalizedNodeMessage.NormalizedNodeXml.Builder builder =
-            SimpleNormalizedNodeMessage.NormalizedNodeXml.newBuilder();
-        builder.setXmlString(xmlString);
-        builder
-            .setNodeIdentifier(normalizedNode.getIdentifier()
-                .getNodeType().toString());
-        return builder.build();
-
-    }
-
-    /**
-     * Utilizes the SimpleNormalizedNodeMessage.NormalizedNodeXml to convert into NormalizedNode
-     *
-     * @param sc                -- schema context
-     * @param normalizedNodeXml -- containing the normalized Node XML
-     * @return NormalizedNode return
-     * @throws Exception
-     */
-
-    public static NormalizedNode decode(SchemaContext sc,
-        SimpleNormalizedNodeMessage.NormalizedNodeXml normalizedNodeXml)
-        throws Exception {
-
-        Preconditions
-            .checkArgument(sc != null, "schema context seems to be null");
-
-        Preconditions.checkArgument(normalizedNodeXml != null,
-            "SimpleNormalizedNodeMessage.NormalizedNodeXml found to be null");
-        QName qname = QName.create(normalizedNodeXml.getNodeIdentifier());
-
-        // here we will try to get back the NormalizedNode
-        DataSchemaNode schemaNode = getSchemaNode(sc, qname);
-
-        // now we need to read the XML
-        Document doc =
-            factory.newDocumentBuilder().parse(
-                new ByteArrayInputStream(
-                    normalizedNodeXml.getXmlString().getBytes(
-                        "utf-8"))
-            );
-
-        doc.getDocumentElement().normalize();
-
-
-        return parse(doc, schemaNode);
-    }
-
-
-
-}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeStreamReaderWriterTest.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeStreamReaderWriterTest.java
new file mode 100644 (file)
index 0000000..052f609
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *
+ *  Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ *  This program and the accompanying materials are made available under the
+ *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ *  and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ */
+
+package org.opendaylight.controller.cluster.datastore.node.utils.stream;
+
+
+import org.apache.commons.lang.SerializationUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.util.TestModel;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import static org.junit.Assert.fail;
+
+public class NormalizedNodeStreamReaderWriterTest {
+
+    final NormalizedNode<?, ?> input = TestModel.createTestContainer();
+
+    @Test
+    public void testNormalizedNodeStreamReaderWriter() {
+
+        byte[] byteData = null;
+
+        try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+            NormalizedNodeStreamWriter writer = new NormalizedNodeOutputStreamWriter(byteArrayOutputStream)) {
+
+            NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(writer);
+            normalizedNodeWriter.write(input);
+            byteData = byteArrayOutputStream.toByteArray();
+
+        } catch (IOException e) {
+            fail("Writing to OutputStream failed :" + e.toString());
+        }
+
+        try(NormalizedNodeInputStreamReader reader = new NormalizedNodeInputStreamReader(new ByteArrayInputStream(byteData))) {
+
+            NormalizedNode<?,?> node = reader.readNormalizedNode();
+            Assert.assertEquals(input, node);
+
+        } catch (IOException e) {
+            fail("Reading from InputStream failed :" + e.toString());
+        }
+    }
+
+    @Test
+    public void testWithSerializable() {
+        SampleNormalizedNodeSerializable serializable = new SampleNormalizedNodeSerializable(input);
+        SampleNormalizedNodeSerializable clone = (SampleNormalizedNodeSerializable)SerializationUtils.clone(serializable);
+
+        Assert.assertEquals(input, clone.getInput());
+
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeWriter.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/NormalizedNodeWriter.java
new file mode 100644 (file)
index 0000000..845038e
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.datastore.node.utils.stream;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.util.Collection;
+
+import static org.opendaylight.controller.cluster.datastore.node.utils.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+
+/**
+ * This class is used only for testing purpose for now, we may use similar logic while integrating
+ * with cluster
+ */
+
+public class NormalizedNodeWriter implements Closeable, Flushable {
+    private final NormalizedNodeStreamWriter writer;
+
+    private NormalizedNodeWriter(final NormalizedNodeStreamWriter writer) {
+        this.writer = Preconditions.checkNotNull(writer);
+    }
+
+    protected final NormalizedNodeStreamWriter getWriter() {
+        return writer;
+    }
+
+    /**
+     * Create a new writer backed by a {@link org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter}.
+     *
+     * @param writer Back-end writer
+     * @return A new instance.
+     */
+    public static NormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer) {
+        return new NormalizedNodeWriter(writer);
+    }
+
+
+    /**
+     * Iterate over the provided {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode} and emit write
+     * events to the encapsulated {@link org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter}.
+     *
+     * @param node Node
+     * @return
+     * @throws java.io.IOException when thrown from the backing writer.
+     */
+    public final NormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+        if (wasProcessedAsComplexNode(node)) {
+            return this;
+        }
+
+        if (wasProcessAsSimpleNode(node)) {
+            return this;
+        }
+
+        throw new IllegalStateException("It wasn't possible to serialize node " + node);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        writer.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        writer.flush();
+        writer.close();
+    }
+
+    /**
+     * Emit a best guess of a hint for a particular set of children. It evaluates the
+     * iterable to see if the size can be easily gotten to. If it is, we hint at the
+     * real number of child nodes. Otherwise we emit UNKNOWN_SIZE.
+     *
+     * @param children Child nodes
+     * @return Best estimate of the collection size required to hold all the children.
+     */
+    static final int childSizeHint(final Iterable<?> children) {
+        return (children instanceof Collection) ? ((Collection<?>) children).size() : UNKNOWN_SIZE;
+    }
+
+    private boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
+        if (node instanceof LeafSetEntryNode) {
+            final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>)node;
+            writer.leafSetEntryNode(nodeAsLeafList.getIdentifier(), nodeAsLeafList.getValue());
+            return true;
+        } else if (node instanceof LeafNode) {
+            final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node;
+            writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue());
+            return true;
+        } else if (node instanceof AnyXmlNode) {
+            final AnyXmlNode anyXmlNode = (AnyXmlNode)node;
+            writer.anyxmlNode(anyXmlNode.getIdentifier(), anyXmlNode.getValue());
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Emit events for all children and then emit an endNode() event.
+     *
+     * @param children Child iterable
+     * @return True
+     * @throws java.io.IOException when the writer reports it
+     */
+    protected final boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children) throws IOException {
+        for (NormalizedNode<?, ?> child : children) {
+            write(child);
+        }
+
+        writer.endNode();
+        return true;
+    }
+
+    protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
+        writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
+        return writeChildren(node.getValue());
+    }
+
+    private boolean wasProcessedAsComplexNode(final NormalizedNode<?, ?> node) throws IOException {
+        if (node instanceof ContainerNode) {
+            final ContainerNode n = (ContainerNode) node;
+            writer.startContainerNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof MapEntryNode) {
+            return writeMapEntryNode((MapEntryNode) node);
+        }
+        if (node instanceof UnkeyedListEntryNode) {
+            final UnkeyedListEntryNode n = (UnkeyedListEntryNode) node;
+            writer.startUnkeyedListItem(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof ChoiceNode) {
+            final ChoiceNode n = (ChoiceNode) node;
+            writer.startChoiceNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof AugmentationNode) {
+            final AugmentationNode n = (AugmentationNode) node;
+            writer.startAugmentationNode(n.getIdentifier());
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof UnkeyedListNode) {
+            final UnkeyedListNode n = (UnkeyedListNode) node;
+            writer.startUnkeyedList(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof OrderedMapNode) {
+            final OrderedMapNode n = (OrderedMapNode) node;
+            writer.startOrderedMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof MapNode) {
+            final MapNode n = (MapNode) node;
+            writer.startMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+        if (node instanceof LeafSetNode) {
+            //covers also OrderedLeafSetNode for which doesn't exist start* method
+            final LeafSetNode<?> n = (LeafSetNode<?>) node;
+            writer.startLeafSet(n.getIdentifier(), childSizeHint(n.getValue()));
+            return writeChildren(n.getValue());
+        }
+
+        return false;
+    }
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/SampleNormalizedNodeSerializable.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/stream/SampleNormalizedNodeSerializable.java
new file mode 100644 (file)
index 0000000..33d48a5
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.node.utils.stream;
+
+
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.net.URISyntaxException;
+
+public class SampleNormalizedNodeSerializable implements Serializable {
+
+    private NormalizedNode<?, ?> input;
+
+    public SampleNormalizedNodeSerializable(NormalizedNode<?, ?> input) {
+        this.input = input;
+    }
+
+    public NormalizedNode<?, ?> getInput() {
+        return input;
+    }
+
+    private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException, URISyntaxException {
+        NormalizedNodeStreamReader reader = new NormalizedNodeInputStreamReader(stream);
+        this.input = reader.readNormalizedNode();
+    }
+
+    private void writeObject(final ObjectOutputStream stream) throws IOException {
+        NormalizedNodeStreamWriter writer = new NormalizedNodeOutputStreamWriter(stream);
+        NormalizedNodeWriter normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(writer);
+
+        normalizedNodeWriter.write(this.input);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/util/NormalizedNodeXmlConverterTest.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/util/NormalizedNodeXmlConverterTest.java
deleted file mode 100644 (file)
index 853b3e2..0000000
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- *
- *  Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- *  This program and the accompanying materials are made available under the
- *  terms of the Eclipse Public License v1.0 which accompanies this distribution,
- *  and is available at http://www.eclipse.org/legal/epl-v10.html
- *
- */
-package org.opendaylight.controller.cluster.datastore.util;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import org.custommonkey.xmlunit.Diff;
-import org.custommonkey.xmlunit.XMLUnit;
-import org.junit.Test;
-import org.opendaylight.controller.protobuff.messages.common.SimpleNormalizedNodeMessage;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
-import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer.DomFromNormalizedNodeSerializerFactory;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.xml.sax.SAXException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.TransformerFactoryConfigurationError;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.net.URI;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-
-/**
- * Two of the testcases in the yangtools/yang-data-impl are leveraged (with modification) to create
- * the serialization of NormalizedNode using the ProtocolBuffer
- *
- * @syedbahm
- *
- */
-
-
-public class NormalizedNodeXmlConverterTest {
-  private static final Logger logger = LoggerFactory
-      .getLogger(NormalizedNodeXmlConverterTest.class);
-  public static final String NAMESPACE =
-      "urn:opendaylight:params:xml:ns:yang:controller:test";
-  private static Date revision;
-  private ContainerNode expectedNode;
-  private ContainerSchemaNode containerNode;
-  private String xmlPath;
-
-  static {
-    try {
-      revision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-03-13");
-    } catch (ParseException e) {
-      throw new RuntimeException(e);
-    }
-  }
-
-  public static DataSchemaNode getSchemaNode(final SchemaContext context,
-      final String moduleName, final String childNodeName) {
-    for (Module module : context.getModules()) {
-      if (module.getName().equals(moduleName)) {
-        DataSchemaNode found =
-            findChildNode(module.getChildNodes(), childNodeName);
-        Preconditions.checkState(found != null, "Unable to find %s",
-            childNodeName);
-        return found;
-      }
-    }
-    throw new IllegalStateException("Unable to find child node "
-        + childNodeName);
-  }
-
-  static DataSchemaNode findChildNode(
-      final Collection<DataSchemaNode> children, final String name) {
-    List<DataNodeContainer> containers = Lists.newArrayList();
-
-    for (DataSchemaNode dataSchemaNode : children) {
-      if (dataSchemaNode.getQName().getLocalName().equals(name)) {
-        return dataSchemaNode;
-      }
-      if (dataSchemaNode instanceof DataNodeContainer) {
-        containers.add((DataNodeContainer) dataSchemaNode);
-      } else if (dataSchemaNode instanceof ChoiceNode) {
-        containers.addAll(((ChoiceNode) dataSchemaNode).getCases());
-      }
-    }
-
-    for (DataNodeContainer container : containers) {
-      DataSchemaNode retVal = findChildNode(container.getChildNodes(), name);
-      if (retVal != null) {
-        return retVal;
-      }
-    }
-
-    return null;
-  }
-
-  public static YangInstanceIdentifier.NodeIdentifier getNodeIdentifier(
-      final String localName) {
-    return new YangInstanceIdentifier.NodeIdentifier(QName.create(
-        URI.create(NAMESPACE), revision, localName));
-  }
-
-  public static YangInstanceIdentifier.AugmentationIdentifier getAugmentIdentifier(
-      final String... childNames) {
-    Set<QName> qn = Sets.newHashSet();
-
-    for (String childName : childNames) {
-      qn.add(getNodeIdentifier(childName).getNodeType());
-    }
-
-    return new YangInstanceIdentifier.AugmentationIdentifier(qn);
-  }
-
-
-  public static ContainerNode augmentChoiceExpectedNode() {
-
-    DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> b =
-        Builders.containerBuilder();
-    b.withNodeIdentifier(getNodeIdentifier("container"));
-
-    b.withChild(Builders
-        .choiceBuilder()
-        .withNodeIdentifier(getNodeIdentifier("ch2"))
-        .withChild(
-            Builders.leafBuilder()
-                .withNodeIdentifier(getNodeIdentifier("c2Leaf")).withValue("2")
-                .build())
-        .withChild(
-            Builders
-                .choiceBuilder()
-                .withNodeIdentifier(getNodeIdentifier("c2DeepChoice"))
-                .withChild(
-                    Builders
-                        .leafBuilder()
-                        .withNodeIdentifier(
-                            getNodeIdentifier("c2DeepChoiceCase1Leaf2"))
-                        .withValue("2").build()).build()).build());
-
-    b.withChild(Builders
-        .choiceBuilder()
-        .withNodeIdentifier(getNodeIdentifier("ch3"))
-        .withChild(
-            Builders.leafBuilder()
-                .withNodeIdentifier(getNodeIdentifier("c3Leaf")).withValue("3")
-                .build()).build());
-
-    b.withChild(Builders
-        .augmentationBuilder()
-        .withNodeIdentifier(getAugmentIdentifier("augLeaf"))
-        .withChild(
-            Builders.leafBuilder()
-                .withNodeIdentifier(getNodeIdentifier("augLeaf"))
-                .withValue("augment").build()).build());
-
-    b.withChild(Builders
-        .augmentationBuilder()
-        .withNodeIdentifier(getAugmentIdentifier("ch"))
-        .withChild(
-            Builders
-                .choiceBuilder()
-                .withNodeIdentifier(getNodeIdentifier("ch"))
-                .withChild(
-                    Builders.leafBuilder()
-                        .withNodeIdentifier(getNodeIdentifier("c1Leaf"))
-                        .withValue("1").build())
-                .withChild(
-                    Builders
-                        .augmentationBuilder()
-                        .withNodeIdentifier(
-                            getAugmentIdentifier("c1Leaf_AnotherAugment",
-                                "deepChoice"))
-                        .withChild(
-                            Builders
-                                .leafBuilder()
-                                .withNodeIdentifier(
-                                    getNodeIdentifier("c1Leaf_AnotherAugment"))
-                                .withValue("1").build())
-                        .withChild(
-                            Builders
-                                .choiceBuilder()
-                                .withNodeIdentifier(
-                                    getNodeIdentifier("deepChoice"))
-                                .withChild(
-                                    Builders
-                                        .leafBuilder()
-                                        .withNodeIdentifier(
-                                            getNodeIdentifier("deepLeafc1"))
-                                        .withValue("1").build()).build())
-                        .build()).build()).build());
-
-    return b.build();
-  }
-
-
-
-  public void init(final String yangPath, final String xmlPath,
-      final ContainerNode expectedNode) throws Exception {
-    SchemaContext schema = parseTestSchema(yangPath);
-    this.xmlPath = xmlPath;
-    this.containerNode =
-        (ContainerSchemaNode) getSchemaNode(schema, "test", "container");
-    this.expectedNode = expectedNode;
-  }
-
-  SchemaContext parseTestSchema(final String yangPath) throws Exception {
-
-    YangParserImpl yangParserImpl = new YangParserImpl();
-    InputStream stream =
-        NormalizedNodeXmlConverterTest.class.getResourceAsStream(yangPath);
-    ArrayList<InputStream> al = new ArrayList<InputStream>();
-    al.add(stream);
-    Set<Module> modules = yangParserImpl.parseYangModelsFromStreams(al);
-    return yangParserImpl.resolveSchemaContext(modules);
-
-  }
-
-
-  @Test
-  public void testConversionWithAugmentChoice() throws Exception {
-    init("/augment_choice.yang", "/augment_choice.xml",
-        augmentChoiceExpectedNode());
-    Document doc = loadDocument(xmlPath);
-
-    ContainerNode built =
-        DomToNormalizedNodeParserFactory
-            .getInstance(DomUtils.defaultValueCodecProvider())
-            .getContainerNodeParser()
-            .parse(Collections.singletonList(doc.getDocumentElement()),
-                containerNode);
-
-    if (expectedNode != null) {
-      junit.framework.Assert.assertEquals(expectedNode, built);
-    }
-
-    logger.info("{}", built);
-
-    Iterable<Element> els =
-        DomFromNormalizedNodeSerializerFactory
-            .getInstance(XmlDocumentUtils.getDocument(),
-                DomUtils.defaultValueCodecProvider())
-            .getContainerNodeSerializer().serialize(containerNode, built);
-
-    Element el = els.iterator().next();
-
-    XMLUnit.setIgnoreWhitespace(true);
-    XMLUnit.setIgnoreComments(true);
-
-    System.out.println(toString(doc.getDocumentElement()));
-    System.out.println(toString(el));
-
-    new Diff(XMLUnit.buildControlDocument(toString(doc.getDocumentElement())),
-        XMLUnit.buildTestDocument(toString(el))).similar();
-  }
-
-  private static ContainerNode listLeafListWithAttributes() {
-    DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> b =
-        Builders.containerBuilder();
-    b.withNodeIdentifier(getNodeIdentifier("container"));
-
-    CollectionNodeBuilder<MapEntryNode, MapNode> listBuilder =
-        Builders.mapBuilder().withNodeIdentifier(getNodeIdentifier("list"));
-
-    Map<QName, Object> predicates = Maps.newHashMap();
-    predicates.put(getNodeIdentifier("uint32InList").getNodeType(), 3L);
-
-    DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> list1Builder =
-        Builders.mapEntryBuilder().withNodeIdentifier(
-            new YangInstanceIdentifier.NodeIdentifierWithPredicates(
-                getNodeIdentifier("list").getNodeType(), predicates));
-    NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>> uint32InListBuilder =
-        Builders.leafBuilder().withNodeIdentifier(
-            getNodeIdentifier("uint32InList"));
-
-    list1Builder.withChild(uint32InListBuilder.withValue(3L).build());
-
-    listBuilder.withChild(list1Builder.build());
-    b.withChild(listBuilder.build());
-
-    NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>> booleanBuilder =
-        Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("boolean"));
-    booleanBuilder.withValue(false);
-    b.withChild(booleanBuilder.build());
-
-    ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafListBuilder =
-        Builders.leafSetBuilder().withNodeIdentifier(
-            getNodeIdentifier("leafList"));
-
-    NormalizedNodeBuilder<YangInstanceIdentifier.NodeWithValue, Object, LeafSetEntryNode<Object>> leafList1Builder =
-        Builders.leafSetEntryBuilder().withNodeIdentifier(
-            new YangInstanceIdentifier.NodeWithValue(getNodeIdentifier(
-                "leafList").getNodeType(), "a"));
-
-    leafList1Builder.withValue("a");
-
-    leafListBuilder.withChild(leafList1Builder.build());
-    b.withChild(leafListBuilder.build());
-
-    return b.build();
-  }
-
-
-  @Test
-  public void testConversionWithAttributes() throws Exception {
-    init("/test.yang", "/simple_xml_with_attributes.xml",
-        listLeafListWithAttributes());
-    Document doc = loadDocument(xmlPath);
-
-    ContainerNode built =
-        DomToNormalizedNodeParserFactory
-            .getInstance(DomUtils.defaultValueCodecProvider())
-            .getContainerNodeParser()
-            .parse(Collections.singletonList(doc.getDocumentElement()),
-                containerNode);
-
-    if (expectedNode != null) {
-      junit.framework.Assert.assertEquals(expectedNode, built);
-    }
-
-    logger.info("{}", built);
-
-    Iterable<Element> els =
-        DomFromNormalizedNodeSerializerFactory
-            .getInstance(XmlDocumentUtils.getDocument(),
-                DomUtils.defaultValueCodecProvider())
-            .getContainerNodeSerializer().serialize(containerNode, built);
-
-    Element el = els.iterator().next();
-
-    XMLUnit.setIgnoreWhitespace(true);
-    XMLUnit.setIgnoreComments(true);
-
-    System.out.println(toString(doc.getDocumentElement()));
-    System.out.println(toString(el));
-
-    new Diff(XMLUnit.buildControlDocument(toString(doc.getDocumentElement())),
-        XMLUnit.buildTestDocument(toString(el))).similar();
-  }
-
-
-  private Document loadDocument(final String xmlPath) throws Exception {
-    InputStream resourceAsStream =
-        NormalizedNodeXmlConverterTest.class.getResourceAsStream(xmlPath);
-
-    Document currentConfigElement = readXmlToDocument(resourceAsStream);
-    Preconditions.checkNotNull(currentConfigElement);
-    return currentConfigElement;
-  }
-
-  private static final DocumentBuilderFactory BUILDERFACTORY;
-
-  static {
-    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-    factory.setNamespaceAware(true);
-    factory.setCoalescing(true);
-    factory.setIgnoringElementContentWhitespace(true);
-    factory.setIgnoringComments(true);
-    BUILDERFACTORY = factory;
-  }
-
-  private Document readXmlToDocument(final InputStream xmlContent)
-      throws IOException, SAXException {
-    DocumentBuilder dBuilder;
-    try {
-      dBuilder = BUILDERFACTORY.newDocumentBuilder();
-    } catch (ParserConfigurationException e) {
-      throw new RuntimeException("Failed to parse XML document", e);
-    }
-    Document doc = dBuilder.parse(xmlContent);
-
-    doc.getDocumentElement().normalize();
-    return doc;
-  }
-
-  public static String toString(final Element xml) {
-    try {
-      Transformer transformer =
-          TransformerFactory.newInstance().newTransformer();
-      transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-
-      StreamResult result = new StreamResult(new StringWriter());
-      DOMSource source = new DOMSource(xml);
-      transformer.transform(source, result);
-
-      return result.getWriter().toString();
-    } catch (IllegalArgumentException | TransformerFactoryConfigurationError
-        | TransformerException e) {
-      throw new RuntimeException("Unable to serialize xml element " + xml, e);
-    }
-  }
-
-  @Test
-  public void testConversionToNormalizedXml() throws Exception {
-    SimpleNormalizedNodeMessage.NormalizedNodeXml nnXml =
-        EncoderDecoderUtil.encode(parseTestSchema("/augment_choice.yang"),
-            augmentChoiceExpectedNode());
-    Document expectedDoc = loadDocument("/augment_choice.xml");
-    Document convertedDoc =
-        EncoderDecoderUtil.factory.newDocumentBuilder().parse(
-            new ByteArrayInputStream(nnXml.getXmlString().getBytes("utf-8")));
-    System.out.println(toString(convertedDoc.getDocumentElement()));
-    XMLUnit.setIgnoreWhitespace(true);
-    XMLUnit.setIgnoreComments(true);
-    new Diff(XMLUnit.buildControlDocument(toString(expectedDoc
-        .getDocumentElement())),
-        XMLUnit.buildTestDocument(toString(convertedDoc.getDocumentElement())))
-        .similar();
-    System.out.println(toString(expectedDoc.getDocumentElement()));
-
-  }
-
-
-  @Test
-  public void testConversionFromXmlToNormalizedNode() throws Exception {
-    SimpleNormalizedNodeMessage.NormalizedNodeXml nnXml =
-        EncoderDecoderUtil.encode(parseTestSchema("/test.yang"),
-            listLeafListWithAttributes());
-    Document expectedDoc = loadDocument("/simple_xml_with_attributes.xml");
-    Document convertedDoc =
-        EncoderDecoderUtil.factory.newDocumentBuilder().parse(
-            new ByteArrayInputStream(nnXml.getXmlString().getBytes("utf-8")));
-    System.out.println(toString(convertedDoc.getDocumentElement()));
-    XMLUnit.setIgnoreWhitespace(true);
-    XMLUnit.setIgnoreComments(true);
-    new Diff(XMLUnit.buildControlDocument(toString(expectedDoc
-        .getDocumentElement())),
-        XMLUnit.buildTestDocument(toString(convertedDoc.getDocumentElement())))
-        .similar();
-    System.out.println(toString(expectedDoc.getDocumentElement()));
-
-    // now we will try to convert xml back to normalize node.
-    ContainerNode cn =
-        (ContainerNode) EncoderDecoderUtil.decode(
-            parseTestSchema("/test.yang"), nnXml);
-    junit.framework.Assert.assertEquals(listLeafListWithAttributes(), cn);
-
-  }
-
-}
index 2eac2400b5a39807c9b188b37f0d693c2c827213..58d805b2b50680be8ab1feb6c6d30bb365bfdf64 100644 (file)
@@ -13,4 +13,5 @@ import akka.actor.ActorRef;
 public interface ClusterWrapper {
     void subscribeToMemberEvents(ActorRef actorRef);
     String getCurrentMemberName();
+    String getSelfAddress();
 }
index 8910137ec4583272b55ead555b0ed12e11caa02c..857510ad4b5360ef3870ac16bce00e188cee88a7 100644 (file)
@@ -17,6 +17,7 @@ import com.google.common.base.Preconditions;
 public class ClusterWrapperImpl implements ClusterWrapper {
     private final Cluster cluster;
     private final String currentMemberName;
+    private final String selfAddress;
 
     public ClusterWrapperImpl(ActorSystem actorSystem){
         Preconditions.checkNotNull(actorSystem, "actorSystem should not be null");
@@ -31,6 +32,7 @@ public class ClusterWrapperImpl implements ClusterWrapper {
         );
 
         currentMemberName = (String) cluster.getSelfRoles().toArray()[0];
+        selfAddress = cluster.selfAddress().toString();
 
     }
 
@@ -45,4 +47,8 @@ public class ClusterWrapperImpl implements ClusterWrapper {
     public String getCurrentMemberName() {
         return currentMemberName;
     }
+
+    public String getSelfAddress() {
+        return selfAddress;
+    }
 }
index a498826e98977d8b3fba89348e3284f0a4781ef3..9a77e4d568961b72f26dec9716b29a5f5f0b9ccd 100644 (file)
@@ -19,8 +19,12 @@ import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class DataChangeListener extends AbstractUntypedActor {
+    private static final Logger LOG = LoggerFactory.getLogger(DataChangeListener.class);
+
     private final AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> listener;
     private boolean notificationsEnabled = false;
 
@@ -29,7 +33,8 @@ public class DataChangeListener extends AbstractUntypedActor {
         this.listener = Preconditions.checkNotNull(listener, "listener should not be null");
     }
 
-    @Override public void handleReceive(Object message) throws Exception {
+    @Override
+    public void handleReceive(Object message) throws Exception {
         if(message instanceof DataChanged){
             dataChanged(message);
         } else if(message instanceof EnableNotification){
@@ -39,18 +44,24 @@ public class DataChangeListener extends AbstractUntypedActor {
 
     private void enableNotification(EnableNotification message) {
         notificationsEnabled = message.isEnabled();
+        LOG.debug("{} notifications for listener {}", (notificationsEnabled ? "Enabled" : "Disabled"),
+                listener);
     }
 
     private void dataChanged(Object message) {
 
         // Do nothing if notifications are not enabled
-        if(!notificationsEnabled){
+        if(!notificationsEnabled) {
+            LOG.debug("Notifications not enabled for listener {} - dropping change notification",
+                    listener);
             return;
         }
 
         DataChanged reply = (DataChanged) message;
-        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>>
-            change = reply.getChange();
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = reply.getChange();
+
+        LOG.debug("Sending change notification {} to listener {}", change, listener);
+
         this.listener.onDataChanged(change);
 
         // It seems the sender is never null but it doesn't hurt to check. If the caller passes in
index acf630e2e95598e71fdbd786da628f3524a29408..b2ae060c3d30c219615432ad59d0b495f93e4294 100644 (file)
@@ -8,14 +8,27 @@
 
 package org.opendaylight.controller.cluster.datastore;
 
+import java.util.concurrent.TimeUnit;
 import akka.actor.ActorRef;
 import akka.actor.ActorSelection;
 import akka.actor.PoisonPill;
+import akka.dispatch.OnComplete;
+import akka.util.Timeout;
+import org.opendaylight.controller.cluster.datastore.exceptions.LocalShardNotFoundException;
 import org.opendaylight.controller.cluster.datastore.messages.CloseDataChangeListenerRegistration;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.google.common.annotations.VisibleForTesting;
+import scala.concurrent.Future;
 
 /**
  * ListenerRegistrationProxy acts as a proxy for a ListenerRegistration that was done on a remote shard
@@ -24,25 +37,36 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
  * The ListenerRegistrationProxy talks to a remote ListenerRegistration actor.
  * </p>
  */
+@SuppressWarnings("rawtypes")
 public class DataChangeListenerRegistrationProxy implements ListenerRegistration {
+
+    private static final Logger LOG = LoggerFactory.getLogger(DataChangeListenerRegistrationProxy.class);
+
+    public static final Timeout REGISTER_TIMEOUT = new Timeout(5, TimeUnit.MINUTES);
+
     private volatile ActorSelection listenerRegistrationActor;
-    private final AsyncDataChangeListener listener;
-    private final ActorRef dataChangeListenerActor;
+    private final AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> listener;
+    private ActorRef dataChangeListenerActor;
+    private final String shardName;
+    private final ActorContext actorContext;
     private boolean closed = false;
 
     public <L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>>
-    DataChangeListenerRegistrationProxy(
-        ActorSelection listenerRegistrationActor,
-        L listener, ActorRef dataChangeListenerActor) {
-        this.listenerRegistrationActor = listenerRegistrationActor;
+                                                              DataChangeListenerRegistrationProxy (
+            String shardName, ActorContext actorContext, L listener) {
+        this.shardName = shardName;
+        this.actorContext = actorContext;
         this.listener = listener;
-        this.dataChangeListenerActor = dataChangeListenerActor;
     }
 
-    public <L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>>
-    DataChangeListenerRegistrationProxy(
-        L listener, ActorRef dataChangeListenerActor) {
-        this(null, listener, dataChangeListenerActor);
+    @VisibleForTesting
+    ActorSelection getListenerRegistrationActor() {
+        return listenerRegistrationActor;
+    }
+
+    @VisibleForTesting
+    ActorRef getDataChangeListenerActor() {
+        return dataChangeListenerActor;
     }
 
     @Override
@@ -50,7 +74,11 @@ public class DataChangeListenerRegistrationProxy implements ListenerRegistration
         return listener;
     }
 
-    public void setListenerRegistrationActor(ActorSelection listenerRegistrationActor) {
+    private void setListenerRegistrationActor(ActorSelection listenerRegistrationActor) {
+        if(listenerRegistrationActor == null) {
+            return;
+        }
+
         boolean sendCloseMessage = false;
         synchronized(this) {
             if(closed) {
@@ -59,16 +87,55 @@ public class DataChangeListenerRegistrationProxy implements ListenerRegistration
                 this.listenerRegistrationActor = listenerRegistrationActor;
             }
         }
+
         if(sendCloseMessage) {
             listenerRegistrationActor.tell(new
                 CloseDataChangeListenerRegistration().toSerializable(), null);
         }
+    }
 
-        this.listenerRegistrationActor = listenerRegistrationActor;
+    public void init(final YangInstanceIdentifier path, final AsyncDataBroker.DataChangeScope scope) {
+
+        dataChangeListenerActor = actorContext.getActorSystem().actorOf(
+                DataChangeListener.props(listener));
+
+        Future<ActorRef> findFuture = actorContext.findLocalShardAsync(shardName, REGISTER_TIMEOUT);
+        findFuture.onComplete(new OnComplete<ActorRef>() {
+            @Override
+            public void onComplete(Throwable failure, ActorRef shard) {
+                if(failure instanceof LocalShardNotFoundException) {
+                    LOG.debug("No local shard found for {} - DataChangeListener {} at path {} " +
+                            "cannot be registered", shardName, listener, path);
+                } else if(failure != null) {
+                    LOG.error("Failed to find local shard {} - DataChangeListener {} at path {} " +
+                            "cannot be registered: {}", shardName, listener, path, failure);
+                } else {
+                    doRegistration(shard, path, scope);
+                }
+            }
+        }, actorContext.getActorSystem().dispatcher());
     }
 
-    public ActorSelection getListenerRegistrationActor() {
-        return listenerRegistrationActor;
+    private void doRegistration(ActorRef shard, final YangInstanceIdentifier path,
+            DataChangeScope scope) {
+
+        Future<Object> future = actorContext.executeOperationAsync(shard,
+                new RegisterChangeListener(path, dataChangeListenerActor.path(), scope),
+                REGISTER_TIMEOUT);
+
+        future.onComplete(new OnComplete<Object>(){
+            @Override
+            public void onComplete(Throwable failure, Object result) {
+                if(failure != null) {
+                    LOG.error("Failed to register DataChangeListener {} at path {}",
+                            listener, path.toString(), failure);
+                } else {
+                    RegisterChangeListenerReply reply = (RegisterChangeListenerReply) result;
+                    setListenerRegistrationActor(actorContext.actorSelection(
+                            reply.getListenerRegistrationPath()));
+                }
+            }
+        }, actorContext.getActorSystem().dispatcher());
     }
 
     @Override
@@ -79,11 +146,16 @@ public class DataChangeListenerRegistrationProxy implements ListenerRegistration
             sendCloseMessage = !closed && listenerRegistrationActor != null;
             closed = true;
         }
+
         if(sendCloseMessage) {
-            listenerRegistrationActor.tell(new
-                CloseDataChangeListenerRegistration().toSerializable(), null);
+            listenerRegistrationActor.tell(new CloseDataChangeListenerRegistration().toSerializable(),
+                    ActorRef.noSender());
+            listenerRegistrationActor = null;
         }
 
-        dataChangeListenerActor.tell(PoisonPill.getInstance(), null);
+        if(dataChangeListenerActor != null) {
+            dataChangeListenerActor.tell(PoisonPill.getInstance(), ActorRef.noSender());
+            dataChangeListenerActor = null;
+        }
     }
 }
index f6c31aab04c76a376e18dad96dae95aa9cffb637..2c73807dca858f754dd4521a390e0777d6ff60d6 100644 (file)
@@ -8,16 +8,10 @@
 
 package org.opendaylight.controller.cluster.datastore;
 
-import akka.actor.ActorRef;
 import akka.actor.ActorSystem;
-import akka.dispatch.OnComplete;
-import akka.util.Timeout;
-import com.google.common.base.Optional;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import org.opendaylight.controller.cluster.datastore.identifiers.ShardManagerIdentifier;
-import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
-import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
 import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategyFactory;
 import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
@@ -34,7 +28,6 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import scala.concurrent.Future;
 
 /**
  *
@@ -83,39 +76,11 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au
 
         String shardName = ShardStrategyFactory.getStrategy(path).findShard(path);
 
-        Optional<ActorRef> shard = actorContext.findLocalShard(shardName);
-
-        //if shard is NOT local
-        if (!shard.isPresent()) {
-            LOG.debug("No local shard for shardName {} was found so returning a noop registration", shardName);
-            return new NoOpDataChangeListenerRegistration(listener);
-        }
-        //if shard is local
-        ActorRef dataChangeListenerActor = actorContext.getActorSystem().actorOf(DataChangeListener.props(listener));
-        Future future = actorContext.executeOperationAsync(shard.get(),
-                new RegisterChangeListener(path, dataChangeListenerActor.path(), scope),
-                new Timeout(actorContext.getOperationDuration().$times(REGISTER_DATA_CHANGE_LISTENER_TIMEOUT_FACTOR)));
-
         final DataChangeListenerRegistrationProxy listenerRegistrationProxy =
-                new DataChangeListenerRegistrationProxy(listener, dataChangeListenerActor);
-
-        future.onComplete(new OnComplete() {
-
-            @Override
-            public void onComplete(Throwable failure, Object result)
-                    throws Throwable {
-                if (failure != null) {
-                    LOG.error("Failed to register listener at path " + path.toString(), failure);
-                    return;
-                }
-                RegisterChangeListenerReply reply = (RegisterChangeListenerReply) result;
-                listenerRegistrationProxy.setListenerRegistrationActor(actorContext
-                        .actorSelection(reply.getListenerRegistrationPath()));
-            }
-        }, actorContext.getActorSystem().dispatcher());
+                new DataChangeListenerRegistrationProxy(shardName, actorContext, listener);
+        listenerRegistrationProxy.init(path, scope);
 
         return listenerRegistrationProxy;
-
     }
 
     @Override
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/NoOpCohort.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/NoOpCohort.java
deleted file mode 100644 (file)
index eb28159..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.cluster.datastore;
-
-import akka.actor.UntypedActor;
-import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction;
-import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply;
-import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransaction;
-import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransactionReply;
-import org.opendaylight.controller.cluster.datastore.messages.CommitTransaction;
-import org.opendaylight.controller.cluster.datastore.messages.CommitTransactionReply;
-import org.opendaylight.controller.cluster.datastore.messages.PreCommitTransaction;
-import org.opendaylight.controller.cluster.datastore.messages.PreCommitTransactionReply;
-
-public class NoOpCohort extends UntypedActor {
-
-    @Override public void onReceive(Object message) throws Exception {
-        if (message.getClass().equals(CanCommitTransaction.SERIALIZABLE_CLASS)) {
-            getSender().tell(new CanCommitTransactionReply(false).toSerializable(), getSelf());
-        } else if (message.getClass().equals(PreCommitTransaction.SERIALIZABLE_CLASS)) {
-            getSender().tell(
-                new PreCommitTransactionReply().toSerializable(),
-                getSelf());
-        } else if (message.getClass().equals(CommitTransaction.SERIALIZABLE_CLASS)) {
-            getSender().tell(new CommitTransactionReply().toSerializable(), getSelf());
-        } else if (message.getClass().equals(AbortTransaction.SERIALIZABLE_CLASS)) {
-            getSender().tell(new AbortTransactionReply().toSerializable(), getSelf());
-        } else {
-            throw new Exception ("Not recognized message received,message="+message);
-        }
-
-    }
-}
-
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/NoOpDataChangeListenerRegistration.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/NoOpDataChangeListenerRegistration.java
deleted file mode 100644 (file)
index 14af31e..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.cluster.datastore;
-
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-
-/**
- * When a consumer registers a data change listener and no local shard is
- * available to register that listener with then we return an instance of
- * NoOpDataChangeListenerRegistration
- *
- * <p>
- *
- * The NoOpDataChangeListenerRegistration as it's name suggests does
- * nothing when an operation is invoked on it
- */
-public class NoOpDataChangeListenerRegistration
-    implements ListenerRegistration {
-
-    private final AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>
-        listener;
-
-    public <L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> NoOpDataChangeListenerRegistration(
-        AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> listener) {
-
-        this.listener = listener;
-    }
-
-    @Override
-    public AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> getInstance() {
-        return listener;
-    }
-
-    @Override public void close() {
-
-    }
-}
index fef7e228737b9cb0ab63db21596c4bba10230065..789d51a19f88942e3a35ceb7fc4d69cb20c8abcb 100644 (file)
@@ -34,9 +34,9 @@ import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier
 import org.opendaylight.controller.cluster.datastore.identifiers.ShardTransactionIdentifier;
 import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardMBeanFactory;
 import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats;
-import org.opendaylight.controller.cluster.datastore.messages.ActorInitialized;
 import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction;
 import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.ActorInitialized;
 import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransaction;
 import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionChain;
 import org.opendaylight.controller.cluster.datastore.messages.CommitTransaction;
@@ -74,14 +74,14 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import scala.concurrent.duration.Duration;
 import scala.concurrent.duration.FiniteDuration;
-import java.util.ArrayList;
+
+import javax.annotation.Nonnull;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
-import javax.annotation.Nonnull;
 
 /**
  * A Shard represents a portion of the logical data tree <br/>
@@ -112,7 +112,10 @@ public class Shard extends RaftActor {
 
     private final ShardStats shardMBean;
 
-    private final List<ActorSelection> dataChangeListeners = new ArrayList<>();
+    private final List<ActorSelection> dataChangeListeners =  Lists.newArrayList();
+
+    private final List<DelayedListenerRegistration> delayedListenerRegistrations =
+                                                                       Lists.newArrayList();
 
     private final DatastoreContext datastoreContext;
 
@@ -216,6 +219,10 @@ public class Shard extends RaftActor {
 
         if (message instanceof RecoveryFailure){
             LOG.error(((RecoveryFailure) message).cause(), "Recovery failed because of this cause");
+
+            // Even though recovery failed, we still need to finish our recovery, eg send the
+            // ActorInitialized message and start the txCommitTimeoutCheckSchedule.
+            onRecoveryComplete();
         } else {
             super.onReceiveRecover(message);
         }
@@ -383,8 +390,11 @@ public class Shard extends RaftActor {
                 ready.getModification());
 
         // Return our actor path as we'll handle the three phase commit.
-        getSender().tell(new ReadyTransactionReply(Serialization.serializedActorPath(self())).
-                toSerializable(), getSelf());
+        ReadyTransactionReply readyTransactionReply =
+            new ReadyTransactionReply(Serialization.serializedActorPath(self()));
+        getSender().tell(
+            ready.isReturnSerialized() ? readyTransactionReply.toSerializable() : readyTransactionReply,
+            getSelf());
     }
 
     private void handleAbortTransaction(AbortTransaction abort) {
@@ -568,53 +578,60 @@ public class Shard extends RaftActor {
         store.onGlobalContextUpdated(message.getSchemaContext());
     }
 
-    @VisibleForTesting void updateSchemaContext(SchemaContext schemaContext) {
+    @VisibleForTesting
+    void updateSchemaContext(SchemaContext schemaContext) {
         store.onGlobalContextUpdated(schemaContext);
     }
 
-    private void registerChangeListener(
-        RegisterChangeListener registerChangeListener) {
+    private void registerChangeListener(RegisterChangeListener registerChangeListener) {
 
-        if(LOG.isDebugEnabled()) {
-            LOG.debug("registerDataChangeListener for {}", registerChangeListener
-                .getPath());
+        LOG.debug("registerDataChangeListener for {}", registerChangeListener.getPath());
+
+        ListenerRegistration<AsyncDataChangeListener<YangInstanceIdentifier,
+                                                     NormalizedNode<?, ?>>> registration;
+        if(isLeader()) {
+            registration = doChangeListenerRegistration(registerChangeListener);
+        } else {
+            LOG.debug("Shard is not the leader - delaying registration");
+
+            DelayedListenerRegistration delayedReg =
+                    new DelayedListenerRegistration(registerChangeListener);
+            delayedListenerRegistrations.add(delayedReg);
+            registration = delayedReg;
         }
 
+        ActorRef listenerRegistration = getContext().actorOf(
+                DataChangeListenerRegistration.props(registration));
+
+        LOG.debug("registerDataChangeListener sending reply, listenerRegistrationPath = {} ",
+                    listenerRegistration.path());
 
-        ActorSelection dataChangeListenerPath = getContext()
-            .system().actorSelection(
-                registerChangeListener.getDataChangeListenerPath());
+        getSender().tell(new RegisterChangeListenerReply(listenerRegistration.path()),getSelf());
+    }
 
+    private ListenerRegistration<AsyncDataChangeListener<YangInstanceIdentifier,
+                                               NormalizedNode<?, ?>>> doChangeListenerRegistration(
+            RegisterChangeListener registerChangeListener) {
+
+        ActorSelection dataChangeListenerPath = getContext().system().actorSelection(
+                registerChangeListener.getDataChangeListenerPath());
 
         // Notify the listener if notifications should be enabled or not
         // If this shard is the leader then it will enable notifications else
         // it will not
-        dataChangeListenerPath
-            .tell(new EnableNotification(isLeader()), getSelf());
+        dataChangeListenerPath.tell(new EnableNotification(true), getSelf());
 
         // Now store a reference to the data change listener so it can be notified
         // at a later point if notifications should be enabled or disabled
         dataChangeListeners.add(dataChangeListenerPath);
 
-        AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>
-            listener = new DataChangeListenerProxy(schemaContext, dataChangeListenerPath);
+        AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> listener =
+                new DataChangeListenerProxy(schemaContext, dataChangeListenerPath);
 
-        ListenerRegistration<AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>>
-            registration = store.registerChangeListener(registerChangeListener.getPath(),
-                listener, registerChangeListener.getScope());
-        ActorRef listenerRegistration =
-            getContext().actorOf(
-                DataChangeListenerRegistration.props(registration));
+        LOG.debug("Registering for path {}", registerChangeListener.getPath());
 
-        if(LOG.isDebugEnabled()) {
-            LOG.debug(
-                "registerDataChangeListener sending reply, listenerRegistrationPath = {} "
-                , listenerRegistration.path().toString());
-        }
-
-        getSender()
-            .tell(new RegisterChangeListenerReply(listenerRegistration.path()),
-                getSelf());
+        return store.registerChangeListener(registerChangeListener.getPath(), listener,
+                registerChangeListener.getScope());
     }
 
     private boolean isMetricsCaptureEnabled(){
@@ -695,12 +712,15 @@ public class Shard extends RaftActor {
         //notify shard manager
         getContext().parent().tell(new ActorInitialized(), getSelf());
 
-        // Schedule a message to be periodically sent to check if the current in-progress
-        // transaction should be expired and aborted.
-        FiniteDuration period = Duration.create(transactionCommitTimeout / 3, TimeUnit.MILLISECONDS);
-        txCommitTimeoutCheckSchedule = getContext().system().scheduler().schedule(
-                period, period, getSelf(),
-                TX_COMMIT_TIMEOUT_CHECK_MESSAGE, getContext().dispatcher(), ActorRef.noSender());
+        // Being paranoid here - this method should only be called once but just in case...
+        if(txCommitTimeoutCheckSchedule == null) {
+            // Schedule a message to be periodically sent to check if the current in-progress
+            // transaction should be expired and aborted.
+            FiniteDuration period = Duration.create(transactionCommitTimeout / 3, TimeUnit.MILLISECONDS);
+            txCommitTimeoutCheckSchedule = getContext().system().scheduler().schedule(
+                    period, period, getSelf(),
+                    TX_COMMIT_TIMEOUT_CHECK_MESSAGE, getContext().dispatcher(), ActorRef.noSender());
+        }
     }
 
     @Override
@@ -787,17 +807,28 @@ public class Shard extends RaftActor {
         }
     }
 
-    @Override protected void onStateChanged() {
+    @Override
+    protected void onStateChanged() {
+        boolean isLeader = isLeader();
         for (ActorSelection dataChangeListener : dataChangeListeners) {
-            dataChangeListener
-                .tell(new EnableNotification(isLeader()), getSelf());
+            dataChangeListener.tell(new EnableNotification(isLeader), getSelf());
+        }
+
+        if(isLeader) {
+            for(DelayedListenerRegistration reg: delayedListenerRegistrations) {
+                if(!reg.isClosed()) {
+                    reg.setDelegate(doChangeListenerRegistration(reg.getRegisterChangeListener()));
+                }
+            }
+
+            delayedListenerRegistrations.clear();
         }
 
         shardMBean.setRaftState(getRaftState().name());
         shardMBean.setCurrentTerm(getCurrentTerm());
 
         // If this actor is no longer the leader close all the transaction chains
-        if(!isLeader()){
+        if(!isLeader){
             for(Map.Entry<String, DOMStoreTransactionChain> entry : transactionChains.entrySet()){
                 if(LOG.isDebugEnabled()) {
                     LOG.debug(
@@ -851,4 +882,45 @@ public class Shard extends RaftActor {
     ShardStats getShardMBean() {
         return shardMBean;
     }
+
+    private static class DelayedListenerRegistration implements
+        ListenerRegistration<AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> {
+
+        private volatile boolean closed;
+
+        private final RegisterChangeListener registerChangeListener;
+
+        private volatile ListenerRegistration<AsyncDataChangeListener<YangInstanceIdentifier,
+                                                             NormalizedNode<?, ?>>> delegate;
+
+        DelayedListenerRegistration(RegisterChangeListener registerChangeListener) {
+            this.registerChangeListener = registerChangeListener;
+        }
+
+        void setDelegate( ListenerRegistration<AsyncDataChangeListener<YangInstanceIdentifier,
+                                            NormalizedNode<?, ?>>> registration) {
+            this.delegate = registration;
+        }
+
+        boolean isClosed() {
+            return closed;
+        }
+
+        RegisterChangeListener getRegisterChangeListener() {
+            return registerChangeListener;
+        }
+
+        @Override
+        public AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> getInstance() {
+            return delegate != null ? delegate.getInstance() : null;
+        }
+
+        @Override
+        public void close() {
+            closed = true;
+            if(delegate != null) {
+                delegate.close();
+            }
+        }
+    }
 }
index 157f1cb3771cd71ddd1ddf14d2541bef3a0aefc3..e861165c6ba2592d7d5d14d98e5fb34591344d85 100644 (file)
@@ -25,6 +25,7 @@ import akka.persistence.RecoveryFailure;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Supplier;
+import com.google.common.collect.Lists;
 import org.opendaylight.controller.cluster.common.actor.AbstractUntypedPersistentActorWithMetering;
 import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier;
 import org.opendaylight.controller.cluster.datastore.identifiers.ShardManagerIdentifier;
@@ -163,7 +164,7 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering {
         LOG.debug("Initializing shard [{}]", shardName);
         ShardInformation shardInformation = localShards.get(shardName);
         if (shardInformation != null) {
-            shardInformation.setShardInitialized(true);
+            shardInformation.setActorInitialized();
         }
     }
 
@@ -192,7 +193,7 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering {
             return;
         }
 
-        sendResponse(shardInformation, new Supplier<Object>() {
+        sendResponse(shardInformation, message.isWaitUntilInitialized(), new Supplier<Object>() {
             @Override
             public Object get() {
                 return new LocalShardFound(shardInformation.getActor());
@@ -200,9 +201,22 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering {
         });
     }
 
-    private void sendResponse(ShardInformation shardInformation,  Supplier<Object> messageSupplier) {
-        if (shardInformation.getActor() == null || !shardInformation.isShardInitialized()) {
-            getSender().tell(new ActorNotInitialized(), getSelf());
+    private void sendResponse(ShardInformation shardInformation, boolean waitUntilInitialized,
+            final Supplier<Object> messageSupplier) {
+        if (!shardInformation.isShardInitialized()) {
+            if(waitUntilInitialized) {
+                final ActorRef sender = getSender();
+                final ActorRef self = self();
+                shardInformation.addRunnableOnInitialized(new Runnable() {
+                    @Override
+                    public void run() {
+                        sender.tell(messageSupplier.get(), self);
+                    }
+                });
+            } else {
+                getSender().tell(new ActorNotInitialized(), getSelf());
+            }
+
             return;
         }
 
@@ -277,7 +291,7 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering {
         // First see if the there is a local replica for the shard
         final ShardInformation info = localShards.get(shardName);
         if (info != null) {
-            sendResponse(info, new Supplier<Object>() {
+            sendResponse(info, message.isWaitUntilInitialized(), new Supplier<Object>() {
                 @Override
                 public Object get() {
                     return new PrimaryFound(info.getActorPath().toString()).toSerializable();
@@ -422,7 +436,11 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering {
         private ActorRef actor;
         private ActorPath actorPath;
         private final Map<ShardIdentifier, String> peerAddresses;
-        private boolean shardInitialized = false; // flag that determines if the actor is ready for business
+
+        // flag that determines if the actor is ready for business
+        private boolean actorInitialized = false;
+
+        private final List<Runnable> runnablesOnInitialized = Lists.newArrayList();
 
         private ShardInformation(String shardName, ShardIdentifier shardId,
                 Map<ShardIdentifier, String> peerAddresses) {
@@ -474,11 +492,21 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering {
         }
 
         boolean isShardInitialized() {
-            return shardInitialized;
+            return getActor() != null && actorInitialized;
+        }
+
+        void setActorInitialized() {
+            this.actorInitialized = true;
+
+            for(Runnable runnable: runnablesOnInitialized) {
+                runnable.run();
+            }
+
+            runnablesOnInitialized.clear();
         }
 
-        void setShardInitialized(boolean shardInitialized) {
-            this.shardInitialized = shardInitialized;
+        void addRunnableOnInitialized(Runnable runnable) {
+            runnablesOnInitialized.add(runnable);
         }
     }
 
@@ -505,8 +533,6 @@ public class ShardManager extends AbstractUntypedPersistentActorWithMetering {
     }
 
     static class SchemaContextModules implements Serializable {
-        private static final long serialVersionUID = 1L;
-
         private final Set<String> modules;
 
         SchemaContextModules(Set<String> modules){
index 29f22b28f42f9f05741ac0f2f60601a717d10249..d12e9997bb29175c7d6c414efdc72b339b706932 100644 (file)
@@ -11,7 +11,6 @@
 package org.opendaylight.controller.cluster.datastore;
 
 import akka.actor.ActorRef;
-
 import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats;
 import org.opendaylight.controller.cluster.datastore.messages.DataExists;
 import org.opendaylight.controller.cluster.datastore.messages.ReadData;
@@ -34,10 +33,18 @@ public class ShardReadTransaction extends ShardTransaction {
 
     @Override
     public void handleReceive(Object message) throws Exception {
-        if(ReadData.SERIALIZABLE_CLASS.equals(message.getClass())) {
-            readData(transaction, ReadData.fromSerializable(message));
+        if(message instanceof ReadData) {
+            readData(transaction, (ReadData) message, !SERIALIZED_REPLY);
+
+        } else if (message instanceof DataExists) {
+            dataExists(transaction, (DataExists) message, !SERIALIZED_REPLY);
+
+        } else if(ReadData.SERIALIZABLE_CLASS.equals(message.getClass())) {
+            readData(transaction, ReadData.fromSerializable(message), SERIALIZED_REPLY);
+
         } else if(DataExists.SERIALIZABLE_CLASS.equals(message.getClass())) {
-            dataExists(transaction, DataExists.fromSerializable(message));
+            dataExists(transaction, DataExists.fromSerializable(message), SERIALIZED_REPLY);
+
         } else {
             super.handleReceive(message);
         }
index 2e174ebf56d9d4d357029a4a0f8e38b4c300ca10..b1fd02d2172cc4c28679c6e4ae86cd31cf2a2657 100644 (file)
@@ -33,10 +33,18 @@ public class ShardReadWriteTransaction extends ShardWriteTransaction {
 
     @Override
     public void handleReceive(Object message) throws Exception {
-        if(ReadData.SERIALIZABLE_CLASS.equals(message.getClass())) {
-            readData(transaction, ReadData.fromSerializable(message));
+        if (message instanceof ReadData) {
+            readData(transaction, (ReadData) message, !SERIALIZED_REPLY);
+
+        } else if (message instanceof DataExists) {
+            dataExists(transaction, (DataExists) message, !SERIALIZED_REPLY);
+
+        } else if(ReadData.SERIALIZABLE_CLASS.equals(message.getClass())) {
+            readData(transaction, ReadData.fromSerializable(message), SERIALIZED_REPLY);
+
         } else if(DataExists.SERIALIZABLE_CLASS.equals(message.getClass())) {
-            dataExists(transaction, DataExists.fromSerializable(message));
+            dataExists(transaction, DataExists.fromSerializable(message), SERIALIZED_REPLY);
+
         } else {
             super.handleReceive(message);
         }
index bb676e3757e5038d49dcbfb56a745e8e27d11fa5..32de47f451d9ff53f301339975357440e651bea5 100644 (file)
@@ -61,6 +61,7 @@ public abstract class ShardTransaction extends AbstractUntypedActorWithMetering
     private final SchemaContext schemaContext;
     private final ShardStats shardStats;
     private final String transactionID;
+    protected static final boolean SERIALIZED_REPLY = true;
 
     protected ShardTransaction(ActorRef shardActor, SchemaContext schemaContext,
             ShardStats shardStats, String transactionID) {
@@ -116,23 +117,24 @@ public abstract class ShardTransaction extends AbstractUntypedActorWithMetering
         getSelf().tell(PoisonPill.getInstance(), getSelf());
     }
 
-    protected void readData(DOMStoreReadTransaction transaction,ReadData message) {
+    protected void readData(DOMStoreReadTransaction transaction, ReadData message, final boolean returnSerialized) {
         final ActorRef sender = getSender();
         final ActorRef self = getSelf();
         final YangInstanceIdentifier path = message.getPath();
         final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
                 transaction.read(path);
 
+
         future.addListener(new Runnable() {
             @Override
             public void run() {
                 try {
                     Optional<NormalizedNode<?, ?>> optional = future.checkedGet();
-                    if (optional.isPresent()) {
-                        sender.tell(new ReadDataReply(schemaContext,optional.get()).toSerializable(), self);
-                    } else {
-                        sender.tell(new ReadDataReply(schemaContext,null).toSerializable(), self);
-                    }
+                    ReadDataReply readDataReply = new ReadDataReply(schemaContext, optional.orNull());
+
+                    sender.tell((returnSerialized ? readDataReply.toSerializable():
+                        readDataReply), self);
+
                 } catch (Exception e) {
                     shardStats.incrementFailedReadTransactionsCount();
                     sender.tell(new akka.actor.Status.Failure(e), self);
@@ -142,12 +144,15 @@ public abstract class ShardTransaction extends AbstractUntypedActorWithMetering
         }, getContext().dispatcher());
     }
 
-    protected void dataExists(DOMStoreReadTransaction transaction, DataExists message) {
+    protected void dataExists(DOMStoreReadTransaction transaction, DataExists message,
+        final boolean returnSerialized) {
         final YangInstanceIdentifier path = message.getPath();
 
         try {
             Boolean exists = transaction.exists(path).checkedGet();
-            getSender().tell(new DataExistsReply(exists).toSerializable(), getSelf());
+            DataExistsReply dataExistsReply = new DataExistsReply(exists);
+            getSender().tell(returnSerialized ? dataExistsReply.toSerializable() :
+                dataExistsReply, getSelf());
         } catch (ReadFailedException e) {
             getSender().tell(new akka.actor.Status.Failure(e),getSelf());
         }
index e993e4b55ccd8c18cd427f0a45068002c1efc54d..21c210daf252fc4633b12882bb18dfea99779aa5 100644 (file)
@@ -53,14 +53,31 @@ public class ShardWriteTransaction extends ShardTransaction {
 
     @Override
     public void handleReceive(Object message) throws Exception {
-        if(WriteData.SERIALIZABLE_CLASS.equals(message.getClass())) {
-            writeData(transaction, WriteData.fromSerializable(message, getSchemaContext()));
+
+        if (message instanceof WriteData) {
+            writeData(transaction, (WriteData) message, !SERIALIZED_REPLY);
+
+        } else if (message instanceof MergeData) {
+            mergeData(transaction, (MergeData) message, !SERIALIZED_REPLY);
+
+        } else if (message instanceof DeleteData) {
+            deleteData(transaction, (DeleteData) message, !SERIALIZED_REPLY);
+
+        } else if (message instanceof ReadyTransaction) {
+            readyTransaction(transaction, new ReadyTransaction(), !SERIALIZED_REPLY);
+
+        } else if(WriteData.SERIALIZABLE_CLASS.equals(message.getClass())) {
+            writeData(transaction, WriteData.fromSerializable(message, getSchemaContext()), SERIALIZED_REPLY);
+
         } else if(MergeData.SERIALIZABLE_CLASS.equals(message.getClass())) {
-            mergeData(transaction, MergeData.fromSerializable(message, getSchemaContext()));
+            mergeData(transaction, MergeData.fromSerializable(message, getSchemaContext()), SERIALIZED_REPLY);
+
         } else if(DeleteData.SERIALIZABLE_CLASS.equals(message.getClass())) {
-            deleteData(transaction, DeleteData.fromSerializable(message));
+            deleteData(transaction, DeleteData.fromSerializable(message), SERIALIZED_REPLY);
+
         } else if(ReadyTransaction.SERIALIZABLE_CLASS.equals(message.getClass())) {
-            readyTransaction(transaction, new ReadyTransaction());
+            readyTransaction(transaction, new ReadyTransaction(), SERIALIZED_REPLY);
+
         } else if (message instanceof GetCompositedModification) {
             // This is here for testing only
             getSender().tell(new GetCompositeModificationReply(
@@ -70,7 +87,7 @@ public class ShardWriteTransaction extends ShardTransaction {
         }
     }
 
-    private void writeData(DOMStoreWriteTransaction transaction, WriteData message) {
+    private void writeData(DOMStoreWriteTransaction transaction, WriteData message, boolean returnSerialized) {
         modification.addModification(
                 new WriteModification(message.getPath(), message.getData(), getSchemaContext()));
         if(LOG.isDebugEnabled()) {
@@ -78,13 +95,15 @@ public class ShardWriteTransaction extends ShardTransaction {
         }
         try {
             transaction.write(message.getPath(), message.getData());
-            getSender().tell(new WriteDataReply().toSerializable(), getSelf());
+            WriteDataReply writeDataReply = new WriteDataReply();
+            getSender().tell(returnSerialized ? writeDataReply.toSerializable() : writeDataReply,
+                getSelf());
         }catch(Exception e){
             getSender().tell(new akka.actor.Status.Failure(e), getSelf());
         }
     }
 
-    private void mergeData(DOMStoreWriteTransaction transaction, MergeData message) {
+    private void mergeData(DOMStoreWriteTransaction transaction, MergeData message, boolean returnSerialized) {
         modification.addModification(
                 new MergeModification(message.getPath(), message.getData(), getSchemaContext()));
         if(LOG.isDebugEnabled()) {
@@ -92,29 +111,34 @@ public class ShardWriteTransaction extends ShardTransaction {
         }
         try {
             transaction.merge(message.getPath(), message.getData());
-            getSender().tell(new MergeDataReply().toSerializable(), getSelf());
+            MergeDataReply mergeDataReply = new MergeDataReply();
+            getSender().tell(returnSerialized ? mergeDataReply.toSerializable() : mergeDataReply ,
+                getSelf());
         }catch(Exception e){
             getSender().tell(new akka.actor.Status.Failure(e), getSelf());
         }
     }
 
-    private void deleteData(DOMStoreWriteTransaction transaction, DeleteData message) {
+    private void deleteData(DOMStoreWriteTransaction transaction, DeleteData message, boolean returnSerialized) {
         if(LOG.isDebugEnabled()) {
             LOG.debug("deleteData at path : " + message.getPath().toString());
         }
         modification.addModification(new DeleteModification(message.getPath()));
         try {
             transaction.delete(message.getPath());
-            getSender().tell(new DeleteDataReply().toSerializable(), getSelf());
+            DeleteDataReply deleteDataReply = new DeleteDataReply();
+            getSender().tell(returnSerialized ? deleteDataReply.toSerializable() : deleteDataReply,
+                getSelf());
         }catch(Exception e){
             getSender().tell(new akka.actor.Status.Failure(e), getSelf());
         }
     }
 
-    private void readyTransaction(DOMStoreWriteTransaction transaction, ReadyTransaction message) {
+    private void readyTransaction(DOMStoreWriteTransaction transaction, ReadyTransaction message, boolean returnSerialized) {
         DOMStoreThreePhaseCommitCohort cohort =  transaction.ready();
 
-        getShardActor().forward(new ForwardedReadyTransaction(getTransactionID(), cohort, modification),
+        getShardActor().forward(new ForwardedReadyTransaction(
+            getTransactionID(), cohort, modification, returnSerialized),
                 getContext());
     }
 
index ec198510d3586f88dee40d04f9294a3fb370a9ae..715f48c3492156d1b14005462da2c26aacb1768c 100644 (file)
@@ -157,7 +157,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
                 for(ActorSelection actor : remoteTransactionActors) {
                     LOG.trace("Sending CloseTransaction to {}", actor);
                     actorContext.sendOperationAsync(actor,
-                            new CloseTransaction().toSerializable());
+                        new CloseTransaction().toSerializable());
                 }
             }
         }
@@ -385,8 +385,8 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
             }
 
             Object response = actorContext.executeOperation(primaryShard.get(),
-                    new CreateTransaction(identifier.toString(), this.transactionType.ordinal(),
-                            getTransactionChainId()).toSerializable());
+                new CreateTransaction(identifier.toString(), this.transactionType.ordinal(),
+                    getTransactionChainId()).toSerializable());
             if (response.getClass().equals(CreateTransactionReply.SERIALIZABLE_CLASS)) {
                 CreateTransactionReply reply =
                     CreateTransactionReply.fromSerializable(response);
@@ -408,8 +408,12 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
                     remoteTransactionActorsMB.set(true);
                 }
 
+                // TxActor is always created where the leader of the shard is.
+                // Check if TxActor is created in the same node
+                boolean isTxActorLocal = actorContext.isLocalPath(transactionPath);
+
                 transactionContext = new TransactionContextImpl(shardName, transactionPath,
-                    transactionActor, identifier, actorContext, schemaContext);
+                    transactionActor, identifier, actorContext, schemaContext, isTxActorLocal);
 
                 remoteTransactionPaths.put(shardName, transactionContext);
             } else {
@@ -483,15 +487,17 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
         private final SchemaContext schemaContext;
         private final String actorPath;
         private final ActorSelection actor;
+        private final boolean isTxActorLocal;
 
         private TransactionContextImpl(String shardName, String actorPath,
                 ActorSelection actor, TransactionIdentifier identifier, ActorContext actorContext,
-                SchemaContext schemaContext) {
+                SchemaContext schemaContext, boolean isTxActorLocal) {
             super(shardName, identifier);
             this.actorPath = actorPath;
             this.actor = actor;
             this.actorContext = actorContext;
             this.schemaContext = schemaContext;
+            this.isTxActorLocal = isTxActorLocal;
         }
 
         private ActorSelection getActor() {
@@ -514,8 +520,9 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
             }
             // Send the ReadyTransaction message to the Tx actor.
 
+            ReadyTransaction readyTransaction = new ReadyTransaction();
             final Future<Object> replyFuture = actorContext.executeOperationAsync(getActor(),
-                    new ReadyTransaction().toSerializable());
+                isTxActorLocal ? readyTransaction : readyTransaction.toSerializable());
 
             // Combine all the previously recorded put/merge/delete operation reply Futures and the
             // ReadyTransactionReply Future into one Future. If any one fails then the combined
@@ -549,15 +556,15 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
 
                     // Note the Future get call here won't block as it's complete.
                     Object serializedReadyReply = replyFuture.value().get().get();
-                    if(serializedReadyReply.getClass().equals(
-                                                     ReadyTransactionReply.SERIALIZABLE_CLASS)) {
-                        ReadyTransactionReply reply = ReadyTransactionReply.fromSerializable(
-                               serializedReadyReply);
+                    if (serializedReadyReply instanceof ReadyTransactionReply) {
+                        return actorContext.actorSelection(((ReadyTransactionReply)serializedReadyReply).getCohortPath());
 
+                    } else if(serializedReadyReply.getClass().equals(ReadyTransactionReply.SERIALIZABLE_CLASS)) {
+                        ReadyTransactionReply reply = ReadyTransactionReply.fromSerializable(serializedReadyReply);
                         return actorContext.actorSelection(reply.getCohortPath());
+
                     } else {
                         // Throwing an exception here will fail the Future.
-
                         throw new IllegalArgumentException(String.format("Invalid reply type {}",
                                 serializedReadyReply.getClass()));
                     }
@@ -570,8 +577,10 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
             if(LOG.isDebugEnabled()) {
                 LOG.debug("Tx {} deleteData called path = {}", identifier, path);
             }
+
+            DeleteData deleteData = new DeleteData(path);
             recordedOperationFutures.add(actorContext.executeOperationAsync(getActor(),
-                    new DeleteData(path).toSerializable()));
+                isTxActorLocal ? deleteData : deleteData.toSerializable()));
         }
 
         @Override
@@ -579,8 +588,10 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
             if(LOG.isDebugEnabled()) {
                 LOG.debug("Tx {} mergeData called path = {}", identifier, path);
             }
+
+            MergeData mergeData = new MergeData(path, data, schemaContext);
             recordedOperationFutures.add(actorContext.executeOperationAsync(getActor(),
-                    new MergeData(path, data, schemaContext).toSerializable()));
+                isTxActorLocal ? mergeData : mergeData.toSerializable()));
         }
 
         @Override
@@ -588,8 +599,10 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
             if(LOG.isDebugEnabled()) {
                 LOG.debug("Tx {} writeData called path = {}", identifier, path);
             }
+
+            WriteData writeData = new WriteData(path, data, schemaContext);
             recordedOperationFutures.add(actorContext.executeOperationAsync(getActor(),
-                    new WriteData(path, data, schemaContext).toSerializable()));
+                isTxActorLocal ? writeData : writeData.toSerializable()));
         }
 
         @Override
@@ -619,6 +632,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
                 Future<Iterable<Object>> combinedFutures = akka.dispatch.Futures.sequence(
                         Lists.newArrayList(recordedOperationFutures),
                         actorContext.getActorSystem().dispatcher());
+
                 OnComplete<Iterable<Object>> onComplete = new OnComplete<Iterable<Object>>() {
                     @Override
                     public void onComplete(Throwable failure, Iterable<Object> notUsed)
@@ -663,25 +677,27 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
                         if(LOG.isDebugEnabled()) {
                             LOG.debug("Tx {} read operation succeeded", identifier, failure);
                         }
-                        if (readResponse.getClass().equals(ReadDataReply.SERIALIZABLE_CLASS)) {
-                            ReadDataReply reply = ReadDataReply.fromSerializable(schemaContext,
-                                    path, readResponse);
-                            if (reply.getNormalizedNode() == null) {
-                                returnFuture.set(Optional.<NormalizedNode<?, ?>>absent());
-                            } else {
-                                returnFuture.set(Optional.<NormalizedNode<?, ?>>of(
-                                        reply.getNormalizedNode()));
-                            }
+
+                        if (readResponse instanceof ReadDataReply) {
+                            ReadDataReply reply = (ReadDataReply) readResponse;
+                            returnFuture.set(Optional.<NormalizedNode<?, ?>>fromNullable(reply.getNormalizedNode()));
+
+                        } else if (readResponse.getClass().equals(ReadDataReply.SERIALIZABLE_CLASS)) {
+                            ReadDataReply reply = ReadDataReply.fromSerializable(schemaContext, path, readResponse);
+                            returnFuture.set(Optional.<NormalizedNode<?, ?>>fromNullable(reply.getNormalizedNode()));
+
                         } else {
                             returnFuture.setException(new ReadFailedException(
-                                    "Invalid response reading data for path " + path));
+                                "Invalid response reading data for path " + path));
                         }
                     }
                 }
             };
 
+            ReadData readData = new ReadData(path);
             Future<Object> readFuture = actorContext.executeOperationAsync(getActor(),
-                    new ReadData(path).toSerializable());
+                isTxActorLocal ? readData : readData.toSerializable());
+
             readFuture.onComplete(onComplete, actorContext.getActorSystem().dispatcher());
         }
 
@@ -756,9 +772,13 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
                         if(LOG.isDebugEnabled()) {
                             LOG.debug("Tx {} dataExists operation succeeded", identifier, failure);
                         }
-                        if (response.getClass().equals(DataExistsReply.SERIALIZABLE_CLASS)) {
-                            returnFuture.set(Boolean.valueOf(DataExistsReply.
-                                        fromSerializable(response).exists()));
+
+                        if (response instanceof DataExistsReply) {
+                            returnFuture.set(Boolean.valueOf(((DataExistsReply) response).exists()));
+
+                        } else if (response.getClass().equals(DataExistsReply.SERIALIZABLE_CLASS)) {
+                            returnFuture.set(Boolean.valueOf(DataExistsReply.fromSerializable(response).exists()));
+
                         } else {
                             returnFuture.setException(new ReadFailedException(
                                     "Invalid response checking exists for path " + path));
@@ -767,8 +787,10 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction {
                 }
             };
 
+            DataExists dataExists = new DataExists(path);
             Future<Object> future = actorContext.executeOperationAsync(getActor(),
-                    new DataExists(path).toSerializable());
+                isTxActorLocal ? dataExists : dataExists.toSerializable());
+
             future.onComplete(onComplete, actorContext.getActorSystem().dispatcher());
         }
     }
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/exceptions/LocalShardNotFoundException.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/exceptions/LocalShardNotFoundException.java
new file mode 100644 (file)
index 0000000..7a976b8
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.datastore.exceptions;
+
+/**
+ * Exception thrown when attempting to find a local shard but it doesn't exist.
+ *
+ * @author Thomas Pantelis
+ */
+public class LocalShardNotFoundException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    public LocalShardNotFoundException(String message){
+        super(message);
+    }
+}
index c415db6efe2fd3da9677db7d16239408253ff2e2..b6560a8347d806e62d1717bea1bab5600238cec2 100644 (file)
@@ -14,12 +14,18 @@ package org.opendaylight.controller.cluster.datastore.messages;
  */
 public class FindLocalShard {
     private final String shardName;
+    private final boolean waitUntilInitialized;
 
-    public FindLocalShard(String shardName) {
+    public FindLocalShard(String shardName, boolean waitUntilInitialized) {
         this.shardName = shardName;
+        this.waitUntilInitialized = waitUntilInitialized;
     }
 
     public String getShardName() {
         return shardName;
     }
+
+    public boolean isWaitUntilInitialized() {
+        return waitUntilInitialized;
+    }
 }
index f5a6a348415308377d755011b133796659d3f27d..a34330bcf6864c26799aae0c6a48990dee1f6f82 100644 (file)
@@ -15,26 +15,33 @@ import com.google.common.base.Preconditions;
  *
  */
 public class FindPrimary implements SerializableMessage{
-  public static final Class SERIALIZABLE_CLASS = FindPrimary.class;
+    public static final Class<FindPrimary> SERIALIZABLE_CLASS = FindPrimary.class;
+
     private final String shardName;
+    private final boolean waitUntilInitialized;
 
-    public FindPrimary(String shardName){
+    public FindPrimary(String shardName, boolean waitUntilInitialized){
 
         Preconditions.checkNotNull(shardName, "shardName should not be null");
 
         this.shardName = shardName;
+        this.waitUntilInitialized = waitUntilInitialized;
     }
 
     public String getShardName() {
         return shardName;
     }
 
-  @Override
-  public Object toSerializable() {
-    return this;
-  }
+    public boolean isWaitUntilInitialized() {
+        return waitUntilInitialized;
+    }
 
-  public static FindPrimary fromSerializable(Object message){
-    return (FindPrimary) message;
-  }
+    @Override
+    public Object toSerializable() {
+        return this;
+    }
+
+    public static FindPrimary fromSerializable(Object message){
+        return (FindPrimary) message;
+    }
 }
index 4f8ea51f784ea2e0352eb6aedd8d1dc35469f0ef..180108f2186b6efbab2977f1d54cf33811a4c638 100644 (file)
@@ -19,12 +19,15 @@ public class ForwardedReadyTransaction {
     private final String transactionID;
     private final DOMStoreThreePhaseCommitCohort cohort;
     private final Modification modification;
+    private final boolean returnSerialized;
 
     public ForwardedReadyTransaction(String transactionID, DOMStoreThreePhaseCommitCohort cohort,
-            Modification modification) {
+            Modification modification, boolean returnSerialized) {
         this.transactionID = transactionID;
         this.cohort = cohort;
         this.modification = modification;
+        this.returnSerialized = returnSerialized;
+
     }
 
     public String getTransactionID() {
@@ -38,4 +41,8 @@ public class ForwardedReadyTransaction {
     public Modification getModification() {
         return modification;
     }
+
+    public boolean isReturnSerialized() {
+        return returnSerialized;
+    }
 }
index d8af09c86b6ec01479f038104fdad350170f4991..0a1e80b0cbaea069f3a75cb558bad130d7562dce 100644 (file)
@@ -13,17 +13,21 @@ import akka.actor.ActorRef;
 import akka.actor.ActorSelection;
 import akka.actor.ActorSystem;
 import akka.actor.PoisonPill;
+import akka.dispatch.Mapper;
 import akka.util.Timeout;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import org.opendaylight.controller.cluster.datastore.ClusterWrapper;
 import org.opendaylight.controller.cluster.datastore.Configuration;
+import org.opendaylight.controller.cluster.datastore.exceptions.LocalShardNotFoundException;
 import org.opendaylight.controller.cluster.datastore.exceptions.NotInitializedException;
 import org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException;
+import org.opendaylight.controller.cluster.datastore.exceptions.UnknownMessageException;
 import org.opendaylight.controller.cluster.datastore.messages.ActorNotInitialized;
 import org.opendaylight.controller.cluster.datastore.messages.FindLocalShard;
 import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
 import org.opendaylight.controller.cluster.datastore.messages.LocalShardFound;
+import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound;
 import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound;
 import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -33,9 +37,7 @@ import scala.concurrent.Await;
 import scala.concurrent.Future;
 import scala.concurrent.duration.Duration;
 import scala.concurrent.duration.FiniteDuration;
-
 import java.util.concurrent.TimeUnit;
-
 import static akka.pattern.Patterns.ask;
 
 /**
@@ -117,14 +119,14 @@ public class ActorContext {
     }
 
     /**
-     * Finds a local shard given it's shard name and return it's ActorRef
+     * Finds a local shard given its shard name and return it's ActorRef
      *
      * @param shardName the name of the local shard that needs to be found
      * @return a reference to a local shard actor which represents the shard
      *         specified by the shardName
      */
     public Optional<ActorRef> findLocalShard(String shardName) {
-        Object result = executeOperation(shardManager, new FindLocalShard(shardName));
+        Object result = executeOperation(shardManager, new FindLocalShard(shardName, false));
 
         if (result instanceof LocalShardFound) {
             LocalShardFound found = (LocalShardFound) result;
@@ -135,9 +137,40 @@ public class ActorContext {
         return Optional.absent();
     }
 
+    /**
+     * Finds a local shard async given its shard name and return a Future from which to obtain the
+     * ActorRef.
+     *
+     * @param shardName the name of the local shard that needs to be found
+     */
+    public Future<ActorRef> findLocalShardAsync( final String shardName, Timeout timeout) {
+        Future<Object> future = executeOperationAsync(shardManager,
+                new FindLocalShard(shardName, true), timeout);
+
+        return future.map(new Mapper<Object, ActorRef>() {
+            @Override
+            public ActorRef checkedApply(Object response) throws Throwable {
+                if(response instanceof LocalShardFound) {
+                    LocalShardFound found = (LocalShardFound)response;
+                    LOG.debug("Local shard found {}", found.getPath());
+                    return found.getPath();
+                } else if(response instanceof ActorNotInitialized) {
+                    throw new NotInitializedException(
+                            String.format("Found local shard for %s but it's not initialized yet.",
+                                    shardName));
+                } else if(response instanceof LocalShardNotFound) {
+                    throw new LocalShardNotFoundException(
+                            String.format("Local shard for %s does not exist.", shardName));
+                }
+
+                throw new UnknownMessageException(String.format(
+                        "FindLocalShard returned unkown response: %s", response));
+            }
+        }, getActorSystem().dispatcher());
+    }
 
     private String findPrimaryPathOrNull(String shardName) {
-        Object result = executeOperation(shardManager, new FindPrimary(shardName).toSerializable());
+        Object result = executeOperation(shardManager, new FindPrimary(shardName, false).toSerializable());
 
         if (result.getClass().equals(PrimaryFound.SERIALIZABLE_CLASS)) {
             PrimaryFound found = PrimaryFound.fromSerializable(result);
@@ -237,6 +270,10 @@ public class ActorContext {
         actorSystem.shutdown();
     }
 
+    public ClusterWrapper getClusterWrapper() {
+        return clusterWrapper;
+    }
+
     public String getCurrentMemberName(){
         return clusterWrapper.getCurrentMemberName();
     }
@@ -262,4 +299,30 @@ public class ActorContext {
     public FiniteDuration getOperationDuration() {
         return operationDuration;
     }
+
+    public boolean isLocalPath(String path) {
+        String selfAddress = clusterWrapper.getSelfAddress();
+        if (path == null || selfAddress == null) {
+            return false;
+        }
+
+        int atIndex1 = path.indexOf("@");
+        int atIndex2 = selfAddress.indexOf("@");
+
+        if (atIndex1 == -1 || atIndex2 == -1) {
+            return false;
+        }
+
+        int slashIndex1 = path.indexOf("/", atIndex1);
+        int slashIndex2 = selfAddress.indexOf("/", atIndex2);
+
+        if (slashIndex1 == -1 || slashIndex2 == -1) {
+            return false;
+        }
+
+        String hostPort1 = path.substring(atIndex1, slashIndex1);
+        String hostPort2 = selfAddress.substring(atIndex2, slashIndex2);
+
+        return hostPort1.equals(hostPort2);
+    }
 }
index aaf080bdf7d8d50de4a3f31713143389994872a6..c27993f97b9fef669c03f0c378e83f8474944e96 100644 (file)
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
 package org.opendaylight.controller.cluster.datastore;
 
+import java.util.concurrent.TimeUnit;
 import akka.actor.ActorRef;
+import akka.actor.ActorSystem;
 import akka.actor.Props;
-import junit.framework.Assert;
+import akka.actor.Terminated;
+import akka.dispatch.ExecutionContexts;
+import akka.dispatch.Futures;
+import akka.testkit.JavaTestKit;
+import akka.util.Timeout;
+import org.junit.Assert;
 import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.cluster.datastore.messages.ActorNotInitialized;
 import org.opendaylight.controller.cluster.datastore.messages.CloseDataChangeListenerRegistration;
+import org.opendaylight.controller.cluster.datastore.messages.FindLocalShard;
+import org.opendaylight.controller.cluster.datastore.messages.LocalShardFound;
+import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
 import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
 import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
-import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor;
-import org.opendaylight.controller.cluster.datastore.utils.MockClusterWrapper;
-import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.Uninterruptibles;
+import scala.concurrent.ExecutionContextExecutor;
+import scala.concurrent.Future;
+import scala.concurrent.duration.FiniteDuration;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.eq;
 
-import java.util.List;
+/**
+ * Unit tests for DataChangeListenerRegistrationProxy.
+ *
+ * @author Thomas Pantelis
+ */
+public class DataChangeListenerRegistrationProxyTest extends AbstractActorTest {
 
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNotNull;
-import static junit.framework.TestCase.assertTrue;
+    @SuppressWarnings("unchecked")
+    private final AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> mockListener =
+            Mockito.mock(AsyncDataChangeListener.class);
 
-public class DataChangeListenerRegistrationProxyTest extends AbstractActorTest{
+    @Test
+    public void testGetInstance() throws Exception {
+        DataChangeListenerRegistrationProxy proxy = new DataChangeListenerRegistrationProxy(
+                "shard", Mockito.mock(ActorContext.class), mockListener);
+
+        Assert.assertEquals(mockListener, proxy.getInstance());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test(timeout=10000)
+    public void testSuccessfulRegistration() {
+        new JavaTestKit(getSystem()) {{
+            ActorContext actorContext = new ActorContext(getSystem(), getRef(),
+                    mock(ClusterWrapper.class), mock(Configuration.class));
+
+            final DataChangeListenerRegistrationProxy proxy = new DataChangeListenerRegistrationProxy(
+                    "shard-1", actorContext, mockListener);
+
+            final YangInstanceIdentifier path = YangInstanceIdentifier.of(TestModel.TEST_QNAME);
+            final DataChangeScope scope = AsyncDataBroker.DataChangeScope.ONE;
+            new Thread() {
+                @Override
+                public void run() {
+                    proxy.init(path, scope);
+                }
+
+            }.start();
+
+            FiniteDuration timeout = duration("5 seconds");
+            FindLocalShard findLocalShard = expectMsgClass(timeout, FindLocalShard.class);
+            Assert.assertEquals("getShardName", "shard-1", findLocalShard.getShardName());
+
+            reply(new LocalShardFound(getRef()));
+
+            RegisterChangeListener registerMsg = expectMsgClass(timeout, RegisterChangeListener.class);
+            Assert.assertEquals("getPath", path, registerMsg.getPath());
+            Assert.assertEquals("getScope", scope, registerMsg.getScope());
+
+            reply(new RegisterChangeListenerReply(getRef().path()));
+
+            for(int i = 0; (i < 20 * 5) && proxy.getListenerRegistrationActor() == null; i++) {
+                Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
+            }
+
+            Assert.assertEquals("getListenerRegistrationActor", getSystem().actorSelection(getRef().path()),
+                    proxy.getListenerRegistrationActor());
+
+            watch(proxy.getDataChangeListenerActor());
 
-    private ActorRef dataChangeListenerActor = getSystem().actorOf(Props.create(DoNothingActor.class));
+            proxy.close();
 
-    private static class MockDataChangeListener implements
-        AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+            // The listener registration actor should get a Close message
+            expectMsgClass(timeout, CloseDataChangeListenerRegistration.SERIALIZABLE_CLASS);
 
-        @Override public void onDataChanged(
-            AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
-            throw new UnsupportedOperationException("onDataChanged");
-        }
+            // The DataChangeListener actor should be terminated
+            expectMsgClass(timeout, Terminated.class);
+
+            proxy.close();
+
+            expectNoMsg();
+        }};
     }
 
-    @Test
-    public void testGetInstance() throws Exception {
-        final Props props = Props.create(MessageCollectorActor.class);
-        final ActorRef actorRef = getSystem().actorOf(props);
+    @Test(timeout=10000)
+    public void testLocalShardNotFound() {
+        new JavaTestKit(getSystem()) {{
+            ActorContext actorContext = new ActorContext(getSystem(), getRef(),
+                    mock(ClusterWrapper.class), mock(Configuration.class));
 
-        MockDataChangeListener listener =
-            new MockDataChangeListener();
-        DataChangeListenerRegistrationProxy proxy =
-            new DataChangeListenerRegistrationProxy(
-                getSystem().actorSelection(actorRef.path()),
-                listener, dataChangeListenerActor);
+            final DataChangeListenerRegistrationProxy proxy = new DataChangeListenerRegistrationProxy(
+                    "shard-1", actorContext, mockListener);
 
-        Assert.assertEquals(listener, proxy.getInstance());
+            final YangInstanceIdentifier path = YangInstanceIdentifier.of(TestModel.TEST_QNAME);
+            final DataChangeScope scope = AsyncDataBroker.DataChangeScope.ONE;
+            new Thread() {
+                @Override
+                public void run() {
+                    proxy.init(path, scope);
+                }
 
+            }.start();
+
+            FiniteDuration timeout = duration("5 seconds");
+            FindLocalShard findLocalShard = expectMsgClass(timeout, FindLocalShard.class);
+            Assert.assertEquals("getShardName", "shard-1", findLocalShard.getShardName());
+
+            reply(new LocalShardNotFound("shard-1"));
+
+            expectNoMsg(duration("1 seconds"));
+        }};
     }
 
-    @Test
-    public void testClose() throws Exception {
-        final Props props = Props.create(MessageCollectorActor.class);
-        final ActorRef actorRef = getSystem().actorOf(props);
+    @Test(timeout=10000)
+    public void testLocalShardNotInitialized() {
+        new JavaTestKit(getSystem()) {{
+            ActorContext actorContext = new ActorContext(getSystem(), getRef(),
+                    mock(ClusterWrapper.class), mock(Configuration.class));
 
-        DataChangeListenerRegistrationProxy proxy =
-            new DataChangeListenerRegistrationProxy(
-                getSystem().actorSelection(actorRef.path()),
-                new MockDataChangeListener(), dataChangeListenerActor);
+            final DataChangeListenerRegistrationProxy proxy = new DataChangeListenerRegistrationProxy(
+                    "shard-1", actorContext, mockListener);
 
-        proxy.close();
+            final YangInstanceIdentifier path = YangInstanceIdentifier.of(TestModel.TEST_QNAME);
+            final DataChangeScope scope = AsyncDataBroker.DataChangeScope.ONE;
+            new Thread() {
+                @Override
+                public void run() {
+                    proxy.init(path, scope);
+                }
+
+            }.start();
+
+            FiniteDuration timeout = duration("5 seconds");
+            FindLocalShard findLocalShard = expectMsgClass(timeout, FindLocalShard.class);
+            Assert.assertEquals("getShardName", "shard-1", findLocalShard.getShardName());
+
+            reply(new ActorNotInitialized());
+
+            new Within(duration("1 seconds")) {
+                @Override
+                protected void run() {
+                    expectNoMsg();
+                }
+            };
+        }};
+    }
+
+    @Test
+    public void testFailedRegistration() {
+        new JavaTestKit(getSystem()) {{
+            ActorSystem mockActorSystem = mock(ActorSystem.class);
 
-        //Check if it was received by the remote actor
-        ActorContext
-            testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)),new MockClusterWrapper(), new MockConfiguration());
-        Object messages = testContext
-            .executeOperation(actorRef, "messages");
+            ActorRef mockActor = getSystem().actorOf(Props.create(DoNothingActor.class),
+                    "testFailedRegistration");
+            doReturn(mockActor).when(mockActorSystem).actorOf(any(Props.class));
+            ExecutionContextExecutor executor = ExecutionContexts.fromExecutor(
+                    MoreExecutors.sameThreadExecutor());
+            doReturn(executor).when(mockActorSystem).dispatcher();
 
-        assertNotNull(messages);
+            ActorContext actorContext = mock(ActorContext.class);
 
-        assertTrue(messages instanceof List);
+            String shardName = "shard-1";
+            final DataChangeListenerRegistrationProxy proxy = new DataChangeListenerRegistrationProxy(
+                    shardName, actorContext, mockListener);
 
-        List<Object> listMessages = (List<Object>) messages;
+            doReturn(mockActorSystem).when(actorContext).getActorSystem();
+            doReturn(duration("5 seconds")).when(actorContext).getOperationDuration();
+            doReturn(Futures.successful(getRef())).when(actorContext).findLocalShardAsync(eq(shardName),
+                    any(Timeout.class));
+            doReturn(Futures.failed(new RuntimeException("mock"))).
+                    when(actorContext).executeOperationAsync(any(ActorRef.class),
+                            any(Object.class), any(Timeout.class));
 
-        assertEquals(1, listMessages.size());
+            proxy.init(YangInstanceIdentifier.of(TestModel.TEST_QNAME),
+                    AsyncDataBroker.DataChangeScope.ONE);
 
-        assertTrue(listMessages.get(0).getClass()
-            .equals(CloseDataChangeListenerRegistration.SERIALIZABLE_CLASS));
+            Assert.assertEquals("getListenerRegistrationActor", null,
+                    proxy.getListenerRegistrationActor());
+        }};
     }
 
+    @SuppressWarnings("unchecked")
     @Test
-    public void testCloseWhenRegistrationIsNull() throws Exception {
-        final Props props = Props.create(MessageCollectorActor.class);
-        final ActorRef actorRef = getSystem().actorOf(props);
+    public void testCloseBeforeRegistration() {
+        new JavaTestKit(getSystem()) {{
+            ActorContext actorContext = mock(ActorContext.class);
 
-        DataChangeListenerRegistrationProxy proxy =
-            new DataChangeListenerRegistrationProxy(
-                new MockDataChangeListener(), dataChangeListenerActor);
+            String shardName = "shard-1";
+            final DataChangeListenerRegistrationProxy proxy = new DataChangeListenerRegistrationProxy(
+                    shardName, actorContext, mockListener);
 
-        proxy.close();
+            doReturn(getSystem()).when(actorContext).getActorSystem();
+            doReturn(getSystem().actorSelection(getRef().path())).
+                    when(actorContext).actorSelection(getRef().path());
+            doReturn(duration("5 seconds")).when(actorContext).getOperationDuration();
+            doReturn(Futures.successful(getRef())).when(actorContext).findLocalShardAsync(eq(shardName),
+                    any(Timeout.class));
 
-        //Check if it was received by the remote actor
-        ActorContext
-            testContext = new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)),new MockClusterWrapper(), new MockConfiguration());
-        Object messages = testContext
-            .executeOperation(actorRef, "messages");
+            Answer<Future<Object>> answer = new Answer<Future<Object>>() {
+                @Override
+                public Future<Object> answer(InvocationOnMock invocation) {
+                    proxy.close();
+                    return Futures.successful((Object)new RegisterChangeListenerReply(getRef().path()));
+                }
+            };
 
-        assertNotNull(messages);
+            doAnswer(answer).when(actorContext).executeOperationAsync(any(ActorRef.class),
+                    any(Object.class), any(Timeout.class));
 
-        assertTrue(messages instanceof List);
+            proxy.init(YangInstanceIdentifier.of(TestModel.TEST_QNAME),
+                    AsyncDataBroker.DataChangeScope.ONE);
 
-        List<Object> listMessages = (List<Object>) messages;
+            expectMsgClass(duration("5 seconds"), CloseDataChangeListenerRegistration.SERIALIZABLE_CLASS);
 
-        assertEquals(0, listMessages.size());
+            Assert.assertEquals("getListenerRegistrationActor", null,
+                    proxy.getListenerRegistrationActor());
+        }};
     }
 }
index 395021d361c6d4b210fcf29bf20707802b6c590a..1cc7ae8ad02f93dd9f80135a47c1b5dfdfc2b58c 100644 (file)
@@ -10,15 +10,18 @@ import static org.junit.Assert.assertNotNull;
 import org.junit.Test;
 import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategyFactory;
 import org.opendaylight.controller.cluster.datastore.utils.MockClusterWrapper;
+import org.opendaylight.controller.cluster.datastore.utils.MockDataChangeListener;
 import org.opendaylight.controller.md.cluster.datastore.model.CarsModel;
 import org.opendaylight.controller.md.cluster.datastore.model.PeopleModel;
 import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper;
 import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
@@ -204,6 +207,45 @@ public class DistributedDataStoreIntegrationTest extends AbstractActorTest {
         }};
     }
 
+    @Test
+    public void testChangeListenerRegistration() throws Exception{
+        new IntegrationTestKit(getSystem()) {{
+            DistributedDataStore dataStore =
+                    setupDistributedDataStore("testChangeListenerRegistration", "test-1");
+
+            MockDataChangeListener listener = new MockDataChangeListener(3);
+
+            ListenerRegistration<MockDataChangeListener>
+                    listenerReg = dataStore.registerChangeListener(TestModel.TEST_PATH, listener,
+                            DataChangeScope.SUBTREE);
+
+            assertNotNull("registerChangeListener returned null", listenerReg);
+
+            testWriteTransaction(dataStore, TestModel.TEST_PATH,
+                    ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+            testWriteTransaction(dataStore, TestModel.OUTER_LIST_PATH,
+                    ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+
+            YangInstanceIdentifier listPath = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH).
+                    nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).build();
+            testWriteTransaction(dataStore, listPath,
+                    ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1));
+
+            listener.waitForChangeEvents(TestModel.TEST_PATH, TestModel.OUTER_LIST_PATH, listPath );
+
+            listenerReg.close();
+
+            testWriteTransaction(dataStore, YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH).
+                    nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2).build(),
+                    ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2));
+
+            listener.expectNoMoreChanges("Received unexpected change after close");
+
+            cleanup(dataStore);
+        }};
+    }
+
     class IntegrationTestKit extends ShardTestKit {
 
         IntegrationTestKit(ActorSystem actorSystem) {
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreTest.java
deleted file mode 100644 (file)
index 00243ea..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-package org.opendaylight.controller.cluster.datastore;
-
-import akka.actor.ActorPath;
-import akka.actor.ActorRef;
-import akka.actor.ActorSelection;
-import akka.actor.ActorSystem;
-import akka.actor.Props;
-import akka.dispatch.ExecutionContexts;
-import akka.dispatch.Futures;
-import akka.util.Timeout;
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.MoreExecutors;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
-import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategyFactory;
-import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
-import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor;
-import org.opendaylight.controller.cluster.datastore.utils.MockActorContext;
-import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration;
-import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
-import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages.CreateTransactionReply;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
-import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import scala.concurrent.ExecutionContextExecutor;
-import scala.concurrent.Future;
-import scala.concurrent.duration.FiniteDuration;
-import java.util.concurrent.TimeUnit;
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertNull;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class DistributedDataStoreTest extends AbstractActorTest{
-
-    private DistributedDataStore distributedDataStore;
-    private MockActorContext mockActorContext;
-    private ActorRef doNothingActorRef;
-
-    @Before
-    public void setUp() throws Exception {
-        ShardStrategyFactory.setConfiguration(new MockConfiguration());
-        final Props props = Props.create(DoNothingActor.class);
-
-        doNothingActorRef = getSystem().actorOf(props);
-
-        mockActorContext = new MockActorContext(getSystem(), doNothingActorRef);
-        distributedDataStore = new DistributedDataStore(mockActorContext);
-        distributedDataStore.onGlobalContextUpdated(
-            TestModel.createTestContext());
-
-        // Make CreateTransactionReply as the default response. Will need to be
-        // tuned if a specific test requires some other response
-        mockActorContext.setExecuteShardOperationResponse(
-            CreateTransactionReply.newBuilder()
-                .setTransactionActorPath(doNothingActorRef.path().toString())
-                .setTransactionId("txn-1 ")
-                .build());
-    }
-
-    @After
-    public void tearDown() throws Exception {
-
-    }
-
-    @SuppressWarnings("resource")
-    @Test
-    public void testConstructor(){
-        ActorSystem actorSystem = mock(ActorSystem.class);
-
-        new DistributedDataStore(actorSystem, "config",
-            mock(ClusterWrapper.class), mock(Configuration.class),
-            DatastoreContext.newBuilder().build());
-
-        verify(actorSystem).actorOf(any(Props.class), eq("shardmanager-config"));
-    }
-
-    @Test
-    public void testRegisterChangeListenerWhenShardIsNotLocal() throws Exception {
-
-        ListenerRegistration registration =
-                distributedDataStore.registerChangeListener(TestModel.TEST_PATH, new AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>() {
-                    @Override
-                    public void onDataChanged(AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
-                        throw new UnsupportedOperationException("onDataChanged");
-                    }
-                }, AsyncDataBroker.DataChangeScope.BASE);
-
-        // Since we do not expect the shard to be local registration will return a NoOpRegistration
-        assertTrue(registration instanceof NoOpDataChangeListenerRegistration);
-
-        assertNotNull(registration);
-    }
-
-    @Test
-    public void testRegisterChangeListenerWhenShardIsLocal() throws Exception {
-        ActorContext actorContext = mock(ActorContext.class);
-
-        distributedDataStore = new DistributedDataStore(actorContext);
-        distributedDataStore.onGlobalContextUpdated(TestModel.createTestContext());
-
-        Future future = mock(Future.class);
-        when(actorContext.getOperationDuration()).thenReturn(FiniteDuration.apply(5, TimeUnit.SECONDS));
-        when(actorContext.getActorSystem()).thenReturn(getSystem());
-        when(actorContext.findLocalShard(anyString())).thenReturn(Optional.of(doNothingActorRef));
-        when(actorContext
-                .executeOperationAsync(eq(doNothingActorRef), anyObject(), any(Timeout.class))).thenReturn(future);
-
-        ListenerRegistration registration =
-            distributedDataStore.registerChangeListener(TestModel.TEST_PATH,
-                mock(AsyncDataChangeListener.class),
-                AsyncDataBroker.DataChangeScope.BASE);
-
-        assertNotNull(registration);
-
-        assertEquals(DataChangeListenerRegistrationProxy.class, registration.getClass());
-    }
-
-    @Test
-    public void testRegisterChangeListenerWhenSuccessfulReplyReceived() throws Exception {
-        ActorContext actorContext = mock(ActorContext.class);
-
-        distributedDataStore = new DistributedDataStore(actorContext);
-        distributedDataStore.onGlobalContextUpdated(
-            TestModel.createTestContext());
-
-        ExecutionContextExecutor executor = ExecutionContexts.fromExecutor(MoreExecutors.sameThreadExecutor());
-
-        // Make Future successful
-        Future f = Futures.successful(new RegisterChangeListenerReply(doNothingActorRef.path()));
-
-        // Setup the mocks
-        ActorSystem actorSystem = mock(ActorSystem.class);
-        ActorSelection actorSelection = mock(ActorSelection.class);
-
-        when(actorContext.getOperationDuration()).thenReturn(FiniteDuration.apply(5, TimeUnit.SECONDS));
-        when(actorSystem.dispatcher()).thenReturn(executor);
-        when(actorSystem.actorOf(any(Props.class))).thenReturn(doNothingActorRef);
-        when(actorContext.getActorSystem()).thenReturn(actorSystem);
-        when(actorContext.findLocalShard(anyString())).thenReturn(Optional.of(doNothingActorRef));
-        when(actorContext
-            .executeOperationAsync(eq(doNothingActorRef), anyObject(), any(Timeout.class))).thenReturn(f);
-        when(actorContext.actorSelection(any(ActorPath.class))).thenReturn(actorSelection);
-
-        ListenerRegistration registration =
-            distributedDataStore.registerChangeListener(TestModel.TEST_PATH,
-                mock(AsyncDataChangeListener.class),
-                AsyncDataBroker.DataChangeScope.BASE);
-
-        assertNotNull(registration);
-
-        assertEquals(DataChangeListenerRegistrationProxy.class, registration.getClass());
-
-        ActorSelection listenerRegistrationActor =
-            ((DataChangeListenerRegistrationProxy) registration).getListenerRegistrationActor();
-
-        assertNotNull(listenerRegistrationActor);
-
-        assertEquals(actorSelection, listenerRegistrationActor);
-    }
-
-    @Test
-    public void testRegisterChangeListenerWhenSuccessfulReplyFailed() throws Exception {
-        ActorContext actorContext = mock(ActorContext.class);
-
-        distributedDataStore = new DistributedDataStore(actorContext);
-        distributedDataStore.onGlobalContextUpdated(
-            TestModel.createTestContext());
-
-        ExecutionContextExecutor executor = ExecutionContexts.fromExecutor(MoreExecutors.sameThreadExecutor());
-
-        // Make Future fail
-        Future f = Futures.failed(new IllegalArgumentException());
-
-        // Setup the mocks
-        ActorSystem actorSystem = mock(ActorSystem.class);
-        ActorSelection actorSelection = mock(ActorSelection.class);
-
-        when(actorContext.getOperationDuration()).thenReturn(FiniteDuration.apply(5, TimeUnit.SECONDS));
-        when(actorSystem.dispatcher()).thenReturn(executor);
-        when(actorSystem.actorOf(any(Props.class))).thenReturn(doNothingActorRef);
-        when(actorContext.getActorSystem()).thenReturn(actorSystem);
-        when(actorContext.findLocalShard(anyString())).thenReturn(Optional.of(doNothingActorRef));
-        when(actorContext
-            .executeOperationAsync(eq(doNothingActorRef), anyObject(), any(Timeout.class))).thenReturn(f);
-        when(actorContext.actorSelection(any(ActorPath.class))).thenReturn(actorSelection);
-
-        ListenerRegistration registration =
-            distributedDataStore.registerChangeListener(TestModel.TEST_PATH,
-                mock(AsyncDataChangeListener.class),
-                AsyncDataBroker.DataChangeScope.BASE);
-
-        assertNotNull(registration);
-
-        assertEquals(DataChangeListenerRegistrationProxy.class, registration.getClass());
-
-        ActorSelection listenerRegistrationActor =
-            ((DataChangeListenerRegistrationProxy) registration).getListenerRegistrationActor();
-
-        assertNull(listenerRegistrationActor);
-
-    }
-
-
-    @Test
-    public void testCreateTransactionChain() throws Exception {
-        final DOMStoreTransactionChain transactionChain = distributedDataStore.createTransactionChain();
-        assertNotNull(transactionChain);
-    }
-
-    @Test
-    public void testNewReadOnlyTransaction() throws Exception {
-        final DOMStoreReadTransaction transaction = distributedDataStore.newReadOnlyTransaction();
-        assertNotNull(transaction);
-    }
-
-    @Test
-    public void testNewWriteOnlyTransaction() throws Exception {
-        final DOMStoreWriteTransaction transaction = distributedDataStore.newWriteOnlyTransaction();
-        assertNotNull(transaction);
-    }
-
-    @Test
-    public void testNewReadWriteTransaction() throws Exception {
-        final DOMStoreReadWriteTransaction transaction = distributedDataStore.newReadWriteTransaction();
-        assertNotNull(transaction);
-    }
-}
index 5022d97997dfad32ef29ae16865a7e7dc3c2b6e1..c04dcf1534506609865e95e7b7a6789d3ab676eb 100644 (file)
@@ -2,9 +2,11 @@ package org.opendaylight.controller.cluster.datastore;
 
 import akka.actor.ActorRef;
 import akka.actor.Props;
+import akka.pattern.Patterns;
 import akka.persistence.RecoveryCompleted;
 import akka.testkit.JavaTestKit;
 import akka.testkit.TestActorRef;
+import akka.util.Timeout;
 import akka.japi.Creator;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
@@ -29,6 +31,8 @@ import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration;
 import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import scala.concurrent.Await;
+import scala.concurrent.Future;
 import java.net.URI;
 import java.util.Collection;
 import java.util.HashSet;
@@ -76,7 +80,7 @@ public class ShardManagerTest extends AbstractActorTest {
 
             shardManager.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
 
-            shardManager.tell(new FindPrimary("non-existent").toSerializable(), getRef());
+            shardManager.tell(new FindPrimary("non-existent", false).toSerializable(), getRef());
 
             expectMsgEquals(duration("5 seconds"),
                     new PrimaryNotFound("non-existent").toSerializable());
@@ -91,25 +95,44 @@ public class ShardManagerTest extends AbstractActorTest {
             shardManager.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
             shardManager.tell(new ActorInitialized(), mockShardActor);
 
-            shardManager.tell(new FindPrimary(Shard.DEFAULT_NAME).toSerializable(), getRef());
+            shardManager.tell(new FindPrimary(Shard.DEFAULT_NAME, false).toSerializable(), getRef());
 
             expectMsgClass(duration("5 seconds"), PrimaryFound.SERIALIZABLE_CLASS);
         }};
     }
 
     @Test
-    public void testOnReceiveFindPrimaryForNotInitialzedShard() throws Exception {
+    public void testOnReceiveFindPrimaryForNotInitializedShard() throws Exception {
         new JavaTestKit(getSystem()) {{
             final ActorRef shardManager = getSystem().actorOf(newShardMgrProps());
 
             shardManager.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
 
-            shardManager.tell(new FindPrimary(Shard.DEFAULT_NAME).toSerializable(), getRef());
+            shardManager.tell(new FindPrimary(Shard.DEFAULT_NAME, false).toSerializable(), getRef());
 
             expectMsgClass(duration("5 seconds"), ActorNotInitialized.class);
         }};
     }
 
+    @Test
+    public void testOnReceiveFindPrimaryWaitForShardInitialized() throws Exception {
+        new JavaTestKit(getSystem()) {{
+            final ActorRef shardManager = getSystem().actorOf(newShardMgrProps());
+
+            shardManager.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
+
+            // We're passing waitUntilInitialized = true to FindPrimary so the response should be
+            // delayed until we send ActorInitialized.
+            Future<Object> future = Patterns.ask(shardManager, new FindPrimary(Shard.DEFAULT_NAME, true),
+                    new Timeout(5, TimeUnit.SECONDS));
+
+            shardManager.tell(new ActorInitialized(), mockShardActor);
+
+            Object resp = Await.result(future, duration("5 seconds"));
+            assertTrue("Expected: PrimaryFound, Actual: " + resp, resp instanceof PrimaryFound);
+        }};
+    }
+
     @Test
     public void testOnReceiveFindLocalShardForNonExistentShard() throws Exception {
         new JavaTestKit(getSystem()) {{
@@ -117,7 +140,7 @@ public class ShardManagerTest extends AbstractActorTest {
 
             shardManager.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
 
-            shardManager.tell(new FindLocalShard("non-existent"), getRef());
+            shardManager.tell(new FindLocalShard("non-existent", false), getRef());
 
             LocalShardNotFound notFound = expectMsgClass(duration("5 seconds"), LocalShardNotFound.class);
 
@@ -133,7 +156,7 @@ public class ShardManagerTest extends AbstractActorTest {
             shardManager.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
             shardManager.tell(new ActorInitialized(), mockShardActor);
 
-            shardManager.tell(new FindLocalShard(Shard.DEFAULT_NAME), getRef());
+            shardManager.tell(new FindLocalShard(Shard.DEFAULT_NAME, false), getRef());
 
             LocalShardFound found = expectMsgClass(duration("5 seconds"), LocalShardFound.class);
 
@@ -148,14 +171,32 @@ public class ShardManagerTest extends AbstractActorTest {
             final ActorRef shardManager = getSystem().actorOf(newShardMgrProps());
 
             shardManager.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
-            //shardManager.tell(new ActorInitialized(), mockShardActor);
 
-            shardManager.tell(new FindLocalShard(Shard.DEFAULT_NAME), getRef());
+            shardManager.tell(new FindLocalShard(Shard.DEFAULT_NAME, false), getRef());
 
             expectMsgClass(duration("5 seconds"), ActorNotInitialized.class);
         }};
     }
 
+    @Test
+    public void testOnReceiveFindLocalShardWaitForShardInitialized() throws Exception {
+        new JavaTestKit(getSystem()) {{
+            final ActorRef shardManager = getSystem().actorOf(newShardMgrProps());
+
+            shardManager.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
+
+            // We're passing waitUntilInitialized = true to FindLocalShard so the response should be
+            // delayed until we send ActorInitialized.
+            Future<Object> future = Patterns.ask(shardManager, new FindLocalShard(Shard.DEFAULT_NAME, true),
+                    new Timeout(5, TimeUnit.SECONDS));
+
+            shardManager.tell(new ActorInitialized(), mockShardActor);
+
+            Object resp = Await.result(future, duration("5 seconds"));
+            assertTrue("Expected: LocalShardFound, Actual: " + resp, resp instanceof LocalShardFound);
+        }};
+    }
+
     @Test
     public void testOnReceiveMemberUp() throws Exception {
         new JavaTestKit(getSystem()) {{
@@ -163,7 +204,7 @@ public class ShardManagerTest extends AbstractActorTest {
 
             MockClusterWrapper.sendMemberUp(shardManager, "member-2", getRef().path().toString());
 
-            shardManager.tell(new FindPrimary("astronauts").toSerializable(), getRef());
+            shardManager.tell(new FindPrimary("astronauts", false).toSerializable(), getRef());
 
             PrimaryFound found = PrimaryFound.fromSerializable(expectMsgClass(duration("5 seconds"),
                     PrimaryFound.SERIALIZABLE_CLASS));
@@ -180,13 +221,13 @@ public class ShardManagerTest extends AbstractActorTest {
 
             MockClusterWrapper.sendMemberUp(shardManager, "member-2", getRef().path().toString());
 
-            shardManager.tell(new FindPrimary("astronauts").toSerializable(), getRef());
+            shardManager.tell(new FindPrimary("astronauts", false).toSerializable(), getRef());
 
             expectMsgClass(duration("5 seconds"), PrimaryFound.SERIALIZABLE_CLASS);
 
             MockClusterWrapper.sendMemberRemoved(shardManager, "member-2", getRef().path().toString());
 
-            shardManager.tell(new FindPrimary("astronauts").toSerializable(), getRef());
+            shardManager.tell(new FindPrimary("astronauts", false).toSerializable(), getRef());
 
             expectMsgClass(duration("5 seconds"), PrimaryNotFound.SERIALIZABLE_CLASS);
         }};
index 9b4f77b7c42be1c841ee6575acb5764d90e78d5e..03a18ea6c38c39600f2c4bd3a49b848ace89a09d 100644 (file)
@@ -1,12 +1,12 @@
 package org.opendaylight.controller.cluster.datastore;
 
 import akka.actor.ActorRef;
+import akka.actor.PoisonPill;
 import akka.actor.Props;
 import akka.dispatch.Dispatchers;
 import akka.dispatch.OnComplete;
 import akka.japi.Creator;
 import akka.pattern.Patterns;
-import akka.testkit.JavaTestKit;
 import akka.testkit.TestActorRef;
 import akka.util.Timeout;
 import com.google.common.base.Function;
@@ -15,6 +15,7 @@ import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.Uninterruptibles;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -29,7 +30,6 @@ import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransacti
 import org.opendaylight.controller.cluster.datastore.messages.CommitTransaction;
 import org.opendaylight.controller.cluster.datastore.messages.CommitTransactionReply;
 import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
-import org.opendaylight.controller.cluster.datastore.messages.EnableNotification;
 import org.opendaylight.controller.cluster.datastore.messages.ForwardedReadyTransaction;
 import org.opendaylight.controller.cluster.datastore.messages.PeerAddressResolved;
 import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply;
@@ -43,6 +43,7 @@ import org.opendaylight.controller.cluster.datastore.modification.WriteModificat
 import org.opendaylight.controller.cluster.datastore.node.NormalizedNodeToNodeCodec;
 import org.opendaylight.controller.cluster.datastore.utils.InMemoryJournal;
 import org.opendaylight.controller.cluster.datastore.utils.InMemorySnapshotStore;
+import org.opendaylight.controller.cluster.datastore.utils.MockDataChangeListener;
 import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
 import org.opendaylight.controller.cluster.raft.ReplicatedLogImplEntry;
 import org.opendaylight.controller.cluster.raft.Snapshot;
@@ -50,6 +51,9 @@ import org.opendaylight.controller.cluster.raft.base.messages.ApplyLogEntries;
 import org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot;
 import org.opendaylight.controller.cluster.raft.base.messages.ApplyState;
 import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot;
+import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
+import org.opendaylight.controller.cluster.raft.client.messages.FindLeader;
+import org.opendaylight.controller.cluster.raft.client.messages.FindLeaderReply;
 import org.opendaylight.controller.cluster.raft.protobuff.client.messages.CompositeModificationPayload;
 import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
 import org.opendaylight.controller.md.cluster.datastore.model.SchemaContextHelper;
@@ -78,6 +82,7 @@ import scala.concurrent.duration.FiniteDuration;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -86,6 +91,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
@@ -97,17 +103,14 @@ public class ShardTest extends AbstractActorTest {
 
     private static final SchemaContext SCHEMA_CONTEXT = TestModel.createTestContext();
 
-    private static final ShardIdentifier IDENTIFIER = ShardIdentifier.builder().memberName("member-1")
-            .shardName("inventory").type("config").build();
-
     private static final AtomicInteger NEXT_SHARD_NUM = new AtomicInteger();
 
-    private static String shardName() {
-        return "shard" + NEXT_SHARD_NUM.getAndIncrement();
-    }
+    private final ShardIdentifier shardID = ShardIdentifier.builder().memberName("member-1")
+            .shardName("inventory").type("config" + NEXT_SHARD_NUM.getAndIncrement()).build();
 
     private DatastoreContext dataStoreContext = DatastoreContext.newBuilder().
-            shardJournalRecoveryLogBatchSize(3).shardSnapshotBatchCount(5000).build();
+            shardJournalRecoveryLogBatchSize(3).shardSnapshotBatchCount(5000).
+            shardHeartbeatIntervalInMillis(100).build();
 
     @Before
     public void setUp() {
@@ -124,40 +127,149 @@ public class ShardTest extends AbstractActorTest {
     }
 
     private Props newShardProps() {
-        return Shard.props(IDENTIFIER, Collections.<ShardIdentifier,String>emptyMap(),
+        return Shard.props(shardID, Collections.<ShardIdentifier,String>emptyMap(),
                 dataStoreContext, SCHEMA_CONTEXT);
     }
 
     @Test
-    public void testOnReceiveRegisterListener() throws Exception {
-        new JavaTestKit(getSystem()) {{
-            ActorRef subject = getSystem().actorOf(newShardProps(), "testRegisterChangeListener");
+    public void testRegisterChangeListener() throws Exception {
+        new ShardTestKit(getSystem()) {{
+            TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
+                    newShardProps(),  "testRegisterChangeListener");
 
-            subject.tell(new UpdateSchemaContext(SchemaContextHelper.full()), getRef());
+            waitUntilLeader(shard);
 
-            subject.tell(new RegisterChangeListener(TestModel.TEST_PATH,
-                    getRef().path(), AsyncDataBroker.DataChangeScope.BASE), getRef());
+            shard.tell(new UpdateSchemaContext(SchemaContextHelper.full()), ActorRef.noSender());
 
-            EnableNotification enable = expectMsgClass(duration("3 seconds"), EnableNotification.class);
-            assertEquals("isEnabled", false, enable.isEnabled());
+            MockDataChangeListener listener = new MockDataChangeListener(1);
+            ActorRef dclActor = getSystem().actorOf(DataChangeListener.props(listener),
+                    "testRegisterChangeListener-DataChangeListener");
+
+            shard.tell(new RegisterChangeListener(TestModel.TEST_PATH,
+                    dclActor.path(), AsyncDataBroker.DataChangeScope.BASE), getRef());
 
             RegisterChangeListenerReply reply = expectMsgClass(duration("3 seconds"),
                     RegisterChangeListenerReply.class);
-            assertTrue(reply.getListenerRegistrationPath().toString().matches(
+            String replyPath = reply.getListenerRegistrationPath().toString();
+            assertTrue("Incorrect reply path: " + replyPath, replyPath.matches(
                     "akka:\\/\\/test\\/user\\/testRegisterChangeListener\\/\\$.*"));
+
+            YangInstanceIdentifier path = TestModel.TEST_PATH;
+            writeToStore(shard, path, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+            listener.waitForChangeEvents(path);
+
+            dclActor.tell(PoisonPill.getInstance(), ActorRef.noSender());
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
+        }};
+    }
+
+    @SuppressWarnings("serial")
+    @Test
+    public void testChangeListenerNotifiedWhenNotTheLeaderOnRegistration() throws Exception {
+        // This test tests the timing window in which a change listener is registered before the
+        // shard becomes the leader. We verify that the listener is registered and notified of the
+        // existing data when the shard becomes the leader.
+        new ShardTestKit(getSystem()) {{
+            // For this test, we want to send the RegisterChangeListener message after the shard
+            // has recovered from persistence and before it becomes the leader. So we subclass
+            // Shard to override onReceiveCommand and, when the first ElectionTimeout is received,
+            // we know that the shard has been initialized to a follower and has started the
+            // election process. The following 2 CountDownLatches are used to coordinate the
+            // ElectionTimeout with the sending of the RegisterChangeListener message.
+            final CountDownLatch onFirstElectionTimeout = new CountDownLatch(1);
+            final CountDownLatch onChangeListenerRegistered = new CountDownLatch(1);
+            Creator<Shard> creator = new Creator<Shard>() {
+                boolean firstElectionTimeout = true;
+
+                @Override
+                public Shard create() throws Exception {
+                    return new Shard(shardID, Collections.<ShardIdentifier,String>emptyMap(),
+                            dataStoreContext, SCHEMA_CONTEXT) {
+                        @Override
+                        public void onReceiveCommand(final Object message) {
+                            if(message instanceof ElectionTimeout && firstElectionTimeout) {
+                                // Got the first ElectionTimeout. We don't forward it to the
+                                // base Shard yet until we've sent the RegisterChangeListener
+                                // message. So we signal the onFirstElectionTimeout latch to tell
+                                // the main thread to send the RegisterChangeListener message and
+                                // start a thread to wait on the onChangeListenerRegistered latch,
+                                // which the main thread signals after it has sent the message.
+                                // After the onChangeListenerRegistered is triggered, we send the
+                                // original ElectionTimeout message to proceed with the election.
+                                firstElectionTimeout = false;
+                                final ActorRef self = getSelf();
+                                new Thread() {
+                                    @Override
+                                    public void run() {
+                                        Uninterruptibles.awaitUninterruptibly(
+                                                onChangeListenerRegistered, 5, TimeUnit.SECONDS);
+                                        self.tell(message, self);
+                                    }
+                                }.start();
+
+                                onFirstElectionTimeout.countDown();
+                            } else {
+                                super.onReceiveCommand(message);
+                            }
+                        }
+                    };
+                }
+            };
+
+            MockDataChangeListener listener = new MockDataChangeListener(1);
+            ActorRef dclActor = getSystem().actorOf(DataChangeListener.props(listener),
+                    "testRegisterChangeListenerWhenNotLeaderInitially-DataChangeListener");
+
+            TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
+                    Props.create(new DelegatingShardCreator(creator)),
+                    "testRegisterChangeListenerWhenNotLeaderInitially");
+
+            // Write initial data into the in-memory store.
+            YangInstanceIdentifier path = TestModel.TEST_PATH;
+            writeToStore(shard, path, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+            // Wait until the shard receives the first ElectionTimeout message.
+            assertEquals("Got first ElectionTimeout", true,
+                    onFirstElectionTimeout.await(5, TimeUnit.SECONDS));
+
+            // Now send the RegisterChangeListener and wait for the reply.
+            shard.tell(new RegisterChangeListener(path, dclActor.path(),
+                    AsyncDataBroker.DataChangeScope.SUBTREE), getRef());
+
+            RegisterChangeListenerReply reply = expectMsgClass(duration("5 seconds"),
+                    RegisterChangeListenerReply.class);
+            assertNotNull("getListenerRegistrationPath", reply.getListenerRegistrationPath());
+
+            // Sanity check - verify the shard is not the leader yet.
+            shard.tell(new FindLeader(), getRef());
+            FindLeaderReply findLeadeReply =
+                    expectMsgClass(duration("5 seconds"), FindLeaderReply.class);
+            assertNull("Expected the shard not to be the leader", findLeadeReply.getLeaderActor());
+
+            // Signal the onChangeListenerRegistered latch to tell the thread above to proceed
+            // with the election process.
+            onChangeListenerRegistered.countDown();
+
+            // Wait for the shard to become the leader and notify our listener with the existing
+            // data in the store.
+            listener.waitForChangeEvents(path);
+
+            dclActor.tell(PoisonPill.getInstance(), ActorRef.noSender());
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
     @Test
     public void testCreateTransaction(){
         new ShardTestKit(getSystem()) {{
-            ActorRef subject = getSystem().actorOf(newShardProps(), "testCreateTransaction");
+            ActorRef shard = getSystem().actorOf(newShardProps(), "testCreateTransaction");
 
-            waitUntilLeader(subject);
+            waitUntilLeader(shard);
 
-            subject.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
+            shard.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
 
-            subject.tell(new CreateTransaction("txn-1",
+            shard.tell(new CreateTransaction("txn-1",
                     TransactionProxy.TransactionType.READ_ONLY.ordinal() ).toSerializable(), getRef());
 
             CreateTransactionReply reply = expectMsgClass(duration("3 seconds"),
@@ -166,18 +278,19 @@ public class ShardTest extends AbstractActorTest {
             String path = reply.getTransactionActorPath().toString();
             assertTrue("Unexpected transaction path " + path,
                     path.contains("akka://test/user/testCreateTransaction/shard-txn-1"));
-            expectNoMsg();
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
     @Test
     public void testCreateTransactionOnChain(){
         new ShardTestKit(getSystem()) {{
-            final ActorRef subject = getSystem().actorOf(newShardProps(), "testCreateTransactionOnChain");
+            final ActorRef shard = getSystem().actorOf(newShardProps(), "testCreateTransactionOnChain");
 
-            waitUntilLeader(subject);
+            waitUntilLeader(shard);
 
-            subject.tell(new CreateTransaction("txn-1",
+            shard.tell(new CreateTransaction("txn-1",
                     TransactionProxy.TransactionType.READ_ONLY.ordinal() , "foobar").toSerializable(),
                     getRef());
 
@@ -187,47 +300,69 @@ public class ShardTest extends AbstractActorTest {
             String path = reply.getTransactionActorPath().toString();
             assertTrue("Unexpected transaction path " + path,
                     path.contains("akka://test/user/testCreateTransactionOnChain/shard-txn-1"));
-            expectNoMsg();
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
     @Test
     public void testPeerAddressResolved(){
-        new JavaTestKit(getSystem()) {{
-            final ShardIdentifier identifier =
-                ShardIdentifier.builder().memberName("member-1")
-                    .shardName("inventory").type("config").build();
+        new ShardTestKit(getSystem()) {{
+            final CountDownLatch recoveryComplete = new CountDownLatch(1);
+            class TestShard extends Shard {
+                TestShard() {
+                    super(shardID, Collections.<ShardIdentifier, String>singletonMap(shardID, null),
+                            dataStoreContext, SCHEMA_CONTEXT);
+                }
 
-            Props props = Shard.props(identifier,
-                    Collections.<ShardIdentifier, String>singletonMap(identifier, null),
-                    dataStoreContext, SCHEMA_CONTEXT);
-            final ActorRef subject = getSystem().actorOf(props, "testPeerAddressResolved");
+                Map<String, String> getPeerAddresses() {
+                    return getRaftActorContext().getPeerAddresses();
+                }
 
-            new Within(duration("3 seconds")) {
                 @Override
-                protected void run() {
+                protected void onRecoveryComplete() {
+                    try {
+                        super.onRecoveryComplete();
+                    } finally {
+                        recoveryComplete.countDown();
+                    }
+                }
+            }
 
-                    subject.tell(
-                        new PeerAddressResolved(identifier, "akka://foobar"),
-                        getRef());
+            final TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
+                    Props.create(new DelegatingShardCreator(new Creator<Shard>() {
+                        @Override
+                        public TestShard create() throws Exception {
+                            return new TestShard();
+                        }
+                    })), "testPeerAddressResolved");
 
-                    expectNoMsg();
-                }
-            };
+            //waitUntilLeader(shard);
+            assertEquals("Recovery complete", true,
+                    Uninterruptibles.awaitUninterruptibly(recoveryComplete, 5, TimeUnit.SECONDS));
+
+            String address = "akka://foobar";
+            shard.underlyingActor().onReceiveCommand(new PeerAddressResolved(shardID, address));
+
+            assertEquals("getPeerAddresses", address,
+                    ((TestShard)shard.underlyingActor()).getPeerAddresses().get(shardID.toString()));
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
     @Test
     public void testApplySnapshot() throws ExecutionException, InterruptedException {
-        TestActorRef<Shard> ref = TestActorRef.create(getSystem(), newShardProps());
+        TestActorRef<Shard> shard = TestActorRef.create(getSystem(), newShardProps(),
+                "testApplySnapshot");
 
         NormalizedNodeToNodeCodec codec =
             new NormalizedNodeToNodeCodec(SCHEMA_CONTEXT);
 
-        writeToStore(ref, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        writeToStore(shard, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
 
         YangInstanceIdentifier root = YangInstanceIdentifier.builder().build();
-        NormalizedNode<?,?> expected = readStore(ref, root);
+        NormalizedNode<?,?> expected = readStore(shard, root);
 
         NormalizedNodeMessages.Container encode = codec.encode(expected);
 
@@ -235,17 +370,19 @@ public class ShardTest extends AbstractActorTest {
                 encode.getNormalizedNode().toByteString().toByteArray(),
                 Collections.<ReplicatedLogEntry>emptyList(), 1, 2, 3, 4));
 
-        ref.underlyingActor().onReceiveCommand(applySnapshot);
+        shard.underlyingActor().onReceiveCommand(applySnapshot);
 
-        NormalizedNode<?,?> actual = readStore(ref, root);
+        NormalizedNode<?,?> actual = readStore(shard, root);
 
         assertEquals(expected, actual);
+
+        shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
     }
 
     @Test
     public void testApplyState() throws Exception {
 
-        TestActorRef<Shard> shard = TestActorRef.create(getSystem(), newShardProps());
+        TestActorRef<Shard> shard = TestActorRef.create(getSystem(), newShardProps(), "testApplyState");
 
         NormalizedNode<?, ?> node = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
@@ -259,6 +396,8 @@ public class ShardTest extends AbstractActorTest {
 
         NormalizedNode<?,?> actual = readStore(shard, TestModel.TEST_PATH);
         assertEquals("Applied state", node, actual);
+
+        shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
     }
 
     @SuppressWarnings("serial")
@@ -279,7 +418,7 @@ public class ShardTest extends AbstractActorTest {
         DOMStoreReadTransaction readTx = testStore.newReadOnlyTransaction();
         NormalizedNode<?, ?> root = readTx.read(YangInstanceIdentifier.builder().build()).get().get();
 
-        InMemorySnapshotStore.addSnapshot(IDENTIFIER.toString(), Snapshot.create(
+        InMemorySnapshotStore.addSnapshot(shardID.toString(), Snapshot.create(
                 new NormalizedNodeToNodeCodec(SCHEMA_CONTEXT).encode(
                         root).
                                 getNormalizedNode().toByteString().toByteArray(),
@@ -287,7 +426,7 @@ public class ShardTest extends AbstractActorTest {
 
         // Set up the InMemoryJournal.
 
-        InMemoryJournal.addEntry(IDENTIFIER.toString(), 0, new ReplicatedLogImplEntry(0, 1, newPayload(
+        InMemoryJournal.addEntry(shardID.toString(), 0, new ReplicatedLogImplEntry(0, 1, newPayload(
                   new WriteModification(TestModel.OUTER_LIST_PATH,
                           ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build(),
                           SCHEMA_CONTEXT))));
@@ -301,11 +440,11 @@ public class ShardTest extends AbstractActorTest {
             Modification mod = new MergeModification(path,
                     ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, i),
                     SCHEMA_CONTEXT);
-            InMemoryJournal.addEntry(IDENTIFIER.toString(), i, new ReplicatedLogImplEntry(i, 1,
+            InMemoryJournal.addEntry(shardID.toString(), i, new ReplicatedLogImplEntry(i, 1,
                     newPayload(mod)));
         }
 
-        InMemoryJournal.addEntry(IDENTIFIER.toString(), nListEntries + 1,
+        InMemoryJournal.addEntry(shardID.toString(), nListEntries + 1,
                 new ApplyLogEntries(nListEntries));
 
         // Create the actor and wait for recovery complete.
@@ -315,7 +454,7 @@ public class ShardTest extends AbstractActorTest {
         Creator<Shard> creator = new Creator<Shard>() {
             @Override
             public Shard create() throws Exception {
-                return new Shard(IDENTIFIER, Collections.<ShardIdentifier,String>emptyMap(),
+                return new Shard(shardID, Collections.<ShardIdentifier,String>emptyMap(),
                         dataStoreContext, SCHEMA_CONTEXT) {
                     @Override
                     protected void onRecoveryComplete() {
@@ -363,6 +502,8 @@ public class ShardTest extends AbstractActorTest {
                 shard.underlyingActor().getShardMBean().getCommitIndex());
         assertEquals("Last applied", nListEntries,
                 shard.underlyingActor().getShardMBean().getLastApplied());
+
+        shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
     }
 
     private CompositeModificationPayload newPayload(Modification... mods) {
@@ -433,7 +574,8 @@ public class ShardTest extends AbstractActorTest {
         System.setProperty("shard.persistent", "true");
         new ShardTestKit(getSystem()) {{
             final TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
-                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()), shardName());
+                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()),
+                    "testConcurrentThreePhaseCommits");
 
             waitUntilLeader(shard);
 
@@ -468,7 +610,7 @@ public class ShardTest extends AbstractActorTest {
             // Simulate the ForwardedReadyTransaction message for the first Tx that would be sent
             // by the ShardTransaction.
 
-            shard.tell(new ForwardedReadyTransaction(transactionID1, cohort1, modification1), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID1, cohort1, modification1, true), getRef());
             ReadyTransactionReply readyReply = ReadyTransactionReply.fromSerializable(
                     expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS));
             assertEquals("Cohort path", shard.path().toString(), readyReply.getCohortPath());
@@ -482,10 +624,10 @@ public class ShardTest extends AbstractActorTest {
 
             // Send the ForwardedReadyTransaction for the next 2 Tx's.
 
-            shard.tell(new ForwardedReadyTransaction(transactionID2, cohort2, modification2), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID2, cohort2, modification2, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
-            shard.tell(new ForwardedReadyTransaction(transactionID3, cohort3, modification3), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID3, cohort3, modification3, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
             // Send the CanCommitTransaction message for the next 2 Tx's. These should get queued and
@@ -518,7 +660,6 @@ public class ShardTest extends AbstractActorTest {
                 @Override
                 public void onComplete(Throwable error, Object resp) {
                     if(error != null) {
-                        System.out.println(new java.util.Date()+": "+getClass().getSimpleName() + " failure: "+error);
                         caughtEx.set(new AssertionError(getClass().getSimpleName() + " failure", error));
                     } else {
                         try {
@@ -606,7 +747,17 @@ public class ShardTest extends AbstractActorTest {
             assertTrue("Missing leaf " + TestModel.ID_QNAME.getLocalName(), idLeaf.isPresent());
             assertEquals(TestModel.ID_QNAME.getLocalName() + " value", 1, idLeaf.get().getValue());
 
+            for(int i = 0; i < 20 * 5; i++) {
+                long lastLogIndex = shard.underlyingActor().getShardMBean().getLastLogIndex();
+                if(lastLogIndex == 2) {
+                    break;
+                }
+                Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
+            }
+
             assertEquals("Last log index", 2, shard.underlyingActor().getShardMBean().getLastLogIndex());
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
@@ -614,7 +765,8 @@ public class ShardTest extends AbstractActorTest {
     public void testCommitPhaseFailure() throws Throwable {
         new ShardTestKit(getSystem()) {{
             final TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
-                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()), shardName());
+                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()),
+                    "testCommitPhaseFailure");
 
             waitUntilLeader(shard);
 
@@ -639,10 +791,10 @@ public class ShardTest extends AbstractActorTest {
             // Simulate the ForwardedReadyTransaction messages that would be sent
             // by the ShardTransaction.
 
-            shard.tell(new ForwardedReadyTransaction(transactionID1, cohort1, modification1), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID1, cohort1, modification1, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
-            shard.tell(new ForwardedReadyTransaction(transactionID2, cohort2, modification2), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID2, cohort2, modification2, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
             // Send the CanCommitTransaction message for the first Tx.
@@ -681,6 +833,8 @@ public class ShardTest extends AbstractActorTest {
             inOrder.verify(cohort1).preCommit();
             inOrder.verify(cohort1).commit();
             inOrder.verify(cohort2).canCommit();
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
@@ -688,7 +842,8 @@ public class ShardTest extends AbstractActorTest {
     public void testPreCommitPhaseFailure() throws Throwable {
         new ShardTestKit(getSystem()) {{
             final TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
-                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()), shardName());
+                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()),
+                    "testPreCommitPhaseFailure");
 
             waitUntilLeader(shard);
 
@@ -703,7 +858,7 @@ public class ShardTest extends AbstractActorTest {
             // Simulate the ForwardedReadyTransaction messages that would be sent
             // by the ShardTransaction.
 
-            shard.tell(new ForwardedReadyTransaction(transactionID, cohort, modification), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID, cohort, modification, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
             // Send the CanCommitTransaction message.
@@ -722,6 +877,8 @@ public class ShardTest extends AbstractActorTest {
             InOrder inOrder = inOrder(cohort);
             inOrder.verify(cohort).canCommit();
             inOrder.verify(cohort).preCommit();
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
@@ -729,7 +886,8 @@ public class ShardTest extends AbstractActorTest {
     public void testCanCommitPhaseFailure() throws Throwable {
         new ShardTestKit(getSystem()) {{
             final TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
-                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()), shardName());
+                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()),
+                    "testCanCommitPhaseFailure");
 
             waitUntilLeader(shard);
 
@@ -743,13 +901,15 @@ public class ShardTest extends AbstractActorTest {
             // Simulate the ForwardedReadyTransaction messages that would be sent
             // by the ShardTransaction.
 
-            shard.tell(new ForwardedReadyTransaction(transactionID, cohort, modification), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID, cohort, modification, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
             // Send the CanCommitTransaction message.
 
             shard.tell(new CanCommitTransaction(transactionID).toSerializable(), getRef());
             expectMsgClass(duration, akka.actor.Status.Failure.class);
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
@@ -758,7 +918,8 @@ public class ShardTest extends AbstractActorTest {
         System.setProperty("shard.persistent", "true");
         new ShardTestKit(getSystem()) {{
             final TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
-                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()), shardName());
+                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()),
+                    "testAbortBeforeFinishCommit");
 
             waitUntilLeader(shard);
 
@@ -793,7 +954,7 @@ public class ShardTest extends AbstractActorTest {
                     TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME),
                     modification, preCommit);
 
-            shard.tell(new ForwardedReadyTransaction(transactionID, cohort, modification), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID, cohort, modification, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
             shard.tell(new CanCommitTransaction(transactionID).toSerializable(), getRef());
@@ -810,6 +971,8 @@ public class ShardTest extends AbstractActorTest {
 
             NormalizedNode<?, ?> node = readStore(shard, TestModel.TEST_PATH);
             assertNotNull(TestModel.TEST_QNAME.getLocalName() + " not found", node);
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
@@ -819,7 +982,8 @@ public class ShardTest extends AbstractActorTest {
 
         new ShardTestKit(getSystem()) {{
             final TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
-                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()), shardName());
+                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()),
+                    "testTransactionCommitTimeout");
 
             waitUntilLeader(shard);
 
@@ -854,10 +1018,10 @@ public class ShardTest extends AbstractActorTest {
 
             // Ready the Tx's
 
-            shard.tell(new ForwardedReadyTransaction(transactionID1, cohort1, modification1), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID1, cohort1, modification1, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
-            shard.tell(new ForwardedReadyTransaction(transactionID2, cohort2, modification2), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID2, cohort2, modification2, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
             // canCommit 1st Tx. We don't send the commit so it should timeout.
@@ -877,6 +1041,8 @@ public class ShardTest extends AbstractActorTest {
 
             NormalizedNode<?, ?> node = readStore(shard, listNodePath);
             assertNotNull(listNodePath + " not found", node);
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
@@ -886,7 +1052,8 @@ public class ShardTest extends AbstractActorTest {
 
         new ShardTestKit(getSystem()) {{
             final TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
-                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()), shardName());
+                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()),
+                    "testTransactionCommitQueueCapacityExceeded");
 
             waitUntilLeader(shard);
 
@@ -913,13 +1080,13 @@ public class ShardTest extends AbstractActorTest {
 
             // Ready the Tx's
 
-            shard.tell(new ForwardedReadyTransaction(transactionID1, cohort1, modification1), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID1, cohort1, modification1, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
-            shard.tell(new ForwardedReadyTransaction(transactionID2, cohort2, modification2), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID2, cohort2, modification2, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
-            shard.tell(new ForwardedReadyTransaction(transactionID3, cohort3, modification3), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID3, cohort3, modification3, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
             // canCommit 1st Tx.
@@ -935,6 +1102,8 @@ public class ShardTest extends AbstractActorTest {
 
             shard.tell(new CanCommitTransaction(transactionID3).toSerializable(), getRef());
             expectMsgClass(duration, akka.actor.Status.Failure.class);
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
@@ -942,10 +1111,13 @@ public class ShardTest extends AbstractActorTest {
     public void testCanCommitBeforeReadyFailure() throws Throwable {
         new ShardTestKit(getSystem()) {{
             final TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
-                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()), shardName());
+                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()),
+                    "testCanCommitBeforeReadyFailure");
 
             shard.tell(new CanCommitTransaction("tx").toSerializable(), getRef());
             expectMsgClass(duration("5 seconds"), akka.actor.Status.Failure.class);
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
@@ -953,7 +1125,8 @@ public class ShardTest extends AbstractActorTest {
     public void testAbortTransaction() throws Throwable {
         new ShardTestKit(getSystem()) {{
             final TestActorRef<Shard> shard = TestActorRef.create(getSystem(),
-                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()), shardName());
+                    newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()),
+                    "testAbortTransaction");
 
             waitUntilLeader(shard);
 
@@ -976,10 +1149,10 @@ public class ShardTest extends AbstractActorTest {
             // Simulate the ForwardedReadyTransaction messages that would be sent
             // by the ShardTransaction.
 
-            shard.tell(new ForwardedReadyTransaction(transactionID1, cohort1, modification1), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID1, cohort1, modification1, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
-            shard.tell(new ForwardedReadyTransaction(transactionID2, cohort2, modification2), getRef());
+            shard.tell(new ForwardedReadyTransaction(transactionID2, cohort2, modification2, true), getRef());
             expectMsgClass(duration, ReadyTransactionReply.SERIALIZABLE_CLASS);
 
             // Send the CanCommitTransaction message for the first Tx.
@@ -1016,6 +1189,8 @@ public class ShardTest extends AbstractActorTest {
             InOrder inOrder = inOrder(cohort1, cohort2);
             inOrder.verify(cohort1).canCommit();
             inOrder.verify(cohort2).canCommit();
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
@@ -1026,7 +1201,7 @@ public class ShardTest extends AbstractActorTest {
             Creator<Shard> creator = new Creator<Shard>() {
                 @Override
                 public Shard create() throws Exception {
-                    return new Shard(IDENTIFIER, Collections.<ShardIdentifier,String>emptyMap(),
+                    return new Shard(shardID, Collections.<ShardIdentifier,String>emptyMap(),
                             dataStoreContext, SCHEMA_CONTEXT) {
                         @Override
                         public void saveSnapshot(Object snapshot) {
@@ -1050,6 +1225,8 @@ public class ShardTest extends AbstractActorTest {
             shard.tell(new CaptureSnapshot(-1,-1,-1,-1), getRef());
 
             assertEquals("Snapshot saved", true, latch.get().await(5, TimeUnit.SECONDS));
+
+            shard.tell(PoisonPill.getInstance(), ActorRef.noSender());
         }};
     }
 
index 9a0e8f9e18416d104f059e316577d377e5c718f2..d08258a2a026ec890ddb736f1456c6682e2a3c5c 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.controller.cluster.datastore;
 
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import org.junit.Assert;
 import org.opendaylight.controller.cluster.raft.client.messages.FindLeader;
 import org.opendaylight.controller.cluster.raft.client.messages.FindLeaderReply;
@@ -15,6 +16,7 @@ import com.google.common.util.concurrent.Uninterruptibles;
 import scala.concurrent.Await;
 import scala.concurrent.Future;
 import scala.concurrent.duration.Duration;
+import scala.concurrent.duration.FiniteDuration;
 import akka.actor.ActorRef;
 import akka.actor.ActorSystem;
 import akka.pattern.Patterns;
@@ -45,17 +47,21 @@ class ShardTestKit extends JavaTestKit {
     }
 
     protected void waitUntilLeader(ActorRef shard) {
+        FiniteDuration duration = Duration.create(100, TimeUnit.MILLISECONDS);
         for(int i = 0; i < 20 * 5; i++) {
-            Future<Object> future = Patterns.ask(shard, new FindLeader(), new Timeout(5, TimeUnit.SECONDS));
+            Future<Object> future = Patterns.ask(shard, new FindLeader(), new Timeout(duration));
             try {
-                FindLeaderReply resp = (FindLeaderReply)Await.result(future, Duration.create(5, TimeUnit.SECONDS));
+                FindLeaderReply resp = (FindLeaderReply)Await.result(future, duration);
                 if(resp.getLeaderActor() != null) {
                     return;
                 }
-            } catch (Exception e) {
+            } catch(TimeoutException e) {
+            } catch(Exception e) {
+                System.err.println("FindLeader threw ex");
                 e.printStackTrace();
             }
 
+
             Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
         }
 
index 8ce8f4d4b548be7080ac1d57ec158da5eeecb629..711f3d7a72a16b615224246e07e3adb750b7cff6 100644 (file)
@@ -9,6 +9,7 @@ import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.ShardWriteTransaction.GetCompositeModificationReply;
 import org.opendaylight.controller.cluster.datastore.exceptions.UnknownMessageException;
 import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier;
 import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats;
@@ -33,6 +34,7 @@ import org.opendaylight.controller.cluster.datastore.modification.Modification;
 import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
 import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
 import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -40,6 +42,8 @@ import scala.concurrent.duration.Duration;
 import java.util.Collections;
 import java.util.concurrent.TimeUnit;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 public class ShardTransactionTest extends AbstractActorTest {
@@ -73,41 +77,35 @@ public class ShardTransactionTest extends AbstractActorTest {
     public void testOnReceiveReadData() throws Exception {
         new JavaTestKit(getSystem()) {{
             final ActorRef shard = createShard();
-            final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard,
+            Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard,
                     testSchemaContext, datastoreContext, shardStats, "txn");
-            final ActorRef subject = getSystem().actorOf(props, "testReadData");
 
-            new Within(duration("1 seconds")) {
-                @Override
-                protected void run() {
-
-                    subject.tell(
-                        new ReadData(YangInstanceIdentifier.builder().build()).toSerializable(),
-                        getRef());
-
-                    final String out = new ExpectMsg<String>(duration("1 seconds"), "match hint") {
-                        // do not put code outside this method, will run afterwards
-                        @Override
-                        protected String match(Object in) {
-                            if (in.getClass().equals(ReadDataReply.SERIALIZABLE_CLASS)) {
-                              if (ReadDataReply.fromSerializable(testSchemaContext,YangInstanceIdentifier.builder().build(), in)
-                                  .getNormalizedNode()!= null) {
-                                    return "match";
-                                }
-                                return null;
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get(); // this extracts the received message
-
-                    assertEquals("match", out);
-
-                    expectNoMsg();
-                }
+            testOnReceiveReadData(getSystem().actorOf(props, "testReadDataRO"));
+
+            props = ShardTransaction.props(store.newReadWriteTransaction(), shard,
+                    testSchemaContext, datastoreContext, shardStats, "txn");
+
+            testOnReceiveReadData(getSystem().actorOf(props, "testReadDataRW"));
+        }
+
+        private void testOnReceiveReadData(final ActorRef subject) {
+            //serialized read
+            subject.tell(new ReadData(YangInstanceIdentifier.builder().build()).toSerializable(),
+                getRef());
+
+            ShardTransactionMessages.ReadDataReply replySerialized =
+                expectMsgClass(duration("5 seconds"), ReadDataReply.SERIALIZABLE_CLASS);
+
+            assertNotNull(ReadDataReply.fromSerializable(
+                testSchemaContext,YangInstanceIdentifier.builder().build(), replySerialized)
+                .getNormalizedNode());
+
+            // unserialized read
+            subject.tell(new ReadData(YangInstanceIdentifier.builder().build()),getRef());
 
+            ReadDataReply reply = expectMsgClass(duration("5 seconds"), ReadDataReply.class);
 
-            };
+            assertNotNull(reply.getNormalizedNode());
         }};
     }
 
@@ -115,42 +113,35 @@ public class ShardTransactionTest extends AbstractActorTest {
     public void testOnReceiveReadDataWhenDataNotFound() throws Exception {
         new JavaTestKit(getSystem()) {{
             final ActorRef shard = createShard();
-            final Props props = ShardTransaction.props( store.newReadOnlyTransaction(), shard,
+            Props props = ShardTransaction.props( store.newReadOnlyTransaction(), shard,
                     testSchemaContext, datastoreContext, shardStats, "txn");
-            final ActorRef subject = getSystem().actorOf(props, "testReadDataWhenDataNotFound");
 
-            new Within(duration("1 seconds")) {
-                @Override
-                protected void run() {
-
-                    subject.tell(
-                        new ReadData(TestModel.TEST_PATH).toSerializable(),
-                        getRef());
-
-                    final String out = new ExpectMsg<String>(duration("1 seconds"), "match hint") {
-                        // do not put code outside this method, will run afterwards
-                        @Override
-                        protected String match(Object in) {
-                            if (in.getClass().equals(ReadDataReply.SERIALIZABLE_CLASS)) {
-                                if (ReadDataReply.fromSerializable(testSchemaContext,TestModel.TEST_PATH, in)
-                                    .getNormalizedNode()
-                                    == null) {
-                                    return "match";
-                                }
-                                return null;
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get(); // this extracts the received message
-
-                    assertEquals("match", out);
-
-                    expectNoMsg();
-                }
+            testOnReceiveReadDataWhenDataNotFound(getSystem().actorOf(
+                    props, "testReadDataWhenDataNotFoundRO"));
+
+            props = ShardTransaction.props( store.newReadWriteTransaction(), shard,
+                    testSchemaContext, datastoreContext, shardStats, "txn");
+
+            testOnReceiveReadDataWhenDataNotFound(getSystem().actorOf(
+                    props, "testReadDataWhenDataNotFoundRW"));
+        }
+
+        private void testOnReceiveReadDataWhenDataNotFound(final ActorRef subject) {
+            // serialized read
+            subject.tell(new ReadData(TestModel.TEST_PATH).toSerializable(), getRef());
+
+            ShardTransactionMessages.ReadDataReply replySerialized =
+                expectMsgClass(duration("5 seconds"), ReadDataReply.SERIALIZABLE_CLASS);
+
+            assertTrue(ReadDataReply.fromSerializable(
+                testSchemaContext, TestModel.TEST_PATH, replySerialized).getNormalizedNode() == null);
 
+            // unserialized read
+            subject.tell(new ReadData(TestModel.TEST_PATH),getRef());
 
-            };
+            ReadDataReply reply = expectMsgClass(duration("5 seconds"), ReadDataReply.class);
+
+            assertTrue(reply.getNormalizedNode() == null);
         }};
     }
 
@@ -158,41 +149,32 @@ public class ShardTransactionTest extends AbstractActorTest {
     public void testOnReceiveDataExistsPositive() throws Exception {
         new JavaTestKit(getSystem()) {{
             final ActorRef shard = createShard();
-            final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard,
+            Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard,
                     testSchemaContext, datastoreContext, shardStats, "txn");
-            final ActorRef subject = getSystem().actorOf(props, "testDataExistsPositive");
 
-            new Within(duration("1 seconds")) {
-                @Override
-                protected void run() {
-
-                    subject.tell(
-                        new DataExists(YangInstanceIdentifier.builder().build()).toSerializable(),
-                        getRef());
-
-                    final String out = new ExpectMsg<String>(duration("1 seconds"), "match hint") {
-                        // do not put code outside this method, will run afterwards
-                        @Override
-                        protected String match(Object in) {
-                            if (in.getClass().equals(DataExistsReply.SERIALIZABLE_CLASS)) {
-                                if (DataExistsReply.fromSerializable(in)
-                                    .exists()) {
-                                    return "match";
-                                }
-                                return null;
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get(); // this extracts the received message
-
-                    assertEquals("match", out);
-
-                    expectNoMsg();
-                }
+            testOnReceiveDataExistsPositive(getSystem().actorOf(props, "testDataExistsPositiveRO"));
+
+            props = ShardTransaction.props(store.newReadWriteTransaction(), shard,
+                    testSchemaContext, datastoreContext, shardStats, "txn");
+
+            testOnReceiveDataExistsPositive(getSystem().actorOf(props, "testDataExistsPositiveRW"));
+        }
+
+        private void testOnReceiveDataExistsPositive(final ActorRef subject) {
+            subject.tell(new DataExists(YangInstanceIdentifier.builder().build()).toSerializable(),
+                getRef());
+
+            ShardTransactionMessages.DataExistsReply replySerialized =
+                expectMsgClass(duration("5 seconds"), ShardTransactionMessages.DataExistsReply.class);
 
+            assertTrue(DataExistsReply.fromSerializable(replySerialized).exists());
 
-            };
+            // unserialized read
+            subject.tell(new DataExists(YangInstanceIdentifier.builder().build()),getRef());
+
+            DataExistsReply reply = expectMsgClass(duration("5 seconds"), DataExistsReply.class);
+
+            assertTrue(reply.exists());
         }};
     }
 
@@ -200,76 +182,44 @@ public class ShardTransactionTest extends AbstractActorTest {
     public void testOnReceiveDataExistsNegative() throws Exception {
         new JavaTestKit(getSystem()) {{
             final ActorRef shard = createShard();
-            final Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard,
+            Props props = ShardTransaction.props(store.newReadOnlyTransaction(), shard,
                     testSchemaContext, datastoreContext, shardStats, "txn");
-            final ActorRef subject = getSystem().actorOf(props, "testDataExistsNegative");
 
-            new Within(duration("1 seconds")) {
-                @Override
-                protected void run() {
-
-                    subject.tell(
-                        new DataExists(TestModel.TEST_PATH).toSerializable(),
-                        getRef());
-
-                    final String out = new ExpectMsg<String>(duration("1 seconds"), "match hint") {
-                        // do not put code outside this method, will run afterwards
-                        @Override
-                        protected String match(Object in) {
-                            if (in.getClass().equals(DataExistsReply.SERIALIZABLE_CLASS)) {
-                                if (!DataExistsReply.fromSerializable(in)
-                                    .exists()) {
-                                    return "match";
-                                }
-                                return null;
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get(); // this extracts the received message
-
-                    assertEquals("match", out);
-
-                    expectNoMsg();
-                }
+            testOnReceiveDataExistsNegative(getSystem().actorOf(props, "testDataExistsNegativeRO"));
+
+            props = ShardTransaction.props(store.newReadWriteTransaction(), shard,
+                    testSchemaContext, datastoreContext, shardStats, "txn");
+
+            testOnReceiveDataExistsNegative(getSystem().actorOf(props, "testDataExistsNegativeRW"));
+        }
+
+        private void testOnReceiveDataExistsNegative(final ActorRef subject) {
+            subject.tell(new DataExists(TestModel.TEST_PATH).toSerializable(), getRef());
 
+            ShardTransactionMessages.DataExistsReply replySerialized =
+                expectMsgClass(duration("5 seconds"), ShardTransactionMessages.DataExistsReply.class);
 
-            };
+            assertFalse(DataExistsReply.fromSerializable(replySerialized).exists());
+
+            // unserialized read
+            subject.tell(new DataExists(TestModel.TEST_PATH),getRef());
+
+            DataExistsReply reply = expectMsgClass(duration("5 seconds"), DataExistsReply.class);
+
+            assertFalse(reply.exists());
         }};
     }
 
     private void assertModification(final ActorRef subject,
         final Class<? extends Modification> modificationType) {
         new JavaTestKit(getSystem()) {{
-            new Within(duration("3 seconds")) {
-                @Override
-                protected void run() {
-                    subject
-                        .tell(new ShardWriteTransaction.GetCompositedModification(),
-                            getRef());
-
-                    final CompositeModification compositeModification =
-                        new ExpectMsg<CompositeModification>(duration("3 seconds"), "match hint") {
-                            // do not put code outside this method, will run afterwards
-                            @Override
-                            protected CompositeModification match(Object in) {
-                                if (in instanceof ShardWriteTransaction.GetCompositeModificationReply) {
-                                    return ((ShardWriteTransaction.GetCompositeModificationReply) in)
-                                        .getModification();
-                                } else {
-                                    throw noMatch();
-                                }
-                            }
-                        }.get(); // this extracts the received message
-
-                    assertTrue(
-                        compositeModification.getModifications().size() == 1);
-                    assertEquals(modificationType,
-                        compositeModification.getModifications().get(0)
-                            .getClass());
+            subject.tell(new ShardWriteTransaction.GetCompositedModification(), getRef());
 
-                }
-            };
+            CompositeModification compositeModification = expectMsgClass(duration("3 seconds"),
+                    GetCompositeModificationReply.class).getModification();
+
+            assertTrue(compositeModification.getModifications().size() == 1);
+            assertEquals(modificationType, compositeModification.getModifications().get(0).getClass());
         }};
     }
 
@@ -282,34 +232,22 @@ public class ShardTransactionTest extends AbstractActorTest {
             final ActorRef subject =
                 getSystem().actorOf(props, "testWriteData");
 
-            new Within(duration("1 seconds")) {
-                @Override
-                protected void run() {
-
-                    subject.tell(new WriteData(TestModel.TEST_PATH,
-                        ImmutableNodes.containerNode(TestModel.TEST_QNAME), TestModel.createTestContext()).toSerializable(),
-                        getRef());
-
-                    final String out = new ExpectMsg<String>(duration("1 seconds"), "match hint") {
-                        // do not put code outside this method, will run afterwards
-                        @Override
-                        protected String match(Object in) {
-                            if (in.getClass().equals(WriteDataReply.SERIALIZABLE_CLASS)) {
-                                return "match";
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get(); // this extracts the received message
-
-                    assertEquals("match", out);
-
-                    assertModification(subject, WriteModification.class);
-                    expectNoMsg();
-                }
+            subject.tell(new WriteData(TestModel.TEST_PATH,
+                ImmutableNodes.containerNode(TestModel.TEST_QNAME), TestModel.createTestContext()).toSerializable(),
+                getRef());
+
+            ShardTransactionMessages.WriteDataReply replySerialized =
+                expectMsgClass(duration("5 seconds"), ShardTransactionMessages.WriteDataReply.class);
 
+            assertModification(subject, WriteModification.class);
 
-            };
+            //unserialized write
+            subject.tell(new WriteData(TestModel.TEST_PATH,
+                ImmutableNodes.containerNode(TestModel.TEST_QNAME),
+                TestModel.createTestContext()),
+                getRef());
+
+            expectMsgClass(duration("5 seconds"), WriteDataReply.class);
         }};
     }
 
@@ -322,35 +260,21 @@ public class ShardTransactionTest extends AbstractActorTest {
             final ActorRef subject =
                 getSystem().actorOf(props, "testMergeData");
 
-            new Within(duration("1 seconds")) {
-                @Override
-                protected void run() {
-
-                    subject.tell(new MergeData(TestModel.TEST_PATH,
-                        ImmutableNodes.containerNode(TestModel.TEST_QNAME), testSchemaContext).toSerializable(),
-                        getRef());
-
-                    final String out = new ExpectMsg<String>(duration("500 milliseconds"), "match hint") {
-                        // do not put code outside this method, will run afterwards
-                        @Override
-                        protected String match(Object in) {
-                            if (in.getClass().equals(MergeDataReply.SERIALIZABLE_CLASS)) {
-                                return "match";
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get(); // this extracts the received message
+            subject.tell(new MergeData(TestModel.TEST_PATH,
+                ImmutableNodes.containerNode(TestModel.TEST_QNAME), testSchemaContext).toSerializable(),
+                getRef());
 
-                    assertEquals("match", out);
+            ShardTransactionMessages.MergeDataReply replySerialized =
+                expectMsgClass(duration("5 seconds"), ShardTransactionMessages.MergeDataReply.class);
 
-                    assertModification(subject, MergeModification.class);
-
-                    expectNoMsg();
-                }
+            assertModification(subject, MergeModification.class);
 
+            //unserialized merge
+            subject.tell(new MergeData(TestModel.TEST_PATH,
+                ImmutableNodes.containerNode(TestModel.TEST_QNAME), testSchemaContext),
+                getRef());
 
-            };
+            expectMsgClass(duration("5 seconds"), MergeDataReply.class);
         }};
     }
 
@@ -363,32 +287,17 @@ public class ShardTransactionTest extends AbstractActorTest {
             final ActorRef subject =
                 getSystem().actorOf(props, "testDeleteData");
 
-            new Within(duration("1 seconds")) {
-                @Override
-                protected void run() {
-
-                    subject.tell(new DeleteData(TestModel.TEST_PATH).toSerializable(), getRef());
-
-                    final String out = new ExpectMsg<String>(duration("1 seconds"), "match hint") {
-                        // do not put code outside this method, will run afterwards
-                        @Override
-                        protected String match(Object in) {
-                            if (in.getClass().equals(DeleteDataReply.SERIALIZABLE_CLASS)) {
-                                return "match";
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get(); // this extracts the received message
-
-                    assertEquals("match", out);
-
-                    assertModification(subject, DeleteModification.class);
-                    expectNoMsg();
-                }
+            subject.tell(new DeleteData(TestModel.TEST_PATH).toSerializable(), getRef());
 
+            ShardTransactionMessages.DeleteDataReply replySerialized =
+                expectMsgClass(duration("5 seconds"), ShardTransactionMessages.DeleteDataReply.class);
 
-            };
+            assertModification(subject, DeleteModification.class);
+
+            //unserialized merge
+            subject.tell(new DeleteData(TestModel.TEST_PATH), getRef());
+
+            expectMsgClass(duration("5 seconds"), DeleteDataReply.class);
         }};
     }
 
@@ -402,83 +311,41 @@ public class ShardTransactionTest extends AbstractActorTest {
             final ActorRef subject =
                 getSystem().actorOf(props, "testReadyTransaction");
 
-            new Within(duration("1 seconds")) {
-                @Override
-                protected void run() {
-
-                    subject.tell(new ReadyTransaction().toSerializable(), getRef());
+            subject.tell(new ReadyTransaction().toSerializable(), getRef());
 
-                    final String out = new ExpectMsg<String>(duration("1 seconds"), "match hint") {
-                        // do not put code outside this method, will run afterwards
-                        @Override
-                        protected String match(Object in) {
-                            if (in.getClass().equals(ReadyTransactionReply.SERIALIZABLE_CLASS)) {
-                                return "match";
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get(); // this extracts the received message
-
-                    assertEquals("match", out);
+            expectMsgClass(duration("5 seconds"), ReadyTransactionReply.SERIALIZABLE_CLASS);
+        }};
 
-                    expectNoMsg();
-                }
+        // test
+        new JavaTestKit(getSystem()) {{
+            final ActorRef shard = createShard();
+            final Props props = ShardTransaction.props( store.newReadWriteTransaction(), shard,
+                testSchemaContext, datastoreContext, shardStats, "txn");
+            final ActorRef subject =
+                getSystem().actorOf(props, "testReadyTransaction2");
 
+            subject.tell(new ReadyTransaction(), getRef());
 
-            };
+            expectMsgClass(duration("5 seconds"), ReadyTransactionReply.class);
         }};
 
     }
 
+    @SuppressWarnings("unchecked")
     @Test
     public void testOnReceiveCloseTransaction() throws Exception {
         new JavaTestKit(getSystem()) {{
             final ActorRef shard = createShard();
             final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard,
                     testSchemaContext, datastoreContext, shardStats, "txn");
-            final ActorRef subject =
-                getSystem().actorOf(props, "testCloseTransaction");
+            final ActorRef subject = getSystem().actorOf(props, "testCloseTransaction");
 
             watch(subject);
 
-            new Within(duration("6 seconds")) {
-                @Override
-                protected void run() {
-
-                    subject.tell(new CloseTransaction().toSerializable(), getRef());
-
-                    final String out = new ExpectMsg<String>(duration("3 seconds"), "match hint") {
-                        // do not put code outside this method, will run afterwards
-                        @Override
-                        protected String match(Object in) {
-                            System.out.println("!!!IN match 1: "+(in!=null?in.getClass():"NULL"));
-                            if (in.getClass().equals(CloseTransactionReply.SERIALIZABLE_CLASS)) {
-                                return "match";
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get(); // this extracts the received message
-
-                    assertEquals("match", out);
-
-                    final String termination = new ExpectMsg<String>(duration("3 seconds"), "match hint") {
-                        // do not put code outside this method, will run afterwards
-                        @Override
-                        protected String match(Object in) {
-                            System.out.println("!!!IN match 2: "+(in!=null?in.getClass():"NULL"));
-                            if (in instanceof Terminated) {
-                                return "match";
-                            } else {
-                                throw noMatch();
-                            }
-                        }
-                    }.get(); // this extracts the received message
-
-                    assertEquals("match", termination);
-                }
-            };
+            subject.tell(new CloseTransaction().toSerializable(), getRef());
+
+            expectMsgClass(duration("3 seconds"), CloseTransactionReply.SERIALIZABLE_CLASS);
+            expectMsgClass(duration("3 seconds"), Terminated.class);
         }};
     }
 
index 592337f93f2db9cce227fb9db1f76c1dad06d508..f2b849122a3c0fced5f8925b0ab6d7e80080152c 100644 (file)
@@ -1,12 +1,12 @@
 package org.opendaylight.controller.cluster.datastore;
 
-import com.google.common.util.concurrent.CheckedFuture;
-
 import akka.actor.ActorRef;
 import akka.actor.ActorSelection;
+import akka.actor.ActorSystem;
 import akka.actor.Props;
 import akka.dispatch.Futures;
 import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentMatcher;
@@ -44,10 +44,8 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import scala.concurrent.Await;
 import scala.concurrent.Future;
 import scala.concurrent.duration.Duration;
-
 import java.util.List;
 import java.util.concurrent.TimeUnit;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -83,6 +81,9 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     private SchemaContext schemaContext;
 
+    @Mock
+    private ClusterWrapper mockClusterWrapper;
+
     String memberName = "mock-member";
 
     @Before
@@ -94,6 +95,7 @@ public class TransactionProxyTest extends AbstractActorTest {
         doReturn(getSystem()).when(mockActorContext).getActorSystem();
         doReturn(memberName).when(mockActorContext).getCurrentMemberName();
         doReturn(schemaContext).when(mockActorContext).getSchemaContext();
+        doReturn(mockClusterWrapper).when(mockActorContext).getClusterWrapper();
 
         ShardStrategyFactory.setConfiguration(configuration);
     }
@@ -112,7 +114,7 @@ public class TransactionProxyTest extends AbstractActorTest {
         return argThat(matcher);
     }
 
-    private DataExists eqDataExists() {
+    private DataExists eqSerializedDataExists() {
         ArgumentMatcher<DataExists> matcher = new ArgumentMatcher<DataExists>() {
             @Override
             public boolean matches(Object argument) {
@@ -124,7 +126,19 @@ public class TransactionProxyTest extends AbstractActorTest {
         return argThat(matcher);
     }
 
-    private ReadData eqReadData() {
+    private DataExists eqDataExists() {
+        ArgumentMatcher<DataExists> matcher = new ArgumentMatcher<DataExists>() {
+            @Override
+            public boolean matches(Object argument) {
+                return (argument instanceof DataExists) &&
+                    ((DataExists)argument).getPath().equals(TestModel.TEST_PATH);
+            }
+        };
+
+        return argThat(matcher);
+    }
+
+    private ReadData eqSerializedReadData() {
         ArgumentMatcher<ReadData> matcher = new ArgumentMatcher<ReadData>() {
             @Override
             public boolean matches(Object argument) {
@@ -136,7 +150,19 @@ public class TransactionProxyTest extends AbstractActorTest {
         return argThat(matcher);
     }
 
-    private WriteData eqWriteData(final NormalizedNode<?, ?> nodeToWrite) {
+    private ReadData eqReadData() {
+        ArgumentMatcher<ReadData> matcher = new ArgumentMatcher<ReadData>() {
+            @Override
+            public boolean matches(Object argument) {
+                return (argument instanceof ReadData) &&
+                    ((ReadData)argument).getPath().equals(TestModel.TEST_PATH);
+            }
+        };
+
+        return argThat(matcher);
+    }
+
+    private WriteData eqSerializedWriteData(final NormalizedNode<?, ?> nodeToWrite) {
         ArgumentMatcher<WriteData> matcher = new ArgumentMatcher<WriteData>() {
             @Override
             public boolean matches(Object argument) {
@@ -153,7 +179,23 @@ public class TransactionProxyTest extends AbstractActorTest {
         return argThat(matcher);
     }
 
-    private MergeData eqMergeData(final NormalizedNode<?, ?> nodeToWrite) {
+    private WriteData eqWriteData(final NormalizedNode<?, ?> nodeToWrite) {
+        ArgumentMatcher<WriteData> matcher = new ArgumentMatcher<WriteData>() {
+            @Override
+            public boolean matches(Object argument) {
+                if(argument instanceof WriteData) {
+                    WriteData obj = (WriteData) argument;
+                    return obj.getPath().equals(TestModel.TEST_PATH) &&
+                        obj.getData().equals(nodeToWrite);
+                }
+                return false;
+            }
+        };
+
+        return argThat(matcher);
+    }
+
+    private MergeData eqSerializedMergeData(final NormalizedNode<?, ?> nodeToWrite) {
         ArgumentMatcher<MergeData> matcher = new ArgumentMatcher<MergeData>() {
             @Override
             public boolean matches(Object argument) {
@@ -170,7 +212,24 @@ public class TransactionProxyTest extends AbstractActorTest {
         return argThat(matcher);
     }
 
-    private DeleteData eqDeleteData() {
+    private MergeData eqMergeData(final NormalizedNode<?, ?> nodeToWrite) {
+        ArgumentMatcher<MergeData> matcher = new ArgumentMatcher<MergeData>() {
+            @Override
+            public boolean matches(Object argument) {
+                if(argument instanceof MergeData) {
+                    MergeData obj = ((MergeData) argument);
+                    return obj.getPath().equals(TestModel.TEST_PATH) &&
+                        obj.getData().equals(nodeToWrite);
+                }
+
+               return false;
+            }
+        };
+
+        return argThat(matcher);
+    }
+
+    private DeleteData eqSerializedDeleteData() {
         ArgumentMatcher<DeleteData> matcher = new ArgumentMatcher<DeleteData>() {
             @Override
             public boolean matches(Object argument) {
@@ -182,30 +241,67 @@ public class TransactionProxyTest extends AbstractActorTest {
         return argThat(matcher);
     }
 
-    private Future<Object> readyTxReply(String path) {
+        private DeleteData eqDeleteData() {
+        ArgumentMatcher<DeleteData> matcher = new ArgumentMatcher<DeleteData>() {
+            @Override
+            public boolean matches(Object argument) {
+                return argument instanceof DeleteData &&
+                    ((DeleteData)argument).getPath().equals(TestModel.TEST_PATH);
+            }
+        };
+
+        return argThat(matcher);
+    }
+
+    private Future<Object> readySerializedTxReply(String path) {
         return Futures.successful((Object)new ReadyTransactionReply(path).toSerializable());
     }
 
-    private Future<Object> readDataReply(NormalizedNode<?, ?> data) {
+    private Future<Object> readyTxReply(String path) {
+        return Futures.successful((Object)new ReadyTransactionReply(path));
+    }
+
+
+    private Future<Object> readSerializedDataReply(NormalizedNode<?, ?> data) {
         return Futures.successful(new ReadDataReply(schemaContext, data).toSerializable());
     }
 
-    private Future<Object> dataExistsReply(boolean exists) {
+    private Future<ReadDataReply> readDataReply(NormalizedNode<?, ?> data) {
+        return Futures.successful(new ReadDataReply(schemaContext, data));
+    }
+
+    private Future<Object> dataExistsSerializedReply(boolean exists) {
         return Futures.successful(new DataExistsReply(exists).toSerializable());
     }
 
-    private Future<Object> writeDataReply() {
+    private Future<DataExistsReply> dataExistsReply(boolean exists) {
+        return Futures.successful(new DataExistsReply(exists));
+    }
+
+    private Future<Object> writeSerializedDataReply() {
         return Futures.successful(new WriteDataReply().toSerializable());
     }
 
-    private Future<Object> mergeDataReply() {
+    private Future<WriteDataReply> writeDataReply() {
+        return Futures.successful(new WriteDataReply());
+    }
+
+    private Future<Object> mergeSerializedDataReply() {
         return Futures.successful(new MergeDataReply().toSerializable());
     }
 
-    private Future<Object> deleteDataReply() {
+    private Future<MergeDataReply> mergeDataReply() {
+        return Futures.successful(new MergeDataReply());
+    }
+
+    private Future<Object> deleteSerializedDataReply() {
         return Futures.successful(new DeleteDataReply().toSerializable());
     }
 
+    private Future<DeleteDataReply> deleteDataReply() {
+        return Futures.successful(new DeleteDataReply());
+    }
+
     private ActorSelection actorSelection(ActorRef actorRef) {
         return getSystem().actorSelection(actorRef.path());
     }
@@ -216,17 +312,20 @@ public class TransactionProxyTest extends AbstractActorTest {
             .setTransactionId("txn-1").build();
     }
 
-    private ActorRef setupActorContextWithInitialCreateTransaction(TransactionType type) {
-        ActorRef actorRef = getSystem().actorOf(Props.create(DoNothingActor.class));
-        doReturn(getSystem().actorSelection(actorRef.path())).
+    private ActorRef setupActorContextWithInitialCreateTransaction(ActorSystem actorSystem, TransactionType type) {
+        ActorRef actorRef = actorSystem.actorOf(Props.create(DoNothingActor.class));
+        doReturn(actorSystem.actorSelection(actorRef.path())).
                 when(mockActorContext).actorSelection(actorRef.path().toString());
 
-        doReturn(Optional.of(getSystem().actorSelection(actorRef.path()))).
+        doReturn(Optional.of(actorSystem.actorSelection(actorRef.path()))).
                 when(mockActorContext).findPrimaryShard(eq(DefaultShardStrategy.DEFAULT_SHARD));
 
         doReturn(createTransactionReply(actorRef)).when(mockActorContext).
-                executeOperation(eq(getSystem().actorSelection(actorRef.path())),
+                executeOperation(eq(actorSystem.actorSelection(actorRef.path())),
                         eqCreateTransaction(memberName, type));
+
+        doReturn(false).when(mockActorContext).isLocalPath(actorRef.path().toString());
+
         return actorRef;
     }
 
@@ -243,13 +342,13 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test
     public void testRead() throws Exception {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_ONLY);
 
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 READ_ONLY);
 
-        doReturn(readDataReply(null)).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqReadData());
+        doReturn(readSerializedDataReply(null)).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedReadData());
 
         Optional<NormalizedNode<?, ?>> readOptional = transactionProxy.read(
                 TestModel.TEST_PATH).get(5, TimeUnit.SECONDS);
@@ -258,8 +357,8 @@ public class TransactionProxyTest extends AbstractActorTest {
 
         NormalizedNode<?, ?> expectedNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
-        doReturn(readDataReply(expectedNode)).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqReadData());
+        doReturn(readSerializedDataReply(expectedNode)).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedReadData());
 
         readOptional = transactionProxy.read(TestModel.TEST_PATH).get(5, TimeUnit.SECONDS);
 
@@ -270,7 +369,7 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test(expected = ReadFailedException.class)
     public void testReadWithInvalidReplyMessageType() throws Exception {
-        setupActorContextWithInitialCreateTransaction(READ_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_ONLY);
 
         doReturn(Futures.successful(new Object())).when(mockActorContext).
                 executeOperationAsync(any(ActorSelection.class), any());
@@ -283,7 +382,7 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test(expected = TestException.class)
     public void testReadWithAsyncRemoteOperatonFailure() throws Throwable {
-        setupActorContextWithInitialCreateTransaction(READ_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_ONLY);
 
         doReturn(Futures.failed(new TestException())).when(mockActorContext).
                 executeOperationAsync(any(ActorSelection.class), any());
@@ -338,18 +437,18 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test(expected = TestException.class)
     public void testReadWithPriorRecordingOperationFailure() throws Throwable {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_WRITE);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_WRITE);
 
         NormalizedNode<?, ?> nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
-        doReturn(writeDataReply()).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqWriteData(nodeToWrite));
+        doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite));
 
         doReturn(Futures.failed(new TestException())).when(mockActorContext).
-                executeOperationAsync(eq(actorSelection(actorRef)), eqDeleteData());
+                executeOperationAsync(eq(actorSelection(actorRef)), eqSerializedDeleteData());
 
-        doReturn(readDataReply(null)).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqReadData());
+        doReturn(readSerializedDataReply(null)).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedReadData());
 
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 READ_WRITE);
@@ -362,21 +461,21 @@ public class TransactionProxyTest extends AbstractActorTest {
             propagateReadFailedExceptionCause(transactionProxy.read(TestModel.TEST_PATH));
         } finally {
             verify(mockActorContext, times(0)).executeOperationAsync(
-                    eq(actorSelection(actorRef)), eqReadData());
+                    eq(actorSelection(actorRef)), eqSerializedReadData());
         }
     }
 
     @Test
     public void testReadWithPriorRecordingOperationSuccessful() throws Throwable {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_WRITE);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_WRITE);
 
         NormalizedNode<?, ?> expectedNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
-        doReturn(writeDataReply()).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqWriteData(expectedNode));
+        doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedWriteData(expectedNode));
 
-        doReturn(readDataReply(expectedNode)).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqReadData());
+        doReturn(readSerializedDataReply(expectedNode)).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedReadData());
 
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 READ_WRITE);
@@ -402,20 +501,20 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test
     public void testExists() throws Exception {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_ONLY);
 
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 READ_ONLY);
 
-        doReturn(dataExistsReply(false)).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqDataExists());
+        doReturn(dataExistsSerializedReply(false)).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedDataExists());
 
         Boolean exists = transactionProxy.exists(TestModel.TEST_PATH).checkedGet();
 
         assertEquals("Exists response", false, exists);
 
-        doReturn(dataExistsReply(true)).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqDataExists());
+        doReturn(dataExistsSerializedReply(true)).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedDataExists());
 
         exists = transactionProxy.exists(TestModel.TEST_PATH).checkedGet();
 
@@ -434,7 +533,7 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test(expected = ReadFailedException.class)
     public void testExistsWithInvalidReplyMessageType() throws Exception {
-        setupActorContextWithInitialCreateTransaction(READ_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_ONLY);
 
         doReturn(Futures.successful(new Object())).when(mockActorContext).
                 executeOperationAsync(any(ActorSelection.class), any());
@@ -447,7 +546,7 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test(expected = TestException.class)
     public void testExistsWithAsyncRemoteOperatonFailure() throws Throwable {
-        setupActorContextWithInitialCreateTransaction(READ_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_ONLY);
 
         doReturn(Futures.failed(new TestException())).when(mockActorContext).
                 executeOperationAsync(any(ActorSelection.class), any());
@@ -460,18 +559,18 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test(expected = TestException.class)
     public void testExistsWithPriorRecordingOperationFailure() throws Throwable {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_WRITE);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_WRITE);
 
         NormalizedNode<?, ?> nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
-        doReturn(writeDataReply()).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqWriteData(nodeToWrite));
+        doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite));
 
         doReturn(Futures.failed(new TestException())).when(mockActorContext).
-                executeOperationAsync(eq(actorSelection(actorRef)), eqDeleteData());
+                executeOperationAsync(eq(actorSelection(actorRef)), eqSerializedDeleteData());
 
-        doReturn(dataExistsReply(false)).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqDataExists());
+        doReturn(dataExistsSerializedReply(false)).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedDataExists());
 
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 READ_WRITE);
@@ -484,21 +583,21 @@ public class TransactionProxyTest extends AbstractActorTest {
             propagateReadFailedExceptionCause(transactionProxy.exists(TestModel.TEST_PATH));
         } finally {
             verify(mockActorContext, times(0)).executeOperationAsync(
-                    eq(actorSelection(actorRef)), eqDataExists());
+                    eq(actorSelection(actorRef)), eqSerializedDataExists());
         }
     }
 
     @Test
     public void testExistsWithPriorRecordingOperationSuccessful() throws Throwable {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_WRITE);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_WRITE);
 
         NormalizedNode<?, ?> nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
-        doReturn(writeDataReply()).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqWriteData(nodeToWrite));
+        doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite));
 
-        doReturn(dataExistsReply(true)).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqDataExists());
+        doReturn(dataExistsSerializedReply(true)).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedDataExists());
 
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 READ_WRITE);
@@ -544,12 +643,12 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test
     public void testWrite() throws Exception {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(WRITE_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), WRITE_ONLY);
 
         NormalizedNode<?, ?> nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
-        doReturn(writeDataReply()).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqWriteData(nodeToWrite));
+        doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite));
 
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 WRITE_ONLY);
@@ -557,7 +656,7 @@ public class TransactionProxyTest extends AbstractActorTest {
         transactionProxy.write(TestModel.TEST_PATH, nodeToWrite);
 
         verify(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqWriteData(nodeToWrite));
+                eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite));
 
         verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(),
                 WriteDataReply.SERIALIZABLE_CLASS);
@@ -587,12 +686,12 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test
     public void testMerge() throws Exception {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(WRITE_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), WRITE_ONLY);
 
         NormalizedNode<?, ?> nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
-        doReturn(mergeDataReply()).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqMergeData(nodeToWrite));
+        doReturn(mergeSerializedDataReply()).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedMergeData(nodeToWrite));
 
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 WRITE_ONLY);
@@ -600,7 +699,7 @@ public class TransactionProxyTest extends AbstractActorTest {
         transactionProxy.merge(TestModel.TEST_PATH, nodeToWrite);
 
         verify(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqMergeData(nodeToWrite));
+                eq(actorSelection(actorRef)), eqSerializedMergeData(nodeToWrite));
 
         verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(),
                 MergeDataReply.SERIALIZABLE_CLASS);
@@ -608,10 +707,10 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test
     public void testDelete() throws Exception {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(WRITE_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), WRITE_ONLY);
 
-        doReturn(deleteDataReply()).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqDeleteData());
+        doReturn(deleteSerializedDataReply()).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedDeleteData());
 
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 WRITE_ONLY);
@@ -619,7 +718,7 @@ public class TransactionProxyTest extends AbstractActorTest {
         transactionProxy.delete(TestModel.TEST_PATH);
 
         verify(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqDeleteData());
+                eq(actorSelection(actorRef)), eqSerializedDeleteData());
 
         verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(),
                 DeleteDataReply.SERIALIZABLE_CLASS);
@@ -637,7 +736,7 @@ public class TransactionProxyTest extends AbstractActorTest {
             Object expReply = expReplies[i++];
             if(expReply instanceof ActorSelection) {
                 ActorSelection actual = Await.result(future, Duration.create(5, TimeUnit.SECONDS));
-                assertEquals("Cohort actor path", (ActorSelection) expReply, actual);
+                assertEquals("Cohort actor path", expReply, actual);
             } else {
                 // Expecting exception.
                 try {
@@ -653,17 +752,17 @@ public class TransactionProxyTest extends AbstractActorTest {
     @SuppressWarnings("unchecked")
     @Test
     public void testReady() throws Exception {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_WRITE);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_WRITE);
 
         NormalizedNode<?, ?> nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
-        doReturn(readDataReply(null)).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqReadData());
+        doReturn(readSerializedDataReply(null)).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedReadData());
 
-        doReturn(writeDataReply()).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqWriteData(nodeToWrite));
+        doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite));
 
-        doReturn(readyTxReply(actorRef.path().toString())).when(mockActorContext).executeOperationAsync(
+        doReturn(readySerializedTxReply(actorRef.path().toString())).when(mockActorContext).executeOperationAsync(
                 eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS));
 
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
@@ -688,19 +787,21 @@ public class TransactionProxyTest extends AbstractActorTest {
     @SuppressWarnings("unchecked")
     @Test
     public void testReadyWithRecordingOperationFailure() throws Exception {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(WRITE_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), WRITE_ONLY);
 
         NormalizedNode<?, ?> nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
-        doReturn(mergeDataReply()).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqMergeData(nodeToWrite));
+        doReturn(mergeSerializedDataReply()).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedMergeData(nodeToWrite));
 
         doReturn(Futures.failed(new TestException())).when(mockActorContext).
-                executeOperationAsync(eq(actorSelection(actorRef)), eqWriteData(nodeToWrite));
+                executeOperationAsync(eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite));
 
-        doReturn(readyTxReply(actorRef.path().toString())).when(mockActorContext).executeOperationAsync(
+        doReturn(readySerializedTxReply(actorRef.path().toString())).when(mockActorContext).executeOperationAsync(
                 eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS));
 
+        doReturn(false).when(mockActorContext).isLocalPath(actorRef.path().toString());
+
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 WRITE_ONLY);
 
@@ -723,12 +824,12 @@ public class TransactionProxyTest extends AbstractActorTest {
     @SuppressWarnings("unchecked")
     @Test
     public void testReadyWithReplyFailure() throws Exception {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(WRITE_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), WRITE_ONLY);
 
         NormalizedNode<?, ?> nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
-        doReturn(mergeDataReply()).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqMergeData(nodeToWrite));
+        doReturn(mergeSerializedDataReply()).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedMergeData(nodeToWrite));
 
         doReturn(Futures.failed(new TestException())).when(mockActorContext).
                 executeOperationAsync(eq(actorSelection(actorRef)),
@@ -781,12 +882,12 @@ public class TransactionProxyTest extends AbstractActorTest {
     @SuppressWarnings("unchecked")
     @Test
     public void testReadyWithInvalidReplyMessageType() throws Exception {
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(WRITE_ONLY);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), WRITE_ONLY);
 
         NormalizedNode<?, ?> nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
 
-        doReturn(writeDataReply()).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqWriteData(nodeToWrite));
+        doReturn(writeSerializedDataReply()).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedWriteData(nodeToWrite));
 
         doReturn(Futures.successful(new Object())).when(mockActorContext).
                 executeOperationAsync(eq(actorSelection(actorRef)),
@@ -808,7 +909,7 @@ public class TransactionProxyTest extends AbstractActorTest {
 
     @Test
     public void testGetIdentifier() {
-        setupActorContextWithInitialCreateTransaction(READ_ONLY);
+        setupActorContextWithInitialCreateTransaction(getSystem(), READ_ONLY);
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 TransactionProxy.TransactionType.READ_ONLY);
 
@@ -820,10 +921,10 @@ public class TransactionProxyTest extends AbstractActorTest {
     @SuppressWarnings("unchecked")
     @Test
     public void testClose() throws Exception{
-        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_WRITE);
+        ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_WRITE);
 
-        doReturn(readDataReply(null)).when(mockActorContext).executeOperationAsync(
-                eq(actorSelection(actorRef)), eqReadData());
+        doReturn(readSerializedDataReply(null)).when(mockActorContext).executeOperationAsync(
+                eq(actorSelection(actorRef)), eqSerializedReadData());
 
         TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,
                 READ_WRITE);
@@ -835,4 +936,140 @@ public class TransactionProxyTest extends AbstractActorTest {
         verify(mockActorContext).sendOperationAsync(
                 eq(actorSelection(actorRef)), isA(CloseTransaction.SERIALIZABLE_CLASS));
     }
+
+
+    /**
+     * Method to test a local Tx actor. The Tx paths are matched to decide if the
+     * Tx actor is local or not. This is done by mocking the Tx actor path
+     * and the caller paths and ensuring that the paths have the remote-address format
+     *
+     * Note: Since the default akka provider for test is not a RemoteActorRefProvider,
+     * the paths returned for the actors for all the tests are not qualified remote paths.
+     * Hence are treated as non-local/remote actors. In short, all tests except
+     * few below run for remote actors
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testLocalTxActorRead() throws Exception {
+        ActorSystem actorSystem = getSystem();
+        ActorRef shardActorRef = actorSystem.actorOf(Props.create(DoNothingActor.class));
+
+        doReturn(actorSystem.actorSelection(shardActorRef.path())).
+            when(mockActorContext).actorSelection(shardActorRef.path().toString());
+
+        doReturn(Optional.of(actorSystem.actorSelection(shardActorRef.path()))).
+            when(mockActorContext).findPrimaryShard(eq(DefaultShardStrategy.DEFAULT_SHARD));
+
+        String actorPath = "akka.tcp://system@127.0.0.1:2550/user/tx-actor";
+        CreateTransactionReply createTransactionReply = CreateTransactionReply.newBuilder()
+            .setTransactionId("txn-1")
+            .setTransactionActorPath(actorPath)
+            .build();
+
+        doReturn(createTransactionReply).when(mockActorContext).
+            executeOperation(eq(actorSystem.actorSelection(shardActorRef.path())),
+                eqCreateTransaction(memberName, READ_ONLY));
+
+        doReturn(true).when(mockActorContext).isLocalPath(actorPath);
+
+        TransactionProxy transactionProxy = new TransactionProxy(mockActorContext,READ_ONLY);
+
+        // negative test case with null as the reply
+        doReturn(readDataReply(null)).when(mockActorContext).executeOperationAsync(
+            any(ActorSelection.class), eqReadData());
+
+        Optional<NormalizedNode<?, ?>> readOptional = transactionProxy.read(
+            TestModel.TEST_PATH).get(5, TimeUnit.SECONDS);
+
+        assertEquals("NormalizedNode isPresent", false, readOptional.isPresent());
+
+        // test case with node as read data reply
+        NormalizedNode<?, ?> expectedNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
+
+        doReturn(readDataReply(expectedNode)).when(mockActorContext).executeOperationAsync(
+            any(ActorSelection.class), eqReadData());
+
+        readOptional = transactionProxy.read(TestModel.TEST_PATH).get(5, TimeUnit.SECONDS);
+
+        assertEquals("NormalizedNode isPresent", true, readOptional.isPresent());
+
+        assertEquals("Response NormalizedNode", expectedNode, readOptional.get());
+
+        // test for local data exists
+        doReturn(dataExistsReply(true)).when(mockActorContext).executeOperationAsync(
+            any(ActorSelection.class), eqDataExists());
+
+        boolean exists = transactionProxy.exists(TestModel.TEST_PATH).checkedGet();
+
+        assertEquals("Exists response", true, exists);
+    }
+
+    @Test
+    public void testLocalTxActorWrite() throws Exception {
+        ActorSystem actorSystem = getSystem();
+        ActorRef shardActorRef = actorSystem.actorOf(Props.create(DoNothingActor.class));
+
+        doReturn(actorSystem.actorSelection(shardActorRef.path())).
+            when(mockActorContext).actorSelection(shardActorRef.path().toString());
+
+        doReturn(Optional.of(actorSystem.actorSelection(shardActorRef.path()))).
+            when(mockActorContext).findPrimaryShard(eq(DefaultShardStrategy.DEFAULT_SHARD));
+
+        String actorPath = "akka.tcp://system@127.0.0.1:2550/user/tx-actor";
+        CreateTransactionReply createTransactionReply = CreateTransactionReply.newBuilder()
+            .setTransactionId("txn-1")
+            .setTransactionActorPath(actorPath)
+            .build();
+
+        doReturn(createTransactionReply).when(mockActorContext).
+            executeOperation(eq(actorSystem.actorSelection(shardActorRef.path())),
+                eqCreateTransaction(memberName, WRITE_ONLY));
+
+        doReturn(true).when(mockActorContext).isLocalPath(actorPath);
+
+        NormalizedNode<?, ?> nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
+
+        doReturn(writeDataReply()).when(mockActorContext).executeOperationAsync(
+            any(ActorSelection.class), eqWriteData(nodeToWrite));
+
+        TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, WRITE_ONLY);
+        transactionProxy.write(TestModel.TEST_PATH, nodeToWrite);
+
+        verify(mockActorContext).executeOperationAsync(
+            any(ActorSelection.class), eqWriteData(nodeToWrite));
+
+        //testing local merge
+        doReturn(mergeDataReply()).when(mockActorContext).executeOperationAsync(
+            any(ActorSelection.class), eqMergeData(nodeToWrite));
+
+        transactionProxy.merge(TestModel.TEST_PATH, nodeToWrite);
+
+        verify(mockActorContext).executeOperationAsync(
+            any(ActorSelection.class), eqMergeData(nodeToWrite));
+
+
+        //testing local delete
+        doReturn(deleteDataReply()).when(mockActorContext).executeOperationAsync(
+            any(ActorSelection.class), eqDeleteData());
+
+        transactionProxy.delete(TestModel.TEST_PATH);
+
+        verify(mockActorContext).executeOperationAsync(any(ActorSelection.class), eqDeleteData());
+
+        verifyRecordingOperationFutures(transactionProxy.getRecordedOperationFutures(),
+            WriteDataReply.class, MergeDataReply.class, DeleteDataReply.class);
+
+        // testing ready
+        doReturn(readyTxReply(shardActorRef.path().toString())).when(mockActorContext).executeOperationAsync(
+            any(ActorSelection.class), isA(ReadyTransaction.class));
+
+        DOMStoreThreePhaseCommitCohort ready = transactionProxy.ready();
+
+        assertTrue(ready instanceof ThreePhaseCommitCohortProxy);
+
+        ThreePhaseCommitCohortProxy proxy = (ThreePhaseCommitCohortProxy) ready;
+
+        verifyCohortFutures(proxy, getSystem().actorSelection(shardActorRef.path()));
+    }
 }
index 8426b03a37de93242b506df7fd9d4368d24a169f..60f9a2d9dc4d9e2660137eaa77c7df491d124361 100644 (file)
@@ -7,7 +7,6 @@ import akka.actor.UntypedActor;
 import akka.japi.Creator;
 import akka.testkit.JavaTestKit;
 import com.google.common.base.Optional;
-
 import org.junit.Test;
 import org.opendaylight.controller.cluster.datastore.AbstractActorTest;
 import org.opendaylight.controller.cluster.datastore.ClusterWrapper;
@@ -18,9 +17,7 @@ import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound
 import scala.concurrent.Await;
 import scala.concurrent.Future;
 import scala.concurrent.duration.Duration;
-
 import java.util.concurrent.TimeUnit;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
@@ -99,23 +96,15 @@ public class ActorContextTest extends AbstractActorTest{
     @Test
     public void testFindLocalShardWithShardNotFound(){
         new JavaTestKit(getSystem()) {{
+            ActorRef shardManagerActorRef = getSystem()
+                    .actorOf(MockShardManager.props(false, null));
 
-            new Within(duration("1 seconds")) {
-                @Override
-                protected void run() {
-
-                    ActorRef shardManagerActorRef = getSystem()
-                        .actorOf(MockShardManager.props(false, null));
-
-                    ActorContext actorContext =
-                        new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class),
+            ActorContext actorContext =
+                    new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class),
                             mock(Configuration.class));
 
-                    Optional<ActorRef> out = actorContext.findLocalShard("default");
-                    assertTrue(!out.isPresent());
-                    expectNoMsg();
-                }
-            };
+            Optional<ActorRef> out = actorContext.findLocalShard("default");
+            assertTrue(!out.isPresent());
         }};
 
     }
@@ -123,63 +112,74 @@ public class ActorContextTest extends AbstractActorTest{
     @Test
     public void testExecuteRemoteOperation() {
         new JavaTestKit(getSystem()) {{
+            ActorRef shardActorRef = getSystem().actorOf(Props.create(EchoActor.class));
 
-            new Within(duration("3 seconds")) {
-                @Override
-                protected void run() {
-
-                    ActorRef shardActorRef = getSystem().actorOf(Props.create(EchoActor.class));
-
-                    ActorRef shardManagerActorRef = getSystem()
-                        .actorOf(MockShardManager.props(true, shardActorRef));
+            ActorRef shardManagerActorRef = getSystem()
+                    .actorOf(MockShardManager.props(true, shardActorRef));
 
-                    ActorContext actorContext =
-                        new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class),
+            ActorContext actorContext =
+                    new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class),
                             mock(Configuration.class));
 
-                    ActorSelection actor = actorContext.actorSelection(shardActorRef.path());
-
-                    Object out = actorContext.executeOperation(actor, "hello");
+            ActorSelection actor = actorContext.actorSelection(shardActorRef.path());
 
-                    assertEquals("hello", out);
+            Object out = actorContext.executeOperation(actor, "hello");
 
-                    expectNoMsg();
-                }
-            };
+            assertEquals("hello", out);
         }};
     }
 
     @Test
     public void testExecuteRemoteOperationAsync() {
         new JavaTestKit(getSystem()) {{
+            ActorRef shardActorRef = getSystem().actorOf(Props.create(EchoActor.class));
 
-            new Within(duration("3 seconds")) {
-                @Override
-                protected void run() {
+            ActorRef shardManagerActorRef = getSystem()
+                    .actorOf(MockShardManager.props(true, shardActorRef));
 
-                    ActorRef shardActorRef = getSystem().actorOf(Props.create(EchoActor.class));
+            ActorContext actorContext =
+                    new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class),
+                            mock(Configuration.class));
 
-                    ActorRef shardManagerActorRef = getSystem()
-                        .actorOf(MockShardManager.props(true, shardActorRef));
+            ActorSelection actor = actorContext.actorSelection(shardActorRef.path());
 
-                    ActorContext actorContext =
-                        new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class),
-                            mock(Configuration.class));
+            Future<Object> future = actorContext.executeOperationAsync(actor, "hello");
 
-                    ActorSelection actor = actorContext.actorSelection(shardActorRef.path());
+            try {
+                Object result = Await.result(future, Duration.create(3, TimeUnit.SECONDS));
+                assertEquals("Result", "hello", result);
+            } catch(Exception e) {
+                throw new AssertionError(e);
+            }
+        }};
+    }
 
-                    Future<Object> future = actorContext.executeOperationAsync(actor, "hello");
+    @Test
+    public void testIsLocalPath() {
+        MockClusterWrapper clusterWrapper = new MockClusterWrapper();
+        ActorContext actorContext =
+                new ActorContext(getSystem(), null, clusterWrapper, mock(Configuration.class));
 
-                    try {
-                        Object result = Await.result(future, Duration.create(3, TimeUnit.SECONDS));
-                        assertEquals("Result", "hello", result);
-                    } catch(Exception e) {
-                        throw new AssertionError(e);
-                    }
+        clusterWrapper.setSelfAddress("");
+        assertEquals(false, actorContext.isLocalPath(null));
+        assertEquals(false, actorContext.isLocalPath(""));
 
-                    expectNoMsg();
-                }
-            };
-        }};
+        clusterWrapper.setSelfAddress(null);
+        assertEquals(false, actorContext.isLocalPath(""));
+
+        clusterWrapper.setSelfAddress("akka://test/user/$b");
+        assertEquals(false, actorContext.isLocalPath("akka://test/user/$a"));
+
+        clusterWrapper.setSelfAddress("akka.tcp://system@127.0.0.1:2550/");
+        assertEquals(true, actorContext.isLocalPath("akka.tcp://system@127.0.0.1:2550/"));
+
+        clusterWrapper.setSelfAddress("akka.tcp://system@127.0.0.1:2550");
+        assertEquals(false, actorContext.isLocalPath("akka.tcp://system@127.0.0.1:2550/"));
+
+        clusterWrapper.setSelfAddress("akka.tcp://system@128.0.0.1:2550/");
+        assertEquals(false, actorContext.isLocalPath("akka.tcp://system@127.0.0.1:2550/"));
+
+        clusterWrapper.setSelfAddress("akka.tcp://system@127.0.0.1:2551/");
+        assertEquals(false, actorContext.isLocalPath("akka.tcp://system@127.0.0.1:2550/"));
     }
 }
index 803aa03b7c722311588a5514c2191796b3b9278c..b80506d17d2efa61f5ae5673d3687dc67501bdee 100644 (file)
@@ -15,19 +15,31 @@ import akka.cluster.MemberStatus;
 import akka.cluster.UniqueAddress;
 import org.opendaylight.controller.cluster.datastore.ClusterWrapper;
 import scala.collection.JavaConversions;
-
 import java.util.HashSet;
 import java.util.Set;
 
 public class MockClusterWrapper implements ClusterWrapper{
 
-    @Override public void subscribeToMemberEvents(ActorRef actorRef) {
+    private String selfAddress = "akka.tcp://test@127.0.0.1:2550/user/member-1-shard-test-config";
+
+    @Override
+    public void subscribeToMemberEvents(ActorRef actorRef) {
     }
 
-    @Override public String getCurrentMemberName() {
+    @Override
+    public String getCurrentMemberName() {
         return "member-1";
     }
 
+    @Override
+    public String getSelfAddress() {
+        return selfAddress;
+    }
+
+    public void setSelfAddress(String selfAddress) {
+        this.selfAddress = selfAddress;
+    }
+
     public static void sendMemberUp(ActorRef to, String memberName, String address){
         to.tell(createMemberUp(memberName, address), null);
     }
diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockDataChangeListener.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockDataChangeListener.java
new file mode 100644 (file)
index 0000000..f2f49d1
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.datastore.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Uninterruptibles;
+
+/**
+ * A mock DataChangeListener implementation.
+ *
+ * @author Thomas Pantelis
+ */
+public class MockDataChangeListener implements
+                         AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> {
+
+    private final List<AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>>>
+                                                               changeList = Lists.newArrayList();
+    private final CountDownLatch changeLatch;
+    private final int expChangeEventCount;
+
+    public MockDataChangeListener(int expChangeEventCount) {
+        changeLatch = new CountDownLatch(expChangeEventCount);
+        this.expChangeEventCount = expChangeEventCount;
+    }
+
+    @Override
+    public void onDataChanged(AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+        changeList.add(change);
+        changeLatch.countDown();
+    }
+
+    public void waitForChangeEvents(YangInstanceIdentifier... expPaths) {
+        assertEquals("Change notifications complete", true,
+                Uninterruptibles.awaitUninterruptibly(changeLatch, 5, TimeUnit.SECONDS));
+
+        for(int i = 0; i < expPaths.length; i++) {
+            assertTrue(String.format("Change %d does not contain %s", (i+1), expPaths[i]),
+                    changeList.get(i).getCreatedData().containsKey(expPaths[i]));
+        }
+    }
+
+    public void expectNoMoreChanges(String assertMsg) {
+        Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
+        assertEquals(assertMsg, expChangeEventCount, changeList.size());
+    }
+}
index 81203c55fef05829c7f6540a9c9c93e725d082f8..6c8f37b66b28dcf851c68430bf6b284742eeeda2 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.controller.md.sal.dom.broker.spi.rpc;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -14,17 +16,14 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 
-import com.google.common.base.Optional;
-
 public abstract class RpcRoutingStrategy implements Identifiable<QName> {
 
+    private static final QName CONTEXT_REFERENCE = QName.cachedReference(QName.create("urn:opendaylight:yang:extension:yang-ext",
+            "2013-07-09", "context-reference"));
     private final QName identifier;
-    private static final QName CONTEXT_REFERENCE = QName.create("urn:opendaylight:yang:extension:yang-ext",
-            "2013-07-09", "context-reference");
 
     private RpcRoutingStrategy(final QName identifier) {
-        super();
-        this.identifier = identifier;
+        this.identifier = Preconditions.checkNotNull(identifier);
     }
 
     /**
@@ -47,7 +46,7 @@ public abstract class RpcRoutingStrategy implements Identifiable<QName> {
     public abstract QName getContext();
 
     @Override
-    public QName getIdentifier() {
+    public final QName getIdentifier() {
         return identifier;
     }
 
@@ -64,14 +63,14 @@ public abstract class RpcRoutingStrategy implements Identifiable<QName> {
             for (DataSchemaNode schemaNode : input.getChildNodes()) {
                 Optional<QName> context = getRoutingContext(schemaNode);
                 if (context.isPresent()) {
-                    return createRoutedStrategy(rpc, context.get(), schemaNode.getQName());
+                    return new RoutedRpcStrategy(rpc.getQName(), context.get(), schemaNode.getQName());
                 }
             }
         }
-        return createGlobalStrategy(rpc);
+        return new GlobalRpcStrategy(rpc.getQName());
     }
 
-    public static  Optional<QName> getRoutingContext(final DataSchemaNode schemaNode) {
+    public static Optional<QName> getRoutingContext(final DataSchemaNode schemaNode) {
         for (UnknownSchemaNode extension : schemaNode.getUnknownSchemaNodes()) {
             if (CONTEXT_REFERENCE.equals(extension.getNodeType())) {
                 return Optional.fromNullable(extension.getQName());
@@ -80,26 +79,14 @@ public abstract class RpcRoutingStrategy implements Identifiable<QName> {
         return Optional.absent();
     }
 
-    private static RpcRoutingStrategy createRoutedStrategy(final RpcDefinition rpc, final QName context, final QName leafNode) {
-        return new RoutedRpcStrategy(rpc.getQName(), context, leafNode);
-    }
-
-
-
-    private static RpcRoutingStrategy createGlobalStrategy(final RpcDefinition rpc) {
-        GlobalRpcStrategy ret = new GlobalRpcStrategy(rpc.getQName());
-        return ret;
-    }
-
-    private static class RoutedRpcStrategy extends RpcRoutingStrategy {
-
-        final QName context;
+    private static final class RoutedRpcStrategy extends RpcRoutingStrategy {
+        private final QName context;
         private final QName leaf;
 
         private RoutedRpcStrategy(final QName identifier, final QName ctx, final QName leaf) {
             super(identifier);
-            this.context = ctx;
-            this.leaf = leaf;
+            this.context = Preconditions.checkNotNull(ctx);
+            this.leaf = Preconditions.checkNotNull(leaf);
         }
 
         @Override
@@ -118,7 +105,7 @@ public abstract class RpcRoutingStrategy implements Identifiable<QName> {
         }
     }
 
-    private static class GlobalRpcStrategy extends RpcRoutingStrategy {
+    private static final class GlobalRpcStrategy extends RpcRoutingStrategy {
 
         public GlobalRpcStrategy(final QName identifier) {
             super(identifier);
@@ -131,12 +118,12 @@ public abstract class RpcRoutingStrategy implements Identifiable<QName> {
 
         @Override
         public QName getContext() {
-            throw new UnsupportedOperationException("Not routed strategy does not have context.");
+            throw new UnsupportedOperationException("Non-routed strategy does not have a context");
         }
 
         @Override
         public QName getLeaf() {
-            throw new UnsupportedOperationException("Not routed strategy does not have context.");
+            throw new UnsupportedOperationException("Non-routed strategy does not have a context");
         }
     }
 }
\ No newline at end of file
index beab6d2fb1a0014e3dece9b1c07f77aa611ee090..ecea744d14418fdb03216dab312f82c1d0fad1c0 100644 (file)
@@ -30,6 +30,8 @@ public class XSQLAdapter extends Thread implements SchemaContextListener {
     private static final int SLEEP = 10000;
     private static XSQLAdapter a = new XSQLAdapter();
     private static PrintStream l = null;
+    private static String tmpDir = null;
+    private static File xqlLog = null;
     public boolean stopped = false;
     private List<String> elementHosts = new ArrayList<String>();
     private String username;
@@ -79,6 +81,12 @@ public class XSQLAdapter extends Thread implements SchemaContextListener {
         return a;
     }
 
+    public static File getXQLLogfile() {
+        tmpDir = System.getProperty("java.io.tmpdir");
+        xqlLog = new File(tmpDir + "/xql.log");
+        return xqlLog;
+    }
+
     public static void main(String args[]) {
         XSQLAdapter adapter = new XSQLAdapter();
         adapter.start();
@@ -90,7 +98,7 @@ public class XSQLAdapter extends Thread implements SchemaContextListener {
                 synchronized (XSQLAdapter.class) {
                     if (l == null) {
                         l = new PrintStream(
-                                new FileOutputStream("/tmp/xql.log"));
+                                new FileOutputStream(getXQLLogfile()));
                     }
                 }
             }
@@ -108,7 +116,7 @@ public class XSQLAdapter extends Thread implements SchemaContextListener {
                 synchronized (XSQLAdapter.class) {
                     if (l == null) {
                         l = new PrintStream(
-                                new FileOutputStream("/tmp/xql.log"));
+                                new FileOutputStream(getXQLLogfile()));
                     }
                 }
             }
index 3988a495cb15de246c29647bbf042d7a40be8702..95fb9a4826d2b6c5115c31d6dfff066b9a0b4af5 100644 (file)
@@ -7,9 +7,18 @@
  */
 package org.opendaylight.controller.sal.restconf.impl;
 
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import javax.ws.rs.core.Response.Status;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
@@ -37,16 +46,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.ws.rs.core.Response.Status;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-
-import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
-import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
-
 public class BrokerFacade {
     private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
 
@@ -261,15 +260,13 @@ public class BrokerFacade {
 
             try {
 
-                CheckedFuture<Boolean, ReadFailedException> future =
-                    rwTx.exists(store, currentPath);
+                CheckedFuture<Boolean, ReadFailedException> future = rwTx.exists(store, currentPath);
                 exists = future.checkedGet();
             } catch (ReadFailedException e) {
                 LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
                 throw new IllegalStateException("Failed to read pre-existing data", e);
             }
 
-
             if (!exists && iterator.hasNext()) {
                 rwTx.merge(store, currentPath, currentOp.createDefault(currentArg));
             }
index a95a64b2c23d2011979726d24e95a3e382c06ce2..cd860efab75073e13cf0cd2cfd93e12fd7200a5e 100644 (file)
@@ -9,9 +9,12 @@
 package org.opendaylight.controller.sal.restconf.impl;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
@@ -63,6 +66,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceI
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
 import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
@@ -976,9 +980,13 @@ public class RestconfImpl implements RestconfService {
                 broker.commitConfigurationDataDelete(normalizedII).get();
             }
         } catch (Exception e) {
-            throw new RestconfDocumentedException("Error creating data", e);
+            final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
+                    Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
+            if (searchedException.isPresent()) {
+                throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
+            }
+            throw new RestconfDocumentedException("Error while deleting data", e);
         }
-
         return Response.status(Status.OK).build();
     }
 
index 6b2583024036b6b7ef39863f64f3fa9069d19e94..f533a6360ad847a74e7f203bf32db65a0acfe995 100644 (file)
@@ -8,9 +8,21 @@
 
 package org.opendaylight.controller.sal.restconf.impl.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
+import java.util.concurrent.Future;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -43,19 +55,6 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 
-import java.util.concurrent.Future;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
 /**
  * Unit tests for BrokerFacade.
  *
@@ -234,6 +233,9 @@ public class BrokerFacadeTest {
 
         when(wTransaction.submit()).thenReturn(expFuture);
 
+        NormalizedNode<?, ?> dummyNode2 = createDummyNode("dummy:namespace2", "2014-07-01", "dummy local name2");
+
+
         CheckedFuture<Void, TransactionCommitFailedException> actualFuture = brokerFacade
                 .commitConfigurationDataDelete(instanceID);
 
index b7518e094d0e61594a391c2fc4a78b181294e00a..332d375282ce99e3fac16b5a3e10616886a56bab 100644 (file)
@@ -141,6 +141,8 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
             {
                 darknessFactor.set( darkness );
             }
+
+            LOG.info("onDataChanged - new Toaster config: {}", toaster);
         }
     }
 
index 44227bb4d816042c3aae502a52102da49ce2e534..350b0aa247470e4a430618b8560fe45099ec3987 100644 (file)
@@ -107,7 +107,7 @@ public class InstanceRuntime {
 
                 String elementName = jmxToYangChildRbeMapping.get(childMappingEntry.getKey());
 
-                Element innerXml = XmlUtil.createElement(document, elementName, Optional.<String>absent());
+                Element innerXml = XmlUtil.createElement(document, elementName, Optional.of(namespace));
                 childMappingEntry.getValue().toXml(objectName, innerChildRbeOns, document,
                         runtimeInstanceIndex, innerXml, namespace);
                 xml.appendChild(innerXml);
index e41b174b664740c685faa041d64c208630bf21c2..641881cf9ee50b71119c930a495ded9920575f73 100644 (file)
@@ -662,7 +662,7 @@ public class NetconfMappingTest extends AbstractConfigTest {
         assertEquals(8 * 4, getElementsSize(response, "inner-inner-running-data"));
         assertEquals(8 * 4, getElementsSize(response, "deep3"));
         assertEquals(8 * 4 * 2, getElementsSize(response, "list-of-strings"));
-        assertEquals(8, getElementsSize(response, "inner-running-data-additional"));
+        assertEquals(8, getElementsSize(response, "inner-running-data-additional", "urn:opendaylight:params:xml:ns:yang:controller:test:impl"));
         assertEquals(8, getElementsSize(response, "deep4"));
         // TODO assert keys
 
@@ -693,6 +693,10 @@ public class NetconfMappingTest extends AbstractConfigTest {
         return response.getElementsByTagName(elementName).getLength();
     }
 
+    private int getElementsSize(Document response, String elementName, String namespace) {
+        return response.getElementsByTagNameNS(namespace, elementName).getLength();
+    }
+
     private Document executeOp(final NetconfOperation op, final String filename) throws ParserConfigurationException,
             SAXException, IOException, NetconfDocumentedException {
 
diff --git a/opendaylight/netconf/netconf-artifacts/pom.xml b/opendaylight/netconf/netconf-artifacts/pom.xml
new file mode 100644 (file)
index 0000000..eb3cac1
--- /dev/null
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+    Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v1.0 which accompanies this distribution,
+    and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>netconf-artifacts</artifactId>
+    <version>0.3.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-config-dispatcher</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>config-netconf-connector</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>config-persister-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-auth</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-cli</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-client</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-config</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-connector-config</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-impl</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-mapping-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-monitoring</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-netty-util</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-ssh</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-tcp</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-testtool</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-usermanager</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-util</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>ietf-netconf-monitoring</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>ietf-netconf-monitoring-extension</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-client</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-impl</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-netty-util</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-ssh</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>netconf-util</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+            </dependency>
+
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>features-netconf</artifactId>
+                <version>${project.version}</version>
+                <classifier>features</classifier>
+                <type>xml</type>
+                <scope>runtime</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
+
index 6e8158413388d555e9d102354854f684f336d733..42a8bae4484b93a3ed4689223cbf34a9aacbd986 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.controller.netconf.impl;
 
 import com.google.common.base.Optional;
 import java.io.IOException;
+import java.util.Map;
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation.OperationNameAndNamespace;
@@ -73,7 +74,7 @@ public class SubtreeFilter {
         return result;
     }
 
-    private static void addSubtree(XmlElement filter, XmlElement src, XmlElement dst) {
+    private static void addSubtree(XmlElement filter, XmlElement src, XmlElement dst) throws NetconfDocumentedException {
         for (XmlElement srcChild : src.getChildElements()) {
             for (XmlElement filterChild : filter.getChildElements()) {
                 addSubtree2(filterChild, srcChild, dst);
@@ -81,7 +82,7 @@ public class SubtreeFilter {
         }
     }
 
-    private static MatchingResult addSubtree2(XmlElement filter, XmlElement src, XmlElement dstParent) {
+    private static MatchingResult addSubtree2(XmlElement filter, XmlElement src, XmlElement dstParent) throws NetconfDocumentedException {
         Document document = dstParent.getDomElement().getOwnerDocument();
         MatchingResult matches = matches(src, filter);
         if (matches != MatchingResult.NO_MATCH && matches != MatchingResult.CONTENT_MISMATCH) {
@@ -123,7 +124,7 @@ public class SubtreeFilter {
      * Shallow compare src node to filter: tag name and namespace must match.
      * If filter node has no children and has text content, it also must match.
      */
-    private static MatchingResult matches(XmlElement src, XmlElement filter) {
+    private static MatchingResult matches(XmlElement src, XmlElement filter) throws NetconfDocumentedException {
         boolean tagMatch = src.getName().equals(filter.getName()) &&
                 src.getNamespaceOptionally().equals(filter.getNamespaceOptionally());
         MatchingResult result = null;
@@ -131,7 +132,7 @@ public class SubtreeFilter {
             // match text content
             Optional<String> maybeText = filter.getOnlyTextContentOptionally();
             if (maybeText.isPresent()) {
-                if (maybeText.equals(src.getOnlyTextContentOptionally())) {
+                if (maybeText.equals(src.getOnlyTextContentOptionally()) || prefixedContentMatches(filter, src)) {
                     result = MatchingResult.CONTENT_MATCH;
                 } else {
                     result = MatchingResult.CONTENT_MISMATCH;
@@ -159,10 +160,30 @@ public class SubtreeFilter {
         if (result == null) {
             result = MatchingResult.NO_MATCH;
         }
-        logger.debug("Matching {} to {} resulted in {}", src, filter, tagMatch);
+        logger.debug("Matching {} to {} resulted in {}", src, filter, result);
         return result;
     }
 
+    private static boolean prefixedContentMatches(final XmlElement filter, final XmlElement src) throws NetconfDocumentedException {
+        final Map.Entry<String, String> prefixToNamespaceOfFilter = filter.findNamespaceOfTextContent();
+        final Map.Entry<String, String> prefixToNamespaceOfSrc = src.findNamespaceOfTextContent();
+
+        final String prefix = prefixToNamespaceOfFilter.getKey();
+        // If this is not a prefixed content, we do not need to continue since content do not match
+        if(prefix.equals(XmlElement.DEFAULT_NAMESPACE_PREFIX)) {
+            return false;
+        }
+        // Namespace mismatch
+        if(!prefixToNamespaceOfFilter.getValue().equals(prefixToNamespaceOfSrc.getValue())) {
+            return false;
+        }
+
+        final String unprefixedFilterContent = filter.getTextContent().substring(prefix.length());
+        final String unprefixedSrcCOntnet = src.getTextContent().substring(prefix.length());
+        // Finally compare unprefixed content
+        return unprefixedFilterContent.equals(unprefixedSrcCOntnet);
+    }
+
     enum MatchingResult {
         NO_MATCH, TAG_MATCH, CONTENT_MATCH, CONTENT_MISMATCH
     }
index b11834386e0f76e761924ee881977cbc5d0b0aef..5d9470750e54053423b8bb871192fafedf9bf53b 100644 (file)
@@ -36,7 +36,7 @@ public class SubtreeFilterTest {
     @Parameters
     public static Collection<Object[]> data() {
         List<Object[]> result = new ArrayList<>();
-        for (int i = 0; i <= 8; i++) {
+        for (int i = 0; i <= 9; i++) {
             result.add(new Object[]{i});
         }
         return result;
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/post-filter.xml b/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/post-filter.xml
new file mode 100644 (file)
index 0000000..afe9655
--- /dev/null
@@ -0,0 +1,14 @@
+<rpc-reply message-id="5"
+           xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <data>
+    <top xmlns="http://example.com/schema/1.2/config">
+      <users>
+        <user>
+          <name>fred</name>
+            <type xmlns:x="http://java.sun.com/dtd/properties.dtd">x:admin</type>
+          <full-name>Fred Flintstone</full-name>
+        </user>
+      </users>
+    </top>
+  </data>
+</rpc-reply>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/pre-filter.xml b/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/pre-filter.xml
new file mode 100644 (file)
index 0000000..eca3241
--- /dev/null
@@ -0,0 +1,40 @@
+<rpc-reply message-id="5" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <data>
+    <top xmlns="http://example.com/schema/1.2/config">
+      <users>
+        <user>
+          <name>root</name>
+          <type>superuser</type>
+          <full-name>Charlie Root</full-name>
+          <company-info>
+            <dept>1</dept>
+            <id>1</id>
+          </company-info>
+        </user>
+        <user>
+          <name>fred</name>
+          <type xmlns:x="http://java.sun.com/dtd/properties.dtd">x:admin</type>
+          <full-name>Fred Flintstone</full-name>
+          <company-info>
+            <dept>2</dept>
+            <id>2</id>
+          </company-info>
+        </user>
+        <user>
+          <name>barney</name>
+          <type>admin</type>
+          <full-name>Barney Rubble</full-name>
+          <company-info>
+            <dept>2</dept>
+            <id>3</id>
+          </company-info>
+        </user>
+      </users>
+      <groups>
+        <group>
+          <name>admin</name>
+        </group>
+      </groups>
+    </top>
+  </data>
+</rpc-reply>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/request.xml b/opendaylight/netconf/netconf-impl/src/test/resources/subtree/9/request.xml
new file mode 100644 (file)
index 0000000..47da0fe
--- /dev/null
@@ -0,0 +1,19 @@
+<rpc message-id="5"
+     xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+  <get-config>
+    <source>
+      <running/>
+    </source>
+    <filter type="subtree">
+      <top xmlns="http://example.com/schema/1.2/config">
+        <users>
+          <user>
+            <name>fred</name>
+              <type xmlns:a="http://java.sun.com/dtd/properties.dtd">a:admin</type>
+            <full-name/>
+          </user>
+        </users>
+      </top>
+    </filter>
+  </get-config>
+</rpc>
\ No newline at end of file
index 1adcd7e49176e65cc15c5fb558015e5e6e129205..f96f55761939bdd5e7eb05eca5a6b8b645572208 100644 (file)
@@ -21,14 +21,23 @@ import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
+import io.netty.channel.EventLoopGroup;
 import io.netty.channel.local.LocalAddress;
+import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.util.concurrent.GlobalEventExecutor;
 import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.nio.file.Files;
 import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerSession;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -40,13 +49,12 @@ import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
 import org.opendaylight.controller.netconf.client.NetconfClientSessionListener;
 import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener;
+import org.opendaylight.controller.netconf.client.TestingNetconfClient;
 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder;
-import org.opendaylight.controller.netconf.client.TestingNetconfClient;
 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
-import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
 import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
@@ -68,19 +76,32 @@ public class NetconfITSecureTest extends AbstractNetconfConfigTest {
     public static final String USERNAME = "user";
     public static final String PASSWORD = "pwd";
 
-    private NetconfSSHServer sshServer;
+    private SshProxyServer sshProxyServer;
+
+    private ExecutorService nioExec;
+    private EventLoopGroup clientGroup;
+    private ScheduledExecutorService minaTimerEx;
 
     @Before
     public void setUp() throws Exception {
-        final char[] pem = PEMGenerator.generate().toCharArray();
-        sshServer = NetconfSSHServer.start(TLS_ADDRESS.getPort(), NetconfConfigUtil.getNetconfLocalAddress(), getNettyThreadgroup(), pem);
-        sshServer.setAuthProvider(getAuthProvider());
+        nioExec = Executors.newFixedThreadPool(1);
+        clientGroup = new NioEventLoopGroup();
+        minaTimerEx = Executors.newScheduledThreadPool(1);
+        sshProxyServer = new SshProxyServer(minaTimerEx, clientGroup, nioExec);
+        sshProxyServer.bind(TLS_ADDRESS, NetconfConfigUtil.getNetconfLocalAddress(), new PasswordAuthenticator() {
+            @Override
+            public boolean authenticate(final String username, final String password, final ServerSession session) {
+                return true;
+            }
+        }, new PEMGeneratorHostKeyProvider(Files.createTempFile("prefix", "suffix").toAbsolutePath().toString()));
     }
 
     @After
     public void tearDown() throws Exception {
-        sshServer.close();
-        sshServer.join();
+        sshProxyServer.close();
+        clientGroup.shutdownGracefully().await();
+        minaTimerEx.shutdownNow();
+        nioExec.shutdownNow();
     }
 
     @Test
index e2afcc42f54f317d19490a7f9edfe864e9907acf..a9c1e8336d62419a79f6d320cd0784ecc8dc37cb 100644 (file)
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>protocol-framework</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>ganymed</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.apache.sshd</groupId>
       <artifactId>sshd-core</artifactId>
@@ -89,7 +85,7 @@
         <artifactId>maven-bundle-plugin</artifactId>
         <configuration>
           <instructions>
-            <Import-Package>org.apache.sshd.*, ch.ethz.ssh2, com.google.common.base, com.google.common.collect, io.netty.buffer,
+            <Import-Package>org.apache.sshd.*, com.google.common.base, com.google.common.collect, io.netty.buffer,
               io.netty.channel, io.netty.channel.socket, io.netty.handler.codec, io.netty.handler.ssl, io.netty.util,
               io.netty.util.concurrent, javax.xml.transform, javax.xml.transform.dom, javax.xml.transform.sax,
               javax.xml.transform.stream, org.opendaylight.controller.netconf.api,
index 3bd72320232bb7f912a316469b816fa76952f0c4..fa7d0900edc805ebd45f3b2edce8a21d46cfb031 100644 (file)
@@ -8,12 +8,9 @@
 
 package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client;
 
-import com.google.common.base.Preconditions;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelOutboundHandlerAdapter;
-import io.netty.channel.ChannelPromise;
 import java.io.IOException;
 import java.net.SocketAddress;
+
 import org.apache.sshd.ClientChannel;
 import org.apache.sshd.ClientSession;
 import org.apache.sshd.SshClient;
@@ -26,6 +23,13 @@ import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOutboundHandlerAdapter;
+import io.netty.channel.ChannelPromise;
+
 /**
  * Netty SSH handler class. Acts as interface between Netty and SSH library.
  */
@@ -47,7 +51,7 @@ public class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
     private final AuthenticationHandler authenticationHandler;
     private final SshClient sshClient;
 
-    private AsyncSshHanderReader sshReadAsyncListener;
+    private AsyncSshHandlerReader sshReadAsyncListener;
     private AsyncSshHandlerWriter sshWriteAsyncHandler;
 
     private ClientChannel channel;
@@ -138,7 +142,20 @@ public class AsyncSshHandler extends ChannelOutboundHandlerAdapter {
         connectPromise.setSuccess();
         connectPromise = null;
 
-        sshReadAsyncListener = new AsyncSshHanderReader(this, ctx, channel.getAsyncOut());
+        // TODO we should also read from error stream and at least log from that
+
+        sshReadAsyncListener = new AsyncSshHandlerReader(new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+                AsyncSshHandler.this.disconnect(ctx, ctx.newPromise());
+            }
+        }, new AsyncSshHandlerReader.ReadMsgHandler() {
+            @Override
+            public void onMessageRead(final ByteBuf msg) {
+                ctx.fireChannelRead(msg);
+            }
+        }, channel.toString(), channel.getAsyncOut());
+
         // if readAsyncListener receives immediate close, it will close this handler and closing this handler sets channel variable to null
         if(channel != null) {
             sshWriteAsyncHandler = new AsyncSshHandlerWriter(channel.getAsyncIn());
@@ -8,9 +8,8 @@
 
 package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client;
 
+import io.netty.buffer.ByteBuf;
 import io.netty.buffer.Unpooled;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelOutboundHandler;
 import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.io.IoInputStream;
 import org.apache.sshd.common.io.IoReadFuture;
@@ -22,22 +21,24 @@ import org.slf4j.LoggerFactory;
  * Listener on async input stream from SSH session.
  * This listeners schedules reads in a loop until the session is closed or read fails.
  */
-final class AsyncSshHanderReader implements SshFutureListener<IoReadFuture>, AutoCloseable {
+public final class AsyncSshHandlerReader implements SshFutureListener<IoReadFuture>, AutoCloseable {
 
     private static final Logger logger = LoggerFactory.getLogger(AsyncSshHandler.class);
 
     private static final int BUFFER_SIZE = 8192;
 
-    private final ChannelOutboundHandler asyncSshHandler;
-    private final ChannelHandlerContext ctx;
+    private final AutoCloseable connectionClosedCallback;
+    private final ReadMsgHandler readHandler;
 
+    private final String channelId;
     private IoInputStream asyncOut;
     private Buffer buf;
     private IoReadFuture currentReadFuture;
 
-    public AsyncSshHanderReader(final ChannelOutboundHandler asyncSshHandler, final ChannelHandlerContext ctx, final IoInputStream asyncOut) {
-        this.asyncSshHandler = asyncSshHandler;
-        this.ctx = ctx;
+    public AsyncSshHandlerReader(final AutoCloseable connectionClosedCallback, final ReadMsgHandler readHandler, final String channelId, final IoInputStream asyncOut) {
+        this.connectionClosedCallback = connectionClosedCallback;
+        this.readHandler = readHandler;
+        this.channelId = channelId;
         this.asyncOut = asyncOut;
         buf = new Buffer(BUFFER_SIZE);
         asyncOut.read(buf).addListener(this);
@@ -48,16 +49,20 @@ final class AsyncSshHanderReader implements SshFutureListener<IoReadFuture>, Aut
         if(future.getException() != null) {
             if(asyncOut.isClosed() || asyncOut.isClosing()) {
                 // Ssh dropped
-                logger.debug("Ssh session dropped on channel: {}", ctx.channel(), future.getException());
+                logger.debug("Ssh session dropped on channel: {}", channelId, future.getException());
             } else {
-                logger.warn("Exception while reading from SSH remote on channel {}", ctx.channel(), future.getException());
+                logger.warn("Exception while reading from SSH remote on channel {}", channelId, future.getException());
             }
             invokeDisconnect();
             return;
         }
 
         if (future.getRead() > 0) {
-            ctx.fireChannelRead(Unpooled.wrappedBuffer(buf.array(), 0, future.getRead()));
+            final ByteBuf msg = Unpooled.wrappedBuffer(buf.array(), 0, future.getRead());
+            if(logger.isTraceEnabled()) {
+                logger.trace("Reading message on channel: {}, message: {}", channelId, AsyncSshHandlerWriter.byteBufToString(msg));
+            }
+            readHandler.onMessageRead(msg);
 
             // Schedule next read
             buf = new Buffer(BUFFER_SIZE);
@@ -68,7 +73,7 @@ final class AsyncSshHanderReader implements SshFutureListener<IoReadFuture>, Aut
 
     private void invokeDisconnect() {
         try {
-            asyncSshHandler.disconnect(ctx, ctx.newPromise());
+            connectionClosedCallback.close();
         } catch (final Exception e) {
             // This should not happen
             throw new IllegalStateException(e);
@@ -80,8 +85,14 @@ final class AsyncSshHanderReader implements SshFutureListener<IoReadFuture>, Aut
         // Remove self as listener on close to prevent reading from closed input
         if(currentReadFuture != null) {
             currentReadFuture.removeListener(this);
+            currentReadFuture = null;
         }
 
         asyncOut = null;
     }
+
+    public interface ReadMsgHandler {
+
+        void onMessageRead(ByteBuf msg);
+    }
 }
index eace0ac7eafe1d09381fbe597f9f6acfece953e6..8e639bd47cc1b24e18176e365d50df2d3185ae5c 100644 (file)
@@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory;
  * Async Ssh writer. Takes messages(byte arrays) and sends them encrypted to remote server.
  * Also handles pending writes by caching requests until pending state is over.
  */
-final class AsyncSshHandlerWriter implements AutoCloseable {
+public final class AsyncSshHandlerWriter implements AutoCloseable {
 
     private static final Logger logger = LoggerFactory
             .getLogger(AsyncSshHandlerWriter.class);
@@ -116,7 +116,7 @@ final class AsyncSshHandlerWriter implements AutoCloseable {
         writeWithPendingDetection(pendingWrite.ctx, pendingWrite.promise, msg);
     }
 
-    private static String byteBufToString(final ByteBuf msg) {
+    public static String byteBufToString(final ByteBuf msg) {
         msg.resetReaderIndex();
         final String s = msg.toString(Charsets.UTF_8);
         msg.resetReaderIndex();
index d0fc43d04aa2ce656f7616072522437009e92342..212eabb290656cb676c6cf6f925ee132f2b43242 100644 (file)
@@ -459,6 +459,8 @@ public class AsyncSshHandlerTest {
 
     private ChannelSubsystem getMockedSubsystemChannel(final IoInputStream asyncOut, final IoOutputStream asyncIn) throws IOException {
         final ChannelSubsystem subsystemChannel = mock(ChannelSubsystem.class);
+        doReturn("subsystemChannel").when(subsystemChannel).toString();
+
         doNothing().when(subsystemChannel).setStreaming(any(ClientChannel.Streaming.class));
         final OpenFuture openFuture = mock(OpenFuture.class);
 
index 221626b7410a4fa166dbcb63616d93dff2994abd..e0c7dba4fa4945f555f5271f0bef8ab096aa8c1d 100644 (file)
       <groupId>org.bouncycastle</groupId>
       <artifactId>bcprov-jdk15on</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.opendaylight.controller.thirdparty</groupId>
-      <artifactId>ganymed</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.apache.sshd</groupId>
       <artifactId>sshd-core</artifactId>
@@ -60,7 +56,6 @@
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
       <artifactId>netconf-netty-util</artifactId>
-      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.opendaylight.controller</groupId>
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/NetconfSSHServer.java
deleted file mode 100644 (file)
index 86206a7..0000000
+++ /dev/null
@@ -1,134 +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.netconf.ssh;
-
-import com.google.common.base.Preconditions;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opendaylight.controller.netconf.auth.AuthProvider;
-import org.opendaylight.controller.netconf.ssh.threads.Handshaker;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Optional;
-
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.local.LocalAddress;
-
-/**
- * Thread that accepts client connections. Accepted socket is forwarded to {@link org.opendaylight.controller.netconf.ssh.threads.Handshaker},
- * which is executed in {@link #handshakeExecutor}.
- */
-@ThreadSafe
-public final class NetconfSSHServer extends Thread implements AutoCloseable {
-
-    private static final Logger logger = LoggerFactory.getLogger(NetconfSSHServer.class);
-    private static final AtomicLong sessionIdCounter = new AtomicLong();
-
-    private final ServerSocket serverSocket;
-    private final LocalAddress localAddress;
-    private final EventLoopGroup bossGroup;
-    private Optional<AuthProvider> authProvider = Optional.absent();
-    private final ExecutorService handshakeExecutor;
-    private final char[] pem;
-    private volatile boolean up;
-
-    private NetconfSSHServer(final int serverPort, final LocalAddress localAddress, final EventLoopGroup bossGroup, final char[] pem) throws IOException {
-        super(NetconfSSHServer.class.getSimpleName());
-        this.bossGroup = bossGroup;
-        this.pem = pem;
-        logger.trace("Creating SSH server socket on port {}", serverPort);
-        this.serverSocket = new ServerSocket(serverPort);
-        if (serverSocket.isBound() == false) {
-            throw new IllegalStateException("Socket can't be bound to requested port :" + serverPort);
-        }
-        logger.trace("Server socket created.");
-        this.localAddress = localAddress;
-        this.up = true;
-        handshakeExecutor = Executors.newFixedThreadPool(10);
-    }
-
-    public static NetconfSSHServer start(final int serverPort, final LocalAddress localAddress, final EventLoopGroup bossGroup, final char[] pemArray) throws IOException {
-        final NetconfSSHServer netconfSSHServer = new NetconfSSHServer(serverPort, localAddress, bossGroup, pemArray);
-        netconfSSHServer.start();
-        return netconfSSHServer;
-    }
-
-    public synchronized AuthProvider getAuthProvider() {
-        Preconditions.checkState(authProvider.isPresent(), "AuthenticationProvider is not set up, cannot authenticate user");
-        return authProvider.get();
-    }
-
-    public synchronized void setAuthProvider(final AuthProvider authProvider) {
-        if(this.authProvider != null) {
-            logger.debug("Changing auth provider to {}", authProvider);
-        }
-        this.authProvider = Optional.fromNullable(authProvider);
-    }
-
-    @Override
-    public void close() throws IOException {
-        up = false;
-        logger.trace("Closing SSH server socket.");
-        serverSocket.close();
-        bossGroup.shutdownGracefully();
-        logger.trace("SSH server socket closed.");
-    }
-
-    @VisibleForTesting
-    public InetSocketAddress getLocalSocketAddress() {
-        return (InetSocketAddress) serverSocket.getLocalSocketAddress();
-    }
-
-    @Override
-    public void run() {
-        while (up) {
-            Socket acceptedSocket = null;
-            try {
-                acceptedSocket = serverSocket.accept();
-            } catch (final IOException e) {
-                if (up == false) {
-                    logger.trace("Exiting server thread", e);
-                } else {
-                    logger.warn("Exception occurred during socket.accept", e);
-                }
-            }
-            if (acceptedSocket != null) {
-                try {
-                    final Handshaker task = new Handshaker(acceptedSocket, localAddress, sessionIdCounter.incrementAndGet(), getAuthProvider(), bossGroup, pem);
-                    handshakeExecutor.submit(task);
-                } catch (final IOException e) {
-                    logger.warn("Cannot set PEMHostKey, closing connection", e);
-                    closeSocket(acceptedSocket);
-                } catch (final IllegalStateException e) {
-                    logger.warn("Cannot accept connection, closing", e);
-                    closeSocket(acceptedSocket);
-                }
-            }
-        }
-        logger.debug("Server thread is exiting");
-    }
-
-    private void closeSocket(final Socket acceptedSocket) {
-        try {
-            acceptedSocket.close();
-        } catch (final IOException e) {
-            logger.warn("Ignoring exception while closing socket", e);
-        }
-    }
-
-}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/RemoteNetconfCommand.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/RemoteNetconfCommand.java
new file mode 100644 (file)
index 0000000..e642e07
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.ssh;
+
+import com.google.common.base.Preconditions;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.local.LocalChannel;
+import io.netty.util.concurrent.GenericFutureListener;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.io.IoInputStream;
+import org.apache.sshd.common.io.IoOutputStream;
+import org.apache.sshd.server.AsyncCommand;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.ExitCallback;
+import org.apache.sshd.server.SessionAware;
+import org.apache.sshd.server.session.ServerSession;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This command handles all netconf related rpc and forwards to delegate server.
+ * Uses netty to make a local connection to delegate server.
+ *
+ * Command is Apache Mina SSH terminology for objects handling ssh data.
+ */
+public class RemoteNetconfCommand implements AsyncCommand, SessionAware {
+
+    private static final Logger logger = LoggerFactory.getLogger(RemoteNetconfCommand.class);
+
+    private final EventLoopGroup clientEventGroup;
+    private final LocalAddress localAddress;
+
+    private IoInputStream in;
+    private IoOutputStream out;
+    private ExitCallback callback;
+    private NetconfHelloMessageAdditionalHeader netconfHelloMessageAdditionalHeader;
+
+    private Channel clientChannel;
+    private ChannelFuture clientChannelFuture;
+
+    public RemoteNetconfCommand(final EventLoopGroup clientEventGroup, final LocalAddress localAddress) {
+        this.clientEventGroup = clientEventGroup;
+        this.localAddress = localAddress;
+    }
+
+    @Override
+    public void setIoInputStream(final IoInputStream in) {
+        this.in = in;
+    }
+
+    @Override
+    public void setIoOutputStream(final IoOutputStream out) {
+        this.out = out;
+    }
+
+    @Override
+    public void setIoErrorStream(final IoOutputStream err) {
+        // TODO do we want to use error stream in some way ?
+    }
+
+    @Override
+    public void setInputStream(final InputStream in) {
+        throw new UnsupportedOperationException("Synchronous IO is unsupported");
+    }
+
+    @Override
+    public void setOutputStream(final OutputStream out) {
+        throw new UnsupportedOperationException("Synchronous IO is unsupported");
+
+    }
+
+    @Override
+    public void setErrorStream(final OutputStream err) {
+        throw new UnsupportedOperationException("Synchronous IO is unsupported");
+
+    }
+
+    @Override
+    public void setExitCallback(final ExitCallback callback) {
+        this.callback = callback;
+    }
+
+    @Override
+    public void start(final Environment env) throws IOException {
+        logger.trace("Establishing internal connection to netconf server for client: {}", getClientAddress());
+
+        final Bootstrap clientBootstrap = new Bootstrap();
+        clientBootstrap.group(clientEventGroup).channel(LocalChannel.class);
+
+        clientBootstrap
+                .handler(new ChannelInitializer<LocalChannel>() {
+                    @Override
+                    public void initChannel(final LocalChannel ch) throws Exception {
+                        ch.pipeline().addLast(new SshProxyClientHandler(in, out, netconfHelloMessageAdditionalHeader, callback));
+                    }
+                });
+        clientChannelFuture = clientBootstrap.connect(localAddress);
+        clientChannelFuture.addListener(new GenericFutureListener<ChannelFuture>() {
+
+            @Override
+            public void operationComplete(final ChannelFuture future) throws Exception {
+                if(future.isSuccess()) {
+                    clientChannel = clientChannelFuture.channel();
+                } else {
+                    logger.warn("Unable to establish internal connection to netconf server for client: {}", getClientAddress());
+                    Preconditions.checkNotNull(callback, "Exit callback must be set");
+                    callback.onExit(1, "Unable to establish internal connection to netconf server for client: "+ getClientAddress());
+                }
+            }
+        });
+    }
+
+    @Override
+    public void destroy() {
+        logger.trace("Releasing internal connection to netconf server for client: {} on channel: {}",
+                getClientAddress(), clientChannel);
+
+        clientChannelFuture.cancel(true);
+        if(clientChannel != null) {
+            clientChannel.close().addListener(new GenericFutureListener<ChannelFuture>() {
+
+                @Override
+                public void operationComplete(final ChannelFuture future) throws Exception {
+                    if (future.isSuccess() == false) {
+                        logger.warn("Unable to release internal connection to netconf server on channel: {}", clientChannel);
+                    }
+                }
+            });
+        }
+    }
+
+    private String getClientAddress() {
+        return netconfHelloMessageAdditionalHeader.getAddress();
+    }
+
+    @Override
+    public void setSession(final ServerSession session) {
+        final SocketAddress remoteAddress = session.getIoSession().getRemoteAddress();
+        String hostName = "";
+        String port = "";
+        if(remoteAddress instanceof InetSocketAddress) {
+            hostName = ((InetSocketAddress) remoteAddress).getAddress().getHostAddress();
+            port = Integer.toString(((InetSocketAddress) remoteAddress).getPort());
+        }
+        netconfHelloMessageAdditionalHeader = new NetconfHelloMessageAdditionalHeader(
+                session.getUsername(), hostName, port, "ssh", "client");
+    }
+
+    public static class NetconfCommandFactory implements NamedFactory<Command> {
+
+        public static final String NETCONF = "netconf";
+
+        private final EventLoopGroup clientBootstrap;
+        private final LocalAddress localAddress;
+
+        public NetconfCommandFactory(final EventLoopGroup clientBootstrap, final LocalAddress localAddress) {
+
+            this.clientBootstrap = clientBootstrap;
+            this.localAddress = localAddress;
+        }
+
+        @Override
+        public String getName() {
+            return NETCONF;
+        }
+
+        @Override
+        public RemoteNetconfCommand create() {
+            return new RemoteNetconfCommand(clientBootstrap, localAddress);
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyClientHandler.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyClientHandler.java
new file mode 100644 (file)
index 0000000..2b2b3b3
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.ssh;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.apache.sshd.common.io.IoInputStream;
+import org.apache.sshd.common.io.IoOutputStream;
+import org.apache.sshd.server.ExitCallback;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.AsyncSshHandlerReader;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.AsyncSshHandlerWriter;
+import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Netty handler that reads SSH from remote client and writes to delegate server and reads from delegate server and writes to remote client
+ */
+final class SshProxyClientHandler extends ChannelInboundHandlerAdapter {
+
+    private static final Logger logger = LoggerFactory.getLogger(SshProxyClientHandler.class);
+
+    private final IoInputStream in;
+    private final IoOutputStream out;
+
+    private AsyncSshHandlerReader asyncSshHandlerReader;
+    private AsyncSshHandlerWriter asyncSshHandlerWriter;
+
+    private final NetconfHelloMessageAdditionalHeader netconfHelloMessageAdditionalHeader;
+    private final ExitCallback callback;
+
+    public SshProxyClientHandler(final IoInputStream in, final IoOutputStream out,
+                                 final NetconfHelloMessageAdditionalHeader netconfHelloMessageAdditionalHeader,
+                                 final ExitCallback callback) {
+        this.in = in;
+        this.out = out;
+        this.netconfHelloMessageAdditionalHeader = netconfHelloMessageAdditionalHeader;
+        this.callback = callback;
+    }
+
+    @Override
+    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
+        writeAdditionalHeader(ctx);
+
+        asyncSshHandlerWriter = new AsyncSshHandlerWriter(out);
+        asyncSshHandlerReader = new AsyncSshHandlerReader(new AutoCloseable() {
+            @Override
+            public void close() throws Exception {
+                // Close both sessions (delegate server and remote client)
+                ctx.fireChannelInactive();
+                ctx.disconnect();
+                ctx.close();
+                asyncSshHandlerReader.close();
+                asyncSshHandlerWriter.close();
+            }
+        }, new AsyncSshHandlerReader.ReadMsgHandler() {
+            @Override
+            public void onMessageRead(final ByteBuf msg) {
+                if(logger.isTraceEnabled()) {
+                    logger.trace("Forwarding message for client: {} on channel: {}, message: {}",
+                            netconfHelloMessageAdditionalHeader.getAddress(), ctx.channel(), AsyncSshHandlerWriter.byteBufToString(msg));
+                }
+                // Just forward to delegate
+                ctx.writeAndFlush(msg);
+            }
+        }, "ssh" + netconfHelloMessageAdditionalHeader.getAddress(), in);
+
+
+        super.channelActive(ctx);
+    }
+
+    private void writeAdditionalHeader(final ChannelHandlerContext ctx) {
+        ctx.writeAndFlush(Unpooled.copiedBuffer(netconfHelloMessageAdditionalHeader.toFormattedString().getBytes()));
+    }
+
+    @Override
+    public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {
+       asyncSshHandlerWriter.write(ctx, msg, ctx.newPromise());
+    }
+
+    @Override
+    public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
+        logger.debug("Internal connection to netconf server was dropped for client: {} on channel: ",
+                netconfHelloMessageAdditionalHeader.getAddress(), ctx.channel());
+        callback.onExit(1, "Internal connection to netconf server was dropped for client: " +
+                netconfHelloMessageAdditionalHeader.getAddress() + " on channel: " + ctx.channel());
+        super.channelInactive(ctx);
+    }
+
+
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyServer.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/SshProxyServer.java
new file mode 100644 (file)
index 0000000..0b85cf2
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.ssh;
+
+import com.google.common.collect.Lists;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.local.LocalAddress;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.channels.AsynchronousChannelGroup;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.apache.sshd.SshServer;
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.RuntimeSshException;
+import org.apache.sshd.common.io.IoAcceptor;
+import org.apache.sshd.common.io.IoConnector;
+import org.apache.sshd.common.io.IoHandler;
+import org.apache.sshd.common.io.IoServiceFactory;
+import org.apache.sshd.common.io.IoServiceFactoryFactory;
+import org.apache.sshd.common.io.nio2.Nio2Acceptor;
+import org.apache.sshd.common.io.nio2.Nio2Connector;
+import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory;
+import org.apache.sshd.common.util.CloseableUtils;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.PasswordAuthenticator;
+
+/**
+ * Proxy SSH server that just delegates decrypted content to a delegate server within same VM.
+ * Implemented using Apache Mina SSH lib.
+ */
+public class SshProxyServer implements AutoCloseable {
+
+    private final SshServer sshServer;
+    private final ScheduledExecutorService minaTimerExecutor;
+    private final EventLoopGroup clientGroup;
+    private final IoServiceFactoryFactory nioServiceWithPoolFactoryFactory;
+
+    public SshProxyServer(final ScheduledExecutorService minaTimerExecutor, final EventLoopGroup clientGroup, final ExecutorService nioExecutor) {
+        this.minaTimerExecutor = minaTimerExecutor;
+        this.clientGroup = clientGroup;
+        this.nioServiceWithPoolFactoryFactory = new NioServiceWithPoolFactory.NioServiceWithPoolFactoryFactory(nioExecutor);
+        this.sshServer = SshServer.setUpDefaultServer();
+    }
+
+    public void bind(final InetSocketAddress bindingAddress, final LocalAddress localAddress, final PasswordAuthenticator authenticator, final KeyPairProvider keyPairProvider) throws IOException {
+        sshServer.setHost(bindingAddress.getHostString());
+        sshServer.setPort(bindingAddress.getPort());
+
+        sshServer.setPasswordAuthenticator(authenticator);
+        sshServer.setKeyPairProvider(keyPairProvider);
+
+        sshServer.setIoServiceFactoryFactory(nioServiceWithPoolFactoryFactory);
+        sshServer.setScheduledExecutorService(minaTimerExecutor);
+
+        final RemoteNetconfCommand.NetconfCommandFactory netconfCommandFactory =
+                new RemoteNetconfCommand.NetconfCommandFactory(clientGroup, localAddress);
+        sshServer.setSubsystemFactories(Lists.<NamedFactory<Command>>newArrayList(netconfCommandFactory));
+        sshServer.start();
+    }
+
+    @Override
+    public void close() {
+        try {
+            sshServer.stop(true);
+        } catch (final InterruptedException e) {
+            throw new RuntimeException("Interrupted while stopping sshServer", e);
+        } finally {
+            sshServer.close(true);
+        }
+    }
+
+    /**
+     * Based on Nio2ServiceFactory with one addition: injectable executor
+     */
+    private static final class NioServiceWithPoolFactory extends CloseableUtils.AbstractCloseable implements IoServiceFactory {
+
+        private final FactoryManager manager;
+        private final AsynchronousChannelGroup group;
+
+        public NioServiceWithPoolFactory(final FactoryManager manager, final ExecutorService executor) {
+            this.manager = manager;
+            try {
+                group = AsynchronousChannelGroup.withThreadPool(executor);
+            } catch (final IOException e) {
+                throw new RuntimeSshException(e);
+            }
+        }
+
+        public IoConnector createConnector(final IoHandler handler) {
+            return new Nio2Connector(manager, handler, group);
+        }
+
+        public IoAcceptor createAcceptor(final IoHandler handler) {
+            return new Nio2Acceptor(manager, handler, group);
+        }
+
+        @Override
+        protected void doCloseImmediately() {
+            try {
+                group.shutdownNow();
+                group.awaitTermination(5, TimeUnit.SECONDS);
+            } catch (final Exception e) {
+                log.debug("Exception caught while closing channel group", e);
+            } finally {
+                super.doCloseImmediately();
+            }
+        }
+
+        private static final class NioServiceWithPoolFactoryFactory extends Nio2ServiceFactoryFactory {
+
+            private final ExecutorService nioExecutor;
+
+            private NioServiceWithPoolFactoryFactory(final ExecutorService nioExecutor) {
+                this.nioExecutor = nioExecutor;
+            }
+
+            @Override
+            public IoServiceFactory create(final FactoryManager manager) {
+                return new NioServiceWithPoolFactory(manager, nioExecutor);
+            }
+        }
+    }
+
+}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/authentication/PEMGenerator.java
deleted file mode 100644 (file)
index 53ab821..0000000
+++ /dev/null
@@ -1,90 +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.netconf.ssh.authentication;
-
-import com.google.common.annotations.VisibleForTesting;
-import java.io.FileInputStream;
-import java.security.NoSuchAlgorithmException;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.bouncycastle.openssl.PEMWriter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.security.Key;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.SecureRandom;
-
-public class PEMGenerator {
-    private static final Logger logger = LoggerFactory.getLogger(PEMGenerator.class);
-    private static final int KEY_SIZE = 4096;
-
-
-    public static String readOrGeneratePK(File privateKeyFile) throws IOException {
-        if (privateKeyFile.exists() == false) {
-            // generate & save to file
-            try {
-                return generateTo(privateKeyFile);
-            } catch (Exception e) {
-                logger.error("Exception occurred while generating PEM string to {}", privateKeyFile, e);
-                throw new IllegalStateException("Error generating RSA key from file " + privateKeyFile);
-            }
-        } else {
-            // read from file
-            try (FileInputStream fis = new FileInputStream(privateKeyFile)) {
-                return IOUtils.toString(fis);
-            } catch (final IOException e) {
-                logger.error("Error reading RSA key from file {}", privateKeyFile, e);
-                throw new IOException("Error reading RSA key from file " + privateKeyFile, e);
-            }
-        }
-    }
-
-    /**
-     * Generate private key to a file and return its content as string.
-     *
-     * @param privateFile path where private key should be generated
-     * @return String representation of private key
-     * @throws IOException
-     * @throws NoSuchAlgorithmException
-     */
-    @VisibleForTesting
-    public static String generateTo(File privateFile) throws IOException, NoSuchAlgorithmException {
-        logger.info("Generating private key to {}", privateFile.getAbsolutePath());
-        String privatePEM = generate();
-        FileUtils.write(privateFile, privatePEM);
-        return privatePEM;
-    }
-
-    @VisibleForTesting
-    public static String generate() throws NoSuchAlgorithmException, IOException {
-        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
-        SecureRandom sr = new SecureRandom();
-        keyGen.initialize(KEY_SIZE, sr);
-        KeyPair keypair = keyGen.generateKeyPair();
-        return toString(keypair.getPrivate());
-    }
-
-    /**
-     * Get string representation of a key.
-     */
-    private static String toString(Key key) throws IOException {
-        try (StringWriter writer = new StringWriter()) {
-            try (PEMWriter pemWriter = new PEMWriter(writer)) {
-                pemWriter.writeObject(key);
-            }
-            return writer.toString();
-        }
-    }
-
-}
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/AuthProviderTracker.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/osgi/AuthProviderTracker.java
new file mode 100644 (file)
index 0000000..97e611c
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.ssh.osgi;
+
+import com.google.common.base.Preconditions;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+import org.opendaylight.controller.netconf.auth.AuthConstants;
+import org.opendaylight.controller.netconf.auth.AuthProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class AuthProviderTracker implements ServiceTrackerCustomizer<AuthProvider, AuthProvider>, PasswordAuthenticator {
+    private static final Logger logger = LoggerFactory.getLogger(AuthProviderTracker.class);
+
+    private final BundleContext bundleContext;
+
+    private Integer maxPreference;
+    private final ServiceTracker<AuthProvider, AuthProvider> listenerTracker;
+    private AuthProvider authProvider;
+
+    public AuthProviderTracker(final BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+        listenerTracker = new ServiceTracker<>(bundleContext, AuthProvider.class, this);
+        listenerTracker.open();
+    }
+
+    @Override
+    public AuthProvider addingService(final ServiceReference<AuthProvider> reference) {
+        logger.trace("Service {} added", reference);
+        final AuthProvider authService = bundleContext.getService(reference);
+        final Integer newServicePreference = getPreference(reference);
+        if(isBetter(newServicePreference)) {
+            maxPreference = newServicePreference;
+            this.authProvider = authService;
+        }
+        return authService;
+    }
+
+    private Integer getPreference(final ServiceReference<AuthProvider> reference) {
+        final Object preferenceProperty = reference.getProperty(AuthConstants.SERVICE_PREFERENCE_KEY);
+        return preferenceProperty == null ? Integer.MIN_VALUE : Integer.valueOf(preferenceProperty.toString());
+    }
+
+    private boolean isBetter(final Integer newServicePreference) {
+        Preconditions.checkNotNull(newServicePreference);
+        if(maxPreference == null) {
+            return true;
+        }
+
+        return newServicePreference > maxPreference;
+    }
+
+    @Override
+    public void modifiedService(final ServiceReference<AuthProvider> reference, final AuthProvider service) {
+        final AuthProvider authService = bundleContext.getService(reference);
+        final Integer newServicePreference = getPreference(reference);
+        if(isBetter(newServicePreference)) {
+            logger.trace("Replacing modified service {} in netconf SSH.", reference);
+            this.authProvider = authService;
+        }
+    }
+
+    @Override
+    public void removedService(final ServiceReference<AuthProvider> reference, final AuthProvider service) {
+        logger.trace("Removing service {} from netconf SSH. " +
+                "SSH won't authenticate users until AuthProvider service will be started.", reference);
+        maxPreference = null;
+        this.authProvider = null;
+    }
+
+    public void stop() {
+        listenerTracker.close();
+        // sshThread should finish normally since sshServer.close stops processing
+    }
+
+    @Override
+    public boolean authenticate(final String username, final String password, final ServerSession session) {
+        return authProvider == null ? false : authProvider.authenticated(username, password);
+    }
+}
index 0d0f95c3cb4d0856ba56c470f0890eb51371caa1..b871d19db8f062d0dbca93cc18ce6c3c6e09ba2d 100644 (file)
@@ -9,51 +9,51 @@ package org.opendaylight.controller.netconf.ssh.osgi;
 
 import static com.google.common.base.Preconditions.checkState;
 
-import com.google.common.base.Preconditions;
-import java.io.File;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.nio.NioEventLoopGroup;
 import java.io.IOException;
 import java.net.InetSocketAddress;
-
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
 import org.apache.commons.io.FilenameUtils;
-import org.opendaylight.controller.netconf.auth.AuthConstants;
-import org.opendaylight.controller.netconf.auth.AuthProvider;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
-import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import org.apache.sshd.common.util.ThreadUtils;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.InfixProp;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Strings;
-
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.local.LocalAddress;
-import io.netty.channel.nio.NioEventLoopGroup;
-
-/**
- * Activator for netconf SSH bundle which creates SSH bridge between netconf client and netconf server. Activator
- * starts SSH Server in its own thread. This thread is closed when activator calls stop() method. Server opens socket
- * and listens for client connections. Each client connection creation is handled in separate
- * {@link org.opendaylight.controller.netconf.ssh.threads.Handshaker} thread.
- * This thread creates two additional threads {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}
- * forwarding data from/to client.IOThread closes servers session and server connection when it gets -1 on input stream.
- * {@link org.opendaylight.controller.netconf.ssh.threads.IOThread}'s run method waits for -1 on input stream to finish.
- * All threads are daemons.
- */
 public class NetconfSSHActivator implements BundleActivator {
     private static final Logger logger = LoggerFactory.getLogger(NetconfSSHActivator.class);
-    private static AuthProviderTracker authProviderTracker;
 
-    private NetconfSSHServer server;
+    private static final java.lang.String ALGORITHM = "RSA";
+    private static final int KEY_SIZE = 4096;
+    public static final int POOL_SIZE = 8;
+
+    private ScheduledExecutorService minaTimerExecutor;
+    private NioEventLoopGroup clientGroup;
+    private ExecutorService nioExecutor;
+    private AuthProviderTracker authProviderTracker;
+
+    private SshProxyServer server;
 
     @Override
     public void start(final BundleContext bundleContext) throws IOException {
+        minaTimerExecutor = Executors.newScheduledThreadPool(POOL_SIZE, new ThreadFactory() {
+            @Override
+            public Thread newThread(final Runnable r) {
+                return new Thread(r, "netconf-ssh-server-mina-timers");
+            }
+        });
+        clientGroup = new NioEventLoopGroup();
+        nioExecutor = ThreadUtils.newFixedThreadPool("netconf-ssh-server-nio-group", POOL_SIZE);
         server = startSSHServer(bundleContext);
     }
 
@@ -66,11 +66,22 @@ public class NetconfSSHActivator implements BundleActivator {
         if(authProviderTracker != null) {
             authProviderTracker.stop();
         }
+
+        if(nioExecutor!=null) {
+            nioExecutor.shutdownNow();
+        }
+
+        if(clientGroup != null) {
+            clientGroup.shutdownGracefully();
+        }
+
+        if(minaTimerExecutor != null) {
+            minaTimerExecutor.shutdownNow();
+        }
     }
 
-    private static NetconfSSHServer startSSHServer(final BundleContext bundleContext) throws IOException {
-        final Optional<InetSocketAddress> maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext,
-                InfixProp.ssh);
+    private SshProxyServer startSSHServer(final BundleContext bundleContext) throws IOException {
+        final Optional<InetSocketAddress> maybeSshSocketAddress = NetconfConfigUtil.extractNetconfServerAddress(bundleContext, InfixProp.ssh);
 
         if (maybeSshSocketAddress.isPresent() == false) {
             logger.trace("SSH bridge not configured");
@@ -82,92 +93,15 @@ public class NetconfSSHActivator implements BundleActivator {
 
         final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
 
-        final String path = FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(bundleContext));
-        checkState(!Strings.isNullOrEmpty(path), "Path to ssh private key is blank. Reconfigure %s", NetconfConfigUtil.getPrivateKeyKey());
-        final String privateKeyPEMString = PEMGenerator.readOrGeneratePK(new File(path));
-
-        final EventLoopGroup bossGroup  = new NioEventLoopGroup();
-        final NetconfSSHServer server = NetconfSSHServer.start(sshSocketAddress.getPort(), localAddress, bossGroup, privateKeyPEMString.toCharArray());
-
-        authProviderTracker = new AuthProviderTracker(bundleContext, server);
+        authProviderTracker = new AuthProviderTracker(bundleContext);
 
-        return server;
-    }
+        final String path = FilenameUtils.separatorsToSystem(NetconfConfigUtil.getPrivateKeyPath(bundleContext));
+        checkState(!Strings.isNullOrEmpty(path), "Path to ssh private key is blank. Reconfigure %s",
+                NetconfConfigUtil.getPrivateKeyKey());
 
-    private static Thread runNetconfSshThread(final NetconfSSHServer server) {
-        final Thread serverThread = new Thread(server, "netconf SSH server thread");
-        serverThread.setDaemon(true);
-        serverThread.start();
-        logger.trace("Netconf SSH  bridge up and running.");
-        return serverThread;
+        final SshProxyServer sshProxyServer = new SshProxyServer(minaTimerExecutor, clientGroup, nioExecutor);
+        sshProxyServer.bind(sshSocketAddress, localAddress, authProviderTracker, new PEMGeneratorHostKeyProvider(path, ALGORITHM, KEY_SIZE));
+        return sshProxyServer;
     }
 
-    private static class AuthProviderTracker implements ServiceTrackerCustomizer<AuthProvider, AuthProvider> {
-        private final BundleContext bundleContext;
-        private final NetconfSSHServer server;
-
-        private Integer maxPreference;
-        private Thread sshThread;
-        private final ServiceTracker<AuthProvider, AuthProvider> listenerTracker;
-
-        public AuthProviderTracker(final BundleContext bundleContext, final NetconfSSHServer server) {
-            this.bundleContext = bundleContext;
-            this.server = server;
-            listenerTracker = new ServiceTracker<>(bundleContext, AuthProvider.class, this);
-            listenerTracker.open();
-        }
-
-        @Override
-        public AuthProvider addingService(final ServiceReference<AuthProvider> reference) {
-            logger.trace("Service {} added", reference);
-            final AuthProvider authService = bundleContext.getService(reference);
-            final Integer newServicePreference = getPreference(reference);
-            if(isBetter(newServicePreference)) {
-                maxPreference = newServicePreference;
-                server.setAuthProvider(authService);
-                if(sshThread == null) {
-                    sshThread = runNetconfSshThread(server);
-                }
-            }
-            return authService;
-        }
-
-        private Integer getPreference(final ServiceReference<AuthProvider> reference) {
-            final Object preferenceProperty = reference.getProperty(AuthConstants.SERVICE_PREFERENCE_KEY);
-            return preferenceProperty == null ? Integer.MIN_VALUE : Integer.valueOf(preferenceProperty.toString());
-        }
-
-        private boolean isBetter(final Integer newServicePreference) {
-            Preconditions.checkNotNull(newServicePreference);
-            if(maxPreference == null) {
-                return true;
-            }
-
-            return newServicePreference > maxPreference;
-        }
-
-        @Override
-        public void modifiedService(final ServiceReference<AuthProvider> reference, final AuthProvider service) {
-            final AuthProvider authService = bundleContext.getService(reference);
-            final Integer newServicePreference = getPreference(reference);
-            if(isBetter(newServicePreference)) {
-                logger.trace("Replacing modified service {} in netconf SSH.", reference);
-                server.setAuthProvider(authService);
-            }
-        }
-
-        @Override
-        public void removedService(final ServiceReference<AuthProvider> reference, final AuthProvider service) {
-            logger.trace("Removing service {} from netconf SSH. " +
-                    "SSH won't authenticate users until AuthProvider service will be started.", reference);
-            maxPreference = null;
-            server.setAuthProvider(null);
-        }
-
-        public void stop() {
-            listenerTracker.close();
-            // sshThread should finish normally since sshServer.close stops processing
-        }
-
-    }
 }
diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java
deleted file mode 100644 (file)
index eec6c3a..0000000
+++ /dev/null
@@ -1,415 +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.netconf.ssh.threads;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-
-import javax.annotation.concurrent.NotThreadSafe;
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opendaylight.controller.netconf.auth.AuthProvider;
-import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import ch.ethz.ssh2.AuthenticationResult;
-import ch.ethz.ssh2.PtySettings;
-import ch.ethz.ssh2.ServerAuthenticationCallback;
-import ch.ethz.ssh2.ServerConnection;
-import ch.ethz.ssh2.ServerConnectionCallback;
-import ch.ethz.ssh2.ServerSession;
-import ch.ethz.ssh2.ServerSessionCallback;
-import ch.ethz.ssh2.SimpleServerSessionCallback;
-
-import com.google.common.base.Supplier;
-
-import io.netty.bootstrap.Bootstrap;
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufProcessor;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.channel.ChannelInitializer;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.local.LocalAddress;
-import io.netty.channel.local.LocalChannel;
-import io.netty.handler.stream.ChunkedStream;
-
-/**
- * One instance represents per connection, responsible for ssh handshake.
- * Once auth succeeds and correct subsystem is chosen, backend connection with
- * netty netconf server is made. This task finishes right after negotiation is done.
- */
-@ThreadSafe
-public class Handshaker implements Runnable {
-    private static final Logger logger = LoggerFactory.getLogger(Handshaker.class);
-
-    private final ServerConnection ganymedConnection;
-    private final String session;
-
-
-    public Handshaker(Socket socket, LocalAddress localAddress, long sessionId, AuthProvider authProvider,
-                      EventLoopGroup bossGroup, final char[] pem) throws IOException {
-
-        this.session = "Session " + sessionId;
-
-        String remoteAddressWithPort = socket.getRemoteSocketAddress().toString().replace("/", "");
-        logger.debug("{} started with {}", session, remoteAddressWithPort);
-        String remoteAddress, remotePort;
-        if (remoteAddressWithPort.contains(":")) {
-            String[] split = remoteAddressWithPort.split(":");
-            remoteAddress = split[0];
-            remotePort = split[1];
-        } else {
-            remoteAddress = remoteAddressWithPort;
-            remotePort = "";
-        }
-        ServerAuthenticationCallbackImpl serverAuthenticationCallback = new ServerAuthenticationCallbackImpl(
-                authProvider, session);
-
-        ganymedConnection = new ServerConnection(socket);
-
-        ServerConnectionCallbackImpl serverConnectionCallback = new ServerConnectionCallbackImpl(
-                serverAuthenticationCallback, remoteAddress, remotePort, session,
-                getGanymedAutoCloseable(ganymedConnection), localAddress, bossGroup);
-
-        // initialize ganymed
-        ganymedConnection.setPEMHostKey(pem, null);
-        ganymedConnection.setAuthenticationCallback(serverAuthenticationCallback);
-        ganymedConnection.setServerConnectionCallback(serverConnectionCallback);
-    }
-
-
-    private static AutoCloseable getGanymedAutoCloseable(final ServerConnection ganymedConnection) {
-        return new AutoCloseable() {
-            @Override
-            public void close() throws Exception {
-                ganymedConnection.close();
-            }
-        };
-    }
-
-    @Override
-    public void run() {
-        // let ganymed process handshake
-        logger.trace("{} is started", session);
-        try {
-            // TODO this should be guarded with a timer to prevent resource exhaustion
-            ganymedConnection.connect();
-        } catch (IOException e) {
-            logger.debug("{} connection error", session, e);
-        }
-        logger.trace("{} is exiting", session);
-    }
-}
-
-/**
- * Netty client handler that forwards bytes from backed server to supplied output stream.
- * When backend server closes the connection, remoteConnection.close() is called to tear
- * down ssh connection.
- */
-class SSHClientHandler extends ChannelInboundHandlerAdapter {
-    private static final Logger logger = LoggerFactory.getLogger(SSHClientHandler.class);
-    private final AutoCloseable remoteConnection;
-    private final BufferedOutputStream remoteOutputStream;
-    private final String session;
-    private ChannelHandlerContext channelHandlerContext;
-
-    public SSHClientHandler(AutoCloseable remoteConnection, OutputStream remoteOutputStream,
-                            String session) {
-        this.remoteConnection = remoteConnection;
-        this.remoteOutputStream = new BufferedOutputStream(remoteOutputStream);
-        this.session = session;
-    }
-
-    @Override
-    public void channelActive(ChannelHandlerContext ctx) {
-        this.channelHandlerContext = ctx;
-        logger.debug("{} Client active", session);
-    }
-
-    @Override
-    public void channelRead(ChannelHandlerContext ctx, Object msg) throws IOException {
-        ByteBuf bb = (ByteBuf) msg;
-        // we can block the server here so that slow client does not cause memory pressure
-        try {
-            bb.forEachByte(new ByteBufProcessor() {
-                @Override
-                public boolean process(byte value) throws Exception {
-                    remoteOutputStream.write(value);
-                    return true;
-                }
-            });
-        } finally {
-            bb.release();
-        }
-    }
-
-    @Override
-    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
-        logger.trace("{} Flushing", session);
-        remoteOutputStream.flush();
-    }
-
-    @Override
-    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
-        // Close the connection when an exception is raised.
-        logger.warn("{} Unexpected exception from downstream", session, cause);
-        ctx.close();
-    }
-
-    @Override
-    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
-        logger.trace("{} channelInactive() called, closing remote client ctx", session);
-        remoteConnection.close();//this should close socket and all threads created for this client
-        this.channelHandlerContext = null;
-    }
-
-    public ChannelHandlerContext getChannelHandlerContext() {
-        return checkNotNull(channelHandlerContext, "Channel is not active");
-    }
-}
-
-/**
- * Ganymed handler that gets unencrypted input and output streams, connects them to netty.
- * Checks that 'netconf' subsystem is chosen by user.
- * Launches new ClientInputStreamPoolingThread thread once session is established.
- * Writes custom header to netty server, to inform it about IP address and username.
- */
-class ServerConnectionCallbackImpl implements ServerConnectionCallback {
-    private static final Logger logger = LoggerFactory.getLogger(ServerConnectionCallbackImpl.class);
-    public static final String NETCONF_SUBSYSTEM = "netconf";
-
-    private final Supplier<String> currentUserSupplier;
-    private final String remoteAddress;
-    private final String remotePort;
-    private final String session;
-    private final AutoCloseable ganymedConnection;
-    private final LocalAddress localAddress;
-    private final EventLoopGroup bossGroup;
-
-    ServerConnectionCallbackImpl(Supplier<String> currentUserSupplier, String remoteAddress, String remotePort, String session,
-                                 AutoCloseable ganymedConnection, LocalAddress localAddress, EventLoopGroup bossGroup) {
-        this.currentUserSupplier = currentUserSupplier;
-        this.remoteAddress = remoteAddress;
-        this.remotePort = remotePort;
-        this.session = session;
-        this.ganymedConnection = ganymedConnection;
-        // initialize netty local connection
-        this.localAddress = localAddress;
-        this.bossGroup = bossGroup;
-    }
-
-    private static ChannelFuture initializeNettyConnection(LocalAddress localAddress, EventLoopGroup bossGroup,
-                                                           final SSHClientHandler sshClientHandler) {
-        Bootstrap clientBootstrap = new Bootstrap();
-        clientBootstrap.group(bossGroup).channel(LocalChannel.class);
-
-        clientBootstrap.handler(new ChannelInitializer<LocalChannel>() {
-            @Override
-            public void initChannel(LocalChannel ch) throws Exception {
-                ch.pipeline().addLast(sshClientHandler);
-            }
-        });
-        // asynchronously initialize local connection to netconf server
-        return clientBootstrap.connect(localAddress);
-    }
-
-    @Override
-    public ServerSessionCallback acceptSession(final ServerSession serverSession) {
-        String currentUser = currentUserSupplier.get();
-        final String additionalHeader = new NetconfHelloMessageAdditionalHeader(currentUser, remoteAddress,
-                remotePort, "ssh", "client").toFormattedString();
-
-
-        return new SimpleServerSessionCallback() {
-            @Override
-            public Runnable requestSubsystem(final ServerSession ss, final String subsystem) throws IOException {
-                return new Runnable() {
-                    @Override
-                    public void run() {
-                        if (NETCONF_SUBSYSTEM.equals(subsystem)) {
-                            // connect
-                            final SSHClientHandler sshClientHandler = new SSHClientHandler(ganymedConnection, ss.getStdin(), session);
-                            ChannelFuture clientChannelFuture = initializeNettyConnection(localAddress, bossGroup, sshClientHandler);
-                            // get channel
-                            final Channel channel = clientChannelFuture.awaitUninterruptibly().channel();
-
-                            // write additional header before polling thread is started
-                            // polling thread could process and forward data before additional header is written
-                            // This will result into unexpected state:  hello message without additional header and the next message with additional header
-                            channel.writeAndFlush(Unpooled.copiedBuffer(additionalHeader.getBytes()));
-
-                            new ClientInputStreamPoolingThread(session, ss.getStdout(), channel, new AutoCloseable() {
-                                @Override
-                                public void close() throws Exception {
-                                    logger.trace("Closing both ganymed and local connection");
-                                    try {
-                                        ganymedConnection.close();
-                                    } catch (Exception e) {
-                                        logger.warn("Ignoring exception while closing ganymed", e);
-                                    }
-                                    try {
-                                        channel.close();
-                                    } catch (Exception e) {
-                                        logger.warn("Ignoring exception while closing channel", e);
-                                    }
-                                }
-                            }, sshClientHandler.getChannelHandlerContext()).start();
-                        } else {
-                            logger.debug("{} Wrong subsystem requested:'{}', closing ssh session", serverSession, subsystem);
-                            String reason = "Only netconf subsystem is supported, requested:" + subsystem;
-                            closeSession(ss, reason);
-                        }
-                    }
-                };
-            }
-
-            public void closeSession(ServerSession ss, String reason) {
-                logger.trace("{} Closing session - {}", serverSession, reason);
-                try {
-                    ss.getStdin().write(reason.getBytes());
-                } catch (IOException e) {
-                    logger.warn("{} Exception while closing session", serverSession, e);
-                }
-                ss.close();
-            }
-
-            @Override
-            public Runnable requestPtyReq(final ServerSession ss, final PtySettings pty) throws IOException {
-                return new Runnable() {
-                    @Override
-                    public void run() {
-                        closeSession(ss, "PTY request not supported");
-                    }
-                };
-            }
-
-            @Override
-            public Runnable requestShell(final ServerSession ss) throws IOException {
-                return new Runnable() {
-                    @Override
-                    public void run() {
-                        closeSession(ss, "Shell not supported");
-                    }
-                };
-            }
-        };
-    }
-}
-
-/**
- * Only thread that is required during ssh session, forwards client's input to netty.
- * When user closes connection, onEndOfInput.close() is called to tear down the local channel.
- */
-class ClientInputStreamPoolingThread extends Thread {
-    private static final Logger logger = LoggerFactory.getLogger(ClientInputStreamPoolingThread.class);
-
-    private final InputStream fromClientIS;
-    private final Channel serverChannel;
-    private final AutoCloseable onEndOfInput;
-    private final ChannelHandlerContext channelHandlerContext;
-
-    ClientInputStreamPoolingThread(String session, InputStream fromClientIS, Channel serverChannel, AutoCloseable onEndOfInput,
-                                   ChannelHandlerContext channelHandlerContext) {
-        super(ClientInputStreamPoolingThread.class.getSimpleName() + " " + session);
-        this.fromClientIS = fromClientIS;
-        this.serverChannel = serverChannel;
-        this.onEndOfInput = onEndOfInput;
-        this.channelHandlerContext = channelHandlerContext;
-    }
-
-    @Override
-    public void run() {
-        ChunkedStream chunkedStream = new ChunkedStream(fromClientIS);
-        try {
-            ByteBuf byteBuf;
-            while ((byteBuf = chunkedStream.readChunk(channelHandlerContext/*only needed for ByteBuf alloc */)) != null) {
-                serverChannel.writeAndFlush(byteBuf);
-            }
-        } catch (Exception e) {
-            logger.warn("Exception", e);
-        } finally {
-            logger.trace("End of input");
-            // tear down connection
-            try {
-                onEndOfInput.close();
-            } catch (Exception e) {
-                logger.warn("Ignoring exception while closing socket", e);
-            }
-        }
-    }
-}
-
-/**
- * Authentication handler for ganymed.
- * Provides current user name after authenticating using supplied AuthProvider.
- */
-@NotThreadSafe
-class ServerAuthenticationCallbackImpl implements ServerAuthenticationCallback, Supplier<String> {
-    private static final Logger logger = LoggerFactory.getLogger(ServerAuthenticationCallbackImpl.class);
-    private final AuthProvider authProvider;
-    private final String session;
-    private String currentUser;
-
-    ServerAuthenticationCallbackImpl(AuthProvider authProvider, String session) {
-        this.authProvider = authProvider;
-        this.session = session;
-    }
-
-    @Override
-    public String initAuthentication(ServerConnection sc) {
-        logger.trace("{} Established connection", session);
-        return "Established connection" + "\r\n";
-    }
-
-    @Override
-    public String[] getRemainingAuthMethods(ServerConnection sc) {
-        return new String[]{ServerAuthenticationCallback.METHOD_PASSWORD};
-    }
-
-    @Override
-    public AuthenticationResult authenticateWithNone(ServerConnection sc, String username) {
-        return AuthenticationResult.FAILURE;
-    }
-
-    @Override
-    public AuthenticationResult authenticateWithPassword(ServerConnection sc, String username, String password) {
-        checkState(currentUser == null);
-        try {
-            if (authProvider.authenticated(username, password)) {
-                currentUser = username;
-                logger.trace("{} user {} authenticated", session, currentUser);
-                return AuthenticationResult.SUCCESS;
-            }
-        } catch (Exception e) {
-            logger.warn("{} Authentication failed", session, e);
-        }
-        return AuthenticationResult.FAILURE;
-    }
-
-    @Override
-    public AuthenticationResult authenticateWithPublicKey(ServerConnection sc, String username, String algorithm,
-                                                          byte[] publicKey, byte[] signature) {
-        return AuthenticationResult.FAILURE;
-    }
-
-    @Override
-    public String get() {
-        return currentUser;
-    }
-}
index eb2b644cbca1fbaa5f98c2b704d3893ac55e9fa4..62ce58723765231d2c4edd8683cb5e18093c09fe 100644 (file)
@@ -11,9 +11,6 @@ package org.opendaylight.controller.netconf.netty;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 
 import com.google.common.base.Stopwatch;
 import io.netty.bootstrap.Bootstrap;
@@ -23,16 +20,21 @@ import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.channel.socket.nio.NioSocketChannel;
 import io.netty.util.HashedWheelTimer;
 import java.net.InetSocketAddress;
+import java.nio.file.Files;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
-import org.junit.After;
-import org.junit.Before;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerSession;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
-import org.opendaylight.controller.netconf.auth.AuthProvider;
 import org.opendaylight.controller.netconf.netty.EchoClientHandler.State;
 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.AsyncSshHandler;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
-import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,68 +42,77 @@ import org.slf4j.LoggerFactory;
 public class SSHTest {
     public static final Logger logger = LoggerFactory.getLogger(SSHTest.class);
     public static final String AHOJ = "ahoj\n";
-    private EventLoopGroup nettyGroup;
-    HashedWheelTimer hashedWheelTimer;
 
-    @Before
-    public void setUp() throws Exception {
+    private static EventLoopGroup nettyGroup;
+    private static HashedWheelTimer hashedWheelTimer;
+    private static ExecutorService nioExec;
+    private static ScheduledExecutorService minaTimerEx;
+
+    @BeforeClass
+    public static void setUp() throws Exception {
         hashedWheelTimer = new HashedWheelTimer();
         nettyGroup = new NioEventLoopGroup();
+        nioExec = Executors.newFixedThreadPool(1);
+        minaTimerEx = Executors.newScheduledThreadPool(1);
     }
 
-    @After
-    public void tearDown() throws Exception {
+    @AfterClass
+    public static void tearDown() throws Exception {
         hashedWheelTimer.stop();
-        nettyGroup.shutdownGracefully();
+        nettyGroup.shutdownGracefully().await();
+        minaTimerEx.shutdownNow();
+        nioExec.shutdownNow();
     }
 
     @Test
     public void test() throws Exception {
         new Thread(new EchoServer(), "EchoServer").start();
-        AuthProvider authProvider = mock(AuthProvider.class);
-        doReturn(true).when(authProvider).authenticated(anyString(), anyString());
-        doReturn("auth").when(authProvider).toString();
-
-        NetconfSSHServer netconfSSHServer = NetconfSSHServer.start(10831, NetconfConfigUtil.getNetconfLocalAddress(),
-                new NioEventLoopGroup(), PEMGenerator.generate().toCharArray());
-        netconfSSHServer.setAuthProvider(authProvider);
 
-        InetSocketAddress address = netconfSSHServer.getLocalSocketAddress();
+        final InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 10831);
+        final SshProxyServer sshProxyServer = new SshProxyServer(minaTimerEx, nettyGroup, nioExec);
+        sshProxyServer.bind(addr, NetconfConfigUtil.getNetconfLocalAddress(),
+                new PasswordAuthenticator() {
+                    @Override
+                    public boolean authenticate(final String username, final String password, final ServerSession session) {
+                        return true;
+                    }
+                }, new PEMGeneratorHostKeyProvider(Files.createTempFile("prefix", "suffix").toAbsolutePath().toString()));
 
-        final EchoClientHandler echoClientHandler = connectClient(new InetSocketAddress("localhost", address.getPort()));
+        final EchoClientHandler echoClientHandler = connectClient(addr);
 
         Stopwatch stopwatch = new Stopwatch().start();
-        while(echoClientHandler.isConnected() == false && stopwatch.elapsed(TimeUnit.SECONDS) < 5) {
-            Thread.sleep(100);
+        while(echoClientHandler.isConnected() == false && stopwatch.elapsed(TimeUnit.SECONDS) < 30) {
+            Thread.sleep(500);
         }
         assertTrue(echoClientHandler.isConnected());
         logger.info("connected, writing to client");
         echoClientHandler.write(AHOJ);
+
         // check that server sent back the same string
         stopwatch = stopwatch.reset().start();
-        while (echoClientHandler.read().endsWith(AHOJ) == false && stopwatch.elapsed(TimeUnit.SECONDS) < 5) {
-            Thread.sleep(100);
+        while (echoClientHandler.read().endsWith(AHOJ) == false && stopwatch.elapsed(TimeUnit.SECONDS) < 30) {
+            Thread.sleep(500);
         }
+
         try {
-            String read = echoClientHandler.read();
+            final String read = echoClientHandler.read();
             assertTrue(read + " should end with " + AHOJ, read.endsWith(AHOJ));
         } finally {
             logger.info("Closing socket");
-            netconfSSHServer.close();
-            netconfSSHServer.join();
+            sshProxyServer.close();
         }
     }
 
-    public EchoClientHandler connectClient(InetSocketAddress address) {
+    public EchoClientHandler connectClient(final InetSocketAddress address) {
         final EchoClientHandler echoClientHandler = new EchoClientHandler();
-        ChannelInitializer<NioSocketChannel> channelInitializer = new ChannelInitializer<NioSocketChannel>() {
+        final ChannelInitializer<NioSocketChannel> channelInitializer = new ChannelInitializer<NioSocketChannel>() {
             @Override
-            public void initChannel(NioSocketChannel ch) throws Exception {
+            public void initChannel(final NioSocketChannel ch) throws Exception {
                 ch.pipeline().addFirst(AsyncSshHandler.createForNetconfSubsystem(new LoginPassword("a", "a")));
                 ch.pipeline().addLast(echoClientHandler);
             }
         };
-        Bootstrap b = new Bootstrap();
+        final Bootstrap b = new Bootstrap();
 
         b.group(nettyGroup)
                 .channel(NioSocketChannel.class)
@@ -114,9 +125,9 @@ public class SSHTest {
 
     @Test
     public void testClientWithoutServer() throws Exception {
-        InetSocketAddress address = new InetSocketAddress(12345);
+        final InetSocketAddress address = new InetSocketAddress(12345);
         final EchoClientHandler echoClientHandler = connectClient(address);
-        Stopwatch stopwatch = new Stopwatch().start();
+        final Stopwatch stopwatch = new Stopwatch().start();
         while(echoClientHandler.getState() == State.CONNECTING && stopwatch.elapsed(TimeUnit.SECONDS) < 5) {
             Thread.sleep(100);
         }
index 1151abcdf2767de1aa5a70fd19aa3219f9b23b1c..9cd0c9bceab59cb56df91afbb2c26c8421eeea4b 100644 (file)
@@ -12,19 +12,26 @@ import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 
-import ch.ethz.ssh2.Connection;
 import io.netty.channel.EventLoopGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
-import java.io.InputStream;
 import java.net.InetSocketAddress;
-import junit.framework.Assert;
-import org.apache.commons.io.IOUtils;
+import java.nio.file.Files;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.future.AuthFuture;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerSession;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.opendaylight.controller.netconf.auth.AuthProvider;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
 import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceListener;
@@ -39,13 +46,15 @@ public class SSHServerTest {
     private static final String PASSWORD = "netconf";
     private static final String HOST = "127.0.0.1";
     private static final int PORT = 1830;
-    private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 8383);
     private static final Logger logger = LoggerFactory.getLogger(SSHServerTest.class);
-    private Thread sshServerThread;
+
+    private SshProxyServer server;
 
     @Mock
     private BundleContext mockedContext;
-
+    private final ExecutorService nioExec = Executors.newFixedThreadPool(1);
+    private final EventLoopGroup clientGroup = new NioEventLoopGroup();
+    private final ScheduledExecutorService minaTimerEx = Executors.newScheduledThreadPool(1);
 
     @Before
     public void setUp() throws Exception {
@@ -55,42 +64,39 @@ public class SSHServerTest {
         doReturn(new ServiceReference[0]).when(mockedContext).getServiceReferences(anyString(), anyString());
 
         logger.info("Creating SSH server");
-        String pem;
-        try (InputStream is = getClass().getResourceAsStream("/RSA.pk")) {
-            pem = IOUtils.toString(is);
-        }
 
-
-        EventLoopGroup bossGroup = new NioEventLoopGroup();
-        NetconfSSHServer server = NetconfSSHServer.start(PORT, NetconfConfigUtil.getNetconfLocalAddress(),
-                bossGroup, pem.toCharArray());
-        server.setAuthProvider(new AuthProvider() {
-            @Override
-            public boolean authenticated(final String username, final String password) {
-                return true;
-            }
-        });
-
-        sshServerThread = new Thread(server);
-        sshServerThread.setDaemon(true);
-        sshServerThread.start();
-        logger.info("SSH server on " + PORT);
+        final InetSocketAddress addr = InetSocketAddress.createUnresolved(HOST, PORT);
+        server = new SshProxyServer(minaTimerEx, clientGroup, nioExec);
+        server.bind(addr, NetconfConfigUtil.getNetconfLocalAddress(),
+                new PasswordAuthenticator() {
+                    @Override
+                    public boolean authenticate(final String username, final String password, final ServerSession session) {
+                        return true;
+                    }
+                }, new PEMGeneratorHostKeyProvider(Files.createTempFile("prefix", "suffix").toAbsolutePath().toString()));
+        logger.info("SSH server started on " + PORT);
     }
 
     @Test
-    public void connect() {
+    public void connect() throws Exception {
+        final SshClient sshClient = SshClient.setUpDefaultClient();
+        sshClient.start();
         try {
-            Connection conn = new Connection(HOST, PORT);
-            Assert.assertNotNull(conn);
-            logger.info("connecting to SSH server");
-            conn.connect();
-            logger.info("authenticating ...");
-            boolean isAuthenticated = conn.authenticateWithPassword(USER, PASSWORD);
-            Assert.assertTrue(isAuthenticated);
-        } catch (Exception e) {
-            logger.error("Error while starting SSH server.", e);
+            final ConnectFuture connect = sshClient.connect(USER, HOST, PORT);
+            connect.await(30, TimeUnit.SECONDS);
+            org.junit.Assert.assertTrue(connect.isConnected());
+            final ClientSession session = connect.getSession();
+            session.addPasswordIdentity(PASSWORD);
+            final AuthFuture auth = session.auth();
+            auth.await(30, TimeUnit.SECONDS);
+            org.junit.Assert.assertTrue(auth.isSuccess());
+        } finally {
+            sshClient.close(true);
+            server.close();
+            clientGroup.shutdownGracefully().await();
+            minaTimerEx.shutdownNow();
+            nioExec.shutdownNow();
         }
-
     }
 
 }
index d0939a288f3ee0bb85915675e64f441165af8731..e8ba769da547f552dc3fe47aaa9a6e3f35ad0d54 100644 (file)
@@ -19,13 +19,13 @@ import com.google.common.collect.Sets;
 import com.google.common.io.CharStreams;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.local.LocalAddress;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.util.HashedWheelTimer;
 import java.io.Closeable;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -34,6 +34,8 @@ import java.net.Inet4Address;
 import java.net.InetSocketAddress;
 import java.net.URI;
 import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.AbstractMap;
 import java.util.Date;
 import java.util.HashMap;
@@ -42,8 +44,15 @@ import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 import org.antlr.v4.runtime.ParserRuleContext;
 import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.apache.sshd.common.util.ThreadUtils;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerSession;
 import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
 import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
 import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
@@ -58,8 +67,7 @@ import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
 import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService;
-import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
-import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+import org.opendaylight.controller.netconf.ssh.SshProxyServer;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
@@ -85,14 +93,22 @@ public class NetconfDeviceSimulator implements Closeable {
     private final NioEventLoopGroup nettyThreadgroup;
     private final HashedWheelTimer hashedWheelTimer;
     private final List<Channel> devicesChannels = Lists.newArrayList();
+    private final List<SshProxyServer> sshWrappers = Lists.newArrayList();
+    private final ScheduledExecutorService minaTimerExecutor;
+    private final ExecutorService nioExecutor;
 
     public NetconfDeviceSimulator() {
-        this(new NioEventLoopGroup(), new HashedWheelTimer());
+        // TODO make pool size configurable
+        this(new NioEventLoopGroup(), new HashedWheelTimer(),
+                Executors.newScheduledThreadPool(8, new ThreadFactoryBuilder().setNameFormat("netconf-ssh-server-mina-timers-%d").build()),
+                ThreadUtils.newFixedThreadPool("netconf-ssh-server-nio-group", 8));
     }
 
-    public NetconfDeviceSimulator(final NioEventLoopGroup eventExecutors, final HashedWheelTimer hashedWheelTimer) {
+    private NetconfDeviceSimulator(final NioEventLoopGroup eventExecutors, final HashedWheelTimer hashedWheelTimer, final ScheduledExecutorService minaTimerExecutor, final ExecutorService nioExecutor) {
         this.nettyThreadgroup = eventExecutors;
         this.hashedWheelTimer = hashedWheelTimer;
+        this.minaTimerExecutor = minaTimerExecutor;
+        this.nioExecutor = nioExecutor;
     }
 
     private NetconfServerDispatcher createDispatcher(final Map<ModuleBuilder, String> moduleBuilders, final boolean exi, final int generateConfigsTimeout) {
@@ -162,17 +178,31 @@ public class NetconfDeviceSimulator implements Closeable {
         int currentPort = params.startingPort;
 
         final List<Integer> openDevices = Lists.newArrayList();
+
+        // Generate key to temp folder
+        final PEMGeneratorHostKeyProvider keyPairProvider = getPemGeneratorHostKeyProvider();
+
         for (int i = 0; i < params.deviceCount; i++) {
             final InetSocketAddress address = getAddress(currentPort);
 
             final ChannelFuture server;
             if(params.ssh) {
+                final InetSocketAddress bindingAddress = InetSocketAddress.createUnresolved("0.0.0.0", currentPort);
                 final LocalAddress tcpLocalAddress = new LocalAddress(address.toString());
 
                 server = dispatcher.createLocalServer(tcpLocalAddress);
                 try {
-                    final NetconfSSHServer sshServer = NetconfSSHServer.start(currentPort, tcpLocalAddress, nettyThreadgroup, getPemArray());
-                    sshServer.setAuthProvider(new AcceptingAuthProvider());
+                    final SshProxyServer sshServer = new SshProxyServer(minaTimerExecutor, nettyThreadgroup, nioExecutor);
+                    sshServer.bind(bindingAddress, tcpLocalAddress,
+                            new PasswordAuthenticator() {
+                                @Override
+                                public boolean authenticate(final String username, final String password, final ServerSession session) {
+                                    // All connections are accepted
+                                    return true;
+                                }
+                            }, keyPairProvider);
+
+                    sshWrappers.add(sshServer);
                 } catch (final Exception e) {
                     LOG.warn("Cannot start simulated device on {}, skipping", address, e);
                     // Close local server and continue
@@ -225,10 +255,12 @@ public class NetconfDeviceSimulator implements Closeable {
         return openDevices;
     }
 
-    private char[] getPemArray() {
+    private PEMGeneratorHostKeyProvider getPemGeneratorHostKeyProvider() {
         try {
-            return PEMGenerator.readOrGeneratePK(new File("PK")).toCharArray();
+            final Path tempFile = Files.createTempFile("tempKeyNetconfTest", "suffix");
+            return new PEMGeneratorHostKeyProvider(tempFile.toAbsolutePath().toString());
         } catch (final IOException e) {
+            LOG.error("Unable to generate PEM key", e);
             throw new RuntimeException(e);
         }
     }
@@ -317,10 +349,15 @@ public class NetconfDeviceSimulator implements Closeable {
 
     @Override
     public void close() {
+        for (final SshProxyServer sshWrapper : sshWrappers) {
+            sshWrapper.close();
+        }
         for (final Channel deviceCh : devicesChannels) {
             deviceCh.close();
         }
         nettyThreadgroup.shutdownGracefully();
+        minaTimerExecutor.shutdownNow();
+        nioExecutor.shutdownNow();
         // close Everything
     }
 
index 78efe7e9723154bd218bafa5d248af0329fb567d..3c63204881aa9ea0ef3e0909ad302e73187d5b31 100644 (file)
@@ -37,6 +37,8 @@ import org.xml.sax.SAXException;
 
 public final class XmlElement {
 
+    public static final String DEFAULT_NAMESPACE_PREFIX = "";
+
     private final Element element;
     private static final Logger logger = LoggerFactory.getLogger(XmlElement.class);
 
@@ -72,16 +74,16 @@ public final class XmlElement {
         return xmlElement;
     }
 
-    private static Map<String, String> extractNamespaces(Element typeElement) throws NetconfDocumentedException {
+    private Map<String, String> extractNamespaces() throws NetconfDocumentedException {
         Map<String, String> namespaces = new HashMap<>();
-        NamedNodeMap attributes = typeElement.getAttributes();
+        NamedNodeMap attributes = element.getAttributes();
         for (int i = 0; i < attributes.getLength(); i++) {
             Node attribute = attributes.item(i);
             String attribKey = attribute.getNodeName();
             if (attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
                 String prefix;
                 if (attribKey.equals(XmlUtil.XMLNS_ATTRIBUTE_KEY)) {
-                    prefix = "";
+                    prefix = DEFAULT_NAMESPACE_PREFIX;
                 } else {
                     if (!attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY + ":")){
                         throw new NetconfDocumentedException("Attribute doesn't start with :",
@@ -94,6 +96,15 @@ public final class XmlElement {
                 namespaces.put(prefix, attribute.getNodeValue());
             }
         }
+
+        // namespace does not have to be defined on this element but inherited
+        if(!namespaces.containsKey(DEFAULT_NAMESPACE_PREFIX)) {
+            Optional<String> namespaceOptionally = getNamespaceOptionally();
+            if(namespaceOptionally.isPresent()) {
+                namespaces.put(DEFAULT_NAMESPACE_PREFIX, namespaceOptionally.get());
+            }
+        }
+
         return namespaces;
     }
 
@@ -132,7 +143,7 @@ public final class XmlElement {
     }
 
     public String getName() {
-        if (element.getLocalName()!=null && !element.getLocalName().equals("")){
+        if (element.getLocalName()!=null && !element.getLocalName().equals(DEFAULT_NAMESPACE_PREFIX)){
             return element.getLocalName();
         }
         return element.getTagName();
@@ -327,7 +338,7 @@ public final class XmlElement {
     public String getTextContent() throws NetconfDocumentedException {
         NodeList childNodes = element.getChildNodes();
         if (childNodes.getLength() == 0) {
-            return "";
+            return DEFAULT_NAMESPACE_PREFIX;
         }
         for(int i = 0; i < childNodes.getLength(); i++) {
             Node textChild = childNodes.item(i);
@@ -356,7 +367,7 @@ public final class XmlElement {
 
     public String getNamespaceAttribute() throws MissingNameSpaceException {
         String attribute = element.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY);
-        if (attribute == null || attribute.equals("")){
+        if (attribute == null || attribute.equals(DEFAULT_NAMESPACE_PREFIX)){
             throw new MissingNameSpaceException(String.format("Element %s must specify namespace",
                     toString()),
                     NetconfDocumentedException.ErrorType.application,
@@ -415,14 +426,14 @@ public final class XmlElement {
      * is found value will be null.
      */
     public Map.Entry<String/* prefix */, String/* namespace */> findNamespaceOfTextContent() throws NetconfDocumentedException {
-        Map<String, String> namespaces = extractNamespaces(element);
+        Map<String, String> namespaces = extractNamespaces();
         String textContent = getTextContent();
         int indexOfColon = textContent.indexOf(':');
         String prefix;
         if (indexOfColon > -1) {
             prefix = textContent.substring(0, indexOfColon);
         } else {
-            prefix = "";
+            prefix = DEFAULT_NAMESPACE_PREFIX;
         }
         if (!namespaces.containsKey(prefix)) {
             throw new IllegalArgumentException("Cannot find namespace for " + XmlUtil.toString(element) + ". Prefix from content is "
index 361f4cfa0866596233f7101b1eb09b13e126fde1..b392c5b6726ea2e3ed3c8fc63f943a9bed75dacc 100644 (file)
@@ -38,6 +38,8 @@
     <module>netconf-auth</module>
     <module>netconf-usermanager</module>
     <module>netconf-testtool</module>
+
+    <module>netconf-artifacts</module>
   </modules>
 
   <dependencies>
diff --git a/pom.xml b/pom.xml
index bb8ad1dbeba6459a1b9e5acb90070febc1b5a72a..53ef5601bb357de5ed1c8061a9433ef9211e4769 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -53,8 +53,7 @@
     <!-- <module>third-party/net.sf.jung2</module> -->
     <!-- <module>third-party/jersey-servlet</module> -->
     <!-- <module>third-party/org.apache.catalina.filters.CorsFilter</module> -->
-    <module>third-party/ganymed</module>
-
+    
     <module>third-party/commons/thirdparty</module>
 
     <!-- SAL bundles -->
diff --git a/third-party/ganymed/pom.xml b/third-party/ganymed/pom.xml
deleted file mode 100644 (file)
index 676e2a2..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>org.opendaylight.controller</groupId>
-        <artifactId>commons.thirdparty</artifactId>
-        <version>1.2.0-SNAPSHOT</version>
-        <relativePath>../commons/thirdparty</relativePath>
-    </parent>
-
-    <groupId>org.opendaylight.controller.thirdparty</groupId>
-    <artifactId>ganymed</artifactId>
-    <version>1.2.0-SNAPSHOT</version>
-    <packaging>bundle</packaging>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.core</artifactId>
-            <version>5.0.0</version>
-        </dependency>
-        <dependency>
-            <groupId>ch.ethz.ganymed</groupId>
-            <artifactId>ganymed-ssh2</artifactId>
-            <version>261</version>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <configuration>
-                    <instructions>
-                        <Export-Package>ch.ethz.ssh2.*</Export-Package>
-                        <Embed-Dependency>ganymed-ssh2;scope=compile</Embed-Dependency>
-                        <Embed-Transitive>true</Embed-Transitive>
-                    </instructions>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-enforcer-plugin</artifactId>
-                <version>${enforcer.version}</version>
-                <executions>
-                    <execution>
-                        <id>enforce-no-snapshots</id>
-                        <goals>
-                            <goal>enforce</goal>
-                        </goals>
-                        <configuration>
-                            <rules>
-                                <bannedDependencies>
-                                    <excludes>
-                                        <exclude>ch.ethz.ganymed:ganymed-ssh2:*</exclude>
-                                    </excludes>
-                                    <includes>
-                                        <include>ch.ethz.ganymed:ganymed-ssh2:[261]</include>
-                                    </includes>
-                                </bannedDependencies>
-                            </rules>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
-</project>
-
-
diff --git a/third-party/ganymed/src/main/java/ch/ethz/ssh2/Connection.java b/third-party/ganymed/src/main/java/ch/ethz/ssh2/Connection.java
deleted file mode 100644 (file)
index aa13c40..0000000
+++ /dev/null
@@ -1,1424 +0,0 @@
-/*
- * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
- * Please refer to the LICENSE.txt for licensing details.
- */
-
-package ch.ethz.ssh2;
-
-import java.io.CharArrayWriter;
-import java.io.File;
-import java.net.Socket;
-import java.io.FileReader;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.SocketTimeoutException;
-import java.security.SecureRandom;
-import java.util.List;
-import java.util.Vector;
-
-import ch.ethz.ssh2.auth.AuthenticationManager;
-import ch.ethz.ssh2.channel.ChannelManager;
-import ch.ethz.ssh2.crypto.CryptoWishList;
-import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
-import ch.ethz.ssh2.crypto.digest.MAC;
-import ch.ethz.ssh2.packets.PacketIgnore;
-import ch.ethz.ssh2.transport.KexManager;
-import ch.ethz.ssh2.transport.TransportManager;
-import ch.ethz.ssh2.util.TimeoutService;
-import ch.ethz.ssh2.util.TimeoutService.TimeoutToken;
-
-/**
- * A <code>Connection</code> is used to establish an encrypted TCP/IP
- * connection to a SSH-2 server.
- * <p>
- * Typically, one
- * <ol>
- * <li>creates a {@link #Connection(String) Connection} object.</li>
- * <li>calls the {@link #connect() connect()} method.</li>
- * <li>calls some of the authentication methods (e.g., {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
- * <li>calls one or several times the {@link #openSession() openSession()} method.</li>
- * <li>finally, one must close the connection and release resources with the {@link #close() close()} method.</li>
- * </ol>
- *
- * @author Christian Plattner
- * @version $Id: Connection.java 69 2013-08-09 06:39:56Z dkocher@sudo.ch $
- */
-
-public class Connection
-{
-    /**
-     * The identifier presented to the SSH-2 server. This is the same
-     * as the "softwareversion" defined in RFC 4253.
-     * <p/>
-     * <b>NOTE: As per the RFC, the "softwareversion" string MUST consist of printable
-     * US-ASCII characters, with the exception of whitespace characters and the minus sign (-).</b>
-     */
-    private String softwareversion = String.format("Ganymed_%s", Version.getSpecification());
-
-       /* Will be used to generate all random data needed for the current connection.
-        * Note: SecureRandom.nextBytes() is thread safe.
-        */
-
-    private SecureRandom generator;
-
-    private Socket precreatedSocket;
-
-    public Connection(Socket socket) {
-        this.precreatedSocket = socket;
-        this.hostname = socket.getInetAddress().getHostName();
-        this.port = socket.getPort();
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @return The list of supported cipher algorithms by this implementation.
-     */
-    public static synchronized String[] getAvailableCiphers()
-    {
-        return BlockCipherFactory.getDefaultCipherList();
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @return The list of supported MAC algorthims by this implementation.
-     */
-    public static synchronized String[] getAvailableMACs()
-    {
-        return MAC.getMacList();
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @return The list of supported server host key algorthims by this implementation.
-     */
-    public static synchronized String[] getAvailableServerHostKeyAlgorithms()
-    {
-        return KexManager.getDefaultServerHostkeyAlgorithmList();
-    }
-
-    private AuthenticationManager am;
-
-    private boolean authenticated = false;
-    private ChannelManager cm;
-
-    private CryptoWishList cryptoWishList = new CryptoWishList();
-
-    private DHGexParameters dhgexpara = new DHGexParameters();
-
-    private final String hostname;
-
-    private final int port;
-
-    private TransportManager tm;
-
-    private boolean tcpNoDelay = false;
-
-    private ProxyData proxyData = null;
-
-    private List<ConnectionMonitor> connectionMonitors = new Vector<ConnectionMonitor>();
-
-    /**
-     * Prepares a fresh <code>Connection</code> object which can then be used
-     * to establish a connection to the specified SSH-2 server.
-     * <p>
-     * Same as {@link #Connection(String, int) Connection(hostname, 22)}.
-     *
-     * @param hostname the hostname of the SSH-2 server.
-     */
-    public Connection(String hostname)
-    {
-        this(hostname, 22);
-    }
-
-    /**
-     * Prepares a fresh <code>Connection</code> object which can then be used
-     * to establish a connection to the specified SSH-2 server.
-     *
-     * @param hostname
-     *            the host where we later want to connect to.
-     * @param port
-     *            port on the server, normally 22.
-     */
-    public Connection(String hostname, int port)
-    {
-        this.hostname = hostname;
-        this.port = port;
-    }
-
-    /**
-     * Prepares a fresh <code>Connection</code> object which can then be used
-     * to establish a connection to the specified SSH-2 server.
-     *
-     * @param hostname
-     *            the host where we later want to connect to.
-     * @param port
-     *            port on the server, normally 22.
-     * @param softwareversion
-     *                         Allows you to set a custom "softwareversion" string as defined in RFC 4253.
-     *                         <b>NOTE: As per the RFC, the "softwareversion" string MUST consist of printable
-     *          US-ASCII characters, with the exception of whitespace characters and the minus sign (-).</b>
-     */
-    public Connection(String hostname, int port, String softwareversion)
-    {
-        this.hostname = hostname;
-        this.port = port;
-        this.softwareversion = softwareversion;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * is based on DSA (it uses DSA to sign a challenge sent by the server).
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param pem
-     *            A <code>String</code> containing the DSA private key of the
-     *            user in OpenSSH key format (PEM, you can't miss the
-     *            "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain
-     *            linefeeds.
-     * @param password
-     *            If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you
-     *            must specify the password. Otherwise, this argument will be
-     *            ignored and can be set to <code>null</code>.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     *
-     * @deprecated You should use one of the {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
-     *                       methods, this method is just a wrapper for it and will
-     *            disappear in future builds.
-     *
-     */
-    public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        if (pem == null)
-            throw new IllegalArgumentException("pem argument is null");
-
-        authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND());
-
-        return authenticated;
-    }
-
-    /**
-     * A wrapper that calls {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
-     * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod list.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param cb
-     *            An <code>InteractiveCallback</code> which will be used to
-     *            determine the responses to the questions asked by the server.
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)
-            throws IOException
-    {
-        return authenticateWithKeyboardInteractive(user, null, cb);
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * is based on "keyboard-interactive", specified in
-     * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a
-     * callback object which will be feeded with challenges generated by the
-     * server. Answers are then sent back to the server. It is possible that the
-     * callback will be called several times during the invocation of this
-     * method (e.g., if the server replies to the callback's answer(s) with
-     * another challenge...)
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     * <p>
-     * Note: some SSH servers advertise "keyboard-interactive", however, any
-     * interactive request will be denied (without having sent any challenge to
-     * the client).
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param submethods
-     *            An array of submethod names, see
-     *            draft-ietf-secsh-auth-kbdinteract-XX. May be <code>null</code>
-     *            to indicate an empty list.
-     * @param cb
-     *            An <code>InteractiveCallback</code> which will be used to
-     *            determine the responses to the questions asked by the server.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods,
-                                                                    InteractiveCallback cb) throws IOException
-    {
-        if (cb == null)
-            throw new IllegalArgumentException("Callback may not ne NULL!");
-
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        authenticated = am.authenticateInteractive(user, submethods, cb);
-
-        return authenticated;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * sends username and password to the server.
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     * <p>
-     * Note: if this method fails, then please double-check that it is actually
-     * offered by the server (use {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
-     * <p>
-     * Often, password authentication is disabled, but users are not aware of it.
-     * Many servers only offer "publickey" and "keyboard-interactive". However,
-     * even though "keyboard-interactive" *feels* like password authentication
-     * (e.g., when using the putty or openssh clients) it is *not* the same mechanism.
-     *
-     * @param user
-     * @param password
-     * @return if the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithPassword(String user, String password) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        if (password == null)
-            throw new IllegalArgumentException("password argument is null");
-
-        authenticated = am.authenticatePassword(user, password);
-
-        return authenticated;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself.
-     * This method can be used to explicitly use the special "none"
-     * authentication method (where only a username has to be specified).
-     * <p>
-     * Note 1: The "none" method may always be tried by clients, however as by
-     * the specs, the server will not explicitly announce it. In other words,
-     * the "none" token will never show up in the list returned by
-     * {@link #getRemainingAuthMethods(String)}.
-     * <p>
-     * Note 2: no matter which one of the authenticateWithXXX() methods
-     * you call, the library will always issue exactly one initial "none"
-     * authentication request to retrieve the initially allowed list of
-     * authentication methods by the server. Please read RFC 4252 for the
-     * details.
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If further authentication steps are needed, <code>false</code>
-     * is returned and one can retry by any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     *
-     * @param user
-     * @return if the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithNone(String user) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-               /* Trigger the sending of the PacketUserauthRequestNone packet */
-               /* (if not already done)                                       */
-
-        authenticated = am.authenticateNone(user);
-
-        return authenticated;
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself.
-     * The authentication method "publickey" works by signing a challenge
-     * sent by the server. The signature is either DSA or RSA based - it
-     * just depends on the type of private key you specify, either a DSA
-     * or RSA private key in PEM format. And yes, this is may seem to be a
-     * little confusing, the method is called "publickey" in the SSH-2 protocol
-     * specification, however since we need to generate a signature, you
-     * actually have to supply a private key =).
-     * <p>
-     * The private key contained in the PEM file may also be encrypted ("Proc-Type: 4,ENCRYPTED").
-     * The library supports DES-CBC and DES-EDE3-CBC encryption, as well
-     * as the more exotic PEM encrpytions AES-128-CBC, AES-192-CBC and AES-256-CBC.
-     * <p>
-     * If the authentication phase is complete, <code>true</code> will be
-     * returned. If the server does not accept the request (or if further
-     * authentication steps are needed), <code>false</code> is returned and
-     * one can retry either by using this or any other authentication method
-     * (use the <code>getRemainingAuthMethods</code> method to get a list of
-     * the remaining possible methods).
-     * <p>
-     * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
-     * it is not in the expected format. You have to convert it to the OpenSSH
-     * key format by using the "puttygen" tool (can be downloaded from the Putty
-     * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
-     * functionality to get a proper PEM file.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param pemPrivateKey
-     *            A <code>char[]</code> containing a DSA or RSA private key of the
-     *            user in OpenSSH key format (PEM, you can't miss the
-     *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
-     *            tag). The char array may contain linebreaks/linefeeds.
-     * @param password
-     *            If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED") then
-     *            you must specify a password. Otherwise, this argument will be ignored
-     *            and can be set to <code>null</code>.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)
-            throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        if (user == null)
-            throw new IllegalArgumentException("user argument is null");
-
-        if (pemPrivateKey == null)
-            throw new IllegalArgumentException("pemPrivateKey argument is null");
-
-        authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND());
-
-        return authenticated;
-    }
-
-    /**
-     * A convenience wrapper function which reads in a private key (PEM format, either DSA or RSA)
-     * and then calls <code>authenticateWithPublicKey(String, char[], String)</code>.
-     * <p>
-     * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
-     * it is not in the expected format. You have to convert it to the OpenSSH
-     * key format by using the "puttygen" tool (can be downloaded from the Putty
-     * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
-     * functionality to get a proper PEM file.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param pemFile
-     *            A <code>File</code> object pointing to a file containing a DSA or RSA
-     *            private key of the user in OpenSSH key format (PEM, you can't miss the
-     *            "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
-     *            tag).
-     * @param password
-     *            If the PEM file is encrypted then you must specify the password.
-     *            Otherwise, this argument will be ignored and can be set to <code>null</code>.
-     *
-     * @return whether the connection is now authenticated.
-     * @throws IOException
-     */
-    public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password)
-            throws IOException
-    {
-        if (pemFile == null)
-            throw new IllegalArgumentException("pemFile argument is null");
-
-        char[] buff = new char[256];
-
-        CharArrayWriter cw = new CharArrayWriter();
-
-        FileReader fr = new FileReader(pemFile);
-
-        while (true)
-        {
-            int len = fr.read(buff);
-            if (len < 0)
-                break;
-            cw.write(buff, 0, len);
-        }
-
-        fr.close();
-
-        return authenticateWithPublicKey(user, cw.toCharArray(), password);
-    }
-
-    /**
-     * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any time,
-     * but it is best to add connection monitors before invoking
-     * <code>connect()</code> to avoid glitches (e.g., you add a connection monitor after
-     * a successful connect(), but the connection has died in the mean time. Then,
-     * your connection monitor won't be notified.)
-     * <p>
-     * You can add as many monitors as you like. If a monitor has already been added, then
-     * this method does nothing.
-     *
-     * @see ConnectionMonitor
-     *
-     * @param cmon An object implementing the {@link ConnectionMonitor} interface.
-     */
-    public synchronized void addConnectionMonitor(ConnectionMonitor cmon)
-    {
-        if (cmon == null)
-            throw new IllegalArgumentException("cmon argument is null");
-
-        if (!connectionMonitors.contains(cmon))
-        {
-            connectionMonitors.add(cmon);
-
-            if (tm != null)
-                tm.setConnectionMonitors(connectionMonitors);
-        }
-    }
-
-    /**
-     * Remove a {@link ConnectionMonitor} from this connection.
-     *
-     * @param cmon
-     * @return whether the monitor could be removed
-     */
-    public synchronized boolean removeConnectionMonitor(ConnectionMonitor cmon)
-    {
-        if (cmon == null)
-            throw new IllegalArgumentException("cmon argument is null");
-
-        boolean existed = connectionMonitors.remove(cmon);
-
-        if (tm != null)
-            tm.setConnectionMonitors(connectionMonitors);
-
-        return existed;
-    }
-
-    /**
-     * Close the connection to the SSH-2 server. All assigned sessions will be
-     * closed, too. Can be called at any time. Don't forget to call this once
-     * you don't need a connection anymore - otherwise the receiver thread may
-     * run forever.
-     */
-    public synchronized void close()
-    {
-        Throwable t = new Throwable("Closed due to user request.");
-        close(t, false);
-    }
-
-    public synchronized void close(Throwable t, boolean hard)
-    {
-        if (cm != null)
-            cm.closeAllChannels();
-
-        if (tm != null)
-        {
-            tm.close(t, hard == false);
-            tm = null;
-        }
-        am = null;
-        cm = null;
-        authenticated = false;
-    }
-
-    /**
-     * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
-     *
-     * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
-     * @throws IOException
-     */
-    public synchronized ConnectionInfo connect() throws IOException
-    {
-        return connect(null, 0, 0);
-    }
-
-    /**
-     * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
-     *
-     * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
-     * @throws IOException
-     */
-    public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException
-    {
-        return connect(verifier, 0, 0);
-    }
-
-    /**
-     * Connect to the SSH-2 server and, as soon as the server has presented its
-     * host key, use the {@link ServerHostKeyVerifier#verifyServerHostKey(String,
-     * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()}
-     * method of the <code>verifier</code> to ask for permission to proceed.
-     * If <code>verifier</code> is <code>null</code>, then any host key will be
-     * accepted - this is NOT recommended, since it makes man-in-the-middle attackes
-     * VERY easy (somebody could put a proxy SSH server between you and the real server).
-     * <p>
-     * Note: The verifier will be called before doing any crypto calculations
-     * (i.e., diffie-hellman). Therefore, if you don't like the presented host key then
-     * no CPU cycles are wasted (and the evil server has less information about us).
-     * <p>
-     * However, it is still possible that the server presented a fake host key: the server
-     * cheated (typically a sign for a man-in-the-middle attack) and is not able to generate
-     * a signature that matches its host key. Don't worry, the library will detect such
-     * a scenario later when checking the signature (the signature cannot be checked before
-     * having completed the diffie-hellman exchange).
-     * <p>
-     * Note 2: The  {@link ServerHostKeyVerifier#verifyServerHostKey(String,
-     * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method
-     * will *NOT* be called from the current thread, the call is being made from a
-     * background thread (there is a background dispatcher thread for every
-     * established connection).
-     * <p>
-     * Note 3: This method will block as long as the key exchange of the underlying connection
-     * has not been completed (and you have not specified any timeouts).
-     * <p>
-     * Note 4: If you want to re-use a connection object that was successfully connected,
-     * then you must call the {@link #close()} method before invoking <code>connect()</code> again.
-     *
-     * @param verifier
-     *            An object that implements the
-     *            {@link ServerHostKeyVerifier} interface. Pass <code>null</code>
-     *            to accept any server host key - NOT recommended.
-     *
-     * @param connectTimeout
-     *            Connect the underlying TCP socket to the server with the given timeout
-     *            value (non-negative, in milliseconds). Zero means no timeout. If a proxy is being
-     *            used (see {@link #setProxyData(ProxyData)}), then this timeout is used for the
-     *            connection establishment to the proxy.
-     *
-     * @param kexTimeout
-     *            Timeout for complete connection establishment (non-negative,
-     *            in milliseconds). Zero means no timeout. The timeout counts from the
-     *            moment you invoke the connect() method and is cancelled as soon as the
-     *            first key-exchange round has finished. It is possible that
-     *            the timeout event will be fired during the invocation of the
-     *            <code>verifier</code> callback, but it will only have an effect after
-     *            the <code>verifier</code> returns.
-     *
-     * @return A {@link ConnectionInfo} object containing the details of
-     *            the established connection.
-     *
-     * @throws IOException
-     *            If any problem occurs, e.g., the server's host key is not
-     *            accepted by the <code>verifier</code> or there is problem during
-     *            the initial crypto setup (e.g., the signature sent by the server is wrong).
-     *            <p>
-     *            In case of a timeout (either connectTimeout or kexTimeout)
-     *            a SocketTimeoutException is thrown.
-     *            <p>
-     *            An exception may also be thrown if the connection was already successfully
-     *            connected (no matter if the connection broke in the mean time) and you invoke
-     *            <code>connect()</code> again without having called {@link #close()} first.
-     *            <p>
-     *            If a HTTP proxy is being used and the proxy refuses the connection,
-     *            then a {@link HTTPProxyException} may be thrown, which
-     *            contains the details returned by the proxy. If the proxy is buggy and does
-     *            not return a proper HTTP response, then a normal IOException is thrown instead.
-     */
-    public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
-            throws IOException
-    {
-        final class TimeoutState
-        {
-            boolean isCancelled = false;
-            boolean timeoutSocketClosed = false;
-        }
-
-        if (tm != null)
-            throw new IOException("Connection to " + hostname + " is already in connected state!");
-
-        if (connectTimeout < 0)
-            throw new IllegalArgumentException("connectTimeout must be non-negative!");
-
-        if (kexTimeout < 0)
-            throw new IllegalArgumentException("kexTimeout must be non-negative!");
-
-        final TimeoutState state = new TimeoutState();
-
-        tm = new TransportManager();
-        tm.setSoTimeout(connectTimeout);
-        tm.setConnectionMonitors(connectionMonitors);
-
-               /* Make sure that the runnable below will observe the new value of "tm"
-                * and "state" (the runnable will be executed in a different thread, which
-                * may be already running, that is why we need a memory barrier here).
-                * See also the comment in Channel.java if you
-                * are interested in the details.
-                * 
-                * OKOK, this is paranoid since adding the runnable to the todo list
-                * of the TimeoutService will ensure that all writes have been flushed
-                * before the Runnable reads anything
-                * (there is a synchronized block in TimeoutService.addTimeoutHandler).
-                */
-
-        synchronized (tm)
-        {
-                       /* We could actually synchronize on anything. */
-        }
-
-        try
-        {
-            TimeoutToken token = null;
-
-            if (kexTimeout > 0)
-            {
-                final Runnable timeoutHandler = new Runnable()
-                {
-                    public void run()
-                    {
-                        synchronized (state)
-                        {
-                            if (state.isCancelled)
-                                return;
-                            state.timeoutSocketClosed = true;
-                            tm.close(new SocketTimeoutException("The connect timeout expired"), false);
-                        }
-                    }
-                };
-
-                long timeoutHorizont = System.currentTimeMillis() + kexTimeout;
-
-                token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
-            }
-
-            try
-            {
-
-                if (precreatedSocket != null) {
-                    tm.clientInit(precreatedSocket, softwareversion, cryptoWishList, verifier, dhgexpara,
-                            getOrCreateSecureRND());
-                } else {
-                    tm.clientInit(hostname, port, softwareversion, cryptoWishList, verifier, dhgexpara, connectTimeout,
-                            getOrCreateSecureRND(), proxyData);
-                }
-            }
-            catch (SocketTimeoutException se)
-            {
-                throw (SocketTimeoutException) new SocketTimeoutException(
-                        "The connect() operation on the socket timed out.").initCause(se);
-            }
-
-            tm.setTcpNoDelay(tcpNoDelay);
-
-                       /* Wait until first KEX has finished */
-
-            ConnectionInfo ci = tm.getConnectionInfo(1);
-
-                       /* Now try to cancel the timeout, if needed */
-
-            if (token != null)
-            {
-                TimeoutService.cancelTimeoutHandler(token);
-
-                               /* Were we too late? */
-
-                synchronized (state)
-                {
-                    if (state.timeoutSocketClosed)
-                        throw new IOException("This exception will be replaced by the one below =)");
-                                       /* Just in case the "cancelTimeoutHandler" invocation came just a little bit
-                                        * too late but the handler did not enter the semaphore yet - we can
-                                        * still stop it.
-                                        */
-                    state.isCancelled = true;
-                }
-            }
-
-            return ci;
-        }
-        catch (SocketTimeoutException ste)
-        {
-            throw ste;
-        }
-        catch (IOException e1)
-        {
-                       /* This will also invoke any registered connection monitors */
-            close(new Throwable("There was a problem during connect."), false);
-
-            synchronized (state)
-            {
-                               /* Show a clean exception, not something like "the socket is closed!?!" */
-                if (state.timeoutSocketClosed)
-                    throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
-            }
-
-                       /* Do not wrap a HTTPProxyException */
-            if (e1 instanceof HTTPProxyException)
-                throw e1;
-
-            throw (IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port)
-                    .initCause(e1);
-        }
-    }
-
-    /**
-     * Creates a new {@link LocalPortForwarder}.
-     * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
-     * port via the secure tunnel to another host (which may or may not be
-     * identical to the remote SSH-2 server).
-     * <p>
-     * This method must only be called after one has passed successfully the authentication step.
-     * There is no limit on the number of concurrent forwardings.
-     *
-     * @param local_port the local port the LocalPortForwarder shall bind to.
-     * @param host_to_connect target address (IP or hostname)
-     * @param port_to_connect target port
-     * @return A {@link LocalPortForwarder} object.
-     * @throws IOException
-     */
-    public synchronized LocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect,
-                                                                    int port_to_connect) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
-        return new LocalPortForwarder(cm, local_port, host_to_connect, port_to_connect);
-    }
-
-    /**
-     * Creates a new {@link LocalPortForwarder}.
-     * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
-     * port via the secure tunnel to another host (which may or may not be
-     * identical to the remote SSH-2 server).
-     * <p>
-     * This method must only be called after one has passed successfully the authentication step.
-     * There is no limit on the number of concurrent forwardings.
-     *
-     * @param addr specifies the InetSocketAddress where the local socket shall be bound to.
-     * @param host_to_connect target address (IP or hostname)
-     * @param port_to_connect target port
-     * @return A {@link LocalPortForwarder} object.
-     * @throws IOException
-     */
-    public synchronized LocalPortForwarder createLocalPortForwarder(InetSocketAddress addr, String host_to_connect,
-                                                                    int port_to_connect) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
-
-        return new LocalPortForwarder(cm, addr, host_to_connect, port_to_connect);
-    }
-
-    /**
-     * Creates a new {@link LocalStreamForwarder}.
-     * A <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
-     * that is being forwarded via the secure tunnel into a TCP/IP connection to another host
-     * (which may or may not be identical to the remote SSH-2 server).
-     *
-     * @param host_to_connect
-     * @param port_to_connect
-     * @return A {@link LocalStreamForwarder} object.
-     * @throws IOException
-     */
-    public synchronized LocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect)
-            throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Cannot forward, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot forward, connection is not authenticated.");
-
-        return new LocalStreamForwarder(cm, host_to_connect, port_to_connect);
-    }
-
-    /**
-     * Create a very basic {@link SCPClient} that can be used to copy
-     * files from/to the SSH-2 server.
-     * <p>
-     * Works only after one has passed successfully the authentication step.
-     * There is no limit on the number of concurrent SCP clients.
-     * <p>
-     * Note: This factory method will probably disappear in the future.
-     *
-     * @return A {@link SCPClient} object.
-     * @throws IOException
-     */
-    public synchronized SCPClient createSCPClient() throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Cannot create SCP client, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot create SCP client, connection is not authenticated.");
-
-        return new SCPClient(this);
-    }
-
-    /**
-     * Force an asynchronous key re-exchange (the call does not block). The
-     * latest values set for MAC, Cipher and DH group exchange parameters will
-     * be used. If a key exchange is currently in progress, then this method has
-     * the only effect that the so far specified parameters will be used for the
-     * next (server driven) key exchange.
-     * <p>
-     * Note: This implementation will never start a key exchange (other than the initial one)
-     * unless you or the SSH-2 server ask for it.
-     *
-     * @throws IOException
-     *             In case of any failure behind the scenes.
-     */
-    public synchronized void forceKeyExchange() throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("You need to establish a connection first.");
-
-        tm.forceKeyExchange(cryptoWishList, dhgexpara, null, null);
-    }
-
-    /**
-     * Returns the hostname that was passed to the constructor.
-     *
-     * @return the hostname
-     */
-    public synchronized String getHostname()
-    {
-        return hostname;
-    }
-
-    /**
-     * Returns the port that was passed to the constructor.
-     *
-     * @return the TCP port
-     */
-    public synchronized int getPort()
-    {
-        return port;
-    }
-
-    /**
-     * Returns a {@link ConnectionInfo} object containing the details of
-     * the connection. Can be called as soon as the connection has been
-     * established (successfully connected).
-     *
-     * @return A {@link ConnectionInfo} object.
-     * @throws IOException
-     *             In case of any failure behind the scenes.
-     */
-    public synchronized ConnectionInfo getConnectionInfo() throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException(
-                    "Cannot get details of connection, you need to establish a connection first.");
-        return tm.getConnectionInfo(1);
-    }
-
-    /**
-     * After a successful connect, one has to authenticate oneself. This method
-     * can be used to tell which authentication methods are supported by the
-     * server at a certain stage of the authentication process (for the given
-     * username).
-     * <p>
-     * Note 1: the username will only be used if no authentication step was done
-     * so far (it will be used to ask the server for a list of possible
-     * authentication methods by sending the initial "none" request). Otherwise,
-     * this method ignores the user name and returns a cached method list
-     * (which is based on the information contained in the last negative server response).
-     * <p>
-     * Note 2: the server may return method names that are not supported by this
-     * implementation.
-     * <p>
-     * After a successful authentication, this method must not be called
-     * anymore.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     *
-     * @return a (possibly emtpy) array holding authentication method names.
-     * @throws IOException
-     */
-    public synchronized String[] getRemainingAuthMethods(String user) throws IOException
-    {
-        if (user == null)
-            throw new IllegalArgumentException("user argument may not be NULL!");
-
-        if (tm == null)
-            throw new IllegalStateException("Connection is not established!");
-
-        if (authenticated)
-            throw new IllegalStateException("Connection is already authenticated!");
-
-        if (am == null)
-            am = new AuthenticationManager(tm);
-
-        if (cm == null)
-            cm = new ChannelManager(tm);
-
-        return am.getRemainingMethods(user);
-    }
-
-    /**
-     * Determines if the authentication phase is complete. Can be called at any
-     * time.
-     *
-     * @return <code>true</code> if no further authentication steps are
-     *         needed.
-     */
-    public synchronized boolean isAuthenticationComplete()
-    {
-        return authenticated;
-    }
-
-    /**
-     * Returns true if there was at least one failed authentication request and
-     * the last failed authentication request was marked with "partial success"
-     * by the server. This is only needed in the rare case of SSH-2 server setups
-     * that cannot be satisfied with a single successful authentication request
-     * (i.e., multiple authentication steps are needed.)
-     * <p>
-     * If you are interested in the details, then have a look at RFC4252.
-     *
-     * @return if the there was a failed authentication step and the last one
-     *         was marked as a "partial success".
-     */
-    public synchronized boolean isAuthenticationPartialSuccess()
-    {
-        if (am == null)
-            return false;
-
-        return am.getPartialSuccess();
-    }
-
-    /**
-     * Checks if a specified authentication method is available. This method is
-     * actually just a wrapper for {@link #getRemainingAuthMethods(String)
-     * getRemainingAuthMethods()}.
-     *
-     * @param user
-     *            A <code>String</code> holding the username.
-     * @param method
-     *            An authentication method name (e.g., "publickey", "password",
-     *            "keyboard-interactive") as specified by the SSH-2 standard.
-     * @return if the specified authentication method is currently available.
-     * @throws IOException
-     */
-    public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException
-    {
-        if (method == null)
-            throw new IllegalArgumentException("method argument may not be NULL!");
-
-        String methods[] = getRemainingAuthMethods(user);
-
-        for (int i = 0; i < methods.length; i++)
-        {
-            if (methods[i].compareTo(method) == 0)
-                return true;
-        }
-
-        return false;
-    }
-
-    private SecureRandom getOrCreateSecureRND()
-    {
-        if (generator == null)
-            generator = new SecureRandom();
-
-        return generator;
-    }
-
-    /**
-     * Open a new {@link Session} on this connection. Works only after one has passed
-     * successfully the authentication step. There is no limit on the number of
-     * concurrent sessions.
-     *
-     * @return A {@link Session} object.
-     * @throws IOException
-     */
-    public synchronized Session openSession() throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("Cannot open session, you need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("Cannot open session, connection is not authenticated.");
-
-        return new Session(cm, getOrCreateSecureRND());
-    }
-
-    /**
-     * Send an SSH_MSG_IGNORE packet. This method will generate a random data attribute
-     * (length between 0 (invlusive) and 16 (exclusive) bytes, contents are random bytes).
-     * <p>
-     * This method must only be called once the connection is established.
-     *
-     * @throws IOException
-     */
-    public synchronized void sendIgnorePacket() throws IOException
-    {
-        SecureRandom rnd = getOrCreateSecureRND();
-
-        byte[] data = new byte[rnd.nextInt(16)];
-        rnd.nextBytes(data);
-
-        sendIgnorePacket(data);
-    }
-
-    /**
-     * Send an SSH_MSG_IGNORE packet with the given data attribute.
-     * <p>
-     * This method must only be called once the connection is established.
-     *
-     * @throws IOException
-     */
-    public synchronized void sendIgnorePacket(byte[] data) throws IOException
-    {
-        if (data == null)
-            throw new IllegalArgumentException("data argument must not be null.");
-
-        if (tm == null)
-            throw new IllegalStateException(
-                    "Cannot send SSH_MSG_IGNORE packet, you need to establish a connection first.");
-
-        PacketIgnore pi = new PacketIgnore();
-        pi.setData(data);
-
-        tm.sendMessage(pi.getPayload());
-    }
-
-    /**
-     * Removes duplicates from a String array, keeps only first occurence
-     * of each element. Does not destroy order of elements; can handle nulls.
-     * Uses a very efficient O(N^2) algorithm =)
-     *
-     * @param list a String array.
-     * @return a cleaned String array.
-     */
-    private String[] removeDuplicates(String[] list)
-    {
-        if ((list == null) || (list.length < 2))
-            return list;
-
-        String[] list2 = new String[list.length];
-
-        int count = 0;
-
-        for (int i = 0; i < list.length; i++)
-        {
-            boolean duplicate = false;
-
-            String element = list[i];
-
-            for (int j = 0; j < count; j++)
-            {
-                if (((element == null) && (list2[j] == null)) || ((element != null) && (element.equals(list2[j]))))
-                {
-                    duplicate = true;
-                    break;
-                }
-            }
-
-            if (duplicate)
-                continue;
-
-            list2[count++] = list[i];
-        }
-
-        if (count == list2.length)
-            return list2;
-
-        String[] tmp = new String[count];
-        System.arraycopy(list2, 0, tmp, 0, count);
-
-        return tmp;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param ciphers
-     */
-    public synchronized void setClient2ServerCiphers(String[] ciphers)
-    {
-        if ((ciphers == null) || (ciphers.length == 0))
-            throw new IllegalArgumentException();
-        ciphers = removeDuplicates(ciphers);
-        BlockCipherFactory.checkCipherList(ciphers);
-        cryptoWishList.c2s_enc_algos = ciphers;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param macs
-     */
-    public synchronized void setClient2ServerMACs(String[] macs)
-    {
-        if ((macs == null) || (macs.length == 0))
-            throw new IllegalArgumentException();
-        macs = removeDuplicates(macs);
-        MAC.checkMacList(macs);
-        cryptoWishList.c2s_mac_algos = macs;
-    }
-
-    /**
-     * Sets the parameters for the diffie-hellman group exchange. Unless you
-     * know what you are doing, you will never need this. Default values are
-     * defined in the {@link DHGexParameters} class.
-     *
-     * @param dgp {@link DHGexParameters}, non null.
-     *
-     */
-    public synchronized void setDHGexParameters(DHGexParameters dgp)
-    {
-        if (dgp == null)
-            throw new IllegalArgumentException();
-
-        dhgexpara = dgp;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param ciphers
-     */
-    public synchronized void setServer2ClientCiphers(String[] ciphers)
-    {
-        if ((ciphers == null) || (ciphers.length == 0))
-            throw new IllegalArgumentException();
-        ciphers = removeDuplicates(ciphers);
-        BlockCipherFactory.checkCipherList(ciphers);
-        cryptoWishList.s2c_enc_algos = ciphers;
-    }
-
-    /**
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param macs
-     */
-    public synchronized void setServer2ClientMACs(String[] macs)
-    {
-        if ((macs == null) || (macs.length == 0))
-            throw new IllegalArgumentException();
-
-        macs = removeDuplicates(macs);
-        MAC.checkMacList(macs);
-        cryptoWishList.s2c_mac_algos = macs;
-    }
-
-    /**
-     * Define the set of allowed server host key algorithms to be used for
-     * the following key exchange operations.
-     * <p>
-     * Unless you know what you are doing, you will never need this.
-     *
-     * @param algos An array of allowed server host key algorithms.
-     *         SSH-2 defines <code>ssh-dss</code> and <code>ssh-rsa</code>.
-     *         The entries of the array must be ordered after preference, i.e.,
-     *  the entry at index 0 is the most preferred one. You must specify
-     *  at least one entry.
-     */
-    public synchronized void setServerHostKeyAlgorithms(String[] algos)
-    {
-        if ((algos == null) || (algos.length == 0))
-            throw new IllegalArgumentException();
-
-        algos = removeDuplicates(algos);
-        KexManager.checkServerHostkeyAlgorithmsList(algos);
-        cryptoWishList.serverHostKeyAlgorithms = algos;
-    }
-
-    /**
-     * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the underlying socket.
-     * <p>
-     * Can be called at any time. If the connection has not yet been established
-     * then the passed value will be stored and set after the socket has been set up.
-     * The default value that will be used is <code>false</code>.
-     *
-     * @param enable the argument passed to the <code>Socket.setTCPNoDelay()</code> method.
-     * @throws IOException
-     */
-    public synchronized void setTCPNoDelay(boolean enable) throws IOException
-    {
-        tcpNoDelay = enable;
-
-        if (tm != null)
-            tm.setTcpNoDelay(enable);
-    }
-
-    /**
-     * Used to tell the library that the connection shall be established through a proxy server.
-     * It only makes sense to call this method before calling the {@link #connect() connect()}
-     * method.
-     * <p>
-     * At the moment, only HTTP proxies are supported.
-     * <p>
-     * Note: This method can be called any number of times. The {@link #connect() connect()}
-     * method will use the value set in the last preceding invocation of this method.
-     *
-     * @see HTTPProxyData
-     *
-     * @param proxyData Connection information about the proxy. If <code>null</code>, then
-     *                  no proxy will be used (non surprisingly, this is also the default).
-     */
-    public synchronized void setProxyData(ProxyData proxyData)
-    {
-        this.proxyData = proxyData;
-    }
-
-    /**
-     * Request a remote port forwarding.
-     * If successful, then forwarded connections will be redirected to the given target address.
-     * You can cancle a requested remote port forwarding by calling
-     * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
-     * <p>
-     * A call of this method will block until the peer either agreed or disagreed to your request-
-     * <p>
-     * Note 1: this method typically fails if you
-     * <ul>
-     * <li>pass a port number for which the used remote user has not enough permissions (i.e., port
-     * &lt; 1024)</li>
-     * <li>or pass a port number that is already in use on the remote server</li>
-     * <li>or if remote port forwarding is disabled on the server.</li>
-     * </ul>
-     * <p>
-     * Note 2: (from the openssh man page): By default, the listening socket on the server will be
-     * bound to the loopback interface only. This may be overriden by specifying a bind address.
-     * Specifying a remote bind address will only succeed if the server's <b>GatewayPorts</b> option
-     * is enabled (see sshd_config(5)).
-     *
-     * @param bindAddress address to bind to on the server:
-     *                    <ul>
-     *                    <li>"" means that connections are to be accepted on all protocol families
-     *                    supported by the SSH implementation</li>
-     *                    <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
-     *                    <li>"::" means to listen on all IPv6 addresses</li>
-     *                    <li>"localhost" means to listen on all protocol families supported by the SSH
-     *                    implementation on loopback addresses only, [RFC3330] and RFC3513]</li>
-     *                    <li>"127.0.0.1" and "::1" indicate listening on the loopback interfaces for
-     *                    IPv4 and IPv6 respectively</li>
-     *                    </ul>
-     * @param bindPort port number to bind on the server (must be &gt; 0)
-     * @param targetAddress the target address (IP or hostname)
-     * @param targetPort the target port
-     * @throws IOException
-     */
-    public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
-                                                         int targetPort) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("You need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("The connection is not authenticated.");
-
-        if ((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0))
-            throw new IllegalArgumentException();
-
-        cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort);
-    }
-
-    /**
-     * Cancel an earlier requested remote port forwarding.
-     * Currently active forwardings will not be affected (e.g., disrupted).
-     * Note that further connection forwarding requests may be received until
-     * this method has returned.
-     *
-     * @param bindPort the allocated port number on the server
-     * @throws IOException if the remote side refuses the cancel request or another low
-     *         level error occurs (e.g., the underlying connection is closed)
-     */
-    public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException
-    {
-        if (tm == null)
-            throw new IllegalStateException("You need to establish a connection first.");
-
-        if (!authenticated)
-            throw new IllegalStateException("The connection is not authenticated.");
-
-        cm.requestCancelGlobalForward(bindPort);
-    }
-
-    /**
-     * Provide your own instance of SecureRandom. Can be used, e.g., if you
-     * want to seed the used SecureRandom generator manually.
-     * <p>
-     * The SecureRandom instance is used during key exchanges, public key authentication,
-     * x11 cookie generation and the like.
-     *
-     * @param rnd a SecureRandom instance
-     */
-    public synchronized void setSecureRandom(SecureRandom rnd)
-    {
-        if (rnd == null)
-            throw new IllegalArgumentException();
-
-        this.generator = rnd;
-    }
-}
diff --git a/third-party/ganymed/src/main/java/ch/ethz/ssh2/channel/ChannelManager.java b/third-party/ganymed/src/main/java/ch/ethz/ssh2/channel/ChannelManager.java
deleted file mode 100644 (file)
index 8fec9a0..0000000
+++ /dev/null
@@ -1,1845 +0,0 @@
-/*
- * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
- * Please refer to the LICENSE.txt for licensing details.
- */
-
-package ch.ethz.ssh2.channel;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Vector;
-
-import ch.ethz.ssh2.ChannelCondition;
-import ch.ethz.ssh2.PtySettings;
-import ch.ethz.ssh2.ServerConnectionCallback;
-import ch.ethz.ssh2.ServerSessionCallback;
-import ch.ethz.ssh2.log.Logger;
-import ch.ethz.ssh2.packets.PacketChannelFailure;
-import ch.ethz.ssh2.packets.PacketChannelOpenConfirmation;
-import ch.ethz.ssh2.packets.PacketChannelOpenFailure;
-import ch.ethz.ssh2.packets.PacketChannelSuccess;
-import ch.ethz.ssh2.packets.PacketGlobalCancelForwardRequest;
-import ch.ethz.ssh2.packets.PacketGlobalForwardRequest;
-import ch.ethz.ssh2.packets.PacketOpenDirectTCPIPChannel;
-import ch.ethz.ssh2.packets.PacketOpenSessionChannel;
-import ch.ethz.ssh2.packets.PacketSessionExecCommand;
-import ch.ethz.ssh2.packets.PacketSessionPtyRequest;
-import ch.ethz.ssh2.packets.PacketSessionStartShell;
-import ch.ethz.ssh2.packets.PacketSessionSubsystemRequest;
-import ch.ethz.ssh2.packets.PacketSessionX11Request;
-import ch.ethz.ssh2.packets.Packets;
-import ch.ethz.ssh2.packets.TypesReader;
-import ch.ethz.ssh2.server.ServerConnectionState;
-import ch.ethz.ssh2.transport.MessageHandler;
-import ch.ethz.ssh2.transport.TransportManager;
-
-/**
- * ChannelManager. Please read the comments in Channel.java.
- * <p/>
- * Besides the crypto part, this is the core of the library.
- *
- * @author Christian Plattner
- * @version $Id: ChannelManager.java 48 2013-08-01 12:22:33Z cleondris@gmail.com $
- */
-public class ChannelManager implements MessageHandler
-{
-    private static final Logger log = Logger.getLogger(ChannelManager.class);
-
-    private final ServerConnectionState server_state;
-    private final TransportManager tm;
-
-    private final HashMap<String, X11ServerData> x11_magic_cookies = new HashMap<String, X11ServerData>();
-
-    private final List<Channel> channels = new Vector<Channel>();
-    private int nextLocalChannel = 100;
-    private boolean shutdown = false;
-    private int globalSuccessCounter = 0;
-    private int globalFailedCounter = 0;
-
-    private final HashMap<Integer, RemoteForwardingData> remoteForwardings = new HashMap<Integer, RemoteForwardingData>();
-
-    private final List<IChannelWorkerThread> listenerThreads = new Vector<IChannelWorkerThread>();
-
-    private boolean listenerThreadsAllowed = true;
-
-    /**
-     * Constructor for client-mode.
-     * @param tm
-     */
-    public ChannelManager(TransportManager tm)
-    {
-        this.server_state = null;
-        this.tm = tm;
-        tm.registerMessageHandler(this, 80, 100);
-    }
-
-    /**
-     * Constructor for server-mode.
-     * @param state
-     */
-    public ChannelManager(ServerConnectionState state)
-    {
-        this.server_state = state;
-        this.tm = state.tm;
-        tm.registerMessageHandler(this, 80, 100);
-    }
-
-    private Channel getChannel(int id)
-    {
-        synchronized (channels)
-        {
-            for (Channel c : channels)
-            {
-                if (c.localID == id)
-                    return c;
-            }
-        }
-        return null;
-    }
-
-    private void removeChannel(int id)
-    {
-        synchronized (channels)
-        {
-            for (Channel c : channels)
-            {
-                if (c.localID == id)
-                {
-                    channels.remove(c);
-                    break;
-                }
-            }
-        }
-    }
-
-    private int addChannel(Channel c)
-    {
-        synchronized (channels)
-        {
-            channels.add(c);
-            return nextLocalChannel++;
-        }
-    }
-
-    private void waitUntilChannelOpen(Channel c) throws IOException
-    {
-        boolean wasInterrupted = false;
-
-        synchronized (c)
-        {
-            while (c.state == Channel.STATE_OPENING)
-            {
-                try
-                {
-                    c.wait();
-                }
-                catch (InterruptedException ignore)
-                {
-                    wasInterrupted = true;
-                }
-            }
-
-            if (c.state != Channel.STATE_OPEN)
-            {
-                removeChannel(c.localID);
-
-                String detail = c.getReasonClosed();
-
-                if (detail == null)
-                    detail = "state: " + c.state;
-
-                throw new IOException("Could not open channel (" + detail + ")");
-            }
-        }
-
-        if (wasInterrupted)
-            Thread.currentThread().interrupt();
-    }
-
-    private void waitForGlobalSuccessOrFailure() throws IOException
-    {
-        boolean wasInterrupted = false;
-
-        try
-        {
-            synchronized (channels)
-            {
-                while ((globalSuccessCounter == 0) && (globalFailedCounter == 0))
-                {
-                    if (shutdown)
-                    {
-                        throw new IOException("The connection is being shutdown");
-                    }
-
-                    try
-                    {
-                        channels.wait();
-                    }
-                    catch (InterruptedException ignore)
-                    {
-                        wasInterrupted = true;
-                    }
-                }
-
-                if (globalFailedCounter != 0)
-                {
-                    throw new IOException("The server denied the request (did you enable port forwarding?)");
-                }
-
-                if (globalSuccessCounter == 0)
-                {
-                    throw new IOException("Illegal state.");
-                }
-            }
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-    }
-
-    private void waitForChannelSuccessOrFailure(Channel c) throws IOException
-    {
-        boolean wasInterrupted = false;
-
-        try
-        {
-            synchronized (c)
-            {
-                while ((c.successCounter == 0) && (c.failedCounter == 0))
-                {
-                    if (c.state != Channel.STATE_OPEN)
-                    {
-                        String detail = c.getReasonClosed();
-
-                        if (detail == null)
-                            detail = "state: " + c.state;
-
-                        throw new IOException("This SSH2 channel is not open (" + detail + ")");
-                    }
-
-                    try
-                    {
-                        c.wait();
-                    }
-                    catch (InterruptedException ignore)
-                    {
-                        wasInterrupted = true;
-                    }
-                }
-
-                if (c.failedCounter != 0)
-                {
-                    throw new IOException("The server denied the request.");
-                }
-            }
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-    }
-
-    public void registerX11Cookie(String hexFakeCookie, X11ServerData data)
-    {
-        synchronized (x11_magic_cookies)
-        {
-            x11_magic_cookies.put(hexFakeCookie, data);
-        }
-    }
-
-    public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels)
-    {
-        if (hexFakeCookie == null)
-            throw new IllegalStateException("hexFakeCookie may not be null");
-
-        synchronized (x11_magic_cookies)
-        {
-            x11_magic_cookies.remove(hexFakeCookie);
-        }
-
-        if (killChannels == false)
-            return;
-
-        log.debug("Closing all X11 channels for the given fake cookie");
-
-        List<Channel> channel_copy = new Vector<Channel>();
-
-        synchronized (channels)
-        {
-            channel_copy.addAll(channels);
-        }
-
-        for (Channel c : channel_copy)
-        {
-            synchronized (c)
-            {
-                if (hexFakeCookie.equals(c.hexX11FakeCookie) == false)
-                    continue;
-            }
-
-            try
-            {
-                closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
-            }
-            catch (IOException ignored)
-            {
-            }
-        }
-    }
-
-    public X11ServerData checkX11Cookie(String hexFakeCookie)
-    {
-        synchronized (x11_magic_cookies)
-        {
-            if (hexFakeCookie != null)
-                return x11_magic_cookies.get(hexFakeCookie);
-        }
-        return null;
-    }
-
-    public void closeAllChannels()
-    {
-        log.debug("Closing all channels");
-
-        List<Channel> channel_copy = new Vector<Channel>();
-
-        synchronized (channels)
-        {
-            channel_copy.addAll(channels);
-        }
-
-        for (Channel c : channel_copy)
-        {
-            try
-            {
-                closeChannel(c, "Closing all channels", true);
-            }
-            catch (IOException ignored)
-            {
-            }
-        }
-    }
-
-    public void closeChannel(Channel c, String reason, boolean force) throws IOException
-    {
-        byte msg[] = new byte[5];
-
-        synchronized (c)
-        {
-            if (force)
-            {
-                c.state = Channel.STATE_CLOSED;
-                c.EOF = true;
-            }
-
-            c.setReasonClosed(reason);
-
-            msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
-            msg[1] = (byte) (c.remoteID >> 24);
-            msg[2] = (byte) (c.remoteID >> 16);
-            msg[3] = (byte) (c.remoteID >> 8);
-            msg[4] = (byte) (c.remoteID);
-
-            c.notifyAll();
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent == true)
-                return;
-            tm.sendMessage(msg);
-            c.closeMessageSent = true;
-        }
-
-        log.debug("Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
-    }
-
-    public void sendEOF(Channel c) throws IOException
-    {
-        byte[] msg = new byte[5];
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                return;
-
-            msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
-            msg[1] = (byte) (c.remoteID >> 24);
-            msg[2] = (byte) (c.remoteID >> 16);
-            msg[3] = (byte) (c.remoteID >> 8);
-            msg[4] = (byte) (c.remoteID);
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent == true)
-                return;
-            tm.sendMessage(msg);
-        }
-
-
-        log.debug("Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
-    }
-
-    public void sendOpenConfirmation(Channel c) throws IOException
-    {
-        PacketChannelOpenConfirmation pcoc = null;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPENING)
-                return;
-
-            c.state = Channel.STATE_OPEN;
-
-            pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent == true)
-                return;
-            tm.sendMessage(pcoc.getPayload());
-        }
-    }
-
-    public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException
-    {
-        boolean wasInterrupted = false;
-
-        try
-        {
-            while (len > 0)
-            {
-                int thislen = 0;
-                byte[] msg;
-
-                synchronized (c)
-                {
-                    while (true)
-                    {
-                        if (c.state == Channel.STATE_CLOSED)
-                            throw new ChannelClosedException("SSH channel is closed. (" + c.getReasonClosed() + ")");
-
-                        if (c.state != Channel.STATE_OPEN)
-                            throw new ChannelClosedException("SSH channel in strange state. (" + c.state + ")");
-
-                        if (c.remoteWindow != 0)
-                            break;
-
-                        try
-                        {
-                            c.wait();
-                        }
-                        catch (InterruptedException ignore)
-                        {
-                            wasInterrupted = true;
-                        }
-                    }
-
-                    /* len > 0, no sign extension can happen when comparing */
-
-                    thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;
-
-                    int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);
-
-                    /* The worst case scenario =) a true bottleneck */
-
-                    if (estimatedMaxDataLen <= 0)
-                    {
-                        estimatedMaxDataLen = 1;
-                    }
-
-                    if (thislen > estimatedMaxDataLen)
-                        thislen = estimatedMaxDataLen;
-
-                    c.remoteWindow -= thislen;
-
-                    msg = new byte[1 + 8 + thislen];
-
-                    msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
-                    msg[1] = (byte) (c.remoteID >> 24);
-                    msg[2] = (byte) (c.remoteID >> 16);
-                    msg[3] = (byte) (c.remoteID >> 8);
-                    msg[4] = (byte) (c.remoteID);
-                    msg[5] = (byte) (thislen >> 24);
-                    msg[6] = (byte) (thislen >> 16);
-                    msg[7] = (byte) (thislen >> 8);
-                    msg[8] = (byte) (thislen);
-
-                    System.arraycopy(buffer, pos, msg, 9, thislen);
-                }
-
-                synchronized (c.channelSendLock)
-                {
-                    if (c.closeMessageSent == true)
-                        throw new ChannelClosedException("SSH channel is closed. (" + c.getReasonClosed() + ")");
-
-                    tm.sendMessage(msg);
-                }
-
-                pos += thislen;
-                len -= thislen;
-            }
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-    }
-
-    public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)
-            throws IOException
-    {
-        RemoteForwardingData rfd = new RemoteForwardingData();
-
-        rfd.bindAddress = bindAddress;
-        rfd.bindPort = bindPort;
-        rfd.targetAddress = targetAddress;
-        rfd.targetPort = targetPort;
-
-        synchronized (remoteForwardings)
-        {
-            Integer key = new Integer(bindPort);
-
-            if (remoteForwardings.get(key) != null)
-            {
-                throw new IOException("There is already a forwarding for remote port " + bindPort);
-            }
-
-            remoteForwardings.put(key, rfd);
-        }
-
-        synchronized (channels)
-        {
-            globalSuccessCounter = globalFailedCounter = 0;
-        }
-
-        PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
-        tm.sendMessage(pgf.getPayload());
-
-        log.debug("Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");
-
-        try
-        {
-            waitForGlobalSuccessOrFailure();
-        }
-        catch (IOException e)
-        {
-            synchronized (remoteForwardings)
-            {
-                remoteForwardings.remove(rfd);
-            }
-            throw e;
-        }
-
-        return bindPort;
-    }
-
-    public void requestCancelGlobalForward(int bindPort) throws IOException
-    {
-        RemoteForwardingData rfd = null;
-
-        synchronized (remoteForwardings)
-        {
-            rfd = remoteForwardings.get(new Integer(bindPort));
-
-            if (rfd == null)
-                throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort);
-        }
-
-        synchronized (channels)
-        {
-            globalSuccessCounter = globalFailedCounter = 0;
-        }
-
-        PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress,
-                rfd.bindPort);
-        tm.sendMessage(pgcf.getPayload());
-
-        log.debug("Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
-
-        waitForGlobalSuccessOrFailure();
-
-        /* Only now we are sure that no more forwarded connections will arrive */
-
-        synchronized (remoteForwardings)
-        {
-            remoteForwardings.remove(rfd);
-        }
-    }
-
-    public void registerThread(IChannelWorkerThread thr) throws IOException
-    {
-        synchronized (listenerThreads)
-        {
-            if (listenerThreadsAllowed == false)
-                throw new IOException("Too late, this connection is closed.");
-            listenerThreads.add(thr);
-        }
-    }
-
-    public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address,
-                                          int originator_port) throws IOException
-    {
-        Channel c = new Channel(this);
-
-        synchronized (c)
-        {
-            c.localID = addChannel(c);
-            // end of synchronized block forces writing out to main memory
-        }
-
-        PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow,
-                c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port);
-
-        tm.sendMessage(dtc.getPayload());
-
-        waitUntilChannelOpen(c);
-
-        return c;
-    }
-
-    public Channel openSessionChannel() throws IOException
-    {
-        Channel c = new Channel(this);
-
-        synchronized (c)
-        {
-            c.localID = addChannel(c);
-            // end of synchronized block forces the writing out to main memory
-        }
-
-        log.debug("Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");
-
-        PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
-        tm.sendMessage(smo.getPayload());
-
-        waitUntilChannelOpen(c);
-
-        return c;
-    }
-
-    public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
-                           int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException
-    {
-        PacketSessionPtyRequest spr;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
-
-            spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters,
-                    term_width_pixels, term_height_pixels, terminal_modes);
-
-            c.successCounter = c.failedCounter = 0;
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent)
-                throw new IOException("Cannot request PTY on this channel (" + c.getReasonClosed() + ")");
-            tm.sendMessage(spr.getPayload());
-        }
-
-        try
-        {
-            waitForChannelSuccessOrFailure(c);
-        }
-        catch (IOException e)
-        {
-            throw (IOException) new IOException("PTY request failed").initCause(e);
-        }
-    }
-
-    public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
-                           String x11AuthenticationCookie, int x11ScreenNumber) throws IOException
-    {
-        PacketSessionX11Request psr;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
-
-            psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
-                    x11AuthenticationCookie, x11ScreenNumber);
-
-            c.successCounter = c.failedCounter = 0;
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent)
-                throw new IOException("Cannot request X11 on this channel (" + c.getReasonClosed() + ")");
-            tm.sendMessage(psr.getPayload());
-        }
-
-        log.debug("Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
-
-        try
-        {
-            waitForChannelSuccessOrFailure(c);
-        }
-        catch (IOException e)
-        {
-            throw (IOException) new IOException("The X11 request failed.").initCause(e);
-        }
-    }
-
-    public void requestSubSystem(Channel c, String subSystemName) throws IOException
-    {
-        PacketSessionSubsystemRequest ssr;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
-
-            ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
-
-            c.successCounter = c.failedCounter = 0;
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent)
-                throw new IOException("Cannot request subsystem on this channel (" + c.getReasonClosed() + ")");
-            tm.sendMessage(ssr.getPayload());
-        }
-
-        try
-        {
-            waitForChannelSuccessOrFailure(c);
-        }
-        catch (IOException e)
-        {
-            throw (IOException) new IOException("The subsystem request failed.").initCause(e);
-        }
-    }
-
-    public void requestExecCommand(Channel c, String cmd) throws IOException
-    {
-        this.requestExecCommand(c, cmd, null);
-    }
-
-    /**
-     * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings
-     */
-    public void requestExecCommand(Channel c, String cmd, String charsetName) throws IOException
-    {
-        PacketSessionExecCommand sm;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
-
-            sm = new PacketSessionExecCommand(c.remoteID, true, cmd);
-
-            c.successCounter = c.failedCounter = 0;
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent)
-                throw new IOException("Cannot execute command on this channel (" + c.getReasonClosed() + ")");
-            tm.sendMessage(sm.getPayload(charsetName));
-        }
-
-        log.debug("Executing command (channel " + c.localID + ", '" + cmd + "')");
-
-        try
-        {
-            waitForChannelSuccessOrFailure(c);
-        }
-        catch (IOException e)
-        {
-            throw (IOException) new IOException("The execute request failed.").initCause(e);
-        }
-    }
-
-    public void requestShell(Channel c) throws IOException
-    {
-        PacketSessionStartShell sm;
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
-
-            sm = new PacketSessionStartShell(c.remoteID, true);
-
-            c.successCounter = c.failedCounter = 0;
-        }
-
-        synchronized (c.channelSendLock)
-        {
-            if (c.closeMessageSent)
-                throw new IOException("Cannot start shell on this channel (" + c.getReasonClosed() + ")");
-            tm.sendMessage(sm.getPayload());
-        }
-
-        try
-        {
-            waitForChannelSuccessOrFailure(c);
-        }
-        catch (IOException e)
-        {
-            throw (IOException) new IOException("The shell request failed.").initCause(e);
-        }
-    }
-
-    public void msgChannelExtendedData(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen <= 13)
-            throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-        int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
-        int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
-
-        if (dataType != Packets.SSH_EXTENDED_DATA_STDERR)
-            throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
-
-        if (len != (msglen - 13))
-            throw new IOException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msglen - 13)
-                    + ", got " + len + ")");
-
-        log.debug("Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");
-
-        synchronized (c)
-        {
-            if (c.state == Channel.STATE_CLOSED)
-                return; // ignore
-
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
-                        + c.state + ")");
-
-            if (c.localWindow < len)
-                throw new IOException("Remote sent too much data, does not fit into window.");
-
-            c.localWindow -= len;
-
-            System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
-            c.stderrWritepos += len;
-
-            c.notifyAll();
-        }
-    }
-
-    /**
-     * Wait until for a condition.
-     *
-     * @param c Channel
-     * @param timeout in ms, 0 means no timeout.
-     * @param condition_mask minimum event mask (at least one of the conditions must be fulfilled)
-     * @return all current events
-     */
-    public int waitForCondition(Channel c, long timeout, int condition_mask)
-    {
-        boolean wasInterrupted = false;
-
-        try
-        {
-            long end_time = 0;
-            boolean end_time_set = false;
-
-            synchronized (c)
-            {
-                while (true)
-                {
-                    int current_cond = 0;
-
-                    int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
-                    int stderrAvail = c.stderrWritepos - c.stderrReadpos;
-
-                    if (stdoutAvail > 0)
-                        current_cond = current_cond | ChannelCondition.STDOUT_DATA;
-
-                    if (stderrAvail > 0)
-                        current_cond = current_cond | ChannelCondition.STDERR_DATA;
-
-                    if (c.EOF)
-                        current_cond = current_cond | ChannelCondition.EOF;
-
-                    if (c.getExitStatus() != null)
-                        current_cond = current_cond | ChannelCondition.EXIT_STATUS;
-
-                    if (c.getExitSignal() != null)
-                        current_cond = current_cond | ChannelCondition.EXIT_SIGNAL;
-
-                    if (c.state == Channel.STATE_CLOSED)
-                        return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF;
-
-                    if ((current_cond & condition_mask) != 0)
-                        return current_cond;
-
-                    if (timeout > 0)
-                    {
-                        if (!end_time_set)
-                        {
-                            end_time = System.currentTimeMillis() + timeout;
-                            end_time_set = true;
-                        }
-                        else
-                        {
-                            timeout = end_time - System.currentTimeMillis();
-
-                            if (timeout <= 0)
-                                return current_cond | ChannelCondition.TIMEOUT;
-                        }
-                    }
-
-                    try
-                    {
-                        if (timeout > 0)
-                            c.wait(timeout);
-                        else
-                            c.wait();
-                    }
-                    catch (InterruptedException e)
-                    {
-                        wasInterrupted = true;
-                    }
-                }
-            }
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-    }
-
-    public int getAvailable(Channel c, boolean extended) throws IOException
-    {
-        synchronized (c)
-        {
-            int avail;
-
-            if (extended)
-                avail = c.stderrWritepos - c.stderrReadpos;
-            else
-                avail = c.stdoutWritepos - c.stdoutReadpos;
-
-            return ((avail > 0) ? avail : (c.EOF ? -1 : 0));
-        }
-    }
-
-    public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException
-    {
-        boolean wasInterrupted = false;
-
-        try
-        {
-            int copylen = 0;
-            int increment = 0;
-            int remoteID = 0;
-            int localID = 0;
-
-            synchronized (c)
-            {
-                int stdoutAvail = 0;
-                int stderrAvail = 0;
-
-                while (true)
-                {
-                    /*
-                     * Data available? We have to return remaining data even if the
-                     * channel is already closed.
-                     */
-
-                    stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
-                    stderrAvail = c.stderrWritepos - c.stderrReadpos;
-
-                    if ((!extended) && (stdoutAvail != 0))
-                        break;
-
-                    if ((extended) && (stderrAvail != 0))
-                        break;
-
-                    /* Do not wait if more data will never arrive (EOF or CLOSED) */
-
-                    if ((c.EOF) || (c.state != Channel.STATE_OPEN))
-                        return -1;
-
-                    try
-                    {
-                        c.wait();
-                    }
-                    catch (InterruptedException ignore)
-                    {
-                        wasInterrupted = true;
-                    }
-                }
-
-                /* OK, there is some data. Return it. */
-
-                if (!extended)
-                {
-                    copylen = (stdoutAvail > len) ? len : stdoutAvail;
-                    System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
-                    c.stdoutReadpos += copylen;
-
-                    if (c.stdoutReadpos != c.stdoutWritepos)
-
-                        System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos
-                                - c.stdoutReadpos);
-
-                    c.stdoutWritepos -= c.stdoutReadpos;
-                    c.stdoutReadpos = 0;
-                }
-                else
-                {
-                    copylen = (stderrAvail > len) ? len : stderrAvail;
-                    System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
-                    c.stderrReadpos += copylen;
-
-                    if (c.stderrReadpos != c.stderrWritepos)
-
-                        System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos
-                                - c.stderrReadpos);
-
-                    c.stderrWritepos -= c.stderrReadpos;
-                    c.stderrReadpos = 0;
-                }
-
-                if (c.state != Channel.STATE_OPEN)
-                    return copylen;
-
-                if (c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2))
-                {
-                    int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos,
-                            Channel.CHANNEL_BUFFER_SIZE - c.stderrWritepos);
-
-                    increment = minFreeSpace - c.localWindow;
-                    c.localWindow = minFreeSpace;
-                }
-
-                remoteID = c.remoteID; /* read while holding the lock */
-                localID = c.localID; /* read while holding the lock */
-            }
-
-            /*
-             * If a consumer reads stdout and stdin in parallel, we may end up with
-             * sending two msgWindowAdjust messages. Luckily, it
-             * does not matter in which order they arrive at the server.
-             */
-
-            if (increment > 0)
-            {
-                log.debug("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
-
-                synchronized (c.channelSendLock)
-                {
-                    byte[] msg = c.msgWindowAdjust;
-
-                    msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
-                    msg[1] = (byte) (remoteID >> 24);
-                    msg[2] = (byte) (remoteID >> 16);
-                    msg[3] = (byte) (remoteID >> 8);
-                    msg[4] = (byte) (remoteID);
-                    msg[5] = (byte) (increment >> 24);
-                    msg[6] = (byte) (increment >> 16);
-                    msg[7] = (byte) (increment >> 8);
-                    msg[8] = (byte) (increment);
-
-                    if (c.closeMessageSent == false)
-                        tm.sendMessage(msg);
-                }
-            }
-
-            return copylen;
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-
-    }
-
-    public void msgChannelData(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen <= 9)
-            throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-        int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);
-
-        if (len != (msglen - 9))
-            throw new IOException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msglen - 9) + ", got "
-                    + len + ")");
-
-        log.debug("Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")");
-
-        synchronized (c)
-        {
-            if (c.state == Channel.STATE_CLOSED)
-                return; // ignore
-
-            if (c.state != Channel.STATE_OPEN)
-                throw new IOException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")");
-
-            if (c.localWindow < len)
-                throw new IOException("Remote sent too much data, does not fit into window.");
-
-            c.localWindow -= len;
-
-            System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len);
-            c.stdoutWritepos += len;
-
-            c.notifyAll();
-        }
-    }
-
-    public void msgChannelWindowAdjust(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen != 9)
-            throw new IOException("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-        int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id);
-
-        synchronized (c)
-        {
-            final long huge = 0xFFFFffffL; /* 2^32 - 1 */
-
-            c.remoteWindow += (windowChange & huge); /* avoid sign extension */
-
-            /* TODO - is this a good heuristic? */
-
-            if ((c.remoteWindow > huge))
-                c.remoteWindow = huge;
-
-            c.notifyAll();
-        }
-
-
-        log.debug("Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
-    }
-
-    public void msgChannelOpen(byte[] msg, int msglen) throws IOException
-    {
-        TypesReader tr = new TypesReader(msg, 0, msglen);
-
-        tr.readByte(); // skip packet type
-        String channelType = tr.readString();
-        int remoteID = tr.readUINT32(); /* sender channel */
-        int remoteWindow = tr.readUINT32(); /* initial window size */
-        int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */
-
-        if ("x11".equals(channelType))
-        {
-            synchronized (x11_magic_cookies)
-            {
-                /* If we did not request X11 forwarding, then simply ignore this bogus request. */
-
-                if (x11_magic_cookies.size() == 0)
-                {
-                    PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
-                            Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", "");
-
-                    tm.sendAsynchronousMessage(pcof.getPayload());
-
-                    log.warning("Unexpected X11 request, denying it!");
-
-                    return;
-                }
-            }
-
-            String remoteOriginatorAddress = tr.readString();
-            int remoteOriginatorPort = tr.readUINT32();
-
-            Channel c = new Channel(this);
-
-            synchronized (c)
-            {
-                c.remoteID = remoteID;
-                c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
-                c.remoteMaxPacketSize = remoteMaxPacketSize;
-                c.localID = addChannel(c);
-            }
-
-            /*
-             * The open confirmation message will be sent from another thread
-             */
-
-            RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
-            rxat.setDaemon(true);
-            rxat.start();
-
-            return;
-        }
-
-        if ("forwarded-tcpip".equals(channelType))
-        {
-            String remoteConnectedAddress = tr.readString(); /* address that was connected */
-            int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
-            String remoteOriginatorAddress = tr.readString(); /* originator IP address */
-            int remoteOriginatorPort = tr.readUINT32(); /* originator port */
-
-            RemoteForwardingData rfd = null;
-
-            synchronized (remoteForwardings)
-            {
-                rfd = remoteForwardings.get(new Integer(remoteConnectedPort));
-            }
-
-            if (rfd == null)
-            {
-                PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
-                        Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
-                        "No thanks, unknown port in forwarded-tcpip request", "");
-
-                /* Always try to be polite. */
-
-                tm.sendAsynchronousMessage(pcof.getPayload());
-
-                log.debug("Unexpected forwarded-tcpip request, denying it!");
-
-                return;
-            }
-
-            Channel c = new Channel(this);
-
-            synchronized (c)
-            {
-                c.remoteID = remoteID;
-                c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
-                c.remoteMaxPacketSize = remoteMaxPacketSize;
-                c.localID = addChannel(c);
-            }
-
-            /*
-             * The open confirmation message will be sent from another thread.
-             */
-
-            RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
-                    remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);
-
-            rat.setDaemon(true);
-            rat.start();
-
-            return;
-        }
-
-        if ((server_state != null) && ("session".equals(channelType)))
-        {
-            ServerConnectionCallback cb = null;
-            
-            synchronized (server_state)
-            {
-                cb = server_state.cb_conn;
-            }
-            
-            if (cb == null)
-            {
-                tm.sendAsynchronousMessage(new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
-                        "Sessions are currently not enabled", "en").getPayload());
-                
-                return;
-            }
-            
-            final Channel c = new Channel(this);
-
-            synchronized (c)
-            {
-                c.remoteID = remoteID;
-                c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
-                c.remoteMaxPacketSize = remoteMaxPacketSize;
-                c.localID = addChannel(c);
-                c.state = Channel.STATE_OPEN;
-                c.ss = new ServerSessionImpl(c);
-            }
-
-            PacketChannelOpenConfirmation pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID,
-                    c.localWindow, c.localMaxPacketSize);
-
-            tm.sendAsynchronousMessage(pcoc.getPayload());
-
-            c.ss.sscb = cb.acceptSession(c.ss);
-
-            return;
-        }
-
-        /* Tell the server that we have no idea what it is talking about */
-
-        PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
-                "Unknown channel type", "");
-
-        tm.sendAsynchronousMessage(pcof.getPayload());
-
-
-        log.warning("The peer tried to open an unsupported channel type (" + channelType + ")");
-    }
-
-    /* Starts the given runnable in a foreground (non-daemon) thread */
-    private void runAsync(Runnable r)
-    {
-        Thread t = new Thread(r);
-        t.start();        
-    }
-    
-    public void msgChannelRequest(byte[] msg, int msglen) throws IOException
-    {
-        TypesReader tr = new TypesReader(msg, 0, msglen);
-
-        tr.readByte(); // skip packet type
-        int id = tr.readUINT32();
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);
-
-        ServerSessionImpl server_session = null;
-
-        if (server_state != null)
-        {
-            synchronized (c)
-            {
-                server_session = c.ss;
-            }
-        }
-
-        String type = tr.readString("US-ASCII");
-        boolean wantReply = tr.readBoolean();
-
-        log.debug("Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
-
-        if (type.equals("exit-status"))
-        {
-            if (wantReply != false)
-                throw new IOException(
-                        "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-status message, 'want reply' is true");
-
-            int exit_status = tr.readUINT32();
-
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-
-            synchronized (c)
-            {
-                c.exit_status = new Integer(exit_status);
-                c.notifyAll();
-            }
-
-            log.debug("Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
-
-            return;
-        }
-
-        if ((server_state == null) && (type.equals("exit-signal")))
-        {
-            if (wantReply != false)
-                throw new IOException(
-                        "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-signal message, 'want reply' is true");
-
-            String signame = tr.readString("US-ASCII");
-            tr.readBoolean();
-            tr.readString();
-            tr.readString();
-
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-
-            synchronized (c)
-            {
-                c.exit_signal = signame;
-                c.notifyAll();
-            }
-
-            log.debug("Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
-
-            return;
-        }
-
-        if ((server_session != null) && (type.equals("pty-req")))
-        {
-            PtySettings pty = new PtySettings();
-
-            pty.term = tr.readString();
-            pty.term_width_characters = tr.readUINT32();
-            pty.term_height_characters = tr.readUINT32();
-            pty.term_width_pixels = tr.readUINT32();
-            pty.term_height_pixels = tr.readUINT32();
-            pty.terminal_modes = tr.readByteString();
-
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-            
-            Runnable run_after_sending_success = null;
-            
-            ServerSessionCallback sscb = server_session.getServerSessionCallback();
-
-            if (sscb != null)
-                run_after_sending_success = sscb.requestPtyReq(server_session, pty);
-
-            if (wantReply)
-            {
-                if (run_after_sending_success != null)
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
-                }
-                else
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
-                }            
-            }
-            
-            if (run_after_sending_success != null)
-            {
-                runAsync(run_after_sending_success);
-            }
-            
-            return;
-        }
-        
-        if ((server_session != null) && (type.equals("subsystem")))
-        {
-            String command = tr.readString();
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-            
-            Runnable run_after_sending_success = null;
-            ServerSessionCallback sscb = server_session.getServerSessionCallback();
-
-            if (sscb != null)
-                run_after_sending_success = sscb.requestSubsystem(server_session, command);
-
-            if (wantReply)
-            {
-                if (run_after_sending_success != null)
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
-                }
-                else
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
-                }
-            }
-            
-            if (run_after_sending_success != null)
-            {
-                runAsync(run_after_sending_success);
-            }
-            
-            return;
-        }
-
-        if ((server_session != null) && (type.equals("shell")))
-        {
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-            
-            Runnable run_after_sending_success = null;
-            ServerSessionCallback sscb = server_session.getServerSessionCallback();
-
-            if (sscb != null)
-                run_after_sending_success = sscb.requestShell(server_session);
-
-            if (wantReply)
-            {
-                if (run_after_sending_success != null)
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
-                }
-                else
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
-                }
-            }
-            
-            if (run_after_sending_success != null)
-            {
-                runAsync(run_after_sending_success);
-            }
-            
-            return;
-        }
-        
-        if ((server_session != null) && (type.equals("exec")))
-        {
-            String command = tr.readString();
-            
-            if (tr.remain() != 0)
-                throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
-            
-            Runnable run_after_sending_success = null;
-            ServerSessionCallback sscb = server_session.getServerSessionCallback();
-
-            if (sscb != null)
-                run_after_sending_success = sscb.requestExec(server_session, command);
-
-            if (wantReply)
-            {
-                if (run_after_sending_success != null)
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
-                }
-                else
-                {
-                    tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
-                }
-            }
-            
-            if (run_after_sending_success != null)
-            {
-                runAsync(run_after_sending_success);
-            }
-            
-            return;
-        }
-
-        /* We simply ignore unknown channel requests, however, if the server wants a reply,
-         * then we signal that we have no idea what it is about.
-         */
-
-        if (wantReply)
-        {
-            tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
-        }
-
-        log.debug("Channel request '" + type + "' is not known, ignoring it");
-    }
-
-    public void msgChannelEOF(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen != 5)
-            throw new IOException("SSH_MSG_CHANNEL_EOF message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
-
-        synchronized (c)
-        {
-            c.EOF = true;
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
-    }
-
-    public void msgChannelClose(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen != 5)
-            throw new IOException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
-
-        synchronized (c)
-        {
-            c.EOF = true;
-            c.state = Channel.STATE_CLOSED;
-            c.setReasonClosed("Close requested by remote");
-            c.closeMessageRecv = true;
-
-            removeChannel(c.localID);
-
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
-    }
-
-    public void msgChannelSuccess(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen != 5)
-            throw new IOException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
-
-        synchronized (c)
-        {
-            c.successCounter++;
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
-    }
-
-    public void msgChannelFailure(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen != 5)
-            throw new IOException("SSH_MSG_CHANNEL_FAILURE message has wrong size (" + msglen + ")");
-
-        int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
-
-        synchronized (c)
-        {
-            c.failedCounter++;
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
-    }
-
-    public void msgChannelOpenConfirmation(byte[] msg, int msglen) throws IOException
-    {
-        PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg, 0, msglen);
-
-        Channel c = getChannel(sm.recipientChannelID);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
-                    + sm.recipientChannelID);
-
-        synchronized (c)
-        {
-            if (c.state != Channel.STATE_OPENING)
-                throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
-                        + sm.recipientChannelID);
-
-            c.remoteID = sm.senderChannelID;
-            c.remoteWindow = sm.initialWindowSize & 0xFFFFffffL; /* convert UINT32 to long */
-            c.remoteMaxPacketSize = sm.maxPacketSize;
-            c.state = Channel.STATE_OPEN;
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.recipientChannelID + " / remote: "
-                + sm.senderChannelID + ")");
-    }
-
-    public void msgChannelOpenFailure(byte[] msg, int msglen) throws IOException
-    {
-        if (msglen < 5)
-            throw new IOException("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (" + msglen + ")");
-
-        TypesReader tr = new TypesReader(msg, 0, msglen);
-
-        tr.readByte(); // skip packet type
-        int id = tr.readUINT32(); /* sender channel */
-
-        Channel c = getChannel(id);
-
-        if (c == null)
-            throw new IOException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
-
-        int reasonCode = tr.readUINT32();
-        String description = tr.readString("UTF-8");
-
-        String reasonCodeSymbolicName = null;
-
-        switch (reasonCode)
-        {
-            case 1:
-                reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
-                break;
-            case 2:
-                reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
-                break;
-            case 3:
-                reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
-                break;
-            case 4:
-                reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
-                break;
-            default:
-                reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
-        }
-
-        StringBuilder descriptionBuffer = new StringBuilder();
-        descriptionBuffer.append(description);
-
-        for (int i = 0; i < descriptionBuffer.length(); i++)
-        {
-            char cc = descriptionBuffer.charAt(i);
-
-            if ((cc >= 32) && (cc <= 126))
-                continue;
-            descriptionBuffer.setCharAt(i, '\uFFFD');
-        }
-
-        synchronized (c)
-        {
-            c.EOF = true;
-            c.state = Channel.STATE_CLOSED;
-            c.setReasonClosed("The server refused to open the channel (" + reasonCodeSymbolicName + ", '"
-                    + descriptionBuffer.toString() + "')");
-            c.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
-    }
-
-    public void msgGlobalRequest(byte[] msg, int msglen) throws IOException
-    {
-        /* Currently we do not support any kind of global request */
-
-        TypesReader tr = new TypesReader(msg, 0, msglen);
-
-        tr.readByte(); // skip packet type
-        String requestName = tr.readString();
-        boolean wantReply = tr.readBoolean();
-
-        if (wantReply)
-        {
-            byte[] reply_failure = new byte[1];
-            reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
-
-            tm.sendAsynchronousMessage(reply_failure);
-        }
-
-        /* We do not clean up the requestName String - that is OK for debug */
-
-        log.debug("Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
-    }
-
-    public void msgGlobalSuccess() throws IOException
-    {
-        synchronized (channels)
-        {
-            globalSuccessCounter++;
-            channels.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_REQUEST_SUCCESS");
-    }
-
-    public void msgGlobalFailure() throws IOException
-    {
-        synchronized (channels)
-        {
-            globalFailedCounter++;
-            channels.notifyAll();
-        }
-
-        log.debug("Got SSH_MSG_REQUEST_FAILURE");
-    }
-
-    public void handleMessage(byte[] msg, int msglen) throws IOException
-    {
-        if (msg == null)
-        {
-
-            log.debug("HandleMessage: got shutdown");
-
-            synchronized (listenerThreads)
-            {
-                for (IChannelWorkerThread lat : listenerThreads)
-                {
-                    lat.stopWorking();
-                }
-                listenerThreadsAllowed = false;
-            }
-
-            synchronized (channels)
-            {
-                shutdown = true;
-
-                for (Channel c : channels)
-                {
-                    synchronized (c)
-                    {
-                        c.EOF = true;
-                        c.state = Channel.STATE_CLOSED;
-                        c.setReasonClosed("The connection is being shutdown");
-                        c.closeMessageRecv = true; /*
-                                                    * You never know, perhaps
-                                                    * we are waiting for a
-                                                    * pending close message
-                                                    * from the server...
-                                                    */
-                        c.notifyAll();
-                    }
-                }
-
-                channels.clear();
-                channels.notifyAll(); /* Notify global response waiters */
-                return;
-            }
-        }
-
-        switch (msg[0])
-        {
-            case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
-                msgChannelOpenConfirmation(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
-                msgChannelWindowAdjust(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_DATA:
-                msgChannelData(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
-                msgChannelExtendedData(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_REQUEST:
-                msgChannelRequest(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_EOF:
-                msgChannelEOF(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_OPEN:
-                msgChannelOpen(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_CLOSE:
-                msgChannelClose(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_SUCCESS:
-                msgChannelSuccess(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_FAILURE:
-                msgChannelFailure(msg, msglen);
-                break;
-            case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
-                msgChannelOpenFailure(msg, msglen);
-                break;
-            case Packets.SSH_MSG_GLOBAL_REQUEST:
-                msgGlobalRequest(msg, msglen);
-                break;
-            case Packets.SSH_MSG_REQUEST_SUCCESS:
-                msgGlobalSuccess();
-                break;
-            case Packets.SSH_MSG_REQUEST_FAILURE:
-                msgGlobalFailure();
-                break;
-            default:
-                throw new IOException("Cannot handle unknown channel message " + (msg[0] & 0xff));
-        }
-    }
-}
diff --git a/third-party/ganymed/src/main/java/ch/ethz/ssh2/transport/TransportManager.java b/third-party/ganymed/src/main/java/ch/ethz/ssh2/transport/TransportManager.java
deleted file mode 100644 (file)
index 9632670..0000000
+++ /dev/null
@@ -1,990 +0,0 @@
-/*
- * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
- * Please refer to the LICENSE.txt for licensing details.
- */
-
-package ch.ethz.ssh2.transport;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-import java.security.SecureRandom;
-import java.util.List;
-import java.util.Vector;
-
-import ch.ethz.ssh2.ConnectionInfo;
-import ch.ethz.ssh2.ConnectionMonitor;
-import ch.ethz.ssh2.DHGexParameters;
-import ch.ethz.ssh2.HTTPProxyData;
-import ch.ethz.ssh2.HTTPProxyException;
-import ch.ethz.ssh2.ProxyData;
-import ch.ethz.ssh2.ServerHostKeyVerifier;
-import ch.ethz.ssh2.crypto.Base64;
-import ch.ethz.ssh2.crypto.CryptoWishList;
-import ch.ethz.ssh2.crypto.cipher.BlockCipher;
-import ch.ethz.ssh2.crypto.digest.MAC;
-import ch.ethz.ssh2.log.Logger;
-import ch.ethz.ssh2.packets.PacketDisconnect;
-import ch.ethz.ssh2.packets.Packets;
-import ch.ethz.ssh2.packets.TypesReader;
-import ch.ethz.ssh2.server.ServerConnectionState;
-import ch.ethz.ssh2.signature.DSAPrivateKey;
-import ch.ethz.ssh2.signature.RSAPrivateKey;
-import ch.ethz.ssh2.util.StringEncoder;
-import ch.ethz.ssh2.util.Tokenizer;
-
-/*
- * Yes, the "standard" is a big mess. On one side, the say that arbitary channel
- * packets are allowed during kex exchange, on the other side we need to blindly
- * ignore the next _packet_ if the KEX guess was wrong. Where do we know from that
- * the next packet is not a channel data packet? Yes, we could check if it is in
- * the KEX range. But the standard says nothing about this. The OpenSSH guys
- * block local "normal" traffic during KEX. That's fine - however, they assume
- * that the other side is doing the same. During re-key, if they receive traffic
- * other than KEX, they become horribly irritated and kill the connection. Since
- * we are very likely going to communicate with OpenSSH servers, we have to play
- * the same game - even though we could do better.
- * 
- * btw: having stdout and stderr on the same channel, with a shared window, is
- * also a VERY good idea... =(
- */
-
-/**
- * TransportManager.
- *
- * @author Christian Plattner
- * @version $Id: TransportManager.java 47 2013-07-31 23:59:52Z cleondris@gmail.com $
- */
-public class TransportManager
-{
-    private static final Logger log = Logger.getLogger(TransportManager.class);
-
-    private static class HandlerEntry
-    {
-        MessageHandler mh;
-        int low;
-        int high;
-    }
-
-    private final List<AsynchronousEntry> asynchronousQueue = new Vector<AsynchronousEntry>();
-    private Thread asynchronousThread = null;
-    private boolean asynchronousPending = false;
-
-    class AsynchronousEntry
-    {
-        public byte[] msg;
-        public Runnable run;
-
-        public AsynchronousEntry(byte[] msg, Runnable run)
-        {
-            this.msg = msg;
-            this.run = run;
-        }
-    }
-
-    class AsynchronousWorker extends Thread
-    {
-        @Override
-        public void run()
-        {
-            while (true)
-            {
-                AsynchronousEntry item = null;
-
-                synchronized (asynchronousQueue)
-                {
-                    if (asynchronousQueue.size() == 0)
-                    {
-                                               /* Only now we may reset the flag, since we are sure that all queued items
-                                                * have been sent (there is a slight delay between de-queuing and sending,
-                                                * this is why we need this flag! See code below. Sending takes place outside
-                                                * of this lock, this is why a test for size()==0 (from another thread) does not ensure
-                                                * that all messages have been sent.
-                                                */
-
-                        asynchronousPending = false;
-
-                                               /* Notify any senders that they can proceed, all async messages have been delivered */
-
-                        asynchronousQueue.notifyAll();
-
-                                               /* After the queue is empty for about 2 seconds, stop this thread */
-
-                        try
-                        {
-                            asynchronousQueue.wait(2000);
-                        }
-                        catch (InterruptedException ignore)
-                        {
-                        }
-
-                        if (asynchronousQueue.size() == 0)
-                        {
-                            asynchronousThread = null;
-                            return;
-                        }
-                    }
-
-                    item = asynchronousQueue.remove(0);
-                }
-
-                               /* The following invocation may throw an IOException.
-                                * There is no point in handling it - it simply means
-                                * that the connection has a problem and we should stop
-                                * sending asynchronously messages. We do not need to signal that
-                                * we have exited (asynchronousThread = null): further
-                                * messages in the queue cannot be sent by this or any
-                                * other thread.
-                                * Other threads will sooner or later (when receiving or
-                                * sending the next message) get the same IOException and
-                                * get to the same conclusion.
-                                */
-
-                try
-                {
-                    sendMessageImmediate(item.msg);
-                }
-                catch (IOException e)
-                {
-                    return;
-                }
-
-                if (item.run != null)
-                {
-                    try
-                    {
-                        item.run.run();
-                    }
-                    catch (Exception ignore)
-                    {
-                    }
-
-                }
-            }
-        }
-    }
-
-    private Socket sock = new Socket();
-
-    private final Object connectionSemaphore = new Object();
-
-    private boolean flagKexOngoing = false;
-    private boolean connectionClosed = false;
-
-    private Throwable reasonClosedCause = null;
-
-    private TransportConnection tc;
-    private KexManager km;
-
-    private final List<HandlerEntry> messageHandlers = new Vector<HandlerEntry>();
-
-    private Thread receiveThread;
-
-    private List<ConnectionMonitor> connectionMonitors = new Vector<ConnectionMonitor>();
-    private boolean monitorsWereInformed = false;
-
-    /**
-     * There were reports that there are JDKs which use
-     * the resolver even though one supplies a dotted IP
-     * address in the Socket constructor. That is why we
-     * try to generate the InetAdress "by hand".
-     *
-     * @param host
-     * @return the InetAddress
-     * @throws UnknownHostException
-     */
-    private static InetAddress createInetAddress(String host) throws UnknownHostException
-    {
-               /* Check if it is a dotted IP4 address */
-
-        InetAddress addr = parseIPv4Address(host);
-
-        if (addr != null)
-        {
-            return addr;
-        }
-
-        return InetAddress.getByName(host);
-    }
-
-    private static InetAddress parseIPv4Address(String host) throws UnknownHostException
-    {
-        if (host == null)
-        {
-            return null;
-        }
-
-        String[] quad = Tokenizer.parseTokens(host, '.');
-
-        if ((quad == null) || (quad.length != 4))
-        {
-            return null;
-        }
-
-        byte[] addr = new byte[4];
-
-        for (int i = 0; i < 4; i++)
-        {
-            int part = 0;
-
-            if ((quad[i].length() == 0) || (quad[i].length() > 3))
-            {
-                return null;
-            }
-
-            for (int k = 0; k < quad[i].length(); k++)
-            {
-                char c = quad[i].charAt(k);
-
-                               /* No, Character.isDigit is not the same */
-                if ((c < '0') || (c > '9'))
-                {
-                    return null;
-                }
-
-                part = part * 10 + (c - '0');
-            }
-
-            if (part > 255) /* 300.1.2.3 is invalid =) */
-            {
-                return null;
-            }
-
-            addr[i] = (byte) part;
-        }
-
-        return InetAddress.getByAddress(host, addr);
-    }
-
-    public int getPacketOverheadEstimate()
-    {
-        return tc.getPacketOverheadEstimate();
-    }
-
-    public void setTcpNoDelay(boolean state) throws IOException
-    {
-        sock.setTcpNoDelay(state);
-    }
-
-    public void setSoTimeout(int timeout) throws IOException
-    {
-        sock.setSoTimeout(timeout);
-    }
-
-    public ConnectionInfo getConnectionInfo(int kexNumber) throws IOException
-    {
-        return km.getOrWaitForConnectionInfo(kexNumber);
-    }
-
-    public Throwable getReasonClosedCause()
-    {
-        synchronized (connectionSemaphore)
-        {
-            return reasonClosedCause;
-        }
-    }
-
-    public byte[] getSessionIdentifier()
-    {
-        return km.sessionId;
-    }
-
-    public void close(Throwable cause, boolean useDisconnectPacket)
-    {
-        if (useDisconnectPacket == false)
-        {
-                       /* OK, hard shutdown - do not aquire the semaphore,
-                        * perhaps somebody is inside (and waits until the remote
-                        * side is ready to accept new data). */
-
-            try
-            {
-                sock.close();
-            }
-            catch (IOException ignore)
-            {
-            }
-
-                       /* OK, whoever tried to send data, should now agree that
-                        * there is no point in further waiting =)
-                        * It is safe now to aquire the semaphore.
-                        */
-        }
-
-        synchronized (connectionSemaphore)
-        {
-            if (connectionClosed == false)
-            {
-                if (useDisconnectPacket == true)
-                {
-                    try
-                    {
-                        byte[] msg = new PacketDisconnect(Packets.SSH_DISCONNECT_BY_APPLICATION, cause.getMessage(), "")
-                                .getPayload();
-                        if (tc != null)
-                        {
-                            tc.sendMessage(msg);
-                        }
-                    }
-                    catch (IOException ignore)
-                    {
-                    }
-
-                    try
-                    {
-                        sock.close();
-                    }
-                    catch (IOException ignore)
-                    {
-                    }
-                }
-
-                connectionClosed = true;
-                reasonClosedCause = cause; /* may be null */
-            }
-            connectionSemaphore.notifyAll();
-        }
-
-               /* No check if we need to inform the monitors */
-
-        List<ConnectionMonitor> monitors = new Vector<ConnectionMonitor>();
-
-        synchronized (this)
-        {
-                       /* Short term lock to protect "connectionMonitors"
-                        * and "monitorsWereInformed"
-                        * (they may be modified concurrently)
-                        */
-
-            if (monitorsWereInformed == false)
-            {
-                monitorsWereInformed = true;
-                monitors.addAll(connectionMonitors);
-            }
-        }
-
-        for (ConnectionMonitor cmon : monitors)
-        {
-            try
-            {
-                cmon.connectionLost(reasonClosedCause);
-            }
-            catch (Exception ignore)
-            {
-            }
-        }
-    }
-
-    private static Socket establishConnection(String hostname, int port, ProxyData proxyData, int connectTimeout)
-            throws IOException
-    {
-               /* See the comment for createInetAddress() */
-
-        if (proxyData == null)
-        {
-            InetAddress addr = createInetAddress(hostname);
-            Socket s = new Socket();
-            s.connect(new InetSocketAddress(addr, port), connectTimeout);
-            return s;
-        }
-
-        if (proxyData instanceof HTTPProxyData)
-        {
-            HTTPProxyData pd = (HTTPProxyData) proxyData;
-
-                       /* At the moment, we only support HTTP proxies */
-
-            InetAddress addr = createInetAddress(pd.proxyHost);
-            Socket s = new Socket();
-            s.connect(new InetSocketAddress(addr, pd.proxyPort), connectTimeout);
-
-                       /* OK, now tell the proxy where we actually want to connect to */
-
-            StringBuilder sb = new StringBuilder();
-
-            sb.append("CONNECT ");
-            sb.append(hostname);
-            sb.append(':');
-            sb.append(port);
-            sb.append(" HTTP/1.0\r\n");
-
-            if ((pd.proxyUser != null) && (pd.proxyPass != null))
-            {
-                String credentials = pd.proxyUser + ":" + pd.proxyPass;
-                char[] encoded = Base64.encode(StringEncoder.GetBytes(credentials));
-                sb.append("Proxy-Authorization: Basic ");
-                sb.append(encoded);
-                sb.append("\r\n");
-            }
-
-            if (pd.requestHeaderLines != null)
-            {
-                for (int i = 0; i < pd.requestHeaderLines.length; i++)
-                {
-                    if (pd.requestHeaderLines[i] != null)
-                    {
-                        sb.append(pd.requestHeaderLines[i]);
-                        sb.append("\r\n");
-                    }
-                }
-            }
-
-            sb.append("\r\n");
-
-            OutputStream out = s.getOutputStream();
-
-            out.write(StringEncoder.GetBytes(sb.toString()));
-            out.flush();
-
-                       /* Now parse the HTTP response */
-
-            byte[] buffer = new byte[1024];
-            InputStream in = s.getInputStream();
-
-            int len = ClientServerHello.readLineRN(in, buffer);
-
-            String httpReponse = StringEncoder.GetString(buffer, 0, len);
-
-            if (httpReponse.startsWith("HTTP/") == false)
-            {
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-            }
-
-                       /* "HTTP/1.X XYZ X" => 14 characters minimum */
-
-            if ((httpReponse.length() < 14) || (httpReponse.charAt(8) != ' ') || (httpReponse.charAt(12) != ' '))
-            {
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-            }
-
-            int errorCode = 0;
-
-            try
-            {
-                errorCode = Integer.parseInt(httpReponse.substring(9, 12));
-            }
-            catch (NumberFormatException ignore)
-            {
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-            }
-
-            if ((errorCode < 0) || (errorCode > 999))
-            {
-                throw new IOException("The proxy did not send back a valid HTTP response.");
-            }
-
-            if (errorCode != 200)
-            {
-                throw new HTTPProxyException(httpReponse.substring(13), errorCode);
-            }
-
-                       /* OK, read until empty line */
-
-            while (true)
-            {
-                len = ClientServerHello.readLineRN(in, buffer);
-                if (len == 0)
-                {
-                    break;
-                }
-            }
-            return s;
-        }
-
-        throw new IOException("Unsupported ProxyData");
-    }
-
-    private void startReceiver() throws IOException
-    {
-        receiveThread = new Thread(new Runnable()
-        {
-            public void run()
-            {
-                try
-                {
-                    receiveLoop();
-                }
-                catch (Exception e)
-                {
-                    close(e, false);
-
-                    log.warning("Receive thread: error in receiveLoop: " + e.getMessage());
-                }
-
-                if (log.isDebugEnabled())
-                {
-                    log.debug("Receive thread: back from receiveLoop");
-                }
-
-                               /* Tell all handlers that it is time to say goodbye */
-
-                if (km != null)
-                {
-                    try
-                    {
-                        km.handleMessage(null, 0);
-                    }
-                    catch (IOException ignored)
-                    {
-                    }
-                }
-
-                for (HandlerEntry he : messageHandlers)
-                {
-                    try
-                    {
-                        he.mh.handleMessage(null, 0);
-                    }
-                    catch (Exception ignore)
-                    {
-                    }
-                }
-            }
-        });
-
-        receiveThread.setDaemon(true);
-        receiveThread.start();
-    }
-
-    public void clientInit(Socket socket, String softwareversion, CryptoWishList cwl,
-                           ServerHostKeyVerifier verifier, DHGexParameters dhgex, SecureRandom rnd) throws IOException
-    {
-               /* First, establish the TCP connection to the SSH-2 server */
-
-        sock = socket;
-
-               /* Parse the server line and say hello - important: this information is later needed for the
-                * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
-                * for later use.
-                */
-
-        ClientServerHello csh = ClientServerHello.clientHello(softwareversion, sock.getInputStream(),
-                sock.getOutputStream());
-
-        tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd);
-        String hostname = sock.getInetAddress().getHostName();
-        int port = sock.getPort();
-
-        km = new ClientKexManager(this, csh, cwl, hostname, port, verifier, rnd);
-        km.initiateKEX(cwl, dhgex, null, null);
-
-        startReceiver();
-    }
-
-    public void clientInit(String hostname, int port, String softwareversion, CryptoWishList cwl,
-                           ServerHostKeyVerifier verifier, DHGexParameters dhgex, int connectTimeout, SecureRandom rnd,
-                           ProxyData proxyData) throws IOException
-    {
-               /* First, establish the TCP connection to the SSH-2 server */
-
-        sock = establishConnection(hostname, port, proxyData, connectTimeout);
-
-               /* Parse the server line and say hello - important: this information is later needed for the
-                * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
-                * for later use.
-                */
-
-        ClientServerHello csh = ClientServerHello.clientHello(softwareversion, sock.getInputStream(),
-                sock.getOutputStream());
-
-        tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd);
-
-        km = new ClientKexManager(this, csh, cwl, hostname, port, verifier, rnd);
-        km.initiateKEX(cwl, dhgex, null, null);
-
-        startReceiver();
-    }
-
-    public void serverInit(ServerConnectionState state) throws IOException
-    {
-               /* TCP connection is already established */
-
-        this.sock = state.s;
-
-               /* Parse the client line and say hello - important: this information is later needed for the
-                * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
-                * for later use.
-                */
-
-        state.csh = ClientServerHello.serverHello(state.softwareversion, sock.getInputStream(), sock.getOutputStream());
-
-        tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), state.generator);
-
-        km = new ServerKexManager(state);
-        km.initiateKEX(state.next_cryptoWishList, null, state.next_dsa_key, state.next_rsa_key);
-
-        startReceiver();
-    }
-
-    public void registerMessageHandler(MessageHandler mh, int low, int high)
-    {
-        HandlerEntry he = new HandlerEntry();
-        he.mh = mh;
-        he.low = low;
-        he.high = high;
-
-        synchronized (messageHandlers)
-        {
-            messageHandlers.add(he);
-        }
-    }
-
-    public void removeMessageHandler(MessageHandler mh, int low, int high)
-    {
-        synchronized (messageHandlers)
-        {
-            for (int i = 0; i < messageHandlers.size(); i++)
-            {
-                HandlerEntry he = messageHandlers.get(i);
-                if ((he.mh == mh) && (he.low == low) && (he.high == high))
-                {
-                    messageHandlers.remove(i);
-                    break;
-                }
-            }
-        }
-    }
-
-    public void sendKexMessage(byte[] msg) throws IOException
-    {
-        synchronized (connectionSemaphore)
-        {
-            if (connectionClosed)
-            {
-                throw (IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause);
-            }
-
-            flagKexOngoing = true;
-
-            try
-            {
-                tc.sendMessage(msg);
-            }
-            catch (IOException e)
-            {
-                close(e, false);
-                throw e;
-            }
-        }
-    }
-
-    public void kexFinished() throws IOException
-    {
-        synchronized (connectionSemaphore)
-        {
-            flagKexOngoing = false;
-            connectionSemaphore.notifyAll();
-        }
-    }
-
-    /**
-     *
-     * @param cwl
-     * @param dhgex
-     * @param dsa may be null if this is a client connection
-     * @param rsa may be null if this is a client connection
-     * @throws IOException
-     */
-    public void forceKeyExchange(CryptoWishList cwl, DHGexParameters dhgex, DSAPrivateKey dsa, RSAPrivateKey rsa)
-            throws IOException
-    {
-        synchronized (connectionSemaphore)
-        {
-            if (connectionClosed)
-                               /* Inform the caller that there is no point in triggering a new kex */
-                throw (IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause);
-        }
-
-        km.initiateKEX(cwl, dhgex, dsa, rsa);
-    }
-
-    public void changeRecvCipher(BlockCipher bc, MAC mac)
-    {
-        tc.changeRecvCipher(bc, mac);
-    }
-
-    public void changeSendCipher(BlockCipher bc, MAC mac)
-    {
-        tc.changeSendCipher(bc, mac);
-    }
-
-    public void sendAsynchronousMessage(byte[] msg) throws IOException
-    {
-        sendAsynchronousMessage(msg, null);
-    }
-
-    public void sendAsynchronousMessage(byte[] msg, Runnable run) throws IOException
-    {
-        synchronized (asynchronousQueue)
-        {
-            asynchronousQueue.add(new AsynchronousEntry(msg, run));
-            asynchronousPending = true;
-
-                       /* This limit should be flexible enough. We need this, otherwise the peer
-                        * can flood us with global requests (and other stuff where we have to reply
-                        * with an asynchronous message) and (if the server just sends data and does not
-                        * read what we send) this will probably put us in a low memory situation
-                        * (our send queue would grow and grow and...) */
-
-            if (asynchronousQueue.size() > 100)
-            {
-                throw new IOException("Error: the peer is not consuming our asynchronous replies.");
-            }
-
-                       /* Check if we have an asynchronous sending thread */
-
-            if (asynchronousThread == null)
-            {
-                asynchronousThread = new AsynchronousWorker();
-                asynchronousThread.setDaemon(true);
-                asynchronousThread.start();
-
-                               /* The thread will stop after 2 seconds of inactivity (i.e., empty queue) */
-            }
-
-            asynchronousQueue.notifyAll();
-        }
-    }
-
-    public void setConnectionMonitors(List<ConnectionMonitor> monitors)
-    {
-        synchronized (this)
-        {
-            connectionMonitors = new Vector<ConnectionMonitor>();
-            connectionMonitors.addAll(monitors);
-        }
-    }
-
-    /**
-     * True if no response message expected.
-     */
-    private boolean idle;
-
-    /**
-     * Send a message but ensure that all queued messages are being sent first.
-     *
-     * @param msg
-     * @throws IOException
-     */
-    public void sendMessage(byte[] msg) throws IOException
-    {
-        synchronized (asynchronousQueue)
-        {
-            while (asynchronousPending)
-            {
-                try
-                {
-                    asynchronousQueue.wait(1000);
-                }
-                catch (InterruptedException e)
-                {
-                }
-            }
-        }
-
-        sendMessageImmediate(msg);
-    }
-
-    /**
-     * Send message, ignore queued async messages that have not been delivered yet.
-     * Will be called directly from the asynchronousThread thread.
-     *
-     * @param msg
-     * @throws IOException
-     */
-    public void sendMessageImmediate(byte[] msg) throws IOException
-    {
-        if (Thread.currentThread() == receiveThread)
-        {
-            throw new IOException("Assertion error: sendMessage may never be invoked by the receiver thread!");
-        }
-
-        boolean wasInterrupted = false;
-
-        try
-        {
-            synchronized (connectionSemaphore)
-            {
-                while (true)
-                {
-                    if (connectionClosed)
-                    {
-                        throw (IOException) new IOException("Sorry, this connection is closed.")
-                                .initCause(reasonClosedCause);
-                    }
-
-                    if (flagKexOngoing == false)
-                    {
-                        break;
-                    }
-
-                    try
-                    {
-                        connectionSemaphore.wait();
-                    }
-                    catch (InterruptedException e)
-                    {
-                        wasInterrupted = true;
-                    }
-                }
-
-                try
-                {
-                    tc.sendMessage(msg);
-                    idle = false;
-                }
-                catch (IOException e)
-                {
-                    close(e, false);
-                    throw e;
-                }
-            }
-        }
-        finally
-        {
-            if (wasInterrupted)
-                Thread.currentThread().interrupt();
-        }
-    }
-
-    public void receiveLoop() throws IOException
-    {
-        byte[] msg = new byte[35000];
-
-        while (true)
-        {
-            int msglen;
-            try
-            {
-                msglen = tc.receiveMessage(msg, 0, msg.length);
-            }
-            catch (SocketTimeoutException e)
-            {
-                // Timeout in read
-                if (idle)
-                {
-                    log.debug("Ignoring socket timeout");
-                    continue;
-                }
-                throw e;
-            }
-            idle = true;
-
-            int type = msg[0] & 0xff;
-
-            if (type == Packets.SSH_MSG_IGNORE)
-            {
-                continue;
-            }
-
-            if (type == Packets.SSH_MSG_DEBUG)
-            {
-                if (log.isDebugEnabled())
-                {
-                    TypesReader tr = new TypesReader(msg, 0, msglen);
-                    tr.readByte();
-                    tr.readBoolean();
-                    StringBuilder debugMessageBuffer = new StringBuilder();
-                    debugMessageBuffer.append(tr.readString("UTF-8"));
-
-                    for (int i = 0; i < debugMessageBuffer.length(); i++)
-                    {
-                        char c = debugMessageBuffer.charAt(i);
-
-                        if ((c >= 32) && (c <= 126))
-                        {
-                            continue;
-                        }
-                        debugMessageBuffer.setCharAt(i, '\uFFFD');
-                    }
-
-                    log.debug("DEBUG Message from remote: '" + debugMessageBuffer.toString() + "'");
-                }
-                continue;
-            }
-
-            if (type == Packets.SSH_MSG_UNIMPLEMENTED)
-            {
-                throw new IOException("Peer sent UNIMPLEMENTED message, that should not happen.");
-            }
-
-            if (type == Packets.SSH_MSG_DISCONNECT)
-            {
-                TypesReader tr = new TypesReader(msg, 0, msglen);
-                tr.readByte();
-                int reason_code = tr.readUINT32();
-                StringBuilder reasonBuffer = new StringBuilder();
-                reasonBuffer.append(tr.readString("UTF-8"));
-
-                               /*
-                                * Do not get fooled by servers that send abnormal long error
-                                * messages
-                                */
-
-                if (reasonBuffer.length() > 255)
-                {
-                    reasonBuffer.setLength(255);
-                    reasonBuffer.setCharAt(254, '.');
-                    reasonBuffer.setCharAt(253, '.');
-                    reasonBuffer.setCharAt(252, '.');
-                }
-
-                               /*
-                                * Also, check that the server did not send characters that may
-                                * screw up the receiver -> restrict to reasonable US-ASCII
-                                * subset -> "printable characters" (ASCII 32 - 126). Replace
-                                * all others with 0xFFFD (UNICODE replacement character).
-                                */
-
-                for (int i = 0; i < reasonBuffer.length(); i++)
-                {
-                    char c = reasonBuffer.charAt(i);
-
-                    if ((c >= 32) && (c <= 126))
-                    {
-                        continue;
-                    }
-                    reasonBuffer.setCharAt(i, '\uFFFD');
-                }
-
-                throw new IOException("Peer sent DISCONNECT message (reason code " + reason_code + "): "
-                        + reasonBuffer.toString());
-            }
-
-                       /*
-                        * Is it a KEX Packet?
-                        */
-
-            if ((type == Packets.SSH_MSG_KEXINIT) || (type == Packets.SSH_MSG_NEWKEYS)
-                    || ((type >= 30) && (type <= 49)))
-            {
-                km.handleMessage(msg, msglen);
-                continue;
-            }
-
-            MessageHandler mh = null;
-
-            for (int i = 0; i < messageHandlers.size(); i++)
-            {
-                HandlerEntry he = messageHandlers.get(i);
-                if ((he.low <= type) && (type <= he.high))
-                {
-                    mh = he.mh;
-                    break;
-                }
-            }
-
-            if (mh == null)
-            {
-                throw new IOException("Unexpected SSH message (type " + type + ")");
-            }
-
-            mh.handleMessage(msg, msglen);
-        }
-    }
-}