Merge "Snapshot changes"
authorMoiz Raja <moraja@cisco.com>
Fri, 1 Aug 2014 23:22:09 +0000 (23:22 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 1 Aug 2014 23:22:09 +0000 (23:22 +0000)
68 files changed:
features/config-netty/src/main/resources/features.xml
opendaylight/commons/opendaylight/pom.xml
opendaylight/commons/protocol-framework/pom.xml
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/AbstractDispatcher.java
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ProtocolSessionPromise.java
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/ReconnectPromise.java
opendaylight/commons/protocol-framework/src/main/java/org/opendaylight/protocol/framework/SessionListener.java
opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/ServerTest.java
opendaylight/commons/protocol-framework/src/test/java/org/opendaylight/protocol/framework/SimpleDispatcher.java
opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/mapping/CodecRegistryProvider.java
opendaylight/config/config-netty-config/pom.xml [new file with mode: 0644]
opendaylight/config/config-netty-config/src/main/resources/initial/00-netty.xml [moved from opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/00-netty.xml with 100% similarity]
opendaylight/config/pom.xml
opendaylight/distribution/opendaylight/pom.xml
opendaylight/md-sal/md-sal-config/pom.xml [new file with mode: 0644]
opendaylight/md-sal/md-sal-config/src/main/resources/initial/01-md-sal.xml [moved from opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml with 100% similarity]
opendaylight/md-sal/pom.xml
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/MountPointManagerImpl.java
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1333DataChangeListenerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStoreFactory.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/CarsModel.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/PeopleModel.java
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/md/cluster/datastore/model/SampleModelsTest.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/model/SchemaService.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomBrokerImplModule.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/DomInmemoryDataBrokerModule.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/dom/impl/SchemaServiceImplSingletonModule.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/broker/impl/compat/BackwardsCompatibleDataBroker.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/BackwardsCompatibleMountPoint.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/GlobalBundleScanningSchemaServiceImpl.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/SchemaServiceProxy.java
opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryConfigDataStoreProviderModule.java
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/config/yang/inmemory_datastore_provider/InMemoryOperationalDataStoreProviderModule.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/NetconfDeviceDataBroker.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceReadOnlyTx.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java
opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilitiesTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorConstants.java [new file with mode: 0644]
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcProvider.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcManager.java
opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfProviderImpl.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/streams/listeners/Notificator.java
opendaylight/md-sal/samples/pom.xml
opendaylight/md-sal/samples/toaster-config/pom.xml [new file with mode: 0644]
opendaylight/md-sal/samples/toaster-config/src/main/resources/initial/03-toaster-sample.xml [moved from opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/03-toaster-sample.xml with 99% similarity]
opendaylight/netconf/netconf-config/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-config/src/main/resources/initial/01-netconf.xml [moved from opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-netconf.xml with 100% similarity]
opendaylight/netconf/netconf-connector-config/pom.xml [new file with mode: 0644]
opendaylight/netconf/netconf-connector-config/src/main/resources/initial/99-netconf-connector.xml [moved from opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/99-netconf-connector.xml with 100% similarity]
opendaylight/netconf/pom.xml
opendaylight/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/NeutronSubnet.java
opendaylight/northbound/networkconfiguration/neutron/src/main/java/org/opendaylight/controller/networkconfig/neutron/northbound/NeutronRoutersNorthbound.java

index 3121ca0..f1b2d1f 100644 (file)
@@ -12,5 +12,6 @@
     <bundle>mvn:org.opendaylight.controller/threadpool-config-api/${project.version}</bundle>
     <bundle>mvn:org.opendaylight.controller/threadpool-config-impl/${project.version}</bundle>
     <feature version='${project.version}'>odl-config-startup</feature>
+    <configfile finalname="configuration/initial/00-netty.xml">mvn:org.opendaylight.controller/config-netty-config/${config.version}/xml/config</configfile>
   </feature>
 </features>
\ No newline at end of file
index be88e4a..cddff45 100644 (file)
         <artifactId>sal-rest-connector-config</artifactId>
         <version>${mdsal.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>config-netty-config</artifactId>
+        <version>${config.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <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>sample-toaster-provider</artifactId>
         <version>${mdsal.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.opendaylight.controller.samples</groupId>
+        <artifactId>toaster-config</artifactId>
+        <version>${mdsal.version}</version>
+      </dependency>
       <dependency>
         <groupId>org.opendaylight.controller.thirdparty</groupId>
         <artifactId>com.sun.jersey.jersey-servlet</artifactId>
index f706987..774bc7c 100644 (file)
       <artifactId>netty-event-executor-config</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>ch.qos.logback</groupId>
+      <artifactId>logback-classic</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
index a62bd7d..a05d02c 100644 (file)
@@ -7,12 +7,19 @@
  */
 package org.opendaylight.protocol.framework;
 
+import java.io.Closeable;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.google.common.base.Preconditions;
 
 import io.netty.bootstrap.Bootstrap;
 import io.netty.bootstrap.ServerBootstrap;
-import io.netty.channel.Channel;
 import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelOption;
@@ -28,13 +35,6 @@ import io.netty.util.concurrent.Future;
 import io.netty.util.concurrent.GlobalEventExecutor;
 import io.netty.util.concurrent.Promise;
 
-import java.io.Closeable;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 /**
  * Dispatcher class for creating servers and clients. The idea is to first create servers and clients and the run the
  * start method that will handle sockets in different thread.
@@ -155,7 +155,7 @@ public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends
      */
     protected Future<S> createClient(final InetSocketAddress address, final ReconnectStrategy strategy, final PipelineInitializer<S> initializer) {
         final Bootstrap b = new Bootstrap();
-        final ProtocolSessionPromise<S> p = new ProtocolSessionPromise<S>(executor, address, strategy, b);
+        final ProtocolSessionPromise<S> p = new ProtocolSessionPromise<>(executor, address, strategy, b);
         b.option(ChannelOption.SO_KEEPALIVE, true).handler(
                 new ChannelInitializer<SocketChannel>() {
                     @Override
@@ -165,18 +165,36 @@ public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends
                 });
 
         customizeBootstrap(b);
+        setWorkerGroup(b);
+        setChannelFactory(b);
+
+        p.connect();
+        LOG.debug("Client created.");
+        return p;
+    }
 
+    private void setWorkerGroup(final Bootstrap b) {
         if (b.group() == null) {
             b.group(workerGroup);
         }
+    }
 
-        // There is no way to detect if this was already set by
-        // customizeBootstrap()
-        try {
-            b.channel(NioSocketChannel.class);
-        } catch (IllegalStateException e) {
-            LOG.trace("Not overriding channelFactory on bootstrap {}", b, e);
-        }
+    /**
+     * Create a client but use a pre-configured bootstrap.
+     * This method however replaces the ChannelInitializer in the bootstrap. All other configuration is preserved.
+     *
+     * @param address remote address
+     */
+    protected Future<S> createClient(final InetSocketAddress address, final ReconnectStrategy strategy, final Bootstrap bootstrap, final PipelineInitializer<S> initializer) {
+        final ProtocolSessionPromise<S> p = new ProtocolSessionPromise<>(executor, address, strategy, bootstrap);
+
+        bootstrap.handler(
+                new ChannelInitializer<SocketChannel>() {
+                    @Override
+                    protected void initChannel(final SocketChannel ch) {
+                        initializer.initializeChannel(ch, p);
+                    }
+                });
 
         p.connect();
         LOG.debug("Client created.");
@@ -195,6 +213,9 @@ public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends
     }
 
     /**
+     *
+     * @deprecated use {@link org.opendaylight.protocol.framework.AbstractDispatcher#createReconnectingClient(java.net.InetSocketAddress, ReconnectStrategyFactory, org.opendaylight.protocol.framework.AbstractDispatcher.PipelineInitializer)} with only one reconnectStrategyFactory instead.
+     *
      * Creates a client.
      *
      * @param address remote address
@@ -204,15 +225,47 @@ public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends
      * @return Future representing the reconnection task. It will report completion based on reestablishStrategy, e.g.
      *         success if it indicates no further attempts should be made and failure if it reports an error
      */
+    @Deprecated
     protected Future<Void> createReconnectingClient(final InetSocketAddress address, final ReconnectStrategyFactory connectStrategyFactory,
             final ReconnectStrategy reestablishStrategy, final PipelineInitializer<S> initializer) {
+        return createReconnectingClient(address, connectStrategyFactory, initializer);
+    }
 
-        final ReconnectPromise<S, L> p = new ReconnectPromise<S, L>(GlobalEventExecutor.INSTANCE, this, address, connectStrategyFactory, reestablishStrategy, initializer);
-        p.connect();
+    /**
+     * Creates a reconnecting client.
+     *
+     * @param address remote address
+     * @param connectStrategyFactory Factory for creating reconnection strategy for every reconnect attempt
+     *
+     * @return Future representing the reconnection task. It will report completion based on reestablishStrategy, e.g.
+     *         success if it indicates no further attempts should be made and failure if it reports an error
+     */
+    protected Future<Void> createReconnectingClient(final InetSocketAddress address, final ReconnectStrategyFactory connectStrategyFactory,
+            final PipelineInitializer<S> initializer) {
+        final Bootstrap b = new Bootstrap();
+
+        final ReconnectPromise<S, L> p = new ReconnectPromise<>(GlobalEventExecutor.INSTANCE, this, address, connectStrategyFactory, b, initializer);
+
+        b.option(ChannelOption.SO_KEEPALIVE, true);
 
+        customizeBootstrap(b);
+        setWorkerGroup(b);
+        setChannelFactory(b);
+
+        p.connect();
         return p;
     }
 
+    private void setChannelFactory(final Bootstrap b) {
+        // There is no way to detect if this was already set by
+        // customizeBootstrap()
+        try {
+            b.channel(NioSocketChannel.class);
+        } catch (final IllegalStateException e) {
+            LOG.trace("Not overriding channelFactory on bootstrap {}", b, e);
+        }
+    }
+
     /**
      * @deprecated Should only be used with {@link AbstractDispatcher#AbstractDispatcher()}
      */
@@ -225,5 +278,4 @@ public abstract class AbstractDispatcher<S extends ProtocolSession<?>, L extends
             this.bossGroup.shutdownGracefully();
         }
     }
-
 }
index a78274c..a38db61 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.protocol.framework;
 
+import com.google.common.base.Preconditions;
 import io.netty.bootstrap.Bootstrap;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.ChannelFutureListener;
@@ -16,17 +17,12 @@ import io.netty.util.concurrent.EventExecutor;
 import io.netty.util.concurrent.Future;
 import io.netty.util.concurrent.FutureListener;
 import io.netty.util.concurrent.Promise;
-
 import java.net.InetSocketAddress;
-
 import javax.annotation.concurrent.GuardedBy;
 import javax.annotation.concurrent.ThreadSafe;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Preconditions;
-
 @ThreadSafe
 final class ProtocolSessionPromise<S extends ProtocolSession<?>> extends DefaultPromise<S> {
     private static final Logger LOG = LoggerFactory.getLogger(ProtocolSessionPromise.class);
@@ -54,72 +50,12 @@ final class ProtocolSessionPromise<S extends ProtocolSession<?>> extends Default
             LOG.debug("Promise {} attempting connect for {}ms", lock, timeout);
 
             this.b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, timeout);
-            this.pending = this.b.connect(this.address).addListener(new ChannelFutureListener() {
-                @Override
-                public void operationComplete(final ChannelFuture cf) throws Exception {
-                    synchronized (lock) {
-
-                        LOG.debug("Promise {} connection resolved", lock);
-
-                        // Triggered when a connection attempt is resolved.
-                        Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(cf));
-
-                        /*
-                         * The promise we gave out could have been cancelled,
-                         * which cascades to the connect getting cancelled,
-                         * but there is a slight race window, where the connect
-                         * is already resolved, but the listener has not yet
-                         * been notified -- cancellation at that point won't
-                         * stop the notification arriving, so we have to close
-                         * the race here.
-                         */
-                        if (isCancelled()) {
-                            if (cf.isSuccess()) {
-                                LOG.debug("Closing channel for cancelled promise {}", lock);
-                                cf.channel().close();
-                            }
-                            return;
-                        }
-
-                        if (!cf.isSuccess()) {
-                            LOG.debug("Attempt to connect to {} failed", ProtocolSessionPromise.this.address, cf.cause());
-
-                            final Future<Void> rf = ProtocolSessionPromise.this.strategy.scheduleReconnect(cf.cause());
-                            rf.addListener(new FutureListener<Void>() {
-                                @Override
-                                public void operationComplete(final Future<Void> sf) {
-                                    synchronized (lock) {
-                                        // Triggered when a connection attempt is to be made.
-                                        Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(sf));
-
-                                        /*
-                                         * The promise we gave out could have been cancelled,
-                                         * which cascades to the reconnect attempt getting
-                                         * cancelled, but there is a slight race window, where
-                                         * the reconnect attempt is already enqueued, but the
-                                         * listener has not yet been notified -- if cancellation
-                                         * happens at that point, we need to catch it here.
-                                         */
-                                        if (!isCancelled()) {
-                                            if (sf.isSuccess()) {
-                                                connect();
-                                            } else {
-                                                setFailure(sf.cause());
-                                            }
-                                        }
-                                    }
-                                }
-                            });
-
-                            ProtocolSessionPromise.this.pending = rf;
-                        } else {
-                            LOG.debug("Promise {} connection successful", lock);
-                        }
-                    }
-                }
-            });
+            final ChannelFuture connectFuture = this.b.connect(this.address);
+            // Add listener that attempts reconnect by invoking this method again.
+            connectFuture.addListener(new BootstrapConnectListener(lock));
+            this.pending = connectFuture;
         } catch (final Exception e) {
-            LOG.info("Failed to connect to {}", e);
+            LOG.info("Failed to connect to {}", address, e);
             setFailure(e);
         }
     }
@@ -140,4 +76,79 @@ final class ProtocolSessionPromise<S extends ProtocolSession<?>> extends Default
         this.strategy.reconnectSuccessful();
         return super.setSuccess(result);
     }
+
+    private class BootstrapConnectListener implements ChannelFutureListener {
+        private final Object lock;
+
+        public BootstrapConnectListener(final Object lock) {
+            this.lock = lock;
+        }
+
+        @Override
+        public void operationComplete(final ChannelFuture cf) throws Exception {
+            synchronized (lock) {
+
+                LOG.debug("Promise {} connection resolved", lock);
+
+                // Triggered when a connection attempt is resolved.
+                Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(cf));
+
+                /*
+                 * The promise we gave out could have been cancelled,
+                 * which cascades to the connect getting cancelled,
+                 * but there is a slight race window, where the connect
+                 * is already resolved, but the listener has not yet
+                 * been notified -- cancellation at that point won't
+                 * stop the notification arriving, so we have to close
+                 * the race here.
+                 */
+                if (isCancelled()) {
+                    if (cf.isSuccess()) {
+                        LOG.debug("Closing channel for cancelled promise {}", lock);
+                        cf.channel().close();
+                    }
+                    return;
+                }
+
+                if(cf.isSuccess()) {
+                    LOG.debug("Promise {} connection successful", lock);
+                    return;
+                }
+
+                LOG.debug("Attempt to connect to {} failed", ProtocolSessionPromise.this.address, cf.cause());
+
+                final Future<Void> rf = ProtocolSessionPromise.this.strategy.scheduleReconnect(cf.cause());
+                rf.addListener(new ReconnectingStrategyListener());
+                ProtocolSessionPromise.this.pending = rf;
+            }
+        }
+
+        private class ReconnectingStrategyListener implements FutureListener<Void> {
+            @Override
+            public void operationComplete(final Future<Void> sf) {
+                synchronized (lock) {
+                    // Triggered when a connection attempt is to be made.
+                    Preconditions.checkState(ProtocolSessionPromise.this.pending.equals(sf));
+
+                    /*
+                     * The promise we gave out could have been cancelled,
+                     * which cascades to the reconnect attempt getting
+                     * cancelled, but there is a slight race window, where
+                     * the reconnect attempt is already enqueued, but the
+                     * listener has not yet been notified -- if cancellation
+                     * happens at that point, we need to catch it here.
+                     */
+                    if (!isCancelled()) {
+                        if (sf.isSuccess()) {
+                            connect();
+                        } else {
+                            setFailure(sf.cause());
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
 }
index 1fa6a81..fe1012f 100644 (file)
  */
 package org.opendaylight.protocol.framework;
 
-import io.netty.channel.ChannelFuture;
+import com.google.common.base.Preconditions;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.util.concurrent.DefaultPromise;
 import io.netty.util.concurrent.EventExecutor;
 import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.FutureListener;
 import io.netty.util.concurrent.Promise;
-
-import java.io.Closeable;
 import java.net.InetSocketAddress;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.opendaylight.protocol.framework.AbstractDispatcher.PipelineInitializer;
-
-import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 final class ReconnectPromise<S extends ProtocolSession<?>, L extends SessionListener<?, ?, ?>> extends DefaultPromise<Void> {
+    private static final Logger LOG = LoggerFactory.getLogger(ReconnectPromise.class);
+
     private final AbstractDispatcher<S, L> dispatcher;
     private final InetSocketAddress address;
     private final ReconnectStrategyFactory strategyFactory;
-    private final ReconnectStrategy strategy;
-    private final PipelineInitializer<S> initializer;
+    private final Bootstrap b;
+    private final AbstractDispatcher.PipelineInitializer<S> initializer;
     private Future<?> pending;
 
-    private final AtomicBoolean negotiationFinished = new AtomicBoolean(false);
-
     public ReconnectPromise(final EventExecutor executor, final AbstractDispatcher<S, L> dispatcher, final InetSocketAddress address,
-            final ReconnectStrategyFactory connectStrategyFactory, final ReconnectStrategy reestablishStrategy,
-            final PipelineInitializer<S> initializer) {
+                            final ReconnectStrategyFactory connectStrategyFactory, final Bootstrap b, final AbstractDispatcher.PipelineInitializer<S> initializer) {
         super(executor);
+        this.b = b;
+        this.initializer = Preconditions.checkNotNull(initializer);
         this.dispatcher = Preconditions.checkNotNull(dispatcher);
         this.address = Preconditions.checkNotNull(address);
         this.strategyFactory = Preconditions.checkNotNull(connectStrategyFactory);
-        this.strategy = Preconditions.checkNotNull(reestablishStrategy);
-        this.initializer = Preconditions.checkNotNull(initializer);
     }
 
-    // FIXME: BUG-190: refactor
-
     synchronized void connect() {
-        negotiationFinished.set(false);
-
         final ReconnectStrategy cs = this.strategyFactory.createReconnectStrategy();
-        final ReconnectStrategy rs = new ReconnectStrategy() {
-            @Override
-            public Future<Void> scheduleReconnect(final Throwable cause) {
-                return cs.scheduleReconnect(cause);
-            }
 
-            @Override
-            public void reconnectSuccessful() {
-                cs.reconnectSuccessful();
-            }
-
-            @Override
-            public int getConnectTimeout() throws Exception {
-                final int cst = cs.getConnectTimeout();
-                final int rst = ReconnectPromise.this.strategy.getConnectTimeout();
-
-                if (cst == 0) {
-                    return rst;
-                }
-                if (rst == 0) {
-                    return cst;
-                }
-                return Math.min(cst, rst);
-            }
-        };
-
-        final Future<S> cf = this.dispatcher.createClient(this.address, rs, new PipelineInitializer<S>() {
+        // Set up a client with pre-configured bootstrap, but add a closed channel handler into the pipeline to support reconnect attempts
+        pending = this.dispatcher.createClient(this.address, cs, b, new AbstractDispatcher.PipelineInitializer<S>() {
             @Override
             public void initializeChannel(final SocketChannel channel, final Promise<S> promise) {
-                addChannelClosedListener(channel.closeFuture());
                 initializer.initializeChannel(channel, promise);
+
+                // add closed channel handler
+                channel.pipeline().addFirst(new ClosedChannelHandler(ReconnectPromise.this));
             }
         });
+    }
 
-        final Object lock = this;
-        this.pending = cf;
+    /**
+     *
+     * @return true if initial connection was established successfully, false if initial connection failed due to e.g. Connection refused, Negotiation failed
+     */
+    private boolean isInitialConnectFinished() {
+        Preconditions.checkNotNull(pending);
+        return pending.isDone() && pending.isSuccess();
+    }
 
-        cf.addListener(new FutureListener<S>() {
+    @Override
+    public synchronized boolean cancel(final boolean mayInterruptIfRunning) {
+        if (super.cancel(mayInterruptIfRunning)) {
+            Preconditions.checkNotNull(pending);
+            this.pending.cancel(mayInterruptIfRunning);
+            return true;
+        }
 
-            @Override
-            public void operationComplete(final Future<S> future) {
-                synchronized (lock) {
-                    if (!future.isSuccess()) {
-                        final Future<Void> rf = ReconnectPromise.this.strategy.scheduleReconnect(cf.cause());
-
-                        if(rf == null) {
-                            // This should reflect: no more reconnecting strategies, enough
-                            // Currently all reconnect strategies fail with exception, should return null
-                            return;
-                        }
-
-                        ReconnectPromise.this.pending = rf;
-
-                        rf.addListener(new FutureListener<Void>() {
-                            @Override
-                            public void operationComplete(final Future<Void> sf) {
-                                synchronized (lock) {
-                                    /*
-                                     * The promise we gave out could have been cancelled,
-                                     * which cascades to the reconnect attempt getting
-                                     * cancelled, but there is a slight race window, where
-                                     * the reconnect attempt is already enqueued, but the
-                                     * listener has not yet been notified -- if cancellation
-                                     * happens at that point, we need to catch it here.
-                                     */
-                                    if (!isCancelled()) {
-                                        if (sf.isSuccess()) {
-                                            connect();
-                                        } else {
-                                            setFailure(sf.cause());
-                                        }
-                                    }
-                                }
-                            }
-                        });
-                    } else {
-                        /*
-                         *  FIXME: BUG-190: we have a slight race window with cancellation
-                         *         here. Analyze and define its semantics.
-                         */
-                        ReconnectPromise.this.strategy.reconnectSuccessful();
-                        negotiationFinished.set(true);
-                    }
-                }
-            }
-        });
+        return false;
     }
 
-    private final ClosedChannelListener closedChannelListener = new ClosedChannelListener();
-
-    class ClosedChannelListener implements Closeable, FutureListener<Void> {
+    /**
+     * Channel handler that responds to channelInactive event and reconnects the session.
+     * Only if the initial connection was successfully established and promise was not canceled.
+     */
+    private static final class ClosedChannelHandler extends ChannelInboundHandlerAdapter {
+        private final ReconnectPromise<?, ?> promise;
 
-        private final AtomicBoolean stop = new AtomicBoolean(false);
+        public ClosedChannelHandler(final ReconnectPromise<?, ?> promise) {
+            this.promise = promise;
+        }
 
         @Override
-        public void operationComplete(final Future<Void> future) throws Exception {
-            if (stop.get()) {
+        public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
+            if (promise.isCancelled()) {
                 return;
             }
 
-            // Start reconnecting crashed session after negotiation was successful
-            if (!negotiationFinished.get()) {
+            // 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;
             }
 
-            connect();
-        }
-
-        @Override
-        public void close() {
-            this.stop.set(true);
+            LOG.debug("Reconnecting after connection to {} was dropped", promise.address);
+            promise.connect();
         }
     }
 
-    private void addChannelClosedListener(final ChannelFuture channelFuture) {
-        channelFuture.addListener(closedChannelListener);
-    }
-
-    @Override
-    public synchronized boolean cancel(final boolean mayInterruptIfRunning) {
-        closedChannelListener.close();
-
-        if (super.cancel(mayInterruptIfRunning)) {
-            this.pending.cancel(mayInterruptIfRunning);
-            return true;
-        }
-
-        return false;
-    }
 }
index 3c429fc..a756a0d 100644 (file)
@@ -10,7 +10,7 @@ package org.opendaylight.protocol.framework;
 import java.util.EventListener;
 
 /**
- * Listener that receives session state informations. This interface should be
+ * Listener that receives session state information. This interface should be
  * implemented by a protocol specific abstract class, that is extended by
  * a final class that implements the methods.
  */
index bead1ee..63026e3 100644 (file)
@@ -9,6 +9,14 @@ package org.opendaylight.protocol.framework;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelFuture;
 import io.netty.channel.nio.NioEventLoopGroup;
@@ -16,50 +24,139 @@ import io.netty.util.concurrent.DefaultPromise;
 import io.netty.util.concurrent.Future;
 import io.netty.util.concurrent.GlobalEventExecutor;
 import io.netty.util.concurrent.Promise;
-
+import io.netty.util.concurrent.SucceededFuture;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mockito;
 
 public class ServerTest {
     SimpleDispatcher clientDispatcher, dispatcher;
 
-    final SimpleSessionListener pce = new SimpleSessionListener();
-
     SimpleSession session = null;
 
     ChannelFuture server = null;
 
     InetSocketAddress serverAddress;
     private NioEventLoopGroup eventLoopGroup;
-
+    // Dedicated loop group for server, needed for testing reconnection client
+    // With dedicated server group we can simulate session drop by shutting only the server group down
+    private NioEventLoopGroup serverLoopGroup;
 
     @Before
     public void setUp() {
         final int port = 10000 + (int)(10000 * Math.random());
         serverAddress = new InetSocketAddress("127.0.0.1", port);
         eventLoopGroup = new NioEventLoopGroup();
+        serverLoopGroup = new NioEventLoopGroup();
+    }
+
+    @After
+    public void tearDown() throws IOException, InterruptedException, ExecutionException {
+        if(server != null) {
+            this.server.channel().close();
+        }
+        this.eventLoopGroup.shutdownGracefully().get();
+        this.serverLoopGroup.shutdownGracefully().get();
+        try {
+            Thread.sleep(500);
+        } catch (final InterruptedException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     @Test
-    public void testConnectionEstablished() throws Exception {
+    public void testConnectionRefused() throws Exception {
+        this.clientDispatcher = getClientDispatcher();
+
+        final ReconnectStrategy mockReconnectStrategy = getMockedReconnectStrategy();
+
+        this.clientDispatcher.createClient(this.serverAddress,
+                mockReconnectStrategy, new SessionListenerFactory<SimpleSessionListener>() {
+                    @Override
+                    public SimpleSessionListener getSessionListener() {
+                        return new SimpleSessionListener();
+                    }
+                });
+
+        Mockito.verify(mockReconnectStrategy, timeout(5000).atLeast(2)).scheduleReconnect(any(Throwable.class));
+    }
+
+    @Test
+    public void testConnectionReestablishInitial() throws Exception {
+        this.clientDispatcher = getClientDispatcher();
+
+        final ReconnectStrategy mockReconnectStrategy = getMockedReconnectStrategy();
+
+        this.clientDispatcher.createClient(this.serverAddress,
+                mockReconnectStrategy, new SessionListenerFactory<SimpleSessionListener>() {
+                    @Override
+                    public SimpleSessionListener getSessionListener() {
+                        return new SimpleSessionListener();
+                    }
+                });
+
+        Mockito.verify(mockReconnectStrategy, timeout(5000).atLeast(2)).scheduleReconnect(any(Throwable.class));
+
+        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();
+
+        assertEquals(true, p.get(3, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testConnectionDrop() throws Exception {
         final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
 
-        this.dispatcher = new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
+        this.dispatcher = getServerDispatcher(p);
 
+        this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory<SimpleSessionListener>() {
             @Override
-            public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
-                    final Channel channel, final Promise<SimpleSession> promise) {
-                p.setSuccess(true);
-                return new SimpleSessionNegotiator(promise, channel);
+            public SimpleSessionListener getSessionListener() {
+                return new SimpleSessionListener();
             }
-        }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
+        });
+
+        this.server.get();
+
+        this.clientDispatcher = getClientDispatcher();
+
+        final ReconnectStrategy reconnectStrategy = getMockedReconnectStrategy();
+        this.session = this.clientDispatcher.createClient(this.serverAddress,
+                reconnectStrategy, new SessionListenerFactory<SimpleSessionListener>() {
+                    @Override
+                    public SimpleSessionListener getSessionListener() {
+                        return new SimpleSessionListener();
+                    }
+                }).get(6, TimeUnit.SECONDS);
+
+        assertEquals(true, p.get(3, TimeUnit.SECONDS));
+
+        shutdownServer();
+
+        // No reconnect should be scheduled after server drops connection with not-reconnecting client
+        verify(reconnectStrategy, times(0)).scheduleReconnect(any(Throwable.class));
+    }
+
+    @Test
+    public void testConnectionReestablishAfterDrop() 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
@@ -70,13 +167,42 @@ public class ServerTest {
 
         this.server.get();
 
-        this.clientDispatcher = new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
+        this.clientDispatcher = getClientDispatcher();
+
+        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();
+                    }
+                });
+
+        assertEquals(true, p.get(3, TimeUnit.SECONDS));
+        shutdownServer();
+
+        verify(reconnectStrategyFactory, timeout(20000).atLeast(2)).createReconnectStrategy();
+    }
+
+    @Test
+    public void testConnectionEstablished() 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 SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
-                    final Channel channel, final Promise<SimpleSession> promise) {
-                return new SimpleSessionNegotiator(promise, channel);
+            public SimpleSessionListener getSessionListener() {
+                return new SimpleSessionListener();
             }
-        }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
+        });
+
+        this.server.get();
+
+        this.clientDispatcher = getClientDispatcher();
 
         this.session = this.clientDispatcher.createClient(this.serverAddress,
                 new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), new SessionListenerFactory<SimpleSessionListener>() {
@@ -93,15 +219,7 @@ public class ServerTest {
     public void testConnectionFailed() throws IOException, InterruptedException, ExecutionException, TimeoutException {
         final Promise<Boolean> p = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
 
-        this.dispatcher = new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
-
-            @Override
-            public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
-                    final Channel channel, final Promise<SimpleSession> promise) {
-                p.setSuccess(true);
-                return new SimpleSessionNegotiator(promise, channel);
-            }
-        }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
+        this.dispatcher = getServerDispatcher(p);
 
         this.server = this.dispatcher.createServer(this.serverAddress, new SessionListenerFactory<SimpleSessionListener>() {
             @Override
@@ -112,13 +230,7 @@ public class ServerTest {
 
         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);
-            }
-        }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
+        this.clientDispatcher = getClientDispatcher();
 
         this.session = this.clientDispatcher.createClient(this.serverAddress,
                 new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000), new SessionListenerFactory<SimpleSessionListener>() {
@@ -138,14 +250,89 @@ public class ServerTest {
         assertFalse(session.isSuccess());
     }
 
-    @After
-    public void tearDown() throws IOException, InterruptedException {
-        this.server.channel().close();
-        this.eventLoopGroup.shutdownGracefully();
-        try {
-            Thread.sleep(500);
-        } catch (final InterruptedException e) {
-            throw new RuntimeException(e);
-        }
+    @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
+            public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
+                                                                         final Channel channel, final Promise<SimpleSession> promise) {
+                return new SimpleSessionNegotiator(promise, channel);
+            }
+        }, new DefaultPromise<SimpleSession>(GlobalEventExecutor.INSTANCE), eventLoopGroup);
+    }
+
+    private ReconnectStrategy getMockedReconnectStrategy() throws Exception {
+        final ReconnectStrategy mockReconnectStrategy = mock(ReconnectStrategy.class);
+        final Future<Void> future = new SucceededFuture<>(GlobalEventExecutor.INSTANCE, null);
+        doReturn(future).when(mockReconnectStrategy).scheduleReconnect(any(Throwable.class));
+        doReturn(5000).when(mockReconnectStrategy).getConnectTimeout();
+        doNothing().when(mockReconnectStrategy).reconnectSuccessful();
+        return mockReconnectStrategy;
+    }
+
+
+    private void shutdownServer() throws InterruptedException, ExecutionException {
+        // Shutdown server
+        server.channel().close().get();
+        // Closing server channel does not close established connections, eventLoop has to be closed as well to simulate dropped session
+        serverLoopGroup.shutdownGracefully().get();
+    }
+
+    private SimpleDispatcher getServerDispatcher(final Promise<Boolean> p) {
+        return new SimpleDispatcher(new SessionNegotiatorFactory<SimpleMessage, SimpleSession, SimpleSessionListener>() {
+
+            @Override
+            public SessionNegotiator<SimpleSession> getSessionNegotiator(final SessionListenerFactory<SimpleSessionListener> factory,
+                                                                         final Channel channel, final Promise<SimpleSession> promise) {
+                p.setSuccess(true);
+                return new SimpleSessionNegotiator(promise, channel);
+            }
+        }, null, serverLoopGroup);
+    }
+
 }
index 12aac9e..d837385 100644 (file)
@@ -54,6 +54,10 @@ public class SimpleDispatcher extends AbstractDispatcher<SimpleSession, SimpleSe
         return super.createClient(address, strategy, new SimplePipelineInitializer(listenerFactory));
     }
 
+    public Future<Void> createReconnectingClient(final InetSocketAddress address, final ReconnectStrategyFactory strategy, final SessionListenerFactory<SimpleSessionListener> listenerFactory) {
+        return super.createReconnectingClient(address, strategy, new SimplePipelineInitializer(listenerFactory));
+    }
+
     public ChannelFuture createServer(final InetSocketAddress address, final SessionListenerFactory<SimpleSessionListener> listenerFactory) {
         return super.createServer(address, new SimplePipelineInitializer(listenerFactory));
     }
index ec46219..6050f7c 100644 (file)
@@ -14,7 +14,7 @@ import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy
 import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl;
 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.osgi.framework.BundleContext;
 
 /**
@@ -31,7 +31,7 @@ public class CodecRegistryProvider implements AutoCloseable {
     public CodecRegistryProvider(final ClassLoadingStrategy classLoadingStrategy, final BundleContext context) {
         service = new RuntimeGeneratedMappingServiceImpl(CLASS_POOL, classLoadingStrategy);
         registration = OsgiRegistrationUtil.registerService(context, service,
-                SchemaServiceListener.class, BindingIndependentMappingService.class);
+                SchemaContextListener.class, BindingIndependentMappingService.class);
     }
 
     public CodecRegistry getCodecRegistry() {
diff --git a/opendaylight/config/config-netty-config/pom.xml b/opendaylight/config/config-netty-config/pom.xml
new file mode 100644 (file)
index 0000000..8dc31dc
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>config-subsystem</artifactId>
+    <version>0.2.5-SNAPSHOT</version>
+  </parent>
+  <artifactId>config-netty-config</artifactId>
+  <description>Configuration files for sal-rest-connector</description>
+  <packaging>jar</packaging>
+  <build>
+       <plugins>
+               <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-artifacts</id>
+            <goals>
+              <goal>attach-artifact</goal>
+            </goals>
+            <phase>package</phase>
+            <configuration>
+              <artifacts>
+                <artifact>
+                  <file>${project.build.directory}/classes/initial/00-netty.xml</file>
+                  <type>xml</type>
+                  <classifier>config</classifier>
+                </artifact>
+              </artifacts>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
index 66bb01f..26fac47 100644 (file)
@@ -39,6 +39,7 @@
     <module>shutdown-impl</module>
     <module>netconf-config-dispatcher</module>
     <module>config-module-archetype</module>
+    <module>config-netty-config</module>
   </modules>
 
   <dependencies>
index 541c130..4d0770f 100644 (file)
             <phase>generate-resources</phase>
             <configuration>
                <outputDirectory>${project.build.directory}/configuration</outputDirectory>
-               <includeArtifactIds>sal-rest-connector-config</includeArtifactIds>
+               <includeArtifactIds>sal-rest-connector-config,config-netty-config,md-sal-config,netconf-config,toaster-config,netconf-connector-config</includeArtifactIds>
                <includes>**\/*.xml</includes>
                <excludeTransitive>true</excludeTransitive>
                <ignorePermissions>false</ignorePermissions>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>sal-rest-connector-config</artifactId>
         </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>config-netty-config</artifactId>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>md-sal-config</artifactId>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>netconf-config</artifactId>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>netconf-connector-config</artifactId>
+        </dependency>
+        <dependency>
+          <groupId>org.opendaylight.controller.samples</groupId>
+          <artifactId>toaster-config</artifactId>
+        </dependency>
         <dependency>
           <groupId>org.opendaylight.controller</groupId>
           <artifactId>sal-rest-docgen</artifactId>
                 <phase>generate-resources</phase>
                 <configuration>
                    <outputDirectory>${project.build.directory}/configuration</outputDirectory>
-                   <includeArtifactIds>sal-rest-connector-config</includeArtifactIds>
+                   <includeArtifactIds>sal-rest-connector-config,config-netty-config,md-sal-config,netconf-config,toaster-config,netconf-connector-config</includeArtifactIds>
                    <includes>**\/*.xml</includes>
                    <excludeTransitive>true</excludeTransitive>
                    <ignorePermissions>false</ignorePermissions>
diff --git a/opendaylight/md-sal/md-sal-config/pom.xml b/opendaylight/md-sal/md-sal-config/pom.xml
new file mode 100644 (file)
index 0000000..2e19b5a
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>sal-parent</artifactId>
+    <version>1.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>md-sal-config</artifactId>
+  <description>Configuration files for md-sal</description>
+  <packaging>jar</packaging>
+  <build>
+    <plugins>
+        <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-artifacts</id>
+            <goals>
+              <goal>attach-artifact</goal>
+            </goals>
+            <phase>package</phase>
+            <configuration>
+              <artifacts>
+                <artifact>
+                  <file>${project.build.directory}/classes/initial/01-md-sal.xml</file>
+                  <type>xml</type>
+                  <classifier>config</classifier>
+                </artifact>
+              </artifacts>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
index 3f02765..02fbde8 100644 (file)
@@ -32,6 +32,9 @@
 
     <module>sal-binding-util</module>
 
+    <!-- Configuration File -->
+    <module>md-sal-config</module>
+
     <!-- Samples -->
     <module>samples</module>
 
@@ -69,6 +72,9 @@
     <!-- sal-distributed-datastore -->
     <module>sal-distributed-datastore</module>
 
+    <!-- XSQL -->
+    <module>sal-dom-xsql</module>
+
     <!-- Yang Test Models for MD-SAL -->
     <module>sal-test-model</module>
 
index 823a4d9..b0c2d74 100644 (file)
@@ -23,7 +23,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 import org.slf4j.Logger;
@@ -74,7 +74,7 @@ public final class RuntimeMappingModule extends
         }
 
         final RuntimeGeneratedMappingServiceImpl service = new RuntimeGeneratedMappingServiceImpl(SingletonHolder.CLASS_POOL);
-        bundleContext.registerService(SchemaServiceListener.class, service, new Hashtable<String,String>());
+        bundleContext.registerService(SchemaContextListener.class, service, new Hashtable<String,String>());
         return service;
     }
 
index 15e4a46..2d81b60 100644 (file)
@@ -41,7 +41,6 @@ import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMapping
 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -58,14 +57,14 @@ public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBr
     private final BindingToNormalizedNodeCodec codec;
     private BindingIndependentConnector connector;
     private ProviderSession context;
-    private final ListenerRegistration<SchemaServiceListener> schemaListenerRegistration;
+    private final ListenerRegistration<SchemaContextListener> schemaListenerRegistration;
 
     protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker,
             final BindingIndependentMappingService mappingService,final SchemaService schemaService) {
         this.domDataBroker = domDataBroker;
         this.mappingService = mappingService;
         this.codec = new BindingToNormalizedNodeCodec(mappingService);
-        this.schemaListenerRegistration = schemaService.registerSchemaServiceListener(this);
+        this.schemaListenerRegistration = schemaService.registerSchemaContextListener(this);
     }
 
     protected BindingToNormalizedNodeCodec getCodec() {
index df09f78..05651bf 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.controller.sal.binding.impl;
 
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
 
 import org.opendaylight.controller.md.sal.binding.util.AbstractBindingSalProviderInstance;
 import org.opendaylight.controller.sal.binding.api.mount.MountProviderInstance;
@@ -20,6 +21,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
 
 public class MountPointManagerImpl implements MountProviderService {
 
@@ -82,7 +84,7 @@ public class MountPointManagerImpl implements MountProviderService {
         RpcProviderRegistryImpl rpcRegistry = new RpcProviderRegistryImpl("mount");
         NotificationBrokerImpl notificationBroker = new NotificationBrokerImpl(getNotificationExecutor());
         DataBrokerImpl dataBroker = new DataBrokerImpl();
-        dataBroker.setExecutor(getDataCommitExecutor());
+        dataBroker.setExecutor(MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
         BindingMountPointImpl mountInstance = new BindingMountPointImpl(path, rpcRegistry, notificationBroker,
                 dataBroker);
         mountPoints.putIfAbsent(path, mountInstance);
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1333DataChangeListenerTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/Bug1333DataChangeListenerTest.java
new file mode 100644 (file)
index 0000000..60d56db
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * 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.md.sal.binding.impl.test;
+
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertContains;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertEmpty;
+import static org.opendaylight.controller.md.sal.binding.test.AssertCollections.assertNotContains;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.USES_ONE_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.USES_TWO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.complexUsesAugment;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.binding.test.AbstractDataChangeListenerTest;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.TreeComplexUsesAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ * This testsuite tries to replicate bug 1333 and tests regresion of it
+ * using test-model with similar construction as one reported.
+ *
+ *
+ * See  https://bugs.opendaylight.org/show_bug.cgi?id=1333 for Bug Description
+ *
+ */
+public class Bug1333DataChangeListenerTest extends AbstractDataChangeListenerTest{
+
+    private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
+
+    private static final InstanceIdentifier<?> AUGMENT_WILDCARD = TOP_PATH.child(TopLevelList.class).augmentation(
+            TreeComplexUsesAugment.class);
+
+    public void writeTopWithListItem(final LogicalDatastoreType store) {
+        ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
+        Top topItem = top(topLevelList(TOP_FOO_KEY, complexUsesAugment(USES_ONE_KEY, USES_TWO_KEY)));
+        tx.put(store, TOP_PATH, topItem);
+        assertCommit(tx.submit());
+    }
+
+    public void deleteItem(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+        ReadWriteTransaction tx = getDataBroker().newReadWriteTransaction();
+        tx.delete(store, path);
+        assertCommit(tx.submit());
+    }
+
+    @Test
+    public void writeTopWithListItemAugmentedListenTopSubtree() {
+        TestListener listener = createListener(CONFIGURATION,TOP_PATH, DataChangeScope.SUBTREE);
+        listener.startCapture();
+
+        writeTopWithListItem(CONFIGURATION);
+
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+        assertContains(event.getCreatedData(), TOP_PATH);
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY));
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_ONE_KEY));
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+
+        assertEmpty(event.getUpdatedData());
+        assertEmpty(event.getRemovedPaths());
+    }
+
+    @Test
+    public void writeTopWithListItemAugmentedListenAugmentSubtreeWildcarded() {
+        TestListener listener = createListener(CONFIGURATION,AUGMENT_WILDCARD, DataChangeScope.SUBTREE);
+        listener.startCapture();
+        writeTopWithListItem(CONFIGURATION);
+
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+        /*
+         * Event should not contain parent nodes
+         */
+        assertNotContains(event.getCreatedData(), TOP_PATH, path(TOP_FOO_KEY));
+
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_ONE_KEY));
+        assertContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+
+        assertEmpty(event.getUpdatedData());
+        assertEmpty(event.getRemovedPaths());
+    }
+
+    @Test
+    public void deleteAugmentChildListenTopSubtree() {
+        writeTopWithListItem(CONFIGURATION);
+        TestListener listener = createListener(CONFIGURATION, TOP_PATH, DataChangeScope.SUBTREE);
+        InstanceIdentifier<?> deletePath = path(TOP_FOO_KEY,USES_ONE_KEY);
+        deleteItem(CONFIGURATION,deletePath);
+
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+
+        assertEmpty(event.getCreatedData());
+
+        assertContains(event.getRemovedPaths(), deletePath);
+
+        assertContains(event.getUpdatedData(), TOP_PATH);
+        assertContains(event.getUpdatedData(), path(TOP_FOO_KEY));
+        assertContains(event.getUpdatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+
+        assertNotContains(event.getCreatedData(), path(TOP_FOO_KEY, USES_TWO_KEY));
+    }
+
+    @Test
+    public void deleteAugmentChildListenAugmentSubtreeWildcarded() {
+        writeTopWithListItem(CONFIGURATION);
+
+        TestListener listener = createListener(CONFIGURATION, AUGMENT_WILDCARD, DataChangeScope.SUBTREE);
+        InstanceIdentifier<?> deletePath = path(TOP_FOO_KEY,USES_ONE_KEY);
+        deleteItem(CONFIGURATION,deletePath);
+        AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event = listener.event();
+
+        assertEmpty(event.getCreatedData());
+
+        assertContains(event.getUpdatedData(), path(TOP_FOO_KEY, TreeComplexUsesAugment.class));
+
+        /*
+         * Event should not contain parent nodes
+         */
+        assertNotContains(event.getUpdatedData(), TOP_PATH, path(TOP_FOO_KEY));
+
+        assertContains(event.getRemovedPaths(), deletePath);
+    }
+
+}
index 79aa6b6..e0f6f35 100644 (file)
@@ -47,13 +47,13 @@ public class DataBrokerTestCustomizer {
 
     public DOMStore createConfigurationDatastore() {
         InMemoryDOMDataStore store = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
-        schemaService.registerSchemaServiceListener(store);
+        schemaService.registerSchemaContextListener(store);
         return store;
     }
 
     public DOMStore createOperationalDatastore() {
         InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
-        schemaService.registerSchemaServiceListener(store);
+        schemaService.registerSchemaContextListener(store);
         return store;
     }
 
index e82c9d3..deb4a8a 100644 (file)
@@ -144,8 +144,8 @@ public class BindingTestContext implements AutoCloseable {
 
         biCompatibleBroker = new BackwardsCompatibleDataBroker(newDOMDataBroker,mockSchemaService);
 
-        mockSchemaService.registerSchemaServiceListener(configStore);
-        mockSchemaService.registerSchemaServiceListener(operStore);
+        mockSchemaService.registerSchemaContextListener(configStore);
+        mockSchemaService.registerSchemaContextListener(operStore);
         biDataLegacyBroker = biCompatibleBroker;
     }
 
@@ -246,7 +246,7 @@ public class BindingTestContext implements AutoCloseable {
     public void startBindingToDomMappingService() {
         checkState(classPool != null, "ClassPool needs to be present");
         mappingServiceImpl = new RuntimeGeneratedMappingServiceImpl(classPool);
-        mockSchemaService.registerSchemaServiceListener(mappingServiceImpl);
+        mockSchemaService.registerSchemaContextListener(mappingServiceImpl);
     }
 
     private void updateYangSchema(final ImmutableSet<YangModuleInfo> moduleInfos) {
index c8acbcd..63a4ffb 100644 (file)
@@ -13,14 +13,14 @@ import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 
 @SuppressWarnings("deprecation")
 public final class MockSchemaService implements SchemaService, SchemaContextProvider {
 
     private SchemaContext schemaContext;
 
-    ListenerRegistry<SchemaServiceListener> listeners = ListenerRegistry.create();
+    ListenerRegistry<SchemaContextListener> listeners = ListenerRegistry.create();
 
     @Override
     public void addModule(final Module module) {
@@ -38,8 +38,8 @@ public final class MockSchemaService implements SchemaService, SchemaContextProv
     }
 
     @Override
-    public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(
-            final SchemaServiceListener listener) {
+    public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(
+            final SchemaContextListener listener) {
         return listeners.register(listener);
     }
 
@@ -55,8 +55,8 @@ public final class MockSchemaService implements SchemaService, SchemaContextProv
 
     public synchronized void changeSchema(final SchemaContext newContext) {
         schemaContext = newContext;
-        for (ListenerRegistration<SchemaServiceListener> listener : listeners) {
+        for (ListenerRegistration<SchemaContextListener> listener : listeners) {
             listener.getInstance().onGlobalContextUpdated(schemaContext);
         }
     }
-}
\ No newline at end of file
+}
index 692d1b4..6d87271 100644 (file)
@@ -20,7 +20,7 @@ public class DistributedDataStoreFactory {
             new DistributedDataStore(actorSystem, name, new ClusterWrapperImpl(actorSystem),config );
        ShardStrategyFactory.setConfiguration(config);
         schemaService
-            .registerSchemaServiceListener(dataStore);
+            .registerSchemaContextListener(dataStore);
         return dataStore;
 
     }
index 57df201..6860872 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.controller.md.cluster.datastore.model;
 
+import java.math.BigInteger;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
@@ -42,14 +43,14 @@ public class CarsModel {
         MapEntryNode altima =
             ImmutableNodes.mapEntryBuilder(CAR_QNAME, CAR_NAME_QNAME, "altima")
                 .withChild(ImmutableNodes.leafNode(CAR_NAME_QNAME, "altima"))
-                .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, 1000))
+                .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, new BigInteger("1000")))
                 .build();
 
         // Create an entry for the car accord
         MapEntryNode honda =
             ImmutableNodes.mapEntryBuilder(CAR_QNAME, CAR_NAME_QNAME, "accord")
                 .withChild(ImmutableNodes.leafNode(CAR_NAME_QNAME, "accord"))
-                .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, 2000))
+                .withChild(ImmutableNodes.leafNode(CAR_PRICE_QNAME, new BigInteger("2000")))
                 .build();
 
         cars.withChild(altima);
index 1b4020a..e637920 100644 (file)
@@ -42,14 +42,14 @@ public class PeopleModel {
         MapEntryNode jack =
             ImmutableNodes.mapEntryBuilder(PERSON_QNAME, PERSON_NAME_QNAME, "jack")
                 .withChild(ImmutableNodes.leafNode(PERSON_NAME_QNAME, "jack"))
-                .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 100))
+                .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 100L))
                 .build();
 
         // Create an entry for the person jill
         MapEntryNode jill =
             ImmutableNodes.mapEntryBuilder(PERSON_QNAME, PERSON_NAME_QNAME, "jill")
                 .withChild(ImmutableNodes.leafNode(PERSON_NAME_QNAME, "jill"))
-                .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 200))
+                .withChild(ImmutableNodes.leafNode(PERSON_AGE_QNAME, 200L))
                 .build();
 
         cars.withChild(jack);
index d8fefcd..be8713c 100644 (file)
@@ -18,45 +18,45 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 public class SampleModelsTest {
     @Test
     public void testPeopleModel(){
-        NormalizedNode<?, ?> expected = PeopleModel.create();
+        final NormalizedNode<?, ?> expected = PeopleModel.create();
 
 
-        NormalizedNodeMessages.Container node =
+        final NormalizedNodeMessages.Container node =
             new NormalizedNodeToNodeCodec(SchemaContextHelper.full())
                 .encode(YangInstanceIdentifier.of(PeopleModel.BASE_QNAME),
                     expected);
 
-        NormalizedNodeMessages.Node normalizedNode =
+        final NormalizedNodeMessages.Node normalizedNode =
             node.getNormalizedNode();
 
-        NormalizedNode<?,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(YangInstanceIdentifier.of(PeopleModel.BASE_QNAME),
+        final NormalizedNode<?,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(YangInstanceIdentifier.of(PeopleModel.BASE_QNAME),
             normalizedNode);
 
 
-        Assert.assertEquals(expected.toString(), actual.toString());
+        Assert.assertEquals(expected, actual);
 
     }
 
 
     @Test
     public void testCarsModel(){
-        NormalizedNode<?, ?> expected = CarsModel.create();
+        final NormalizedNode<?, ?> expected = CarsModel.create();
 
 
-        NormalizedNodeMessages.Container node =
+        final NormalizedNodeMessages.Container node =
             new NormalizedNodeToNodeCodec(SchemaContextHelper.full())
                 .encode(YangInstanceIdentifier.of(CarsModel.BASE_QNAME),
                     expected);
 
-        NormalizedNodeMessages.Node normalizedNode =
+        final NormalizedNodeMessages.Node normalizedNode =
             node.getNormalizedNode();
 
-        NormalizedNode<?,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(
+        final NormalizedNode<?,?> actual = new NormalizedNodeToNodeCodec(SchemaContextHelper.full()).decode(
             YangInstanceIdentifier.of(CarsModel.BASE_QNAME),
             normalizedNode);
 
 
-        Assert.assertEquals(expected.toString(), actual.toString());
+        Assert.assertEquals(expected, actual);
 
     }
 }
index 34e5b1b..c3e979c 100644 (file)
@@ -11,7 +11,7 @@ import org.opendaylight.controller.sal.core.api.BrokerService;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 
 public interface SchemaService extends BrokerService {
 
@@ -42,5 +42,11 @@ public interface SchemaService extends BrokerService {
      */
     SchemaContext getGlobalContext();
 
-    ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(SchemaServiceListener listener);
+    /**
+     * Register a listener for changes in schema context.
+     *
+     * @param listener Listener which should be registered
+     * @return Listener registration handle
+     */
+    ListenerRegistration<SchemaContextListener> registerSchemaContextListener(SchemaContextListener listener);
 }
index 17b78f4..f1f16cd 100644 (file)
@@ -91,7 +91,7 @@ public final class DomBrokerImplModule extends org.opendaylight.controller.confi
         wrappedStore.changeDelegate(legacyStore);
         wrappedStore.setValidationEnabled(false);
 
-        schemaService.registerSchemaServiceListener(wrappedStore);
+        schemaService.registerSchemaContextListener(wrappedStore);
 
         dataService.registerConfigurationReader(rootPath, wrappedStore);
         dataService.registerCommitHandler(rootPath, wrappedStore);
index 69b17ee..667c0fc 100644 (file)
@@ -50,7 +50,7 @@ public final class DomInmemoryDataBrokerModule extends
            //we will default to InMemoryDOMDataStore creation
           operStore = new InMemoryDOMDataStore("DOM-OPER", storeExecutor);
           //here we will register the SchemaContext listener
-          getSchemaServiceDependency().registerSchemaServiceListener((InMemoryDOMDataStore)operStore);
+          getSchemaServiceDependency().registerSchemaContextListener((InMemoryDOMDataStore)operStore);
         }
 
         DOMStore configStore = getConfigDataStoreDependency();
@@ -58,7 +58,7 @@ public final class DomInmemoryDataBrokerModule extends
            //we will default to InMemoryDOMDataStore creation
            configStore = new InMemoryDOMDataStore("DOM-CFG", storeExecutor);
           //here we will register the SchemaContext listener
-          getSchemaServiceDependency().registerSchemaServiceListener((InMemoryDOMDataStore)configStore);
+          getSchemaServiceDependency().registerSchemaContextListener((InMemoryDOMDataStore)configStore);
         }
         ImmutableMap<LogicalDatastoreType, DOMStore> datastores = ImmutableMap
                 .<LogicalDatastoreType, DOMStore> builder().put(LogicalDatastoreType.OPERATIONAL, operStore)
index fbc418d..62b0264 100644 (file)
@@ -13,7 +13,7 @@ import org.opendaylight.yangtools.concepts.Delegator;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -90,8 +90,8 @@ org.opendaylight.controller.config.yang.md.sal.dom.impl.AbstractSchemaServiceImp
         }
 
         @Override
-        public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener arg0) {
-            return delegate.registerSchemaServiceListener(arg0);
+        public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(final SchemaContextListener arg0) {
+            return delegate.registerSchemaContextListener(arg0);
         }
 
         @Override
index e8f8da5..dc122cf 100644 (file)
@@ -1,3 +1,10 @@
+/*
+ * 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.md.sal.dom.broker.impl.compat;
 
 import javax.annotation.concurrent.ThreadSafe;
@@ -21,18 +28,17 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
 
 @ThreadSafe
 public class BackwardsCompatibleDataBroker implements DataProviderService {
 
     private final DOMDataBroker backingBroker;
     private volatile DataNormalizer normalizer;
-    private final ListenerRegistration<SchemaServiceListener> schemaReg;
+    private final ListenerRegistration<SchemaContextListener> schemaReg;
 
     public BackwardsCompatibleDataBroker(final DOMDataBroker newBiDataImpl, final SchemaService schemaService) {
         backingBroker = newBiDataImpl;
-        schemaReg = schemaService.registerSchemaServiceListener(new SchemaListener());
+        schemaReg = schemaService.registerSchemaContextListener(new SchemaListener());
     }
 
     @Override
index d837d75..61ea47e 100644 (file)
@@ -72,7 +72,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 
 public class BackwardsCompatibleMountPoint implements MountProvisionInstance, SchemaContextProvider, SchemaService {
 
@@ -83,7 +83,7 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc
     private final NotificationPublishService notificationPublishService;
     private final RpcProvisionRegistry rpcs;
 
-    private final ListenerRegistry<SchemaServiceListener> schemaListenerRegistry = new ListenerRegistry<>();
+    private final ListenerRegistry<SchemaContextListener> schemaListenerRegistry = new ListenerRegistry<>();
 
     private SchemaContext schemaContext;
 
@@ -154,7 +154,7 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc
     }
 
     @Override
-    public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener listener) {
+    public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(final SchemaContextListener listener) {
         return schemaListenerRegistry.register(listener);
     }
 
@@ -275,7 +275,7 @@ public class BackwardsCompatibleMountPoint implements MountProvisionInstance, Sc
     @Override
     public void setSchemaContext(final SchemaContext schemaContext) {
         this.schemaContext = schemaContext;
-        for (ListenerRegistration<SchemaServiceListener> schemaServiceListenerListenerRegistration : schemaListenerRegistry.getListeners()) {
+        for (ListenerRegistration<SchemaContextListener> schemaServiceListenerListenerRegistration : schemaListenerRegistry.getListeners()) {
             schemaServiceListenerListenerRegistration.getInstance().onGlobalContextUpdated(schemaContext);
         }
     }
index d8174c3..8263732 100644 (file)
@@ -23,7 +23,7 @@ import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.opendaylight.yangtools.yang.parser.impl.util.URLSchemaContextResolver;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -40,15 +40,15 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
-public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer<SchemaServiceListener, SchemaServiceListener>, AutoCloseable {
+public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvider, SchemaService, ServiceTrackerCustomizer<SchemaContextListener, SchemaContextListener>, AutoCloseable {
     private static final Logger LOG = LoggerFactory.getLogger(GlobalBundleScanningSchemaServiceImpl.class);
 
-    private final ListenerRegistry<SchemaServiceListener> listeners = new ListenerRegistry<>();
+    private final ListenerRegistry<SchemaContextListener> listeners = new ListenerRegistry<>();
     private final URLSchemaContextResolver contextResolver = new URLSchemaContextResolver();
     private final BundleScanner scanner = new BundleScanner();
     private final BundleContext context;
 
-    private ServiceTracker<SchemaServiceListener, SchemaServiceListener> listenerTracker;
+    private ServiceTracker<SchemaContextListener, SchemaContextListener> listenerTracker;
     private BundleTracker<Iterable<Registration>> bundleTracker;
     private boolean starting = true;
     private static GlobalBundleScanningSchemaServiceImpl instance;
@@ -81,7 +81,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
     public void start() {
         checkState(context != null);
 
-        listenerTracker = new ServiceTracker<>(context, SchemaServiceListener.class, GlobalBundleScanningSchemaServiceImpl.this);
+        listenerTracker = new ServiceTracker<>(context, SchemaContextListener.class, GlobalBundleScanningSchemaServiceImpl.this);
         bundleTracker = new BundleTracker<>(context, BundleEvent.RESOLVED | BundleEvent.UNRESOLVED, scanner);
         bundleTracker.open();
         listenerTracker.open();
@@ -115,7 +115,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
     }
 
     @Override
-    public synchronized ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(final SchemaServiceListener listener) {
+    public synchronized ListenerRegistration<SchemaContextListener> registerSchemaContextListener(final SchemaContextListener listener) {
         Optional<SchemaContext> potentialCtx = contextResolver.getSchemaContext();
         if(potentialCtx.isPresent()) {
             listener.onGlobalContextUpdated(potentialCtx.get());
@@ -137,7 +137,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
 
     private synchronized void updateContext(final SchemaContext snapshot) {
         Object[] services = listenerTracker.getServices();
-        for (ListenerRegistration<SchemaServiceListener> listener : listeners) {
+        for (ListenerRegistration<SchemaContextListener> listener : listeners) {
             try {
                 listener.getInstance().onGlobalContextUpdated(snapshot);
             } catch (Exception e) {
@@ -146,7 +146,7 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
         }
         if (services != null) {
             for (Object rawListener : services) {
-                SchemaServiceListener listener = (SchemaServiceListener) rawListener;
+                final SchemaContextListener listener = (SchemaContextListener) rawListener;
                 try {
                     listener.onGlobalContextUpdated(snapshot);
                 } catch (Exception e) {
@@ -213,9 +213,9 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
     }
 
     @Override
-    public synchronized SchemaServiceListener addingService(final ServiceReference<SchemaServiceListener> reference) {
+    public synchronized SchemaContextListener addingService(final ServiceReference<SchemaContextListener> reference) {
 
-        SchemaServiceListener listener = context.getService(reference);
+        SchemaContextListener listener = context.getService(reference);
         SchemaContext _ctxContext = getGlobalContext();
         if (getContext() != null && _ctxContext != null) {
             listener.onGlobalContextUpdated(_ctxContext);
@@ -234,12 +234,12 @@ public class GlobalBundleScanningSchemaServiceImpl implements SchemaContextProvi
     }
 
     @Override
-    public void modifiedService(final ServiceReference<SchemaServiceListener> reference, final SchemaServiceListener service) {
+    public void modifiedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
         // NOOP
     }
 
     @Override
-    public void removedService(final ServiceReference<SchemaServiceListener> reference, final SchemaServiceListener service) {
+    public void removedService(final ServiceReference<SchemaContextListener> reference, final SchemaContextListener service) {
         context.ungetService(reference);
     }
 }
index 1d864ee..d8d2346 100644 (file)
@@ -8,10 +8,10 @@
 package org.opendaylight.controller.sal.dom.broker.osgi;
 
 import org.opendaylight.controller.sal.core.api.model.SchemaService;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 import org.osgi.framework.ServiceReference;
 
 public class SchemaServiceProxy extends AbstractBrokerServiceProxy<SchemaService> implements SchemaService {
@@ -41,12 +41,9 @@ public class SchemaServiceProxy extends AbstractBrokerServiceProxy<SchemaService
     }
 
     @Override
-    public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(SchemaServiceListener listener) {
-        ListenerRegistration<SchemaServiceListener> registration = getDelegate().registerSchemaServiceListener(listener);
+    public ListenerRegistration<SchemaContextListener> registerSchemaContextListener(SchemaContextListener listener) {
+        ListenerRegistration<SchemaContextListener> registration = getDelegate().registerSchemaContextListener(listener);
         addRegistration(registration);
         return registration;
     }
-
-
-
 }
index 399f49b..59cdc76 100644 (file)
@@ -20,7 +20,7 @@ public class XSQLModule extends org.opendaylight.yang.gen.v1.http.netconfcentral
     @Override
     public java.lang.AutoCloseable createInstance() {
         XSQLAdapter xsqlAdapter = XSQLAdapter.getInstance();
-        getSchemaServiceDependency().registerSchemaServiceListener(xsqlAdapter);
+        getSchemaServiceDependency().registerSchemaContextListener(xsqlAdapter);
         xsqlAdapter.setDataBroker(getAsyncDataBrokerDependency());
         XSQLProvider p = new XSQLProvider();
         p.buildXSQL(getDataBrokerDependency());
index 01a5989..805608d 100644 (file)
@@ -23,7 +23,7 @@ public class InMemoryConfigDataStoreProviderModule extends org.opendaylight.cont
     @Override
     public java.lang.AutoCloseable createInstance() {
       InMemoryDOMDataStore   ids = new InMemoryDOMDataStore("DOM-CFG", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
-      getSchemaServiceDependency().registerSchemaServiceListener(ids);
+      getSchemaServiceDependency().registerSchemaContextListener(ids);
       return ids;
     }
 
index b39c9bb..f479558 100644 (file)
@@ -23,7 +23,7 @@ public class InMemoryOperationalDataStoreProviderModule extends org.opendaylight
     @Override
     public java.lang.AutoCloseable createInstance() {
       InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-OPER", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
-      getOperationalSchemaServiceDependency().registerSchemaServiceListener(ids);
+      getOperationalSchemaServiceDependency().registerSchemaContextListener(ids);
       return ids;
     }
 
index 037bfb4..b75df80 100644 (file)
@@ -13,8 +13,10 @@ import static org.opendaylight.controller.config.api.JmxAttributeValidationExcep
 import java.io.File;
 import java.io.InputStream;
 import java.net.InetSocketAddress;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
@@ -24,6 +26,7 @@ import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
 import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
 import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
+import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionCapabilities;
 import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceSalFacade;
 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.controller.sal.core.api.Broker;
@@ -40,6 +43,8 @@ import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Optional;
+
 /**
  *
  */
@@ -49,6 +54,7 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
 
     private static AbstractCachingSchemaSourceProvider<String, InputStream> GLOBAL_NETCONF_SOURCE_PROVIDER = null;
     private BundleContext bundleContext;
+    private Optional<NetconfSessionCapabilities> userCapabilities;
 
     public NetconfConnectorModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
         super(identifier, dependencyResolver);
@@ -82,9 +88,11 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
             checkNotNull(getPassword(), passwordJmxAttribute);
         }
 
+        userCapabilities = getUserCapabilities();
+
     }
 
-    private boolean isHostAddressPresent(Host address) {
+    private boolean isHostAddressPresent(final Host address) {
         return address.getDomainName() != null ||
                address.getIpAddress() != null && (address.getIpAddress().getIpv4Address() != null || address.getIpAddress().getIpv6Address() != null);
     }
@@ -98,10 +106,14 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         final Broker domBroker = getDomRegistryDependency();
         final BindingAwareBroker bindingBroker = getBindingRegistryDependency();
 
-        final RemoteDeviceHandler salFacade = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor);
+        final RemoteDeviceHandler<NetconfSessionCapabilities> salFacade
+                = new NetconfDeviceSalFacade(id, domBroker, bindingBroker, bundleContext, globalProcessingExecutor);
         final NetconfDevice device =
                 NetconfDevice.createNetconfDevice(id, getGlobalNetconfSchemaProvider(), globalProcessingExecutor, salFacade);
-        final NetconfDeviceCommunicator listener = new NetconfDeviceCommunicator(id, device);
+
+        final NetconfDeviceCommunicator listener = userCapabilities.isPresent() ?
+                new NetconfDeviceCommunicator(id, device, userCapabilities.get()) : new NetconfDeviceCommunicator(id, device);
+
         final NetconfReconnectingClientConfiguration clientConfig = getClientConfig(listener);
 
         final NetconfClientDispatcher dispatcher = getClientDispatcherDependency();
@@ -116,6 +128,26 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         };
     }
 
+    private Optional<NetconfSessionCapabilities> getUserCapabilities() {
+        if(getYangModuleCapabilities() == null) {
+            return Optional.absent();
+        }
+
+        final List<String> capabilities = getYangModuleCapabilities().getCapability();
+        if(capabilities == null || capabilities.isEmpty()) {
+            return Optional.absent();
+        }
+
+        final NetconfSessionCapabilities parsedOverrideCapabilities = NetconfSessionCapabilities.fromStrings(capabilities);
+        JmxAttributeValidationException.checkCondition(
+                parsedOverrideCapabilities.getNonModuleCaps().isEmpty(),
+                "Capabilities to override can only contain module based capabilities, non-module capabilities will be retrieved from the device," +
+                        " configured non-module capabilities: " + parsedOverrideCapabilities.getNonModuleCaps(),
+                yangModuleCapabilitiesJmxAttribute);
+
+        return Optional.of(parsedOverrideCapabilities);
+    }
+
     private synchronized AbstractCachingSchemaSourceProvider<String, InputStream> getGlobalNetconfSchemaProvider() {
         if(GLOBAL_NETCONF_SOURCE_PROVIDER == null) {
             final String storageFile = "cache/schema";
@@ -175,8 +207,8 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         if(getAddress().getDomainName() != null) {
             return new InetSocketAddress(getAddress().getDomainName().getValue(), getPort().getValue());
         } else {
-            IpAddress ipAddress = getAddress().getIpAddress();
-            String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
+            final IpAddress ipAddress = getAddress().getIpAddress();
+            final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue();
             return new InetSocketAddress(ip, getPort().getValue());
         }
     }
index de4ac7a..07d3c08 100644 (file)
@@ -118,6 +118,7 @@ public final class NetconfDevice implements RemoteDevice<NetconfSessionCapabilit
                 // Unable to initialize device, set as disconnected
                 logger.error("{}: Initialization failed", id, t);
                 salFacade.onDeviceDisconnected();
+                // TODO ssh connection is still open if sal initialization fails
             }
         });
     }
index 8045f8c..3871cdf 100644 (file)
@@ -7,13 +7,19 @@
  */
 package org.opendaylight.controller.sal.connect.netconf.listener;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.FutureListener;
 import java.util.ArrayDeque;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Queue;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
-
 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
@@ -36,31 +42,35 @@ import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
-import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.FutureListener;
-
 public class NetconfDeviceCommunicator implements NetconfClientSessionListener, RemoteDeviceCommunicator<NetconfMessage> {
 
     private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceCommunicator.class);
 
     private final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice;
+    private final Optional<NetconfSessionCapabilities> overrideNetconfCapabilities;
     private final RemoteDeviceId id;
     private final Lock sessionLock = new ReentrantLock();
 
+    private final Queue<Request> requests = new ArrayDeque<>();
+    private NetconfClientSession session;
+
+    public NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice,
+            final NetconfSessionCapabilities netconfSessionCapabilities) {
+        this(id, remoteDevice, Optional.of(netconfSessionCapabilities));
+    }
+
     public NetconfDeviceCommunicator(final RemoteDeviceId id,
-            final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice) {
+                                     final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice) {
+        this(id, remoteDevice, Optional.<NetconfSessionCapabilities>absent());
+    }
+
+    private NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice,
+            final Optional<NetconfSessionCapabilities> overrideNetconfCapabilities) {
         this.id = id;
         this.remoteDevice = remoteDevice;
+        this.overrideNetconfCapabilities = overrideNetconfCapabilities;
     }
 
-    private final Queue<Request> requests = new ArrayDeque<>();
-    private NetconfClientSession session;
-
     @Override
     public void onSessionUp(final NetconfClientSession session) {
         sessionLock.lock();
@@ -68,10 +78,15 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
             logger.debug("{}: Session established", id);
             this.session = session;
 
-            final NetconfSessionCapabilities netconfSessionCapabilities =
+            NetconfSessionCapabilities netconfSessionCapabilities =
                                              NetconfSessionCapabilities.fromNetconfSession(session);
             logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionCapabilities);
 
+            if(overrideNetconfCapabilities.isPresent()) {
+                netconfSessionCapabilities = netconfSessionCapabilities.replaceModuleCaps(overrideNetconfCapabilities.get());
+                logger.debug("{}: Session capabilities overridden, capabilities that will be used: {}", id, netconfSessionCapabilities);
+            }
+
             remoteDevice.onRemoteSessionUp(netconfSessionCapabilities, this);
         }
         finally {
@@ -223,7 +238,7 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
                 return;
             }
 
-            request.future.set( RpcResultBuilder.<NetconfMessage>success( message ).build() );
+            request.future.set( RpcResultBuilder.success( message ).build() );
         }
     }
 
index 8964a80..1a7d90e 100644 (file)
@@ -8,6 +8,7 @@ import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 
+import com.google.common.collect.Sets;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
@@ -19,6 +20,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public final class NetconfSessionCapabilities {
+
     private static final class ParameterMatcher {
         private final Predicate<String> predicate;
         private final int skipLength;
@@ -57,10 +59,10 @@ public final class NetconfSessionCapabilities {
     };
 
     private final Set<QName> moduleBasedCaps;
-    private final Set<String> capabilities;
+    private final Set<String> nonModuleCaps;
 
-    private NetconfSessionCapabilities(final Set<String> capabilities, final Set<QName> moduleBasedCaps) {
-        this.capabilities = Preconditions.checkNotNull(capabilities);
+    private NetconfSessionCapabilities(final Set<String> nonModuleCaps, final Set<QName> moduleBasedCaps) {
+        this.nonModuleCaps = Preconditions.checkNotNull(nonModuleCaps);
         this.moduleBasedCaps = Preconditions.checkNotNull(moduleBasedCaps);
     }
 
@@ -68,30 +70,49 @@ public final class NetconfSessionCapabilities {
         return moduleBasedCaps;
     }
 
-    public boolean containsCapability(final String capability) {
-        return capabilities.contains(capability);
+    public Set<String> getNonModuleCaps() {
+        return nonModuleCaps;
+    }
+
+    public boolean containsNonModuleCapability(final String capability) {
+        return nonModuleCaps.contains(capability);
     }
 
-    public boolean containsCapability(final QName capability) {
+    public boolean containsModuleCapability(final QName capability) {
         return moduleBasedCaps.contains(capability);
     }
 
     @Override
     public String toString() {
         return Objects.toStringHelper(this)
-                .add("capabilities", capabilities)
+                .add("capabilities", nonModuleCaps)
+                .add("moduleBasedCapabilities", moduleBasedCaps)
                 .add("rollback", isRollbackSupported())
                 .add("monitoring", isMonitoringSupported())
                 .toString();
     }
 
     public boolean isRollbackSupported() {
-        return containsCapability(NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString());
+        return containsNonModuleCapability(NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString());
+    }
+
+    public boolean isCandidateSupported() {
+        return containsNonModuleCapability(NetconfMessageTransformUtil.NETCONF_CANDIDATE_URI.toString());
     }
 
     public boolean isMonitoringSupported() {
-        return containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)
-                || containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
+        return containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)
+                || containsNonModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
+    }
+
+    public NetconfSessionCapabilities replaceModuleCaps(final NetconfSessionCapabilities netconfSessionModuleCapabilities) {
+        final Set<QName> moduleBasedCaps = Sets.newHashSet(netconfSessionModuleCapabilities.getModuleBasedCaps());
+
+        // Preserve monitoring module, since it indicates support for ietf-netconf-monitoring
+        if(containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)) {
+            moduleBasedCaps.add(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING);
+        }
+        return new NetconfSessionCapabilities(getNonModuleCaps(), moduleBasedCaps);
     }
 
     public static NetconfSessionCapabilities fromNetconfSession(final NetconfClientSession session) {
@@ -100,6 +121,7 @@ public final class NetconfSessionCapabilities {
 
     public static NetconfSessionCapabilities fromStrings(final Collection<String> capabilities) {
         final Set<QName> moduleBasedCaps = new HashSet<>();
+        final Set<String> nonModuleCaps = Sets.newHashSet(capabilities);
 
         for (final String capability : capabilities) {
             final int qmark = capability.indexOf('?');
@@ -117,6 +139,7 @@ public final class NetconfSessionCapabilities {
             String revision = REVISION_PARAM.from(queryParams);
             if (revision != null) {
                 moduleBasedCaps.add(QName.create(namespace, revision, moduleName));
+                nonModuleCaps.remove(capability);
                 continue;
             }
 
@@ -136,8 +159,9 @@ public final class NetconfSessionCapabilities {
 
             // FIXME: do we really want to continue here?
             moduleBasedCaps.add(QName.create(namespace, revision, moduleName));
+            nonModuleCaps.remove(capability);
         }
 
-        return new NetconfSessionCapabilities(ImmutableSet.copyOf(capabilities), ImmutableSet.copyOf(moduleBasedCaps));
+        return new NetconfSessionCapabilities(ImmutableSet.copyOf(nonModuleCaps), ImmutableSet.copyOf(moduleBasedCaps));
     }
 }
index ee0c8b7..f3a9acd 100644 (file)
@@ -42,7 +42,7 @@ final class NetconfDeviceDataBroker implements DOMDataBroker {
 
     @Override
     public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
-        return new NetconfDeviceReadOnlyTx(rpc, normalizer);
+        return new NetconfDeviceReadOnlyTx(rpc, normalizer, id);
     }
 
     @Override
@@ -52,8 +52,7 @@ final class NetconfDeviceDataBroker implements DOMDataBroker {
 
     @Override
     public DOMDataWriteTransaction newWriteOnlyTransaction() {
-        // FIXME detect if candidate is supported, true is hardcoded
-        return new NetconfDeviceWriteOnlyTx(id, rpc, normalizer, true, netconfSessionPreferences.isRollbackSupported());
+        return new NetconfDeviceWriteOnlyTx(id, rpc, normalizer, netconfSessionPreferences.isCandidateSupported(), netconfSessionPreferences.isRollbackSupported());
     }
 
     @Override
index 3248453..9ef44f6 100644 (file)
@@ -15,6 +15,7 @@ import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessag
 
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
@@ -22,6 +23,7 @@ import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizat
 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.controller.sal.core.api.RpcImplementation;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
@@ -32,16 +34,19 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction {
 
     private static final Logger LOG  = LoggerFactory.getLogger(NetconfDeviceReadOnlyTx.class);
 
     private final RpcImplementation rpc;
     private final DataNormalizer normalizer;
+    private final RemoteDeviceId id;
 
-    public NetconfDeviceReadOnlyTx(final RpcImplementation rpc, final DataNormalizer normalizer) {
+    public NetconfDeviceReadOnlyTx(final RpcImplementation rpc, final DataNormalizer normalizer, final RemoteDeviceId id) {
         this.rpc = rpc;
         this.normalizer = normalizer;
+        this.id = id;
     }
 
     public ListenableFuture<Optional<NormalizedNode<?, ?>>> readConfigurationData(final YangInstanceIdentifier path) {
@@ -51,6 +56,8 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
         return Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
             @Override
             public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
+                checkReadSuccess(result, path);
+
                 final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
                 final CompositeNode node = (CompositeNode) findNode(data, path);
 
@@ -61,6 +68,11 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
         });
     }
 
+    private void checkReadSuccess(final RpcResult<CompositeNode> result, final YangInstanceIdentifier path) {
+        LOG.warn("{}: Unable to read data: {}, errors: {}", id, path, result.getErrors());
+        Preconditions.checkArgument(result.isSuccessful(), "%s: Unable to read data: %s, errors: %s", id, path, result.getErrors());
+    }
+
     private Optional<NormalizedNode<?, ?>> transform(final YangInstanceIdentifier path, final CompositeNode node) {
         if(node == null) {
             return Optional.absent();
@@ -68,7 +80,7 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
         try {
             return Optional.<NormalizedNode<?, ?>>of(normalizer.toNormalized(path, node).getValue());
         } catch (final Exception e) {
-            LOG.error("Unable to normalize data for {}, data: {}", path, node, e);
+            LOG.error("{}: Unable to normalize data for {}, data: {}", id, path, node, e);
             throw e;
         }
     }
@@ -79,6 +91,8 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
         return Futures.transform(future, new Function<RpcResult<CompositeNode>, Optional<NormalizedNode<?, ?>>>() {
             @Override
             public Optional<NormalizedNode<?, ?>> apply(final RpcResult<CompositeNode> result) {
+                checkReadSuccess(result, path);
+
                 final CompositeNode data = result.getResult().getFirstCompositeByName(NETCONF_DATA_QNAME);
                 final CompositeNode node = (CompositeNode) findNode(data, path);
 
@@ -123,7 +137,7 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
 
     @Override
     public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        final YangInstanceIdentifier legacyPath = toLegacyPath(normalizer, path);
+        final YangInstanceIdentifier legacyPath = toLegacyPath(normalizer, path, id);
 
         switch (store) {
             case CONFIGURATION : {
@@ -134,14 +148,14 @@ public final class NetconfDeviceReadOnlyTx implements DOMDataReadOnlyTransaction
             }
         }
 
-        throw new IllegalArgumentException(String.format("Cannot read data %s for %s datastore, unknown datastore type", path, store));
+        throw new IllegalArgumentException(String.format("%s, Cannot read data %s for %s datastore, unknown datastore type", id, path, store));
     }
 
-    static YangInstanceIdentifier toLegacyPath(final DataNormalizer normalizer, final YangInstanceIdentifier path) {
+    static YangInstanceIdentifier toLegacyPath(final DataNormalizer normalizer, final YangInstanceIdentifier path, final RemoteDeviceId id) {
         try {
             return normalizer.toLegacy(path);
         } catch (final DataNormalizationException e) {
-            throw new IllegalArgumentException("Cannot normalize path " + path, e);
+            throw new IllegalArgumentException(id + ": Cannot normalize path " + path, e);
         }
     }
 
index c8d9028..87f5477 100644 (file)
@@ -8,10 +8,11 @@
 
 package org.opendaylight.controller.sal.connect.netconf.sal.tx;
 
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
-import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CONFIG_QNAME;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_QNAME;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_ERROR_OPTION_QNAME;
 import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME;
@@ -26,13 +27,14 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
-import javax.annotation.Nullable;
+import java.util.concurrent.atomic.AtomicBoolean;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
@@ -57,97 +59,114 @@ import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction {
+public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction, FutureCallback<RpcResult<TransactionStatus>> {
 
     private static final Logger LOG  = LoggerFactory.getLogger(NetconfDeviceWriteOnlyTx.class);
 
     private final RemoteDeviceId id;
     private final RpcImplementation rpc;
     private final DataNormalizer normalizer;
+
     private final boolean rollbackSupported;
+    private final boolean candidateSupported;
     private final CompositeNode targetNode;
 
+    // Allow commit to be called only once
+    private final AtomicBoolean finished = new AtomicBoolean(false);
+
     public NetconfDeviceWriteOnlyTx(final RemoteDeviceId id, final RpcImplementation rpc, final DataNormalizer normalizer, final boolean candidateSupported, final boolean rollbackOnErrorSupported) {
         this.id = id;
         this.rpc = rpc;
         this.normalizer = normalizer;
-        this.targetNode = getTargetNode(candidateSupported);
+
+        this.candidateSupported = candidateSupported;
+        this.targetNode = getTargetNode(this.candidateSupported);
         this.rollbackSupported = rollbackOnErrorSupported;
     }
 
-    // FIXME add logging
-
     @Override
     public boolean cancel() {
-        if(isCommitted()) {
+        if(isFinished()) {
             return false;
         }
 
         return discardChanges();
     }
 
-    private boolean isCommitted() {
-        // TODO 732
-        return true;
+    private boolean isFinished() {
+        return finished.get();
     }
 
     private boolean discardChanges() {
-        // TODO 732
+        finished.set(true);
+
+        if(candidateSupported) {
+            sendDiscardChanges();
+        }
         return true;
     }
 
     // TODO should the edit operations be blocking ?
+    // TODO should the discard-changes operations be blocking ?
 
     @Override
     public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+        checkNotFinished();
         Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can merge only configuration, not %s", store);
 
         try {
-            final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path);
+            final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id);
             final CompositeNode legacyData = normalizer.toLegacy(path, data);
-            sendEditRpc(createEditConfigStructure(legacyPath, Optional.of(ModifyAction.REPLACE), Optional.fromNullable(legacyData)), Optional.of(ModifyAction.NONE));
+            sendEditRpc(
+                    createEditConfigStructure(legacyPath, Optional.of(ModifyAction.REPLACE), Optional.fromNullable(legacyData)), Optional.of(ModifyAction.NONE));
         } catch (final ExecutionException e) {
-            LOG.warn("Error putting data to {}, data: {}, discarding changes", path, data, e);
+            LOG.warn("{}: Error putting data to {}, data: {}, discarding changes", id, path, data, e);
             discardChanges();
-            throw new RuntimeException("Error while replacing " + path, e);
+            throw new RuntimeException(id + ": Error while replacing " + path, e);
         }
     }
 
+    private void checkNotFinished() {
+        Preconditions.checkState(isFinished() == false, "%s: Transaction %s already finished", id, getIdentifier());
+    }
+
     @Override
     public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
-        Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can merge only configuration, not %s", store);
+        checkNotFinished();
+        Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "%s: Can merge only configuration, not %s", id, store);
 
         try {
-            final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path);
+            final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id);
             final CompositeNode legacyData = normalizer.toLegacy(path, data);
             sendEditRpc(
                     createEditConfigStructure(legacyPath, Optional.<ModifyAction> absent(), Optional.fromNullable(legacyData)), Optional.<ModifyAction> absent());
         } catch (final ExecutionException e) {
-            LOG.warn("Error merging data to {}, data: {}, discarding changes", path, data, e);
+            LOG.warn("{}: Error merging data to {}, data: {}, discarding changes", id, path, data, e);
             discardChanges();
-            throw new RuntimeException("Error while merging " + path, e);
+            throw new RuntimeException(id + ": Error while merging " + path, e);
         }
     }
 
     @Override
     public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
-        Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can merge only configuration, not %s", store);
+        checkNotFinished();
+        Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "%s: Can merge only configuration, not %s", id, store);
 
         try {
-            sendEditRpc(createEditConfigStructure(NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path), Optional.of(ModifyAction.DELETE), Optional.<CompositeNode>absent()), Optional.of(ModifyAction.NONE));
+            sendEditRpc(
+                    createEditConfigStructure(NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id), Optional.of(ModifyAction.DELETE), Optional.<CompositeNode>absent()), Optional.of(ModifyAction.NONE));
         } catch (final ExecutionException e) {
-            LOG.warn("Error deleting data {}, discarding changes", path, e);
+            LOG.warn("{}: Error deleting data {}, discarding changes", id, path, e);
             discardChanges();
-            throw new RuntimeException("Error while deleting " + path, e);
+            throw new RuntimeException(id + ": Error while deleting " + path, e);
         }
     }
 
     @Override
     public CheckedFuture<Void, TransactionCommitFailedException> submit() {
         final ListenableFuture<Void> commmitFutureAsVoid = Futures.transform(commit(), new Function<RpcResult<TransactionStatus>, Void>() {
-            @Nullable
             @Override
-            public Void apply(@Nullable final RpcResult<TransactionStatus> input) {
+            public Void apply(final RpcResult<TransactionStatus> input) {
                 return null;
             }
         });
@@ -162,25 +181,46 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction {
 
     @Override
     public ListenableFuture<RpcResult<TransactionStatus>> commit() {
-        // FIXME do not allow commit if closed or failed
+        checkNotFinished();
+        finished.set(true);
 
-        final ListenableFuture<RpcResult<CompositeNode>> rpcResult = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, getCommitRequest());
-        return Futures.transform(rpcResult, new Function<RpcResult<CompositeNode>, RpcResult<TransactionStatus>>() {
-            @Override
-            public RpcResult<TransactionStatus> apply(@Nullable final RpcResult<CompositeNode> input) {
-                if(input.isSuccessful()) {
-                    return RpcResultBuilder.success(TransactionStatus.COMMITED).build();
-                } else {
-                    final RpcResultBuilder<TransactionStatus> failed = RpcResultBuilder.failed();
-                    for (final RpcError rpcError : input.getErrors()) {
-                        failed.withError(rpcError.getErrorType(), rpcError.getTag(), rpcError.getMessage(), rpcError.getApplicationTag(), rpcError.getInfo(), rpcError.getCause());
+        if(candidateSupported == false) {
+            return Futures.immediateFuture(RpcResultBuilder.success(TransactionStatus.COMMITED).build());
+        }
+
+        final ListenableFuture<RpcResult<CompositeNode>> rpcResult = rpc.invokeRpc(
+                NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, NetconfMessageTransformUtil.COMMIT_RPC_CONTENT);
+
+        final ListenableFuture<RpcResult<TransactionStatus>> transformed = Futures.transform(rpcResult,
+                new Function<RpcResult<CompositeNode>, RpcResult<TransactionStatus>>() {
+                    @Override
+                    public RpcResult<TransactionStatus> apply(final RpcResult<CompositeNode> input) {
+                        if (input.isSuccessful()) {
+                            return RpcResultBuilder.success(TransactionStatus.COMMITED).build();
+                        } else {
+                            final RpcResultBuilder<TransactionStatus> failed = RpcResultBuilder.failed();
+                            for (final RpcError rpcError : input.getErrors()) {
+                                failed.withError(rpcError.getErrorType(), rpcError.getTag(), rpcError.getMessage(),
+                                        rpcError.getApplicationTag(), rpcError.getInfo(), rpcError.getCause());
+                            }
+                            return failed.build();
+                        }
                     }
-                    return failed.build();
-                }
-            }
-        });
+                });
 
-        // FIXME 732 detect commit failure
+        Futures.addCallback(transformed, this);
+        return transformed;
+    }
+
+    @Override
+    public void onSuccess(final RpcResult<TransactionStatus> result) {
+        LOG.debug("{}: Write successful, transaction: {}", id, getIdentifier());
+    }
+
+    @Override
+    public void onFailure(final Throwable t) {
+        LOG.warn("{}: Write failed, transaction {}, discarding changes", id, getIdentifier(), t);
+        discardChanges();
     }
 
     private void sendEditRpc(final CompositeNode editStructure, final Optional<ModifyAction> defaultOperation) throws ExecutionException {
@@ -200,6 +240,22 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction {
         }
     }
 
+    private void sendDiscardChanges() {
+        final ListenableFuture<RpcResult<CompositeNode>> discardFuture = rpc.invokeRpc(NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT);
+        Futures.addCallback(discardFuture, new FutureCallback<RpcResult<CompositeNode>>() {
+            @Override
+            public void onSuccess(final RpcResult<CompositeNode> result) {
+                LOG.debug("{}: Discarding transaction: {}", id, getIdentifier());
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                LOG.error("{}: Discarding changes failed, transaction: {}. Device configuration might be corrupted", id, getIdentifier(), t);
+                throw new RuntimeException(id + ": Discarding changes failed, transaction " + getIdentifier(), t);
+            }
+        });
+    }
+
     private CompositeNode createEditConfigStructure(final YangInstanceIdentifier dataPath, final Optional<ModifyAction> operation,
                                                     final Optional<CompositeNode> lastChildOverride) {
         Preconditions.checkArgument(Iterables.isEmpty(dataPath.getPathArguments()) == false, "Instance identifier with empty path %s", dataPath);
@@ -298,13 +354,6 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction {
         }
     }
 
-    private ImmutableCompositeNode getCommitRequest() {
-        final CompositeNodeBuilder<ImmutableCompositeNode> commitInput = ImmutableCompositeNode.builder();
-        commitInput.setQName(NETCONF_COMMIT_QNAME);
-        return commitInput.toInstance();
-    }
-
-
     @Override
     public Object getIdentifier() {
         return this;
index a6924d9..d3faddd 100644 (file)
@@ -35,6 +35,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
 import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
@@ -45,8 +46,7 @@ import org.w3c.dom.Element;
 
 public class NetconfMessageTransformUtil {
 
-    private NetconfMessageTransformUtil() {
-    }
+    private NetconfMessageTransformUtil() {}
 
     public static final QName IETF_NETCONF_MONITORING = QName.create("urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "2010-10-04", "ietf-netconf-monitoring");
     public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
@@ -66,14 +66,27 @@ public class NetconfMessageTransformUtil {
     public static QName NETCONF_DEFAULT_OPERATION_QNAME = QName.create(NETCONF_OPERATION_QNAME, "default-operation");
     public static QName NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config");
     public static QName NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config");
+    public static QName NETCONF_DISCARD_CHANGES_QNAME = QName.create(NETCONF_QNAME, "discard-changes");
     public static QName NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type");
     public static QName NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter");
     public static QName NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get");
     public static QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc");
+
     public static URI NETCONF_ROLLBACK_ON_ERROR_URI = URI
             .create("urn:ietf:params:netconf:capability:rollback-on-error:1.0");
     public static String ROLLBACK_ON_ERROR_OPTION = "rollback-on-error";
 
+    public static URI NETCONF_CANDIDATE_URI = URI
+            .create("urn:ietf:params:netconf:capability:candidate:1.0");
+
+    // Discard changes message
+    public static final CompositeNode DISCARD_CHANGES_RPC_CONTENT =
+            NodeFactory.createImmutableCompositeNode(NETCONF_DISCARD_CHANGES_QNAME, null, Collections.<Node<?>>emptyList());
+
+    // Commit changes message
+    public static final CompositeNode COMMIT_RPC_CONTENT =
+            NodeFactory.createImmutableCompositeNode(NETCONF_COMMIT_QNAME, null, Collections.<Node<?>>emptyList());
+
     public static Node<?> toFilterStructure(final YangInstanceIdentifier identifier) {
         Node<?> previous = null;
         if (Iterables.isEmpty(identifier.getPathArguments())) {
@@ -269,5 +282,4 @@ public class NetconfMessageTransformUtil {
             return it.toInstance();
         }
     }
-
 }
index 6bad479..e13398b 100644 (file)
@@ -58,6 +58,14 @@ module odl-sal-netconf-connector-cfg {
                 type string;
             }
 
+            container yang-module-capabilities {
+                leaf-list capability {
+                    type string;
+                    description "Set a list of capabilities to override capabilities provided in device's hello message.
+                                 Can be used for devices that do not report any yang modules in their hello message";
+                }
+            }
+
             container dom-registry {
                 uses config:service-ref {
                     refine type {
index 391bf9c..001b9a8 100644 (file)
@@ -8,35 +8,35 @@
 
 package org.opendaylight.controller.sal.connect.netconf.listener;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ListenableFuture;
 import io.netty.channel.ChannelFuture;
 import io.netty.util.concurrent.Future;
 import io.netty.util.concurrent.GenericFutureListener;
-
 import java.io.ByteArrayInputStream;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
-
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.same;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY;
-import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
-
 import org.apache.commons.lang3.StringUtils;
 import org.junit.Before;
 import org.junit.Test;
@@ -56,10 +56,6 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import com.google.common.base.Strings;
-import com.google.common.collect.Sets;
-import com.google.common.util.concurrent.ListenableFuture;
-
 public class NetconfDeviceCommunicatorTest {
 
     @Mock
@@ -129,9 +125,9 @@ public class NetconfDeviceCommunicatorTest {
         verify( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) );
 
         NetconfSessionCapabilities actualCapabilites = netconfSessionCapabilities.getValue();
-        assertEquals( "containsCapability", true, actualCapabilites.containsCapability(
-                                NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString() ) );
-        assertEquals( "containsCapability", true, actualCapabilites.containsCapability( testCapability ) );
+        assertEquals( "containsModuleCapability", true, actualCapabilites.containsNonModuleCapability(
+                NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString()) );
+        assertEquals( "containsModuleCapability", false, actualCapabilites.containsNonModuleCapability(testCapability) );
         assertEquals( "getModuleBasedCaps", Sets.newHashSet(
                             QName.create( "urn:opendaylight:params:xml:ns:test", "2014-06-02", "test-module" )),
                       actualCapabilites.getModuleBasedCaps() );
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilitiesTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilitiesTest.java
new file mode 100644 (file)
index 0000000..87947b5
--- /dev/null
@@ -0,0 +1,50 @@
+package org.opendaylight.controller.sal.connect.netconf.listener;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.google.common.collect.Lists;
+import java.util.List;
+import org.junit.Test;
+import org.junit.matchers.JUnitMatchers;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class NetconfSessionCapabilitiesTest {
+
+    @Test
+    public void testMerge() throws Exception {
+        final List<String> caps1 = Lists.newArrayList(
+                "namespace:1?module=module1&revision=2012-12-12",
+                "namespace:2?module=module2&amp;revision=2012-12-12",
+                "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&amp;revision=2010-10-04",
+                "urn:ietf:params:netconf:base:1.0",
+                "urn:ietf:params:netconf:capability:rollback-on-error:1.0"
+        );
+        final NetconfSessionCapabilities sessionCaps1 = NetconfSessionCapabilities.fromStrings(caps1);
+        assertCaps(sessionCaps1, 2, 3);
+
+        final List<String> caps2 = Lists.newArrayList(
+                "namespace:3?module=module3&revision=2012-12-12",
+                "namespace:4?module=module4&revision=2012-12-12",
+                "randomNonModuleCap"
+        );
+        final NetconfSessionCapabilities sessionCaps2 = NetconfSessionCapabilities.fromStrings(caps2);
+        assertCaps(sessionCaps2, 1, 2);
+
+        final NetconfSessionCapabilities merged = sessionCaps1.replaceModuleCaps(sessionCaps2);
+        assertCaps(merged, 2, 2 + 1 /*Preserved monitoring*/);
+        for (final QName qName : sessionCaps2.getModuleBasedCaps()) {
+            assertThat(merged.getModuleBasedCaps(), JUnitMatchers.hasItem(qName));
+        }
+        assertThat(merged.getModuleBasedCaps(), JUnitMatchers.hasItem(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING));
+
+        assertThat(merged.getNonModuleCaps(), JUnitMatchers.hasItem("urn:ietf:params:netconf:base:1.0"));
+        assertThat(merged.getNonModuleCaps(), JUnitMatchers.hasItem("urn:ietf:params:netconf:capability:rollback-on-error:1.0"));
+    }
+
+    private void assertCaps(final NetconfSessionCapabilities sessionCaps1, final int nonModuleCaps, final int moduleCaps) {
+        assertEquals(nonModuleCaps, sessionCaps1.getNonModuleCaps().size());
+        assertEquals(moduleCaps, sessionCaps1.getModuleBasedCaps().size());
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java
new file mode 100644 (file)
index 0000000..a65e426
--- /dev/null
@@ -0,0 +1,79 @@
+package org.opendaylight.controller.sal.connect.netconf.sal.tx;
+
+import static junit.framework.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+public class NetconfDeviceWriteOnlyTxTest {
+
+    private final RemoteDeviceId id = new RemoteDeviceId("test-mount");
+
+    @Mock
+    private RpcImplementation rpc;
+    @Mock
+    private DataNormalizer normalizer;
+    private YangInstanceIdentifier yangIId;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        doReturn(Futures.<RpcResult<CompositeNode>>immediateFailedFuture(new IllegalStateException("Failed tx")))
+        .doReturn(Futures.immediateFuture(RpcResultBuilder.<CompositeNode>success().build()))
+                .when(rpc).invokeRpc(any(QName.class), any(CompositeNode.class));
+
+        yangIId = YangInstanceIdentifier.builder().node(QName.create("namespace", "2012-12-12", "name")).build();
+        doReturn(yangIId).when(normalizer).toLegacy(yangIId);
+    }
+
+    @Test
+    public void testDiscardCahnges() {
+        final NetconfDeviceWriteOnlyTx tx = new NetconfDeviceWriteOnlyTx(id, rpc, normalizer, true, true);
+        final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = tx.submit();
+        try {
+            submitFuture.checkedGet();
+        } catch (final TransactionCommitFailedException e) {
+            // verify discard changes was sent
+            verify(rpc).invokeRpc(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT);
+            return;
+        }
+
+        fail("Submit should fail");
+    }
+
+
+    @Test
+    public void testDiscardCahngesNotSentWithoutCandidate() {
+        doReturn(Futures.immediateFuture(RpcResultBuilder.<CompositeNode>success().build()))
+        .doReturn(Futures.<RpcResult<CompositeNode>>immediateFailedFuture(new IllegalStateException("Failed tx")))
+                .when(rpc).invokeRpc(any(QName.class), any(CompositeNode.class));
+
+        final NetconfDeviceWriteOnlyTx tx = new NetconfDeviceWriteOnlyTx(id, rpc, normalizer, false, true);
+        tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
+        verify(rpc).invokeRpc(eq(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME), any(CompositeNode.class));
+        verifyNoMoreInteractions(rpc);
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorConstants.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/ActorConstants.java
new file mode 100644 (file)
index 0000000..1f1a0f5
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * 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.remote.rpc;
+
+
+public class ActorConstants {
+  public static final String RPC_BROKER = "rpc-broker";
+  public static final String RPC_REGISTRY = "rpc-registry";
+  public static final String RPC_MANAGER = "rpc";
+
+  public static final String RPC_BROKER_PATH= "/user/rpc/rpc-broker";
+  public static final String RPC_REGISTRY_PATH = "/user/rpc/rpc-registry";
+}
index 3df572d..ac50b8f 100644 (file)
@@ -68,7 +68,7 @@ public class RemoteRpcProvider implements AutoCloseable, Provider, SchemaContext
     SchemaService schemaService = brokerSession.getService(SchemaService.class);
     schemaContext = schemaService.getGlobalContext();
 
-    rpcManager = actorSystem.actorOf(RpcManager.props(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry), "rpc");
+    rpcManager = actorSystem.actorOf(RpcManager.props(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry), ActorConstants.RPC_MANAGER);
 
     LOG.debug("Rpc actors are created.");
   }
index 4925a17..5c56455 100644 (file)
@@ -72,14 +72,14 @@ public class RpcManager extends AbstractUntypedActor {
   private void createRpcActors() {
     LOG.debug("Create rpc registry and broker actors");
 
-    rpcRegistry = getContext().actorOf(RpcRegistry.props(clusterWrapper), "rpc-registry");
-    rpcBroker = getContext().actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "rpc-broker");
+    rpcRegistry = getContext().actorOf(RpcRegistry.props(clusterWrapper), ActorConstants.RPC_REGISTRY);
+    rpcBroker = getContext().actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), ActorConstants.RPC_BROKER);
   }
 
   private void startListeners() {
     LOG.debug("Registers rpc listeners");
 
-    String rpcBrokerPath = clusterWrapper.getAddress().toString() + "/user/rpc/rpc-broker";
+    String rpcBrokerPath = clusterWrapper.getAddress().toString() + ActorConstants.RPC_BROKER_PATH;
     rpcListener = new RpcListener(rpcRegistry, rpcBrokerPath);
     routeChangeListener = new RoutedRpcListener(rpcRegistry, rpcBrokerPath);
     rpcImplementation = new RemoteRpcImplementation(rpcBroker, schemaContext);
index 7cb505a..e36060c 100644 (file)
@@ -14,6 +14,7 @@ import akka.cluster.ClusterEvent;
 import akka.cluster.Member;
 import akka.japi.Creator;
 import org.opendaylight.controller.remote.rpc.AbstractUntypedActor;
+import org.opendaylight.controller.remote.rpc.ActorConstants;
 import org.opendaylight.controller.remote.rpc.messages.AddRoutedRpc;
 import org.opendaylight.controller.remote.rpc.messages.AddRpc;
 import org.opendaylight.controller.remote.rpc.messages.GetRoutedRpc;
@@ -171,7 +172,7 @@ public class RpcRegistry extends AbstractUntypedActor {
         }
         if(i == index) {
           if(!currentNodeAddress.equals(member.address())) {
-            actor = this.context().actorSelection(member.address() + "/user/rpc-registry");
+            actor = this.context().actorSelection(member.address() + ActorConstants.RPC_REGISTRY_PATH);
             break;
           } else if(index < memberSize-1){ // pick the next element in the set
             index++;
@@ -180,7 +181,7 @@ public class RpcRegistry extends AbstractUntypedActor {
         i++;
       }
       if(actor == null && previousMember != null) {
-        actor = this.context().actorSelection(previousMember.address() + "/user/rpc-registry");
+        actor = this.context().actorSelection(previousMember.address() + ActorConstants.RPC_REGISTRY_PATH);
       }
     }
     return actor;
index adb176a..559be5a 100644 (file)
@@ -21,13 +21,13 @@ import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 
 public class RestconfProviderImpl implements Provider, AutoCloseable, RestConnector {
 
     public final static String NOT_INITALIZED_MSG = "Restconf is not initialized yet. Please try again later";
 
-    private ListenerRegistration<SchemaServiceListener> listenerRegistration;
+    private ListenerRegistration<SchemaContextListener> listenerRegistration;
     private PortNumber port;
     public void setWebsocketPort(PortNumber port) {
         this.port = port;
@@ -43,7 +43,7 @@ public class RestconfProviderImpl implements Provider, AutoCloseable, RestConnec
         BrokerFacade.getInstance().setDataService(dataService);
 
         SchemaService schemaService = session.getService(SchemaService.class);
-        listenerRegistration = schemaService.registerSchemaServiceListener(ControllerContext.getInstance());
+        listenerRegistration = schemaService.registerSchemaContextListener(ControllerContext.getInstance());
         ControllerContext.getInstance().setSchemas(schemaService.getGlobalContext());
         ControllerContext.getInstance().setMountService(session.getService(MountService.class));
 
index dad7a2c..f8bcbe3 100644 (file)
@@ -508,9 +508,9 @@ public class ControllerContext implements SchemaContextListener {
         DataSchemaNode ret = container.getDataChildByName(name);
         if (ret == null) {
             for (final DataSchemaNode node : container.getChildNodes()) {
-                if ((node instanceof ChoiceCaseNode)) {
-                    final ChoiceCaseNode caseNode = ((ChoiceCaseNode) node);
-                    DataSchemaNode childByQName = ControllerContext.childByQName(caseNode, name);
+                if ((node instanceof ChoiceNode)) {
+                    final ChoiceNode choiceNode = ((ChoiceNode) node);
+                    DataSchemaNode childByQName = ControllerContext.childByQName(choiceNode, name);
                     if (childByQName != null) {
                         return childByQName;
                     }
index cf1bcd6..99bd8c5 100644 (file)
@@ -114,7 +114,7 @@ public class Notificator {
             result = result.substring(1);
         }
         if (result.endsWith("/")) {
-            result = result.substring(0, result.length());
+            result = result.substring(0, result.length()-1);
         }
         return result;
     }
index c601647..ae7d323 100644 (file)
@@ -15,6 +15,7 @@
     <module>toaster</module>
     <module>toaster-consumer</module>
     <module>toaster-provider</module>
+    <module>toaster-config</module>
     <module>l2switch</module>
   </modules>
   <scm>
diff --git a/opendaylight/md-sal/samples/toaster-config/pom.xml b/opendaylight/md-sal/samples/toaster-config/pom.xml
new file mode 100644 (file)
index 0000000..b30c4ba
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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>
+  <parent>
+    <groupId>org.opendaylight.controller.samples</groupId>
+    <artifactId>sal-samples</artifactId>
+    <version>1.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>toaster-config</artifactId>
+  <description>Configuration files for toaster</description>
+  <packaging>jar</packaging>
+  <build>
+    <plugins>
+        <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-artifacts</id>
+            <goals>
+              <goal>attach-artifact</goal>
+            </goals>
+            <phase>package</phase>
+            <configuration>
+              <artifacts>
+                <artifact>
+                  <file>${project.build.directory}/classes/initial/03-toaster-sample.xml</file>
+                  <type>xml</type>
+                  <classifier>config</classifier>
+                </artifact>
+              </artifacts>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
@@ -26,7 +26,7 @@
                         <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
                         <name>binding-data-broker</name>
                     </data-broker>
-                    
+
                     <notification-service>
                         <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
                             binding:binding-notification-service
@@ -54,7 +54,7 @@
                     </notification-service>
                 </module>
             </modules>
-            
+
             <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
                 <service>
                     <type xmlns:kitchen="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
diff --git a/opendaylight/netconf/netconf-config/pom.xml b/opendaylight/netconf/netconf-config/pom.xml
new file mode 100644 (file)
index 0000000..db5d14d
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>netconf-subsystem</artifactId>
+    <version>0.2.5-SNAPSHOT</version>
+  </parent>
+  <artifactId>netconf-config</artifactId>
+  <description>Configuration files for netconf</description>
+  <packaging>jar</packaging>
+  <build>
+    <plugins>
+        <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-artifacts</id>
+            <goals>
+              <goal>attach-artifact</goal>
+            </goals>
+            <phase>package</phase>
+            <configuration>
+              <artifacts>
+                <artifact>
+                  <file>${project.build.directory}/classes/initial/01-netconf.xml</file>
+                  <type>xml</type>
+                  <classifier>config</classifier>
+                </artifact>
+              </artifacts>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/opendaylight/netconf/netconf-connector-config/pom.xml b/opendaylight/netconf/netconf-connector-config/pom.xml
new file mode 100644 (file)
index 0000000..d9cc5ea
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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>
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>netconf-subsystem</artifactId>
+    <version>0.2.5-SNAPSHOT</version>
+  </parent>
+  <artifactId>netconf-connector-config</artifactId>
+  <description>Configuration files for netconf-connector</description>
+  <packaging>jar</packaging>
+  <build>
+    <plugins>
+        <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-artifacts</id>
+            <goals>
+              <goal>attach-artifact</goal>
+            </goals>
+            <phase>package</phase>
+            <configuration>
+              <artifacts>
+                <artifact>
+                  <file>${project.build.directory}/classes/initial/99-netconf-connector.xml</file>
+                  <type>xml</type>
+                  <classifier>config</classifier>
+                </artifact>
+              </artifacts>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
index 937949a..c72705d 100644 (file)
@@ -20,6 +20,7 @@
   <modules>
     <module>netconf-api</module>
     <module>netconf-cli</module>
+    <module>netconf-config</module>
     <module>netconf-impl</module>
     <module>config-netconf-connector</module>
     <module>netconf-util</module>
@@ -32,6 +33,7 @@
     <module>netconf-monitoring</module>
     <module>ietf-netconf-monitoring</module>
     <module>ietf-netconf-monitoring-extension</module>
+    <module>netconf-connector-config</module>
   </modules>
 
   <dependencies>
index 8400290..1f10b39 100644 (file)
@@ -75,7 +75,7 @@ public class NeutronSubnet extends ConfigurationObject implements Serializable,
      */
     List<NeutronPort> myPorts;
 
-    boolean gatewayIPAssigned;
+    Boolean gatewayIPAssigned;
 
     public NeutronSubnet() {
         myPorts = new ArrayList<NeutronPort>();
@@ -299,7 +299,7 @@ public class NeutronSubnet extends ConfigurationObject implements Serializable,
             try {
                 SubnetUtils util = new SubnetUtils(cidr);
                 SubnetInfo info = util.getInfo();
-                if (gatewayIP == null) {
+                if (gatewayIP == null || ("").equals(gatewayIP)) {
                     gatewayIP = info.getLowAddress();
                 }
                 if (allocationPools.size() < 1) {
@@ -460,6 +460,10 @@ public class NeutronSubnet extends ConfigurationObject implements Serializable,
         gatewayIPAssigned = false;
     }
 
+    public Boolean getGatewayIPAllocated() {
+        return gatewayIPAssigned;
+    }
+
     @Override
     public String toString() {
         return "NeutronSubnet [subnetUUID=" + subnetUUID + ", networkUUID=" + networkUUID + ", name=" + name
index 17b2fcf..806e853 100644 (file)
@@ -422,7 +422,9 @@ public class NeutronRoutersNorthbound {
         if (instances != null) {
             for (Object instance : instances) {
                 INeutronRouterAware service = (INeutronRouterAware) instance;
-                service.canAttachInterface(target, input);
+                int status = service.canAttachInterface(target, input);
+                if (status < 200 || status > 299)
+                    return Response.status(status).build();
             }
         }
 
@@ -498,7 +500,9 @@ public class NeutronRoutersNorthbound {
             if (instances != null) {
                 for (Object instance : instances) {
                     INeutronRouterAware service = (INeutronRouterAware) instance;
-                    service.canDetachInterface(target, input);
+                    int status = service.canDetachInterface(target, input);
+                    if (status < 200 || status > 299)
+                        return Response.status(status).build();
                 }
             }
 

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