<feature name="base-eclipselink-persistence" description="EclipseLink Persistence API" version="2.0.4.v201112161009">
<bundle start="true" start-level="35">mvn:eclipselink/javax.persistence/2.0.4.v201112161009</bundle>
<bundle start="true" start-level="35">mvn:eclipselink/javax.resource/1.5.0.v200906010428</bundle>
+ <bundle start="true" start-level="35">mvn:org.eclipse.persistence/org.eclipse.persistence.moxy/2.5.0</bundle>
+ <bundle start="true" start-level="35">mvn:org.eclipse.persistence/org.eclipse.persistence.core/2.5.0</bundle>
</feature>
<feature name="base-gemini-web" description="Gemini Web" version="${geminiweb.version}">
<feature>http</feature>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-subsystem</artifactId>
+ <version>0.2.5-SNAPSHOT</version>
+ <relativePath>../../opendaylight/config/</relativePath>
+ </parent>
+ <artifactId>config-netty-features</artifactId>
+
+ <packaging>pom</packaging>
+
+ <properties>
+ <features.file>features.xml</features.file>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-persister-features</artifactId>
+ <version>${config.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <filtering>true</filtering>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>filter</id>
+ <goals>
+ <goal>resources</goal>
+ </goals>
+ <phase>generate-resources</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/${features.file}</file>
+ <type>xml</type>
+ <classifier>features</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<features name="odl-config-persister-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+ <repository>mvn:org.opendaylight.controller/config-persister-features/${config.version}/xml/features</repository>
+ <feature name='odl-config-netty' version='${project.version}'>
+ <feature version='${project.version}'>odl-config-netty-config-api</feature>
+ <bundle>mvn:org.opendaylight.controller/netty-event-executor-config/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/netty-threadgroup-config/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/netty-timer-config/${project.version}</bundle>
+ <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>
+ </feature>
+</features>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-subsystem</artifactId>
+ <version>0.2.5-SNAPSHOT</version>
+ <relativePath>../../opendaylight/config/</relativePath>
+ </parent>
+ <artifactId>config-persister-features</artifactId>
+
+ <packaging>pom</packaging>
+
+ <properties>
+ <features.file>features.xml</features.file>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-yangtools</artifactId>
+ <version>${yangtools.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-features</artifactId>
+ <version>${netconf.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-features</artifactId>
+ <version>${config.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <filtering>true</filtering>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>filter</id>
+ <goals>
+ <goal>resources</goal>
+ </goals>
+ <phase>generate-resources</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/${features.file}</file>
+ <type>xml</type>
+ <classifier>features</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<features name="odl-config-persister-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+ <repository>mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features</repository>
+ <repository>mvn:org.opendaylight.controller/netconf-features/${netconf.version}/xml/features</repository>
+ <repository>mvn:org.opendaylight.controller/config-features/${config.version}/xml/features</repository>
+ <feature name='odl-config-startup' version='${project.version}'>
+ <feature version='${project.version}'>odl-config-netconf-connector</feature>
+ <feature version='${project.version}'>odl-config-persister</feature>
+ <feature version='${project.version}'>odl-netconf-impl</feature>
+ </feature>
+ <feature name='odl-config-persister' version='${project.version}'>
+ <feature version='${netconf.version}'>odl-netconf-api</feature>
+ <feature version='${project.version}'>odl-config-api</feature>
+ <feature version='${yangtools.version}'>yangtools-binding-generator</feature>
+ <bundle>mvn:org.opendaylight.controller/config-persister-api/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-persister-file-xml-adapter/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-persister-directory-xml-adapter/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-persister-impl/${project.version}</bundle>
+
+ <bundle>mvn:org.opendaylight.controller/netconf-util/${netconf.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/netconf-mapping-api/${netconf.version}</bundle>
+
+ <bundle>mvn:com.google.guava/guava/${guava.version}</bundle>
+ <bundle>mvn:commons-io/commons-io/${commons.io.version}</bundle>
+ <bundle>mvn:org.apache.commons/commons-lang3/${commons.lang3.version}</bundle>
+ <bundle>mvn:org.eclipse.persistence/org.eclipse.persistence.core/${eclipse.persistence.version}</bundle>
+ <bundle>mvn:org.eclipse.persistence/org.eclipse.persistence.moxy/${eclipse.persistence.version}</bundle>
+ </feature>
+</features>
\ No newline at end of file
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-subsystem</artifactId>
<version>0.2.5-SNAPSHOT</version>
+ <relativePath>../../opendaylight/config/</relativePath>
</parent>
<artifactId>config-features</artifactId>
<features.file>features.xml</features.file>
</properties>
- <dependencies></dependencies>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-yangtools</artifactId>
+ <version>${yangtools.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
<build>
<resources>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<features name="odl-config-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+ <repository>mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features</repository>
+
+ <feature name='odl-config-core' version='${project.version}'>
+ <feature version='${yangtools.version}'>yangtools-concepts</feature>
+ <feature version='${yangtools.version}'>yangtools-binding</feature>
+ <feature version='${yangtools.version}'>yangtools-binding-generator</feature>
+ <feature version='${mdsal.version}'>odl-mdsal-commons</feature>
+ <feature version='${project.version}'>odl-config-api</feature>
+ <bundle>mvn:org.opendaylight.controller/config-util/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/yang-jmx-generator/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/shutdown-api/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/shutdown-impl/${project.version}</bundle>
+ <bundle>mvn:org.osgi/org.osgi.core/${osgi.core.version}</bundle>
+ <bundle>wrap:mvn:com.google.guava/guava/${guava.version}</bundle>
+ <bundle>mvn:org.javassist/javassist/${javassist.version}</bundle>
+ </feature>
+ <feature name='odl-config-manager' version='${project.version}'>
+ <feature version='${project.version}'>odl-config-core</feature>
+ <bundle>mvn:org.opendaylight.controller/config-manager/${project.version}</bundle>
+ </feature>
+
+ <feature name='odl-config-api' version='${project.version}'>
+ <bundle>mvn:org.opendaylight.controller/config-api/${project.version}</bundle>
+
+ <!-- yangtools features -->
+ <feature version='${yangtools.version}'>yangtools-concepts</feature>
+ <feature version='${yangtools.version}'>yangtools-binding</feature>
+ </feature>
+
+ <feature name='odl-config-netty-config-api' version='${project.version}'>
+ <bundle>mvn:org.opendaylight.controller/netty-config-api/${project.version}</bundle>
+
+ <!-- netty bundles -->
+ <bundle>mvn:io.netty/netty-transport/${netty.version}</bundle>
+ <bundle>mvn:io.netty/netty-common/${netty.version}</bundle>
+ <bundle>mvn:io.netty/netty-buffer/${netty.version}</bundle>
+
+ <feature version='${project.version}'>odl-config-api</feature>
+ </feature>
+ <feature name='odl-config-dispatcher' version='${project.version}'>
+ <bundle>mvn:org.opendaylight.controller/netconf-config-dispatcher/${project.version}</bundle>
+ </feature>
+
+</features>
\ No newline at end of file
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-parent</artifactId>
<version>1.1-SNAPSHOT</version>
+ <relativePath>../../opendaylight/md-sal</relativePath>
</parent>
<artifactId>mdsal-features</artifactId>
<feature version='${project.version}'>odl-mdsal-restconf</feature>
</feature>
<feature name='odl-mdsal-commons' version='${project.version}'>
- <feature version='${yangtools.version}'>yangtools-concepts</feature>
- <feature version='${yangtools.version}'>yangtools-binding</feature>
+ <feature version='${yangtools.version}'>yangtools-data-binding</feature>
<bundle>mvn:org.opendaylight.controller/sal-common/${project.version}</bundle>
<bundle>mvn:org.opendaylight.controller/sal-common-api/${project.version}</bundle>
<bundle>mvn:org.opendaylight.controller/sal-common-impl/${project.version}</bundle>
<bundle>mvn:org.opendaylight.controller/sal-common-util/${project.version}</bundle>
- <bundle>wrap:mvn:com.google.guava/guava/${guava.version}</bundle>
- <bundle>wrap:mvn:org.eclipse.xtend/org.eclipse.xtend.lib/${xtend.version}</bundle>
</feature>
<feature name='odl-mdsal-broker' version='${project.version}'>
<feature version='${yangtools.version}'>yangtools-concepts</feature>
<feature version='${yangtools.version}'>yangtools-binding</feature>
<feature version='${mdsal.version}'>odl-mdsal-commons</feature>
- <feature version='${config.version}'>odl-config-subsystem</feature>
+ <feature version='${config.version}'>odl-config-core</feature>
+ <feature version='${config.version}'>odl-config-manager</feature>
+ <feature version='${config.version}'>odl-config-api</feature>
+ <feature version='${config.version}'>odl-config-persister</feature>
<bundle>mvn:org.opendaylight.controller/sal-core-api/${project.version}</bundle>
<bundle>mvn:org.opendaylight.controller/sal-core-spi/${project.version}</bundle>
<bundle>mvn:org.opendaylight.controller/sal-broker-impl/${project.version}</bundle>
<bundle>mvn:org.opendaylight.controller/sal-binding-broker-impl/${project.version}</bundle>
<bundle>mvn:org.opendaylight.controller/sal-binding-util/${project.version}</bundle>
<bundle>mvn:org.opendaylight.controller/sal-connector-api/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal-inmemory-datastore/${project.version}</bundle>
</feature>
<feature name='odl-mdsal-restconf' version='${project.version}'>
<feature version='${mdsal.version}'>odl-mdsal-broker</feature>
<bundle>wrap:mvn:io.netty/netty-handler/${netty.version}</bundle>
<bundle>wrap:mvn:io.netty/netty-transport/${netty.version}</bundle>
</feature>
+ <feature name='odl-mdsal-model' version='${project.version}'>
+ <bundle>mvn:org.opendaylight.controller.model/model-flow-base/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.model/model-flow-management/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.model/model-flow-service/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.model/model-flow-statistics/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.model/model-inventory/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.model/model-topology/${project.version}</bundle>
+ </feature>
+ <feature name='odl-mdsal-toaster' version='${project.version}'>
+ <feature version='${yangtools.version}'>yangtools-concepts</feature>
+ <feature version='${yangtools.version}'>yangtools-binding</feature>
+ <feature version='${project.version}'>odl-mdsal-broker</feature>
+ <feature version='${project.version}'>odl-mdsal-all</feature>
+ <bundle>mvn:org.opendaylight.controller.samples/sample-toaster/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.samples/sample-toaster-consumer/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.samples/sample-toaster-provider/${project.version}</bundle>
+ </feature>
+ <feature name='odl-mdsal-misc' version='${project.version}'>
+ <bundle>mvn:org.opendaylight.controller/sal-netconf-connector/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal-restconf-broker/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal-remote/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.md/topology-manager/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.md/topology-lldp-discovery/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.md/statistics-manager/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.md/inventory-manager/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller.md/forwardingrules-manager/${project.version}</bundle>
+ </feature>
+
</features>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>0.2.5-SNAPSHOT</version>
+ <relativePath>../../opendaylight/netconf</relativePath>
+ </parent>
+ <artifactId>netconf-features</artifactId>
+
+ <packaging>pom</packaging>
+
+ <properties>
+ <features.file>features.xml</features.file>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-features</artifactId>
+ <version>${config.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>features-odl-protocol-framework</artifactId>
+ <version>${protocol-framework.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <filtering>true</filtering>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>filter</id>
+ <goals>
+ <goal>resources</goal>
+ </goals>
+ <phase>generate-resources</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/${features.file}</file>
+ <type>xml</type>
+ <classifier>features</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<features name="odl-netconf-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+ <repository>mvn:org.opendaylight.controller/features-odl-protocol-framework/${protocol-framework.version}/xml/features</repository>
+ <repository>mvn:org.opendaylight.controller/config-features/${config.version}/xml/features</repository>
+
+ <feature name='odl-netconf-api' version='${project.version}'>
+ <bundle>mvn:org.opendaylight.controller/netconf-api/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/ietf-netconf-monitoring/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/ietf-netconf-monitoring-extension/${project.version}</bundle>
+ <feature version='${protocol-framework.version}'>odl-protocol-framework</feature>
+ <bundle>mvn:org.opendaylight.yangtools.model/ietf-inet-types/${ietf-inet-types.version}</bundle>
+ <bundle>mvn:org.opendaylight.yangtools.model/ietf-yang-types/${ietf-yang-types.version}</bundle>
+ </feature>
+ <feature name='odl-netconf-mapping-api' version='${project.version}'>
+ <feature version='${project.version}'>odl-netconf-api</feature>
+ <bundle>mvn:org.opendaylight.controller/netconf-mapping-api/${project.version}</bundle>
+ </feature>
+ <feature name='odl-netconf-util' version='${project.version}'>
+ <feature version='${project.version}'>odl-netconf-mapping-api</feature>
+ <bundle>mvn:org.opendaylight.controller/netconf-util/${project.version}</bundle>
+ </feature>
+ <feature name='odl-config-netconf-connector' version='${project.version}'>
+ <feature version='${config.version}'>odl-config-manager</feature>
+ <bundle>mvn:org.opendaylight.controller/config-netconf-connector/${project.version}</bundle>
+ <feature version='${project.version}'>odl-netconf-api</feature>
+ <feature version='${project.version}'>odl-netconf-mapping-api</feature>
+ <feature version='${project.version}'>odl-netconf-util</feature>
+ </feature>
+
+ <feature name='odl-netconf-impl' version='${project.version}'>
+ <bundle>mvn:org.opendaylight.controller/netconf-impl/${project.version}</bundle>
+ <feature version='${project.version}'>odl-netconf-api</feature>
+ <feature version='${project.version}'>odl-netconf-mapping-api</feature>
+ <feature version='${project.version}'>odl-netconf-util</feature>
+ <feature version='${project.version}'>odl-netconf-netty-util</feature>
+ </feature>
+ <feature name='odl-netconf-netty-util' version='${project.version}'>
+ <bundle>mvn:org.opendaylight.controller/netconf-netty-util/${project.version}</bundle>
+ <feature version='${project.version}'>odl-netconf-api</feature>
+ <feature version='${project.version}'>odl-netconf-mapping-api</feature>
+ <feature version='${project.version}'>odl-netconf-util</feature>
+ <bundle>mvn:org.opendaylight.controller.thirdparty/ganymed/${ganymed.version}</bundle>
+ <bundle>mvn:org.openexi/nagasena/${exi.nagasena.version}</bundle>
+ <bundle>mvn:io.netty/netty-codec/${netty.version}</bundle>
+ <bundle>mvn:io.netty/netty-handler/${netty.version}</bundle>
+ <bundle>mvn:io.netty/netty-common/${netty.version}</bundle>
+ <bundle>mvn:io.netty/netty-buffer/${netty.version}</bundle>
+ <bundle>mvn:io.netty/netty-transport/${netty.version}</bundle>
+ </feature>
+ <feature name='odl-netconf-misc' version='${project.version}'>
+ <bundle>mvn:org.opendaylight.controller/netconf-client/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/netconf-monitoring/${project.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/netconf-tcp/${project.version}</bundle>
+ </feature>
+
+</features>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../opendaylight/commons/opendaylight</relativePath>
+ </parent>
+ <artifactId>features-controller</artifactId>
+ <packaging>pom</packaging>
+ <prerequisites>
+ <maven>3.0</maven>
+ </prerequisites>
+ <modules>
+ <module>config</module>
+ <module>config-persister</module>
+ <module>config-netty</module>
+ <module>mdsal</module>
+ <module>netconf</module>
+ <module>protocol-framework</module>
+ </modules>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../../opendaylight/commons/opendaylight</relativePath>
+ </parent>
+ <artifactId>features-odl-protocol-framework</artifactId>
+ <version>${protocol-framework.version}</version>
+ <packaging>pom</packaging>
+
+ <properties>
+ <features.file>features.xml</features.file>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-features</artifactId>
+ <version>${config.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <filtering>true</filtering>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>filter</id>
+ <goals>
+ <goal>resources</goal>
+ </goals>
+ <phase>generate-resources</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/${features.file}</file>
+ <type>xml</type>
+ <classifier>features</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<features name="odl-protocol-framework-${protocol-framework.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+ <repository>mvn:org.opendaylight.controller/config-features/${config.version}/xml/features</repository>
+ <feature name='odl-protocol-framework' version='${project.version}'>
+ <bundle>mvn:org.opendaylight.controller/protocol-framework/${protocol-framework.version}</bundle>
+ <feature version='${config.version}'>odl-config-api</feature> <!-- needed by netty-config-api -->
+ <feature version='${config.version}'>odl-config-netty-config-api</feature> <!-- needed by netty-config-api -->
+ </feature>
+</features>
\ No newline at end of file
initialized = true;
}
- public List<Filter> findMatchingFilters(String pathInfo) {
+ public List<Filter> findMatchingFilters(String path) {
+ logger.trace("findMatchingFilters({})", path);
checkState(initialized, "Not initialized");
- return urlMatcher.findMatchingFilters(pathInfo);
+ return urlMatcher.findMatchingFilters(path);
}
@XmlAttribute(name = "path")
throws IOException, ServletException {
String contextPath = request.getContext().getPath();
- String pathInfo = request.getPathInfo();
+ String path = request.getDecodedRequestURI();
Optional<Context> maybeContext = host.findContext(contextPath);
- logger.trace("Processing context {} path {}, found {}", contextPath, pathInfo, maybeContext);
+ logger.trace("Processing context {} path {}, found {}", contextPath, path, maybeContext);
if (maybeContext.isPresent()) {
// process filters
Context context = maybeContext.get();
- List<Filter> matchingFilters = context.findMatchingFilters(pathInfo);
+ List<Filter> matchingFilters = context.findMatchingFilters(path);
FilterChain fromLast = nextValveFilterChain;
ListIterator<Filter> it = matchingFilters.listIterator(matchingFilters.size());
final boolean trace = logger.isTraceEnabled();
/**
* Find filters matching path
*
- * @param pathInfo as returned by request.getPathInfo()
+ * @param path relative and decoded path to resource
* @return list of matching filters
*/
- public List<FILTER> findMatchingFilters(String pathInfo) {
- checkNotNull(pathInfo);
+ public List<FILTER> findMatchingFilters(String path) {
+ checkNotNull(path);
TreeMap<Integer, FILTER> sortedMap = new TreeMap<>();
// add matching prefixes
for (Entry<String, Entry<FILTER, Integer>> prefixEntry : prefixMap.entrySet()) {
- if (pathInfo.startsWith(prefixEntry.getKey())) {
+ if (path.startsWith(prefixEntry.getKey())) {
put(sortedMap, prefixEntry.getValue());
}
}
// add matching suffixes
for (Entry<String, Entry<FILTER, Integer>> suffixEntry : suffixMap.entrySet()) {
- if (pathInfo.endsWith(suffixEntry.getKey())) {
+ if (path.endsWith(suffixEntry.getKey())) {
put(sortedMap, suffixEntry.getValue());
}
}
// add exact match
- Entry<FILTER, Integer> exactMatch = exactMatchMap.get(pathInfo);
+ Entry<FILTER, Integer> exactMatch = exactMatchMap.get(path);
if (exactMatch != null) {
put(sortedMap, exactMatch);
}
ArrayList<FILTER> filters = new ArrayList<>(sortedMap.values());
- logger.trace("Matching filters for path {} are {}", pathInfo, filters);
+ logger.trace("Matching filters for path {} are {}", path, filters);
return filters;
}
<forwarding.staticrouting.northbound.version>0.4.2-SNAPSHOT</forwarding.staticrouting.northbound.version>
<forwardingrulesmanager.implementation.version>0.4.2-SNAPSHOT</forwardingrulesmanager.implementation.version>
<forwardingrulesmanager.version>0.6.0-SNAPSHOT</forwardingrulesmanager.version>
+ <ganymed.version>1.1-SNAPSHOT</ganymed.version>
<hosttracker.api.version>0.5.2-SNAPSHOT</hosttracker.api.version>
<hosttracker.implementation.version>0.5.2-SNAPSHOT</hosttracker.implementation.version>
<hosttracker.northbound.version>0.4.2-SNAPSHOT</hosttracker.northbound.version>
<dependency>
<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>ganymed</artifactId>
- <version>1.1-SNAPSHOT</version>
+ <version>${ganymed.version}</version>
</dependency>
<!-- Third parties from opendaylight released -->
<dependency>
*/
package org.opendaylight.controller.config.manager.impl.osgi;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
/**
*
- * Extensible bundle tracker. Takes several BundleTrackerCustomizers and propagates bundle events to all of them.
- * Primary customizer
+ * Extensible bundle tracker. Takes several BundleTrackerCustomizers and
+ * propagates bundle events to all of them.
+ *
+ * Primary customizer may return tracking object,
+ * which will be passed to it during invocation of
+ * {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Future)}
+ *
+ *
+ * This extender modifies behaviour to not leak platform thread
+ * in {@link BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)}
+ * but deliver this event from its own single threaded executor.
+ *
+ * If bundle is removed before event for adding bundle was executed,
+ * that event is cancelled. If addingBundle event is currently in progress
+ * or was already executed, platform thread is block untill addingBundle
+ * finishes so bundle could be removed correctly in platform thread.
+ *
+ *
+ * Method {@link BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)}
+ * is never invoked on registered trackers.
*
* @param <T>
*/
-public final class ExtensibleBundleTracker<T> extends BundleTracker<T> {
+public final class ExtensibleBundleTracker<T> extends BundleTracker<Future<T>> {
+ private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder()
+ .setNameFormat("config-bundle-tracker-%d")
+ .build();
+ private final ExecutorService eventExecutor;
private final BundleTrackerCustomizer<T> primaryTracker;
private final BundleTrackerCustomizer<?>[] additionalTrackers;
- private static final Logger logger = LoggerFactory.getLogger(ExtensibleBundleTracker.class);
+ private static final Logger LOG = LoggerFactory.getLogger(ExtensibleBundleTracker.class);
- public ExtensibleBundleTracker(BundleContext context, BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
- BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
+ public ExtensibleBundleTracker(final BundleContext context, final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
+ final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
this(context, Bundle.ACTIVE, primaryBundleTrackerCustomizer, additionalBundleTrackerCustomizers);
}
- public ExtensibleBundleTracker(BundleContext context, int bundleState,
- BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
- BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
+ public ExtensibleBundleTracker(final BundleContext context, final int bundleState,
+ final BundleTrackerCustomizer<T> primaryBundleTrackerCustomizer,
+ final BundleTrackerCustomizer<?>... additionalBundleTrackerCustomizers) {
super(context, bundleState, null);
this.primaryTracker = primaryBundleTrackerCustomizer;
this.additionalTrackers = additionalBundleTrackerCustomizers;
- logger.trace("Registered as extender with context {} and bundle state {}", context, bundleState);
+ eventExecutor = Executors.newSingleThreadExecutor(THREAD_FACTORY);
+ LOG.trace("Registered as extender with context {} and bundle state {}", context, bundleState);
}
@Override
- public T addingBundle(final Bundle bundle, final BundleEvent event) {
- T primaryTrackerRetVal = primaryTracker.addingBundle(bundle, event);
-
- forEachAdditionalBundle(new BundleStrategy() {
+ public Future<T> addingBundle(final Bundle bundle, final BundleEvent event) {
+ LOG.trace("Submiting AddingBundle for bundle {} and event {} to be processed asynchronously",bundle,event);
+ Future<T> future = eventExecutor.submit(new Callable<T>() {
@Override
- public void execute(BundleTrackerCustomizer<?> tracker) {
- tracker.addingBundle(bundle, event);
+ public T call() throws Exception {
+ try {
+ T primaryTrackerRetVal = primaryTracker.addingBundle(bundle, event);
+
+ forEachAdditionalBundle(new BundleStrategy() {
+ @Override
+ public void execute(final BundleTrackerCustomizer<?> tracker) {
+ tracker.addingBundle(bundle, event);
+ }
+ });
+ LOG.trace("AddingBundle for {} and event {} finished successfully",bundle,event);
+ return primaryTrackerRetVal;
+ } catch (Exception e) {
+ LOG.error("Failed to add bundle {}",e);
+ throw e;
+ }
}
});
-
- return primaryTrackerRetVal;
+ return future;
}
@Override
- public void modifiedBundle(final Bundle bundle, final BundleEvent event, final T object) {
- primaryTracker.modifiedBundle(bundle, event, object);
-
- forEachAdditionalBundle(new BundleStrategy() {
- @Override
- public void execute(BundleTrackerCustomizer<?> tracker) {
- tracker.modifiedBundle(bundle, event, null);
- }
- });
+ public void modifiedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
+ // Intentionally NOOP
}
@Override
- public void removedBundle(final Bundle bundle, final BundleEvent event, final T object) {
- primaryTracker.removedBundle(bundle, event, object);
-
- forEachAdditionalBundle(new BundleStrategy() {
- @Override
- public void execute(BundleTrackerCustomizer<?> tracker) {
- tracker.removedBundle(bundle, event, null);
- }
- });
+ public void removedBundle(final Bundle bundle, final BundleEvent event, final Future<T> object) {
+ if(!object.isDone() && object.cancel(false)) {
+ // We canceled adding event before it was processed
+ // so it is safe to return
+ LOG.trace("Adding Bundle event for {} was cancelled. No additional work required.",bundle);
+ return;
+ }
+ try {
+ LOG.trace("Invoking removedBundle event for {}",bundle);
+ primaryTracker.removedBundle(bundle, event, object.get());
+ forEachAdditionalBundle(new BundleStrategy() {
+ @Override
+ public void execute(final BundleTrackerCustomizer<?> tracker) {
+ tracker.removedBundle(bundle, event, null);
+ }
+ });
+ LOG.trace("Removed bundle event for {} finished successfully.",bundle);
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Addition of bundle failed, ", e);
+ }
}
- private void forEachAdditionalBundle(BundleStrategy lambda) {
+ private void forEachAdditionalBundle(final BundleStrategy lambda) {
for (BundleTrackerCustomizer<?> trac : additionalTrackers) {
lambda.execute(trac);
}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-
-<features name="config-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
- <feature name='config-all' version='${project.version}'>
- <feature version='${project.version}'>odl-config-subsystem</feature>
- </feature>
-
- <feature name='odl-config-subsystem' version='${project.version}'>
- <feature version='${yangtools.version}'>yangtools-concepts</feature>
- <feature version='${yangtools.version}'>yangtools-binding</feature>
- <feature version='${yangtools.version}'>yangtools-binding-generator</feature>
- <feature version='${mdsal.version}'>odl-mdsal-commons</feature>
- <bundle>mvn:org.opendaylight.controller/config-api/${project.version}</bundle>
- <bundle>mvn:org.opendaylight.controller/config-util/${project.version}</bundle>
- <bundle>mvn:org.opendaylight.controller/config-manager/${project.version}</bundle>
- <bundle>mvn:org.opendaylight.controller/yang-jmx-generator/${project.version}</bundle>
- <bundle>mvn:org.opendaylight.controller/config-persister-api/${project.version}</bundle>
- <bundle>mvn:org.opendaylight.controller/config-persister-file-xml-adapter/${project.version}</bundle>
- <bundle>mvn:org.opendaylight.controller/config-persister-directory-xml-adapter/${project.version}</bundle>
- <bundle>mvn:org.opendaylight.controller/shutdown-api/${project.version}</bundle>
- <bundle>mvn:org.opendaylight.controller/shutdown-impl/${project.version}</bundle>
- <bundle>mvn:org.osgi/org.osgi.core/${osgi.core.version}</bundle>
- <bundle>wrap:mvn:com.google.guava/guava/${guava.version}</bundle>
- <bundle>mvn:org.javassist/javassist/${javassist.version}</bundle>
- </feature>
-</features>
\ No newline at end of file
<module>shutdown-impl</module>
<module>netconf-config-dispatcher</module>
<module>config-module-archetype</module>
- <module>feature</module>
</modules>
<dependencies>
import java.io.Closeable;
import java.io.IOException;
+import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.config.threadpool.ThreadPool;
+import com.google.common.base.Optional;
+
/**
* Implementation of {@link ThreadPool} using flexible number of threads wraps
* {@link ExecutorService}.
public FlexibleThreadPoolWrapper(int minThreadCount, int maxThreadCount, long keepAlive, TimeUnit timeUnit,
ThreadFactory threadFactory) {
+ this(minThreadCount, maxThreadCount, keepAlive, timeUnit, threadFactory, getQueue(Optional.<Integer>absent()));
+ }
+
+ public FlexibleThreadPoolWrapper(int minThreadCount, int maxThreadCount, long keepAlive, TimeUnit timeUnit,
+ ThreadFactory threadFactory, Optional<Integer> queueCapacity) {
+ this(minThreadCount, maxThreadCount, keepAlive, timeUnit, threadFactory, getQueue(queueCapacity));
+ }
+
+ private FlexibleThreadPoolWrapper(int minThreadCount, int maxThreadCount, long keepAlive, TimeUnit timeUnit,
+ ThreadFactory threadFactory, BlockingQueue<Runnable> queue) {
executor = new ThreadPoolExecutor(minThreadCount, maxThreadCount, keepAlive, timeUnit,
- new SynchronousQueue<Runnable>(), threadFactory);
+ queue, threadFactory, new FlexibleRejectionHandler());
executor.prestartAllCoreThreads();
}
+ /**
+ * Overriding the queue:
+ * ThreadPoolExecutor would not create new threads if the queue is not full, thus adding
+ * occurs in RejectedExecutionHandler.
+ * This impl saturates threadpool first, then queue. When both are full caller will get blocked.
+ */
+ private static ForwardingBlockingQueue getQueue(Optional<Integer> capacity) {
+ final BlockingQueue<Runnable> delegate = capacity.isPresent() ? new LinkedBlockingQueue<Runnable>(capacity.get()) : new LinkedBlockingQueue<Runnable>();
+ return new ForwardingBlockingQueue(delegate);
+ }
+
@Override
public ExecutorService getExecutor() {
return Executors.unconfigurableExecutorService(executor);
executor.shutdown();
}
+ /**
+ * if the max threads are met, then it will raise a rejectedExecution. We then push to the queue.
+ */
+ private static class FlexibleRejectionHandler implements RejectedExecutionHandler {
+ @Override
+ public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
+ try {
+ executor.getQueue().put(r);
+ } catch (InterruptedException e) {
+ throw new RejectedExecutionException("Interrupted while waiting on the queue", e);
+ }
+ }
+ }
+
+ private static class ForwardingBlockingQueue extends com.google.common.util.concurrent.ForwardingBlockingQueue<Runnable> {
+ private final BlockingQueue<Runnable> delegate;
+
+ public ForwardingBlockingQueue(BlockingQueue<Runnable> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected BlockingQueue<Runnable> delegate() {
+ return delegate;
+ }
+
+ @Override
+ public boolean offer(final Runnable r) {
+ // ThreadPoolExecutor will spawn a new thread after core size is reached only
+ // if the queue.offer returns false.
+ return false;
+ }
+ }
}
*/
package org.opendaylight.controller.config.yang.threadpool.impl.flexible;
+import com.google.common.base.Optional;
import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.config.api.JmxAttributeValidationException;
JmxAttributeValidationException.checkNotNull(getMaxThreadCount(), maxThreadCountJmxAttribute);
JmxAttributeValidationException.checkCondition(getMaxThreadCount() > 0, "must be greater than zero",
maxThreadCountJmxAttribute);
+
+ if(getQueueCapacity() != null) {
+ JmxAttributeValidationException.checkCondition(getQueueCapacity() > 0, "Queue capacity cannot be < 1", queueCapacityJmxAttribute);
+ }
}
@Override
public java.lang.AutoCloseable createInstance() {
return new FlexibleThreadPoolWrapper(getMinThreadCount(), getMaxThreadCount(), getKeepAliveMillis(),
- TimeUnit.MILLISECONDS, getThreadFactoryDependency());
+ TimeUnit.MILLISECONDS, getThreadFactoryDependency(), Optional.fromNullable(getQueueCapacity()));
}
}
type uint32;
}
+ leaf queueCapacity {
+ type uint16;
+ mandatory false;
+ description "Capacity of queue that holds waiting tasks";
+ }
+
container threadFactory {
uses config:service-ref {
refine type {
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>config-features</artifactId>
+ <artifactId>config-netty-features</artifactId>
<version>${config.version}</version>
<classifier>features</classifier>
<type>xml</type>
<type>xml</type>
<scope>runtime</scope>
</dependency>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>features-yangtools</artifactId>
- <version>${yangtools.version}</version>
- <classifier>features</classifier>
- <type>xml</type>
- <scope>runtime</scope>
- </dependency>
</dependencies>
<build>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <tasks>
+ <copy todir="${project.build.directory}/assembly/bin" overwrite="true">
+ <fileset dir="${basedir}/src/main/resources/karaf/" includes="karaf,karaf.bat,instance,instance.bat"/>
+ </copy>
+ </tasks>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
<scm>
+++ /dev/null
-<snapshot>
- <configuration>
- <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
- <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">prefix:remote-zeromq-rpc-server</type>
- <name>remoter</name>
- <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">5666</port>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
- <name>dom-broker</name>
- </dom-broker>
- </module>
- </modules>
- <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- </services>
- </data>
- </configuration>
-
- <required-capabilities>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc?module=odl-sal-dom-rpc-remote-cfg&revision=2013-10-28</capability>
- </required-capabilities>
-</snapshot>
-
--- /dev/null
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+DIRNAME=`dirname "$0"`
+PROGNAME=`basename "$0"`
+
+#
+# Sourcing environment settings for karaf similar to tomcats setenv
+#
+KARAF_SCRIPT="instance"
+export KARAF_SCRIPT
+if [ -f "$DIRNAME/setenv" ]; then
+ . "$DIRNAME/setenv"
+fi
+
+#
+# Check/Set up some easily accessible MIN/MAX params for JVM mem usage
+#
+if [ "x$JAVA_MIN_MEM" = "x" ]; then
+ JAVA_MIN_MEM=128M
+ export JAVA_MIN_MEM
+fi
+if [ "x$JAVA_MAX_MEM" = "x" ]; then
+ JAVA_MAX_MEM=512M
+ export JAVA_MAX_MEM
+fi
+
+warn() {
+ echo "${PROGNAME}: $*"
+}
+
+die() {
+ warn "$*"
+ exit 1
+}
+
+detectOS() {
+ # OS specific support (must be 'true' or 'false').
+ cygwin=false;
+ darwin=false;
+ aix=false;
+ os400=false;
+ case "`uname`" in
+ CYGWIN*)
+ cygwin=true
+ ;;
+ Darwin*)
+ darwin=true
+ ;;
+ AIX*)
+ aix=true
+ ;;
+ OS400*)
+ os400=true
+ ;;
+ esac
+ # For AIX, set an environment variable
+ if $aix; then
+ export LDR_CNTRL=MAXDATA=0xB0000000@DSA
+ echo $LDR_CNTRL
+ fi
+}
+
+unlimitFD() {
+ # Use the maximum available, or set MAX_FD != -1 to use that
+ if [ "x$MAX_FD" = "x" ]; then
+ MAX_FD="maximum"
+ fi
+
+ # Increase the maximum file descriptors if we can
+ if [ "$os400" = "false" ] && [ "$cygwin" = "false" ]; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ "$MAX_FD_LIMIT" != 'unlimited' ]; then
+ if [ $? -eq 0 ]; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then
+ # use the system max
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+
+ ulimit -n $MAX_FD > /dev/null
+ # echo "ulimit -n" `ulimit -n`
+ if [ $? -ne 0 ]; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query system maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+ fi
+ fi
+}
+
+locateHome() {
+ if [ "x$KARAF_HOME" != "x" ]; then
+ warn "Ignoring predefined value for KARAF_HOME"
+ fi
+
+ # In POSIX shells, CDPATH may cause cd to write to stdout
+ (unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+ KARAF_HOME=`cd "$DIRNAME/.."; pwd`
+ if [ ! -d "$KARAF_HOME" ]; then
+ die "KARAF_HOME is not valid: $KARAF_HOME"
+ fi
+}
+
+locateBase() {
+ if [ "x$KARAF_BASE" != "x" ]; then
+ if [ ! -d "$KARAF_BASE" ]; then
+ die "KARAF_BASE is not valid: $KARAF_BASE"
+ fi
+ else
+ KARAF_BASE=$KARAF_HOME
+ fi
+}
+
+locateData() {
+ if [ "x$KARAF_DATA" != "x" ]; then
+ if [ ! -d "$KARAF_DATA" ]; then
+ die "KARAF_DATA is not valid: $KARAF_DATA"
+ fi
+ else
+ KARAF_DATA=$KARAF_BASE/data
+ fi
+}
+
+locateEtc() {
+ if [ "x$KARAF_ETC" != "x" ]; then
+ if [ ! -d "$KARAF_ETC" ]; then
+ die "KARAF_ETC is not valid: $KARAF_ETC"
+ fi
+ else
+ KARAF_ETC=$KARAF_BASE/etc
+ fi
+}
+
+setupNativePath() {
+ # Support for loading native libraries
+ LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:$KARAF_BASE/lib:$KARAF_HOME/lib"
+
+ # For Cygwin, set PATH from LD_LIBRARY_PATH
+ if $cygwin; then
+ LD_LIBRARY_PATH=`cygpath --path --windows "$LD_LIBRARY_PATH"`
+ PATH="$PATH;$LD_LIBRARY_PATH"
+ export PATH
+ fi
+ export LD_LIBRARY_PATH
+}
+
+pathCanonical() {
+ local dst="${1}"
+ while [ -h "${dst}" ] ; do
+ ls=`ls -ld "${dst}"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ dst="$link"
+ else
+ dst="`dirname "${dst}"`/$link"
+ fi
+ done
+ local bas=`basename "${dst}"`
+ local dir=`dirname "${dst}"`
+ if [ "$bas" != "$dir" ]; then
+ dst="`pathCanonical "$dir"`/$bas"
+ fi
+ echo "${dst}" | sed -e 's#//#/#g' -e 's#/./#/#g' -e 's#/[^/]*/../#/#g'
+}
+
+locateJava() {
+ # Setup the Java Virtual Machine
+ if $cygwin ; then
+ [ -n "$JAVA" ] && JAVA=`cygpath --unix "$JAVA"`
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ fi
+
+ if [ "x$JAVA_HOME" = "x" ] && [ "$darwin" = "true" ]; then
+ JAVA_HOME="$(/usr/libexec/java_home)"
+ fi
+ if [ "x$JAVA" = "x" ] && [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+ if [ "x$JAVA" = "x" ]; then
+ if [ "x$JAVA_HOME" != "x" ]; then
+ if [ ! -d "$JAVA_HOME" ]; then
+ die "JAVA_HOME is not valid: $JAVA_HOME"
+ fi
+ JAVA="$JAVA_HOME/bin/java"
+ else
+ warn "JAVA_HOME not set; results may vary"
+ JAVA=`type java`
+ JAVA=`expr "$JAVA" : '.*is \(.*\)$'`
+ if [ "x$JAVA" = "x" ]; then
+ die "java command not found"
+ fi
+ fi
+ fi
+ if [ "x$JAVA_HOME" = "x" ]; then
+ JAVA_HOME="$(dirname $(dirname $(pathCanonical "$JAVA")))"
+ fi
+}
+
+detectJVM() {
+ #echo "`$JAVA -version`"
+ # This service should call `java -version`,
+ # read stdout, and look for hints
+ if $JAVA -version 2>&1 | grep "^IBM" ; then
+ JVM_VENDOR="IBM"
+ # on OS/400, java -version does not contain IBM explicitly
+ elif $os400; then
+ JVM_VENDOR="IBM"
+ else
+ JVM_VENDOR="SUN"
+ fi
+ # echo "JVM vendor is $JVM_VENDOR"
+}
+
+setupDebugOptions() {
+ if [ "x$JAVA_OPTS" = "x" ]; then
+ JAVA_OPTS="$DEFAULT_JAVA_OPTS"
+ fi
+ export JAVA_OPTS
+
+ # Set Debug options if enabled
+ if [ "x$KARAF_DEBUG" != "x" ]; then
+ # Use the defaults if JAVA_DEBUG_OPTS was not set
+ if [ "x$JAVA_DEBUG_OPTS" = "x" ]; then
+ JAVA_DEBUG_OPTS="$DEFAULT_JAVA_DEBUG_OPTS"
+ fi
+
+ JAVA_OPTS="$JAVA_DEBUG_OPTS $JAVA_OPTS"
+ warn "Enabling Java debug options: $JAVA_DEBUG_OPTS"
+ fi
+}
+
+setupDefaults() {
+ DEFAULT_JAVA_OPTS="-Xms$JAVA_MIN_MEM -Xmx$JAVA_MAX_MEM "
+
+ #Set the JVM_VENDOR specific JVM flags
+ if [ "$JVM_VENDOR" = "SUN" ]; then
+ #
+ # Check some easily accessible MIN/MAX params for JVM mem usage
+ #
+ if [ "x$JAVA_PERM_MEM" != "x" ]; then
+ DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS -XX:PermSize=$JAVA_PERM_MEM"
+ fi
+ if [ "x$JAVA_MAX_PERM_MEM" != "x" ]; then
+ DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS -XX:MaxPermSize=$JAVA_MAX_PERM_MEM"
+ fi
+ DEFAULT_JAVA_OPTS="-server $DEFAULT_JAVA_OPTS -Dcom.sun.management.jmxremote"
+ elif [ "$JVM_VENDOR" = "IBM" ]; then
+ if $os400; then
+ DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS"
+ elif $aix; then
+ DEFAULT_JAVA_OPTS="-Xverify:none -Xdump:heap -Xlp $DEFAULT_JAVA_OPTS"
+ else
+ DEFAULT_JAVA_OPTS="-Xverify:none $DEFAULT_JAVA_OPTS"
+ fi
+ fi
+
+ # Add the jars in the lib dir
+ for file in "$KARAF_HOME"/lib/*.jar
+ do
+ if [ -z "$CLASSPATH" ]; then
+ CLASSPATH="$file"
+ else
+ CLASSPATH="$CLASSPATH:$file"
+ fi
+ done
+ DEFAULT_JAVA_DEBUG_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"
+
+ ##
+ ## TODO: Move to conf/profiler/yourkit.{sh|cmd}
+ ##
+ # Uncomment to enable YourKit profiling
+ #DEFAULT_JAVA_DEBUG_OPTS="-Xrunyjpagent"
+}
+
+init() {
+ # Determine if there is special OS handling we must perform
+ detectOS
+
+ # Unlimit the number of file descriptors if possible
+ unlimitFD
+
+ # Locate the Karaf home directory
+ locateHome
+
+ # Locate the Karaf base directory
+ locateBase
+
+ # Locate the Karaf data directory
+ locateData
+
+ # Locate the Karaf etc directory
+ locateEtc
+
+ # Setup the native library path
+ setupNativePath
+
+ # Locate the Java VM to execute
+ locateJava
+
+ # Determine the JVM vendor
+ detectJVM
+
+ # Setup default options
+ setupDefaults
+
+ # Install debug options
+ setupDebugOptions
+
+}
+
+run() {
+
+ CLASSPATH="${KARAF_HOME}/system/org/apache/karaf/instance/org.apache.karaf.instance.command/3.0.1/org.apache.karaf.instance.command-3.0.1.jar:${KARAF_HOME}/system/org/apache/karaf/instance/org.apache.karaf.instance.core/3.0.1/org.apache.karaf.instance.core-3.0.1.jar:${KARAF_HOME}/system/org/apache/karaf/shell/org.apache.karaf.shell.console/3.0.1/org.apache.karaf.shell.console-3.0.1.jar:${KARAF_HOME}/system/org/apache/karaf/shell/org.apache.karaf.shell.table/3.0.1/org.apache.karaf.shell.table-3.0.1.jar:${KARAF_HOME}/system/org/apache/aries/blueprint/org.apache.aries.blueprint.api/1.0.0/org.apache.aries.blueprint.api-1.0.0.jar:${KARAF_HOME}/system/org/apache/aries/blueprint/org.apache.aries.blueprint.core/1.4.0/org.apache.aries.blueprint.core-1.4.0.jar:${KARAF_HOME}/system/org/apache/aries/blueprint/org.apache.aries.blueprint.cm/1.0.3/org.apache.aries.blueprint.cm-1.0.3.jar:${KARAF_HOME}/system/org/ops4j/pax/logging/pax-logging-api/1.7.2/pax-logging-api-1.7.2.jar:${KARAF_HOME}/system/org/apache/felix/org.apache.felix.framework/4.2.1/org.apache.felix.framework-4.2.1.jar:${KARAF_HOME}/system/jline/jline/2.11/jline-2.11.jar:$CLASSPATH"
+
+ if $cygwin; then
+ KARAF_HOME=`cygpath --path --windows "$KARAF_HOME"`
+ KARAF_BASE=`cygpath --path --windows "$KARAF_BASE"`
+ KARAF_DATA=`cygpath --path --windows "$KARAF_DATA"`
+ KARAF_ETC=`cygpath --path --windows "$KARAF_ETC"`
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ fi
+
+ exec "$JAVA" $JAVA_OPTS -Dkaraf.instances="${KARAF_HOME}/instances" -Dkaraf.home="$KARAF_HOME" -Dkaraf.base="$KARAF_BASE" -Dkaraf.etc="$KARAF_ETC" -Djava.io.tmpdir="$KARAF_DATA/tmp" -Djava.util.logging.config.file="$KARAF_BASE/etc/java.util.logging.properties" $KARAF_OPTS $OPTS -classpath "$CLASSPATH" org.apache.karaf.instance.main.Execute "$@"
+}
+
+main() {
+ init
+ run "$@"
+}
+
+main "$@"
+
--- /dev/null
+@echo off\r
+rem\r
+rem\r
+rem Licensed to the Apache Software Foundation (ASF) under one or more\r
+rem contributor license agreements. See the NOTICE file distributed with\r
+rem this work for additional information regarding copyright ownership.\r
+rem The ASF licenses this file to You under the Apache License, Version 2.0\r
+rem (the "License"); you may not use this file except in compliance with\r
+rem the License. You may obtain a copy of the License at\r
+rem\r
+rem http://www.apache.org/licenses/LICENSE-2.0\r
+rem\r
+rem Unless required by applicable law or agreed to in writing, software\r
+rem distributed under the License is distributed on an "AS IS" BASIS,\r
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+rem See the License for the specific language governing permissions and\r
+rem limitations under the License.\r
+rem\r
+\r
+if not "%ECHO%" == "" echo %ECHO%\r
+\r
+setlocal\r
+set DIRNAME=%~dp0%\r
+set PROGNAME=%~nx0%\r
+set ARGS=%*\r
+\r
+rem Sourcing environment settings for karaf similar to tomcats setenv\r
+SET KARAF_SCRIPT="instance.bat"\r
+if exist "%DIRNAME%setenv.bat" (\r
+ call "%DIRNAME%setenv.bat"\r
+)\r
+\r
+rem Check console window title. Set to Karaf by default\r
+if not "%KARAF_TITLE%" == "" (\r
+ title %KARAF_TITLE%\r
+) else (\r
+ title Karaf\r
+)\r
+\r
+rem Check/Set up some easily accessible MIN/MAX params for JVM mem usage\r
+if "%JAVA_MIN_MEM%" == "" (\r
+ set JAVA_MIN_MEM=128M\r
+)\r
+if "%JAVA_MAX_MEM%" == "" (\r
+ set JAVA_MAX_MEM=512M\r
+)\r
+\r
+goto BEGIN\r
+\r
+:warn\r
+ echo %PROGNAME%: %*\r
+goto :EOF\r
+\r
+:BEGIN\r
+\r
+rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\r
+\r
+if not "%KARAF_HOME%" == "" (\r
+ call :warn Ignoring predefined value for KARAF_HOME\r
+)\r
+set KARAF_HOME=%DIRNAME%..\r
+if not exist "%KARAF_HOME%" (\r
+ call :warn KARAF_HOME is not valid: "%KARAF_HOME%"\r
+ goto END\r
+)\r
+\r
+if not "%KARAF_BASE%" == "" (\r
+ if not exist "%KARAF_BASE%" (\r
+ call :warn KARAF_BASE is not valid: "%KARAF_BASE%"\r
+ goto END\r
+ )\r
+)\r
+if "%KARAF_BASE%" == "" (\r
+ set "KARAF_BASE=%KARAF_HOME%"\r
+)\r
+\r
+if not "%KARAF_DATA%" == "" (\r
+ if not exist "%KARAF_DATA%" (\r
+ call :warn KARAF_DATA is not valid: "%KARAF_DATA%"\r
+ goto END\r
+ )\r
+)\r
+if "%KARAF_DATA%" == "" (\r
+ set "KARAF_DATA=%KARAF_BASE%\data"\r
+)\r
+\r
+if not "%KARAF_ETC%" == "" (\r
+ if not exist "%KARAF_ETC%" (\r
+ call :warn KARAF_ETC is not valid: "%KARAF_ETC%"\r
+ goto END\r
+ )\r
+)\r
+if "%KARAF_ETC%" == "" (\r
+ set "KARAF_ETC=%KARAF_BASE%\etc"\r
+)\r
+\r
+set DEFAULT_JAVA_OPTS=\r
+set DEFAULT_JAVA_DEBUG_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005\r
+\r
+rem Support for loading native libraries\r
+set PATH=%PATH%;%KARAF_BASE%\lib;%KARAF_HOME%\lib\r
+\r
+rem Setup the Java Virtual Machine\r
+if not "%JAVA%" == "" goto :Check_JAVA_END\r
+ set JAVA=java\r
+ if "%JAVA_HOME%" == "" call :warn JAVA_HOME not set; results may vary\r
+ if not "%JAVA_HOME%" == "" set JAVA=%JAVA_HOME%\bin\java\r
+ if not exist "%JAVA_HOME%" (\r
+ call :warn JAVA_HOME is not valid: "%JAVA_HOME%"\r
+ goto END\r
+ )\r
+:Check_JAVA_END\r
+\r
+if "%JAVA_OPTS%" == "" set JAVA_OPTS=%DEFAULT_JAVA_OPTS%\r
+\r
+if "%KARAF_DEBUG%" == "" goto :KARAF_DEBUG_END\r
+ rem Use the defaults if JAVA_DEBUG_OPTS was not set\r
+ if "%JAVA_DEBUG_OPTS%" == "" set JAVA_DEBUG_OPTS=%DEFAULT_JAVA_DEBUG_OPTS%\r
+\r
+ set "JAVA_OPTS=%JAVA_DEBUG_OPTS% %JAVA_OPTS%"\r
+ call :warn Enabling Java debug options: %JAVA_DEBUG_OPTS%\r
+:KARAF_DEBUG_END\r
+\r
+rem Setup the classpath\r
+pushd "%KARAF_HOME%\lib"\r
+for %%G in (karaf*.jar) do call:APPEND_TO_CLASSPATH %%G\r
+popd\r
+goto CLASSPATH_END\r
+\r
+: APPEND_TO_CLASSPATH\r
+set filename=%~1\r
+set suffix=%filename:~-4%\r
+if %suffix% equ .jar set CLASSPATH=%CLASSPATH%;%KARAF_HOME%\lib\%filename%\r
+goto :EOF\r
+\r
+:CLASSPATH_END\r
+\r
+set CLASSPATH=%KARAF_HOME%\system\org\apache\karaf\instance\org.apache.karaf.instance.command\3.0.1\org.apache.karaf.instance.command-3.0.1.jar;%KARAF_HOME%\system\org\apache\karaf\instance\org.apache.karaf.instance.core\3.0.1\org.apache.karaf.instance.core-3.0.1.jar;%KARAF_HOME%\system\org\apache\karaf\shell\org.apache.karaf.shell.console\3.0.1\org.apache.karaf.shell.console-3.0.1.jar;%KARAF_HOME%\system\org\apache\karaf\shell\org.apache.karaf.shell.table\3.0.1\org.apache.karaf.shell.table-3.0.1.jar;%KARAF_HOME%\system\org\apache\aries\blueprint\org.apache.aries.blueprint.api\1.0.0\org.apache.aries.blueprint.api-1.0.0.jar;%KARAF_HOME%\system\org\apache\aries\blueprint\org.apache.aries.blueprint.core\1.4.0\org.apache.aries.blueprint.core-1.4.0.jar;%KARAF_HOME%\system\org\apache\aries\blueprint\org.apache.aries.blueprint.cm\1.0.3\org.apache.aries.blueprint.cm-1.0.3.jar;%KARAF_HOME%\system\org\ops4j\pax\logging\pax-logging-api\1.7.2\pax-logging-api-1.7.2.jar;%KARAF_HOME%\system\org\apache\felix\org.apache.felix.framework\4.2.1\org.apache.felix.framework-4.2.1.jar;%KARAF_HOME%\system\jline\jline\2.11\jline-2.11.jar;%CLASSPATH%\r
+\r
+:EXECUTE\r
+ if "%SHIFT%" == "true" SET ARGS=%2 %3 %4 %5 %6 %7 %8\r
+ if not "%SHIFT%" == "true" SET ARGS=%1 %2 %3 %4 %5 %6 %7 %8\r
+ rem Execute the Java Virtual Machine\r
+ "%JAVA%" %JAVA_OPTS% %OPTS% -classpath "%CLASSPATH%" -Dkaraf.instances="%KARAF_HOME%\instances" -Dkaraf.home="%KARAF_HOME%" -Dkaraf.base="%KARAF_BASE%" -Dkaraf.etc="%KARAF_ETC%" -Djava.io.tmpdir="%KARAF_DATA%\tmp" -Djava.util.logging.config.file="%KARAF_BASE%\etc\java.util.logging.properties" %KARAF_OPTS% org.apache.karaf.instance.main.Execute %ARGS%\r
+\r
+rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\r
+\r
+:END\r
+\r
+endlocal\r
+\r
--- /dev/null
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+DIRNAME=`dirname $0`
+PROGNAME=`basename $0`
+
+#
+# Sourcing environment settings for karaf similar to tomcats setenv
+#
+KARAF_SCRIPT="karaf"
+export KARAF_SCRIPT
+if [ -f "$DIRNAME/setenv" ]; then
+ . "$DIRNAME/setenv"
+fi
+
+#
+# Set up some easily accessible MIN/MAX params for JVM mem usage
+#
+if [ "x$JAVA_MIN_MEM" = "x" ]; then
+ JAVA_MIN_MEM=128M
+ export JAVA_MIN_MEM
+fi
+if [ "x$JAVA_MAX_MEM" = "x" ]; then
+ JAVA_MAX_MEM=512M
+ export JAVA_MAX_MEM
+fi
+
+#
+# Check the mode that initiated the script
+#
+if [ "x$1" != "x" ]; then
+ MODE=$1
+fi
+
+warn() {
+ echo "${PROGNAME}: $*"
+}
+
+die() {
+ warn "$*"
+ exit 1
+}
+
+detectOS() {
+ # OS specific support (must be 'true' or 'false').
+ cygwin=false;
+ darwin=false;
+ aix=false;
+ os400=false;
+ case "`uname`" in
+ CYGWIN*)
+ cygwin=true
+ ;;
+ Darwin*)
+ darwin=true
+ ;;
+ AIX*)
+ aix=true
+ ;;
+ OS400*)
+ os400=true
+ ;;
+ esac
+ # For AIX, set an environment variable
+ if $aix; then
+ export LDR_CNTRL=MAXDATA=0xB0000000@DSA
+ echo $LDR_CNTRL
+ fi
+}
+
+unlimitFD() {
+ # Use the maximum available, or set MAX_FD != -1 to use that
+ if [ "x$MAX_FD" = "x" ]; then
+ MAX_FD="maximum"
+ fi
+
+ # Increase the maximum file descriptors if we can
+ if [ "$os400" = "false" ] && [ "$cygwin" = "false" ]; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ "$MAX_FD_LIMIT" != 'unlimited' ]; then
+ if [ $? -eq 0 ]; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then
+ # use the system max
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+
+ ulimit -n $MAX_FD > /dev/null
+ # echo "ulimit -n" `ulimit -n`
+ if [ $? -ne 0 ]; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query system maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+ fi
+ fi
+}
+
+locateHome() {
+ if [ "x$KARAF_HOME" != "x" ]; then
+ warn "Ignoring predefined value for KARAF_HOME"
+ fi
+
+ # In POSIX shells, CDPATH may cause cd to write to stdout
+ (unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+ KARAF_HOME=`cd "$DIRNAME/.."; pwd`
+ if [ ! -d "$KARAF_HOME" ]; then
+ die "KARAF_HOME is not valid: $KARAF_HOME"
+ fi
+}
+
+locateBase() {
+ if [ "x$KARAF_BASE" != "x" ]; then
+ if [ ! -d "$KARAF_BASE" ]; then
+ die "KARAF_BASE is not valid: $KARAF_BASE"
+ fi
+ else
+ KARAF_BASE=$KARAF_HOME
+ fi
+}
+
+locateData() {
+ if [ "x$KARAF_DATA" != "x" ]; then
+ if [ ! -d "$KARAF_DATA" ]; then
+ die "KARAF_DATA is not valid: $KARAF_DATA"
+ fi
+ else
+ KARAF_DATA=$KARAF_BASE/data
+ fi
+}
+
+locateEtc() {
+ if [ "x$KARAF_ETC" != "x" ]; then
+ if [ ! -d "$KARAF_ETC" ]; then
+ die "KARAF_ETC is not valid: $KARAF_ETC"
+ fi
+ else
+ KARAF_ETC=$KARAF_BASE/etc
+ fi
+}
+
+setupNativePath() {
+ # Support for loading native libraries
+ LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:$KARAF_BASE/lib:$KARAF_HOME/lib"
+
+ # For Cygwin, set PATH from LD_LIBRARY_PATH
+ if $cygwin; then
+ LD_LIBRARY_PATH=`cygpath --path --windows "$LD_LIBRARY_PATH"`
+ PATH="$PATH;$LD_LIBRARY_PATH"
+ export PATH
+ fi
+ export LD_LIBRARY_PATH
+}
+
+pathCanonical() {
+ local dst="${1}"
+ while [ -h "${dst}" ] ; do
+ ls=`ls -ld "${dst}"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ dst="$link"
+ else
+ dst="`dirname "${dst}"`/$link"
+ fi
+ done
+ local bas=`basename "${dst}"`
+ local dir=`dirname "${dst}"`
+ if [ "$bas" != "$dir" ]; then
+ dst="`pathCanonical "$dir"`/$bas"
+ fi
+ echo "${dst}" | sed -e 's#//#/#g' -e 's#/./#/#g' -e 's#/[^/]*/../#/#g'
+}
+
+locateJava() {
+ # Setup the Java Virtual Machine
+ if $cygwin ; then
+ [ -n "$JAVA" ] && JAVA=`cygpath --unix "$JAVA"`
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ fi
+
+ if [ "x$JAVA_HOME" = "x" ] && [ "$darwin" = "true" ]; then
+ JAVA_HOME="$(/usr/libexec/java_home)"
+ fi
+ if [ "x$JAVA" = "x" ] && [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+ if [ "x$JAVA" = "x" ]; then
+ if [ "x$JAVA_HOME" != "x" ]; then
+ if [ ! -d "$JAVA_HOME" ]; then
+ die "JAVA_HOME is not valid: $JAVA_HOME"
+ fi
+ JAVA="$JAVA_HOME/bin/java"
+ else
+ warn "JAVA_HOME not set; results may vary"
+ JAVA=`type java`
+ JAVA=`expr "$JAVA" : '.*is \(.*\)$'`
+ if [ "x$JAVA" = "x" ]; then
+ die "java command not found"
+ fi
+ fi
+ fi
+ if [ "x$JAVA_HOME" = "x" ]; then
+ JAVA_HOME="$(dirname $(dirname $(pathCanonical "$JAVA")))"
+ fi
+}
+
+detectJVM() {
+ #echo "`$JAVA -version`"
+ # This service should call `java -version`,
+ # read stdout, and look for hints
+ if $JAVA -version 2>&1 | grep "^IBM" ; then
+ JVM_VENDOR="IBM"
+ # on OS/400, java -version does not contain IBM explicitly
+ elif $os400; then
+ JVM_VENDOR="IBM"
+ else
+ JVM_VENDOR="SUN"
+ fi
+ # echo "JVM vendor is $JVM_VENDOR"
+}
+
+checkJvmVersion() {
+ # echo "`$JAVA -version`"
+ VERSION=`$JAVA -version 2>&1 | egrep '"([0-9].[0-9]\..*[0-9])"' | awk '{print substr($3,2,length($3)-2)}' | awk '{print substr($1, 3, 3)}' | sed -e 's;\.;;g'`
+ # echo $VERSION
+ if [ "$VERSION" -lt "60" ]; then
+ echo "JVM must be greater than 1.6"
+ exit 1;
+ fi
+}
+
+setupDebugOptions() {
+ if [ "x$JAVA_OPTS" = "x" ]; then
+ JAVA_OPTS="$DEFAULT_JAVA_OPTS"
+ fi
+ export JAVA_OPTS
+
+ # Set Debug options if enabled
+ if [ "x$KARAF_DEBUG" != "x" ]; then
+ # Ignore DEBUG in case of stop or client mode
+ if [ "x$MODE" = "xstop" ]; then
+ return
+ fi
+ if [ "x$MODE" = "xclient" ]; then
+ return
+ fi
+ # Use the defaults if JAVA_DEBUG_OPTS was not set
+ if [ "x$JAVA_DEBUG_OPTS" = "x" ]; then
+ JAVA_DEBUG_OPTS="$DEFAULT_JAVA_DEBUG_OPTS"
+ fi
+
+ JAVA_OPTS="$JAVA_DEBUG_OPTS $JAVA_OPTS"
+ warn "Enabling Java debug options: $JAVA_DEBUG_OPTS"
+ fi
+}
+
+setupDefaults() {
+ DEFAULT_JAVA_OPTS="-Xms$JAVA_MIN_MEM -Xmx$JAVA_MAX_MEM -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass "
+
+ #Set the JVM_VENDOR specific JVM flags
+ if [ "$JVM_VENDOR" = "SUN" ]; then
+ #
+ # Check some easily accessible MIN/MAX params for JVM mem usage
+ #
+ if [ "x$JAVA_PERM_MEM" != "x" ]; then
+ DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS -XX:PermSize=$JAVA_PERM_MEM"
+ fi
+ if [ "x$JAVA_MAX_PERM_MEM" != "x" ]; then
+ DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS -XX:MaxPermSize=$JAVA_MAX_PERM_MEM"
+ fi
+ DEFAULT_JAVA_OPTS="-server $DEFAULT_JAVA_OPTS -Dcom.sun.management.jmxremote"
+ elif [ "$JVM_VENDOR" = "IBM" ]; then
+ if $os400; then
+ DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS"
+ elif $aix; then
+ DEFAULT_JAVA_OPTS="-Xverify:none -Xdump:heap -Xlp $DEFAULT_JAVA_OPTS"
+ else
+ DEFAULT_JAVA_OPTS="-Xverify:none $DEFAULT_JAVA_OPTS"
+ fi
+ fi
+
+ # Add the jars in the lib dir
+ for file in "$KARAF_HOME"/lib/karaf*.jar
+ do
+ if [ -z "$CLASSPATH" ]; then
+ CLASSPATH="$file"
+ else
+ CLASSPATH="$CLASSPATH:$file"
+ fi
+ done
+ DEFAULT_JAVA_DEBUG_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"
+
+ ##
+ ## TODO: Move to conf/profiler/yourkit.{sh|cmd}
+ ##
+ # Uncomment to enable YourKit profiling
+ #DEFAULT_JAVA_DEBUG_OPTS="-Xrunyjpagent"
+}
+
+init() {
+ # Determine if there is special OS handling we must perform
+ detectOS
+
+ # Unlimit the number of file descriptors if possible
+ unlimitFD
+
+ # Locate the Karaf home directory
+ locateHome
+
+ # Locate the Karaf base directory
+ locateBase
+
+ # Locate the Karaf data directory
+ locateData
+
+ # Locate the Karaf etc directory
+ locateEtc
+
+ # Setup the native library path
+ setupNativePath
+
+ # Locate the Java VM to execute
+ locateJava
+
+ # Determine the JVM vendor
+ detectJVM
+
+ # Determine the JVM version >= 1.6
+ checkJvmVersion
+
+ # Setup default options
+ setupDefaults
+
+ # Install debug options
+ setupDebugOptions
+
+}
+
+run() {
+ OPTS="-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=true"
+ MAIN=org.apache.karaf.main.Main
+ while [ "$1" != "" ]; do
+ case $1 in
+ 'clean')
+ rm -Rf "$KARAF_DATA"
+ shift
+ ;;
+ 'debug')
+ if [ "x$JAVA_DEBUG_OPTS" = "x" ]; then
+ JAVA_DEBUG_OPTS="$DEFAULT_JAVA_DEBUG_OPTS"
+ fi
+ JAVA_OPTS="$JAVA_DEBUG_OPTS $JAVA_OPTS"
+ shift
+ ;;
+ 'status')
+ MAIN=org.apache.karaf.main.Status
+ shift
+ ;;
+ 'stop')
+ MAIN=org.apache.karaf.main.Stop
+ shift
+ ;;
+ 'console')
+ shift
+ ;;
+ 'server')
+ OPTS="-Dkaraf.startLocalConsole=false -Dkaraf.startRemoteShell=true"
+ shift
+ ;;
+ 'client')
+ OPTS="-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=false"
+ shift
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+
+ JAVA_ENDORSED_DIRS="${JAVA_HOME}/jre/lib/endorsed:${JAVA_HOME}/lib/endorsed:${KARAF_HOME}/lib/endorsed"
+ JAVA_EXT_DIRS="${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext:${KARAF_HOME}/lib/ext"
+ if $cygwin; then
+ KARAF_HOME=`cygpath --path --windows "$KARAF_HOME"`
+ KARAF_BASE=`cygpath --path --windows "$KARAF_BASE"`
+ KARAF_DATA=`cygpath --path --windows "$KARAF_DATA"`
+ KARAF_ETC=`cygpath --path --windows "$KARAF_ETC"`
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ JAVA_ENDORSED_DIRS=`cygpath --path --windows "$JAVA_ENDORSED_DIRS"`
+ JAVA_EXT_DIRS=`cygpath --path --windows "$JAVA_EXT_DIRS"`
+ fi
+ cd "$KARAF_BASE"
+
+ exec "$JAVA" $JAVA_OPTS -Djava.endorsed.dirs="${JAVA_ENDORSED_DIRS}" -Djava.ext.dirs="${JAVA_EXT_DIRS}" -Dkaraf.instances="${KARAF_HOME}/instances" -Dkaraf.home="$KARAF_HOME" -Dkaraf.base="$KARAF_BASE" -Dkaraf.data="$KARAF_DATA" -Dkaraf.etc="$KARAF_ETC" -Djava.io.tmpdir="$KARAF_DATA/tmp" -Djava.util.logging.config.file="$KARAF_BASE/etc/java.util.logging.properties" $KARAF_OPTS $OPTS -classpath "$CLASSPATH" $MAIN "$@"
+}
+
+main() {
+ init
+ run "$@"
+}
+
+main "$@"
--- /dev/null
+@echo off\r
+rem\r
+rem\r
+rem Licensed to the Apache Software Foundation (ASF) under one or more\r
+rem contributor license agreements. See the NOTICE file distributed with\r
+rem this work for additional information regarding copyright ownership.\r
+rem The ASF licenses this file to You under the Apache License, Version 2.0\r
+rem (the "License"); you may not use this file except in compliance with\r
+rem the License. You may obtain a copy of the License at\r
+rem\r
+rem http://www.apache.org/licenses/LICENSE-2.0\r
+rem\r
+rem Unless required by applicable law or agreed to in writing, software\r
+rem distributed under the License is distributed on an "AS IS" BASIS,\r
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+rem See the License for the specific language governing permissions and\r
+rem limitations under the License.\r
+rem\r
+\r
+if not "%ECHO%" == "" echo %ECHO%\r
+\r
+setlocal\r
+set DIRNAME=%~dp0%\r
+set PROGNAME=%~nx0%\r
+set ARGS=%*\r
+\r
+rem Sourcing environment settings for karaf similar to tomcats setenv\r
+SET KARAF_SCRIPT="karaf.bat"\r
+if exist "%DIRNAME%setenv.bat" (\r
+ call "%DIRNAME%setenv.bat"\r
+)\r
+\r
+rem Check console window title. Set to Karaf by default\r
+if not "%KARAF_TITLE%" == "" (\r
+ title %KARAF_TITLE%\r
+) else (\r
+ title Karaf\r
+)\r
+\r
+rem Check/Set up some easily accessible MIN/MAX params for JVM mem usage\r
+if "%JAVA_MIN_MEM%" == "" (\r
+ set JAVA_MIN_MEM=128M\r
+)\r
+if "%JAVA_MAX_MEM%" == "" (\r
+ set JAVA_MAX_MEM=512M\r
+)\r
+\r
+goto BEGIN\r
+\r
+:warn\r
+ echo %PROGNAME%: %*\r
+goto :EOF\r
+\r
+:BEGIN\r
+\r
+rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\r
+\r
+if not "%KARAF_HOME%" == "" (\r
+ call :warn Ignoring predefined value for KARAF_HOME\r
+)\r
+set KARAF_HOME=%DIRNAME%..\r
+if not exist "%KARAF_HOME%" (\r
+ call :warn KARAF_HOME is not valid: "%KARAF_HOME%"\r
+ goto END\r
+)\r
+\r
+if not "%KARAF_BASE%" == "" (\r
+ if not exist "%KARAF_BASE%" (\r
+ call :warn KARAF_BASE is not valid: "%KARAF_BASE%"\r
+ goto END\r
+ )\r
+)\r
+if "%KARAF_BASE%" == "" (\r
+ set "KARAF_BASE=%KARAF_HOME%"\r
+)\r
+\r
+if not "%KARAF_DATA%" == "" (\r
+ if not exist "%KARAF_DATA%" (\r
+ call :warn KARAF_DATA is not valid: "%KARAF_DATA%"\r
+ goto END\r
+ )\r
+)\r
+if "%KARAF_DATA%" == "" (\r
+ set "KARAF_DATA=%KARAF_BASE%\data"\r
+)\r
+\r
+if not "%KARAF_ETC%" == "" (\r
+ if not exist "%KARAF_ETC%" (\r
+ call :warn KARAF_ETC is not valid: "%KARAF_ETC%"\r
+ goto END\r
+ )\r
+)\r
+if "%KARAF_ETC%" == "" (\r
+ set "KARAF_ETC=%KARAF_BASE%\etc"\r
+)\r
+\r
+set LOCAL_CLASSPATH=%CLASSPATH%\r
+set JAVA_MODE=-server\r
+if not exist "%JAVA_HOME%\bin\server\jvm.dll" (\r
+ if not exist "%JAVA_HOME%\jre\bin\server\jvm.dll" (\r
+ echo WARNING: Running karaf on a Java HotSpot Client VM because server-mode is not available.\r
+ echo Install Java Developer Kit to fix this.\r
+ echo For more details see http://java.sun.com/products/hotspot/whitepaper.html#client\r
+ set JAVA_MODE=-client\r
+ )\r
+)\r
+set DEFAULT_JAVA_OPTS=%JAVA_MODE% -Xms%JAVA_MIN_MEM% -Xmx%JAVA_MAX_MEM% -Dderby.system.home="%KARAF_DATA%\derby" -Dderby.storage.fileSyncTransactionLog=true -Dcom.sun.management.jmxremote -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass\r
+\r
+rem Check some easily accessible MIN/MAX params for JVM mem usage\r
+if not "%JAVA_PERM_MEM%" == "" (\r
+ set DEFAULT_JAVA_OPTS=%DEFAULT_JAVA_OPTS% -XX:PermSize=%JAVA_PERM_MEM%\r
+)\r
+if not "%JAVA_MAX_PERM_MEM%" == "" (\r
+ set DEFAULT_JAVA_OPTS=%DEFAULT_JAVA_OPTS% -XX:MaxPermSize=%JAVA_MAX_PERM_MEM%\r
+)\r
+\r
+set CLASSPATH=%LOCAL_CLASSPATH%;%KARAF_BASE%\conf\r
+set DEFAULT_JAVA_DEBUG_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005\r
+\r
+if "%LOCAL_CLASSPATH%" == "" goto :KARAF_CLASSPATH_EMPTY\r
+ set CLASSPATH=%LOCAL_CLASSPATH%;%KARAF_BASE%\conf\r
+ goto :KARAF_CLASSPATH_END\r
+:KARAF_CLASSPATH_EMPTY\r
+ set CLASSPATH=%KARAF_BASE%\conf\r
+:KARAF_CLASSPATH_END\r
+\r
+rem Setup Karaf Home\r
+if exist "%KARAF_HOME%\conf\karaf-rc.cmd" call %KARAF_HOME%\conf\karaf-rc.cmd\r
+if exist "%HOME%\karaf-rc.cmd" call %HOME%\karaf-rc.cmd\r
+\r
+rem Support for loading native libraries\r
+set PATH=%PATH%;%KARAF_BASE%\lib;%KARAF_HOME%\lib\r
+\r
+rem Setup the Java Virtual Machine\r
+if not "%JAVA%" == "" goto :Check_JAVA_END\r
+ if not "%JAVA_HOME%" == "" goto :TryJDKEnd\r
+ call :warn JAVA_HOME not set; results may vary\r
+:TryJRE\r
+ start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment"\r
+ if not exist __reg1.txt goto :TryJDK\r
+ type __reg1.txt | find "CurrentVersion" > __reg2.txt\r
+ if errorlevel 1 goto :TryJDK\r
+ for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JavaTemp=%%~x\r
+ if errorlevel 1 goto :TryJDK\r
+ set JavaTemp=%JavaTemp%##\r
+ set JavaTemp=%JavaTemp: ##=##%\r
+ set JavaTemp=%JavaTemp: ##=##%\r
+ set JavaTemp=%JavaTemp: ##=##%\r
+ set JavaTemp=%JavaTemp: ##=##%\r
+ set JavaTemp=%JavaTemp: ##=##%\r
+ set JavaTemp=%JavaTemp:##=%\r
+ del __reg1.txt\r
+ del __reg2.txt\r
+ start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\%JavaTemp%"\r
+ if not exist __reg1.txt goto :TryJDK\r
+ type __reg1.txt | find "JavaHome" > __reg2.txt\r
+ if errorlevel 1 goto :TryJDK\r
+ for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JAVA_HOME=%%~x\r
+ if errorlevel 1 goto :TryJDK\r
+ del __reg1.txt\r
+ del __reg2.txt\r
+ goto TryJDKEnd\r
+:TryJDK\r
+ start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit"\r
+ if not exist __reg1.txt (\r
+ goto TryRegJRE\r
+ )\r
+ type __reg1.txt | find "CurrentVersion" > __reg2.txt\r
+ if errorlevel 1 (\r
+ goto TryRegJRE\r
+ )\r
+ for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JavaTemp=%%~x\r
+ if errorlevel 1 (\r
+ goto TryRegJRE\r
+ )\r
+ set JavaTemp=%JavaTemp%##\r
+ set JavaTemp=%JavaTemp: ##=##%\r
+ set JavaTemp=%JavaTemp: ##=##%\r
+ set JavaTemp=%JavaTemp: ##=##%\r
+ set JavaTemp=%JavaTemp: ##=##%\r
+ set JavaTemp=%JavaTemp: ##=##%\r
+ set JavaTemp=%JavaTemp:##=%\r
+ del __reg1.txt\r
+ del __reg2.txt\r
+ start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\%JavaTemp%"\r
+ if not exist __reg1.txt (\r
+ goto TryRegJRE\r
+ )\r
+ type __reg1.txt | find "JavaHome" > __reg2.txt\r
+ if errorlevel 1 (\r
+ goto TryRegJRE\r
+ )\r
+ for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JAVA_HOME=%%~x\r
+ if errorlevel 1 (\r
+ goto TryRegJRE\r
+ )\r
+ del __reg1.txt\r
+ del __reg2.txt\r
+:TryRegJRE\r
+ rem try getting the JAVA_HOME from registry\r
+ FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Runtime Environment" /v CurrentVersion`) DO (\r
+ set JAVA_VERSION=%%A\r
+ )\r
+ FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Runtime Environment\%JAVA_VERSION%" /v JavaHome`) DO (\r
+ set JAVA_HOME=%%A %%B\r
+ )\r
+ if not exist "%JAVA_HOME%" (\r
+ goto TryRegJDK\r
+ )\r
+ goto TryJDKEnd\r
+:TryRegJDK\r
+ rem try getting the JAVA_HOME from registry\r
+ FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Development Kit" /v CurrentVersion`) DO (\r
+ set JAVA_VERSION=%%A\r
+ )\r
+ FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Development Kit\%JAVA_VERSION%" /v JavaHome`) DO (\r
+ set JAVA_HOME=%%A %%B\r
+ )\r
+ if not exist "%JAVA_HOME%" (\r
+ call :warn Unable to retrieve JAVA_HOME from Registry\r
+ )\r
+ goto TryJDKEnd\r
+:TryJDKEnd\r
+ if not exist "%JAVA_HOME%" (\r
+ call :warn JAVA_HOME is not valid: "%JAVA_HOME%"\r
+ goto END\r
+ )\r
+ set JAVA=%JAVA_HOME%\bin\java\r
+:Check_JAVA_END\r
+\r
+if "%JAVA_OPTS%" == "" set JAVA_OPTS=%DEFAULT_JAVA_OPTS%\r
+\r
+if "%KARAF_DEBUG%" == "" goto :KARAF_DEBUG_END\r
+ if "%1" == "stop" goto :KARAF_DEBUG_END\r
+ if "%1" == "client" goto :KARAF_DEBUG_END\r
+ rem Use the defaults if JAVA_DEBUG_OPTS was not set\r
+ if "%JAVA_DEBUG_OPTS%" == "" set JAVA_DEBUG_OPTS=%DEFAULT_JAVA_DEBUG_OPTS%\r
+\r
+ set "JAVA_OPTS=%JAVA_DEBUG_OPTS% %JAVA_OPTS%"\r
+ call :warn Enabling Java debug options: %JAVA_DEBUG_OPTS%\r
+:KARAF_DEBUG_END\r
+\r
+if "%KARAF_PROFILER%" == "" goto :KARAF_PROFILER_END\r
+ set KARAF_PROFILER_SCRIPT=%KARAF_HOME%\conf\profiler\%KARAF_PROFILER%.cmd\r
+\r
+ if exist "%KARAF_PROFILER_SCRIPT%" goto :KARAF_PROFILER_END\r
+ call :warn Missing configuration for profiler '%KARAF_PROFILER%': %KARAF_PROFILER_SCRIPT%\r
+ goto END\r
+:KARAF_PROFILER_END\r
+\r
+rem Setup the classpath\r
+pushd "%KARAF_HOME%\lib"\r
+for %%G in (karaf*.jar) do call:APPEND_TO_CLASSPATH %%G\r
+popd\r
+goto CLASSPATH_END\r
+\r
+: APPEND_TO_CLASSPATH\r
+set filename=%~1\r
+set suffix=%filename:~-4%\r
+if %suffix% equ .jar set CLASSPATH=%CLASSPATH%;%KARAF_HOME%\lib\%filename%\r
+goto :EOF\r
+\r
+:CLASSPATH_END\r
+\r
+rem Execute the JVM or the load the profiler\r
+if "%KARAF_PROFILER%" == "" goto :RUN\r
+ rem Execute the profiler if it has been configured\r
+ call :warn Loading profiler script: %KARAF_PROFILER_SCRIPT%\r
+ call %KARAF_PROFILER_SCRIPT%\r
+\r
+:RUN\r
+ SET OPTS=-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=true\r
+ SET MAIN=org.apache.karaf.main.Main\r
+ SET SHIFT=false\r
+\r
+:RUN_LOOP\r
+ if "%1" == "stop" goto :EXECUTE_STOP\r
+ if "%1" == "status" goto :EXECUTE_STATUS\r
+ if "%1" == "console" goto :EXECUTE_CONSOLE\r
+ if "%1" == "server" goto :EXECUTE_SERVER\r
+ if "%1" == "client" goto :EXECUTE_CLIENT\r
+ if "%1" == "clean" goto :EXECUTE_CLEAN\r
+ if "%1" == "debug" goto :EXECUTE_DEBUG\r
+ goto :EXECUTE\r
+\r
+:EXECUTE_STOP\r
+ SET MAIN=org.apache.karaf.main.Stop\r
+ shift\r
+ goto :RUN_LOOP\r
+\r
+:EXECUTE_STATUS\r
+ SET MAIN=org.apache.karaf.main.Status\r
+ shift\r
+ goto :RUN_LOOP\r
+\r
+:EXECUTE_CONSOLE\r
+ shift\r
+ goto :RUN_LOOP\r
+\r
+:EXECUTE_SERVER\r
+ SET OPTS=-Dkaraf.startLocalConsole=false -Dkaraf.startRemoteShell=true\r
+ shift\r
+ goto :RUN_LOOP\r
+\r
+:EXECUTE_CLIENT\r
+ SET OPTS=-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=false\r
+ shift\r
+ goto :RUN_LOOP\r
+\r
+:EXECUTE_CLEAN\r
+ rmdir /S /Q "%KARAF_DATA%"\r
+ shift\r
+ goto :RUN_LOOP\r
+\r
+:EXECUTE_DEBUG\r
+ if "%JAVA_DEBUG_OPTS%" == "" set JAVA_DEBUG_OPTS=%DEFAULT_JAVA_DEBUG_OPTS%\r
+ set "JAVA_OPTS=%JAVA_DEBUG_OPTS% %JAVA_OPTS%"\r
+ shift\r
+ goto :RUN_LOOP\r
+\r
+:EXECUTE\r
+ SET ARGS=%1 %2 %3 %4 %5 %6 %7 %8\r
+ rem Execute the Java Virtual Machine\r
+ cd "%KARAF_BASE%"\r
+ "%JAVA%" %JAVA_OPTS% %OPTS% -classpath "%CLASSPATH%" -Djava.endorsed.dirs="%JAVA_HOME%\jre\lib\endorsed;%JAVA_HOME%\lib\endorsed;%KARAF_HOME%\lib\endorsed" -Djava.ext.dirs="%JAVA_HOME%\jre\lib\ext;%JAVA_HOME%\lib\ext;%KARAF_HOME%\lib\ext" -Dkaraf.instances="%KARAF_HOME%\instances" -Dkaraf.home="%KARAF_HOME%" -Dkaraf.base="%KARAF_BASE%" -Dkaraf.etc="%KARAF_ETC%" -Djava.io.tmpdir="%KARAF_DATA%\tmp" -Dkaraf.data="%KARAF_DATA%" -Djava.util.logging.config.file="%KARAF_BASE%\etc\java.util.logging.properties" %KARAF_OPTS% %MAIN% %ARGS%\r
+\r
+rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\r
+\r
+:END\r
+\r
+endlocal\r
+\r
+if not "%PAUSE%" == "" pause\r
+\r
+:END_NO_PAUSE\r
+\r
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
- <!-- Netconf dispatcher to be used by all netconf-connectors -->
+ <!-- Netconf dispatcher to be used by all netconf-connectors -->
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">prefix:netconf-client-dispatcher</type>
<name>global-netconf-dispatcher</name>
</timer>
</module>
- <!-- Thread factory to be used by all threadpools in netconf-connectors -->
+ <!-- Thread factory to be used by all threadpools in netconf-connectors -->
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">prefix:threadfactory-naming</type>
<name>global-netconf-processing-executor-threadfactory</name>
<name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">remote-connector-processing-executor</name-prefix>
- </module>
- <!-- Flexible threadpool for all netconf connectors, Max thread count is set to 4 -->
+ </module>
+ <!-- flexible threadpool for all netconf connectors, Max thread count is set to 4. -->
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">prefix:threadpool-flexible</type>
<name>global-netconf-processing-executor</name>
<minThreadCount xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">1</minThreadCount>
<max-thread-count xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">4</max-thread-count>
<keepAliveMillis xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">600000</keepAliveMillis>
+
<threadFactory xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
<name>global-netconf-processing-executor-threadfactory</name>
</threadFactory>
- </module>
+ </module>
</modules>
<services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
}
continue;
}
+
sstr = Pattern.compile(ActionType.SET_NEXT_HOP.toString() + "=(.*)").matcher(actiongrp);
if (sstr.matches()) {
if (!NetUtils.isIPAddressValid(sstr.group(1))) {
}
continue;
}
- }
+
+ sstr = Pattern.compile(ActionType.OUTPUT + "=(.*)").matcher(actiongrp);
+ if (sstr.matches()) {
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.DROP.toString()).matcher(actiongrp);
+ if (sstr.matches()) {
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.LOOPBACK.toString()).matcher(actiongrp);
+ if (sstr.matches()) {
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.FLOOD.toString()).matcher(actiongrp);
+ if (sstr.matches()) {
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.FLOOD_ALL.toString()).matcher(actiongrp);
+ if (sstr.matches()) {
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.SW_PATH.toString()).matcher(actiongrp);
+ if (sstr.matches()) {
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.HW_PATH.toString()).matcher(actiongrp);
+ if (sstr.matches()) {
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.CONTROLLER.toString()).matcher(actiongrp);
+ if (sstr.matches()) {
+ continue;
+ }
+
+ sstr = Pattern.compile(ActionType.POP_VLAN.toString()).matcher(actiongrp);
+ if (sstr.matches()) {
+ continue;
+ }
+
+ // If execution reached here, it means there is an Action
+ // which is not recognized by the controller. Return error
+
+ return new Status(StatusCode.BADREQUEST, String.format("%s is an UnSupported Action", actiongrp));
+ }
} catch (NumberFormatException e) {
return new Status(StatusCode.BADREQUEST, String.format("Invalid number format %s", e.getMessage()));
}
actions.add(ActionType.SET_NW_SRC.toString() + "=1.1.1.1");
actions.add(ActionType.SET_NW_DST.toString() + "=2.2.2.2");
actions.add(ActionType.CONTROLLER.toString());
- actions.add(ActionType.SET_NW_TOS.toString() + "1");
- actions.add(ActionType.SET_TP_SRC.toString() + "60");
- actions.add(ActionType.SET_TP_DST.toString() + "8080");
+ actions.add(ActionType.SET_NW_TOS.toString() + "=1");
+ actions.add(ActionType.SET_TP_SRC.toString() + "=60");
+ actions.add(ActionType.SET_TP_DST.toString() + "=8080");
actions.add(ActionType.SET_NEXT_HOP.toString() + "=1.1.1.1");
return actions;
*/
package org.opendaylight.controller.md.frm.compatibility;
-import java.util.Collections;
-
import org.opendaylight.controller.forwardingrulesmanager.FlowConfig;
import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager;
import org.opendaylight.controller.md.sal.common.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider;
import org.opendaylight.controller.sal.common.util.Arguments;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.Flows;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.config.rev130819.flows.FlowKey;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
this.manager.removeStaticFlow(flow.getName(), flow.getNode());
this.manager.addStaticFlow(flow);
}
- return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
+ return RpcResultBuilder.<Void> success().build();
}
public RpcResult<Void> rollback(final FlowCommitTransaction transaction) {
import java.math.BigInteger;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.compatibility.InventoryMapping;
import org.opendaylight.controller.sal.compatibility.ToSalConversionsUtils;
import org.opendaylight.controller.sal.flowprogrammer.Flow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
AddFlowOutputBuilder builder = new AddFlowOutputBuilder();
builder.setTransactionId(new TransactionId(BigInteger.valueOf(status.getRequestId())));
AddFlowOutput rpcResultType = builder.build();
- return Futures.immediateFuture(Rpcs.getRpcResult(status.isSuccess(), rpcResultType, null));
+ return Futures.immediateFuture(RpcResultBuilder.<AddFlowOutput>status(status.isSuccess())
+ .withResult(rpcResultType).build());
}
@Override
RemoveFlowOutputBuilder builder = new RemoveFlowOutputBuilder();
builder.setTransactionId(new TransactionId(BigInteger.valueOf(status.getRequestId())));
RemoveFlowOutput rpcResultType = builder.build();
- return Futures.immediateFuture(Rpcs.getRpcResult(status.isSuccess(), rpcResultType, null));
+ return Futures.immediateFuture(RpcResultBuilder.<RemoveFlowOutput>status(status.isSuccess())
+ .withResult(rpcResultType).build());
}
import java.util.concurrent.Future;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.compatibility.FromSalConversionsUtils;
import org.opendaylight.controller.sal.compatibility.InventoryMapping;
import org.opendaylight.controller.sal.compatibility.NodeMapping;
import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMapBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableId;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
LOG.error(e.getMessage());
}
- return Futures.immediateFuture(Rpcs.getRpcResult(rpcResultBool, rpcResultType, null));
+ return Futures.immediateFuture(RpcResultBuilder.<GetAllFlowStatisticsFromFlowTableOutput>
+ status(rpcResultBool).withResult(rpcResultType).build());
}
/**
LOG.error(e.getMessage());
}
- return Futures.immediateFuture(Rpcs.getRpcResult(rpcResultBool, rpcResultType, null));
+ return Futures.immediateFuture(RpcResultBuilder.<GetAllFlowsStatisticsFromAllFlowTablesOutput>
+ status(rpcResultBool).withResult(rpcResultType).build());
}
@Override
LOG.error(e.getMessage());
}
- return Futures.immediateFuture(Rpcs.getRpcResult(rpcResultBool, rpcResultType, null));
+ return Futures.immediateFuture(RpcResultBuilder.<GetFlowStatisticsFromFlowTableOutput>
+ status(rpcResultBool).withResult(rpcResultType).build());
}
@Override
revision "2013-08-19" {
description "Initial revision of Inventory model";
}
-
-
+
+
typedef support-type {
type enumeration {
enum native;
typedef node-id {
type inet:uri;
+ description "Identifier for a particular node. For example:
+
+ myprotocol:<unique_node_id>
+
+ myprotocol:12
+
+ It is a good practice to always lead with a scoping identifier.
+ In the example above the scoping was 'myprotocol'. In your app you
+ could use 'myapp' etc.";
}
typedef node-connector-id {
type inet:uri;
+ description "Identifier for a particular node-connector. For example:
+
+ myprotocol:<unique_node_connector_id>
+ myprotocol:3
+
+ It is a good practice to always lead with a scoping identifier.
+ In the example above the scoping was 'myprotocol'. In your app you
+ could use 'myapp' etc.";
+
}
+ //YANG does not have a statement which limits the scope of an instance-identifier to a particular subtree,
+ //which is why we are using a type capture and not an instance-identifier to define a node-ref and a node-connector-ref.
typedef node-ref {
type instance-identifier;
+ description "A reference that points to an opendaylight-light:nodes/node in the data tree.";
}
typedef node-connector-ref {
type instance-identifier;
+ description "A reference that points to an opendaylight-list:nodes/node/{node-id}/node-connector in the data tree.";
}
identity node-context {
- description "Identity used to mark node context";
+ description "A node-context is a classifier for node elements which allows an RPC to provide a service on behalf of a particular element in the data tree.";
}
identity node-connector-context {
- description "Identity used to mark node connector context";
+ description "A node-connector-context is a classifier for node-connector elements which allows an RPC to provide a service on behalf of a particular element in the data tree.";
}
+ //We are defining a base identity here because there are limitations with yang enums. Yang doesn't allow you to extend enumeratations.
+ //Therefore by defining a base identity we allow other yang files to extend this identity to define additional "enumerations". By
+ //using node-type as their base they are able to pass their object to fields that accept "node-types" while uniquely describing their
+ //type of node, such as "router-node" or "switch-node" etc.
+ //See https://wiki.opendaylight.org/view/YANG_Tools:YANG_to_Java_Mapping#Identity for more information.
identity node-type {
- description "Base identity for node types";
+ description "A base identity definition which represents a generic node type and can be extended in other yang files.";
}
identity node-connector-type {
- description "Base identity for node connectors type";
+ description "A base identity definition which represents a generic node connector type and can be extended in other yang files.";
}
grouping node {
+
+ description "Describes the contents of a generic node -
+ essentially an ID and a list of node-connectors.
+ Acts as an augmentation point where other yang files
+ can add additional information.";
+
leaf id {
type node-id;
+ description "The unique identifier for the node.";
}
list "node-connector" {
key "id";
+
+ description "A list of node connectors that belong this node.";
ext:context-instance "node-connector-context";
uses node-connector;
}
grouping node-connector {
+
+ description "Describes a generic node connector which consists of an ID.
+ Acts as an augmentation point where other yang files can
+ add additional information.";
+
leaf id {
type node-connector-id;
+ description "The unique identifier for the node-connector.";
}
}
grouping node-context-ref {
- description
- "Helper grouping which contains a reference to node context.";
+ description
+ "A helper grouping which contains a reference to a node classified with a node-context. This allows RPCs in other yang files to refine their input to a particular node instance.";
+
leaf node {
ext:context-reference "node-context";
type node-ref;
+ description "A reference to a particular node.";
}
}
/** Base structure **/
container nodes {
+
+ description "The root container of all nodes.";
+
list node {
key "id";
ext:context-instance "node-context";
-
- uses node;
+ description "A list of nodes (as defined by the 'grouping node').";
+ uses node; //this refers to the 'grouping node' defined above.
}
}
+ //The following notifications should really be replaced by direct writes to the data tree with data change listeners listening to those changes.
+ //Notifications should be reserved for one time events which do not require persistence to the data tree.
notification node-updated {
+
+ status deprecated;
+
+ description "A notification sent by someone who realized there was a modification to a node, but did not modify the data tree.
+ Describes that something on the node has been updated (including addition of a new node), but is for
+ whatever reason is not modifying the data tree.
+
+ Deprecated: If a process determines that a node was updated, then that
+ logic should update the node using the DataBroker directly. Listeners interested
+ update changes should register a data change listener for notifications on removals.";
+
leaf node-ref {
ext:context-reference "node-context";
+ description "A reference to the node which changed.";
+
type node-ref;
}
uses node;
}
notification node-connector-updated {
+
+ status deprecated;
+
+ description "A notification sent by someone who realized there was a modification to a node-connector, but did not modify the data tree.
+ Describes that something on the node-connector has been updated (including addition of a new node-connector), but is for
+ whatever reason is not modifying the data tree.
+
+ Deprecated: If a process determines that a node-connector was updated, then that
+ logic should update the node-connector using the DataBroker directly. Listeners interested
+ update changes should register a data change listener for notifications on removals.";
+
leaf node-connector-ref {
ext:context-reference "node-connector-context";
type node-connector-ref;
+ description "A reference to the node-connector which changed.";
}
uses node-connector;
}
notification node-removed {
+
+ status deprecated;
+
+ description "A notification sent by someone who realized there was a node was removed, but did not modify the data tree.
+ Describes that a node has been removed but is for
+ whatever reason is not modifying the data tree.
+
+ Deprecated: If a process determines that a node was removed, then that
+ logic should remove the node from the DataBroker directly. Listeners interested
+ in changes should register a data change listener for notifications on removals.";
+
leaf node-ref {
+ description "A reference to the node that was removed.";
ext:context-reference "node-context";
type node-ref;
}
}
notification node-connector-removed {
+
+ status deprecated;
+
+ description "A notification sent by someone who realized there was a node-connector was removed, but did not modify the data tree.
+ Describes that a node-connector has been removed but is for
+ whatever reason is not modifying the data tree.
+
+ Deprecated: If a process determines that a node-connector was removed, then that
+ logic should remove the node-connector from the DataBroker directly. Listeners interested
+ in changes should register a data change listener for notifications on removals.";
+
leaf node-connector-ref {
+ description "A reference to the node-connector that was removed.";
ext:context-reference "node-connector-context";
type node-connector-ref;
}
<!--sal-protocolbuffer-encoding-->
<module>sal-protocolbuffer-encoding</module>
- <!-- Karaf feature -->
- <module>feature</module>
-
<!-- Yang Test Models for MD-SAL -->
<module>sal-test-model</module>
</modules>
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-public interface BindingTransactionChain extends TransactionChain<InstanceIdentifier<?>, DataObject> {
+public interface BindingTransactionChain extends TransactionFactory, TransactionChain<InstanceIdentifier<?>, DataObject> {
@Override
ReadOnlyTransaction newReadOnlyTransaction();
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionChainFactory;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
* <p>
* For more information on usage, please see the documentation in {@link AsyncDataBroker}.
*/
-public interface DataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, DataChangeListener>, BindingService, TransactionChainFactory<InstanceIdentifier<?>, DataObject> {
+public interface DataBroker extends TransactionFactory, AsyncDataBroker<InstanceIdentifier<?>, DataObject, DataChangeListener>, BindingService, TransactionChainFactory<InstanceIdentifier<?>, DataObject> {
+
@Override
ReadOnlyTransaction newReadOnlyTransaction();
@Override
ListenerRegistration<DataChangeListener> registerDataChangeListener(LogicalDatastoreType store,
InstanceIdentifier<?> path, DataChangeListener listener, DataChangeScope triggeringScope);
+
+ @Override
+ BindingTransactionChain createTransactionChain(TransactionChainListener listener);
}
--- /dev/null
+/*
+ * 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.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+
+public interface MountPoint extends Identifiable<InstanceIdentifier<?>>{
+
+ <T extends BindingAwareService> Optional<T> getService(Class<T> service);
+
+}
--- /dev/null
+/*
+ * 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.api;
+
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+
+public interface MountPointService {
+
+ Optional<MountPoint> getMountPoint(InstanceIdentifier<?> mountPoint);
+
+ <T extends MountPointListener> ListenerRegistration<T> registerListener(InstanceIdentifier<?> path, T listener);
+
+
+ public interface MountPointListener extends EventListener {
+
+ void onMountPointCreated(InstanceIdentifier<?> path);
+
+ void onMountPointRemoved(InstanceIdentifier<?> path);
+
+ }
+
+}
import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
+/**
+ * A transaction that provides read access to a logical data store.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncReadTransaction}.
+ */
public interface ReadTransaction extends AsyncReadTransaction<InstanceIdentifier<?>, DataObject> {
- @Override
- ListenableFuture<Optional<DataObject>> read(LogicalDatastoreType store, InstanceIdentifier<?> path);
+
+ /**
+ * Reads data from the provided logical data store located at the provided path.
+ *<p>
+ * If the target is a subtree, then the whole subtree is read (and will be
+ * accessible from the returned data object).
+ *
+ * @param store
+ * Logical data store from which read should occur.
+ * @param path
+ * Path which uniquely identifies subtree which client want to
+ * read
+ * @return Listenable Future which contains read result
+ * <ul>
+ * <li>If data at supplied path exists the
+ * {@link ListeblaFuture#get()} returns Optional object containing
+ * data once read is done.
+ * <li>If data at supplied path does not exists the
+ * {@link ListenbleFuture#get()} returns {@link Optional#absent()}.
+ * </ul>
+ */
+ <T extends DataObject> ListenableFuture<Optional<T>> read(LogicalDatastoreType store, InstanceIdentifier<T> path);
}
--- /dev/null
+/*
+ * 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.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataTransactionFactory;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface TransactionFactory extends AsyncDataTransactionFactory<InstanceIdentifier<?>, DataObject>{
+
+ @Override
+ ReadOnlyTransaction newReadOnlyTransaction();
+
+ @Override
+ ReadWriteTransaction newReadWriteTransaction();
+
+ @Override
+ WriteTransaction newWriteOnlyTransaction();
+
+}
* For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
*/
public interface WriteTransaction extends AsyncWriteTransaction<InstanceIdentifier<?>, DataObject> {
- @Override
- void put(LogicalDatastoreType store, InstanceIdentifier<?> path, DataObject data);
+
+ /**
+ * Stores a piece of data at the specified path. This acts as an add / replace
+ * operation, which is to say that whole subtree will be replaced by the specified data.
+ * * <p>
+ * This method does not automatically create missing parent nodes. It is equivalent to invoking
+ * {@link #put(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}
+ * with <code>createMissingParents</code> set to false.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ * <p>
+ * If you need to make sure that a parent object exists but you do not want modify
+ * its pre-existing state by using put, consider using {@link #merge} instead.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be written to the specified path
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ <T extends DataObject> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
+
+
+ /**
+ * Stores a piece of data at the specified path. This acts as an add /
+ * replace operation, which is to say that whole subtree will be replaced by
+ * the specified data.
+ * <p>
+ * For more information on usage and examples, please see the documentation
+ * in {@link AsyncWriteTransaction}.
+ * <p>
+ * If you need to make sure that a parent object exists but you do not want
+ * modify its pre-existing state by using put, consider using {@link #merge}
+ * instead.
+ *
+ * Note: Using <code>createMissingParents</code> with value true, may
+ * introduce garbage in data store, or recreate nodes, which were deleted by
+ * previous transaction.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be written to the specified path
+ * @param createMissingParents
+ * if true, any missing parent nodes will be automatically
+ * created using a merge operation.
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ <T extends DataObject> void put(LogicalDatastoreType store, InstanceIdentifier<T> path, T data,
+ boolean createMissingParents);
+
+ /**
+ * Merges a piece of data with the existing data at a specified path. Any pre-existing data
+ * which is not explicitly overwritten will be preserved. This means that if you store a container,
+ * its child lists will be merged.
+ * <p>
+ * This method does not automatically create missing parent nodes. It is equivalent to invoking
+ * {@link #merge(LogicalDatastoreType, InstanceIdentifier, DataObject, boolean)}
+ * with <code>createMissingParents</code> set to false.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ *<p>
+ * If you require an explicit replace operation, use {@link #put} instead.
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be merged to the specified path
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ <T extends DataObject> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data);
+
+ /**
+ * Merges a piece of data with the existing data at a specified path. Any
+ * pre-existing data which is not explicitly overwritten will be preserved.
+ * This means that if you store a container, its child lists will be merged.
+ * <p>
+ * For more information on usage and examples, please see the documentation
+ * in {@link AsyncWriteTransaction}.
+ * <p>
+ * If you require an explicit replace operation, use {@link #put} instead.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be merged to the specified path
+ * @param createMissingParents
+ * if true, any missing parent nodes will be automatically created
+ * using a merge operation.
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ <T extends DataObject> void merge(LogicalDatastoreType store, InstanceIdentifier<T> path, T data,
+ boolean createMissingParents);
@Override
void delete(LogicalDatastoreType store, InstanceIdentifier<?> path);
return codec;
}
- protected final ListenableFuture<Optional<DataObject>> doRead(final DOMDataReadTransaction readTx,
- final LogicalDatastoreType store, final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<?> path) {
+ protected final <T extends DataObject> ListenableFuture<Optional<T>> doRead(final DOMDataReadTransaction readTx,
+ final LogicalDatastoreType store, final org.opendaylight.yangtools.yang.binding.InstanceIdentifier<T> path) {
return Futures.transform(readTx.read(store, codec.toNormalized(path)), codec.deserializeFunction(path));
}
}
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
-import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
super(delegate, codec);
}
- protected final void doPutWithEnsureParents(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
- final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
- .toNormalizedNode(path, data);
-
- final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath = normalized.getKey();
- ensureParentsByMerge(store, normalizedPath, path);
- LOG.debug("Tx: {} : Putting data {}", getDelegate().getIdentifier(), normalizedPath);
- doPut(store, path, data);
- }
-
- protected final void doMergeWithEnsureParents(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
- final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
- .toNormalizedNode(path, data);
-
- final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath = normalized.getKey();
- ensureParentsByMerge(store, normalizedPath, path);
- LOG.debug("Tx: {} : Merge data {}", getDelegate().getIdentifier(), normalizedPath);
- doMerge(store, path, data);
- }
-
- private final void ensureParentsByMerge(final LogicalDatastoreType store,
+ @Override
+ protected final void ensureParentsByMerge(final LogicalDatastoreType store,
final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath,
final InstanceIdentifier<?> path) {
List<PathArgument> currentArguments = new ArrayList<>();
import java.util.Collections;
import java.util.Map.Entry;
-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;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.Identifiable;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.slf4j.Logger;
import com.google.common.base.Optional;
import com.google.common.collect.Iterables;
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.CheckedFuture;
/**
*
* Abstract Base Transaction for transactions which are backed by
* {@link DOMDataWriteTransaction}
*/
-public class AbstractWriteTransaction<T extends DOMDataWriteTransaction> extends
+public abstract class AbstractWriteTransaction<T extends DOMDataWriteTransaction> extends
AbstractForwardedTransaction<T> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractWriteTransaction.class);
super(delegate, codec);
}
- protected final void doPut(final LogicalDatastoreType store,
- final InstanceIdentifier<?> path, final DataObject data) {
+
+ public final <U extends DataObject> void put(final LogicalDatastoreType store,
+ final InstanceIdentifier<U> path, final U data, final boolean createParents) {
final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
.toNormalizedNode(path, data);
- ensureListParentIfNeeded(store,path,normalized);
+ if(createParents) {
+ ensureParentsByMerge(store, normalized.getKey(), path);
+ } else {
+ ensureListParentIfNeeded(store,path,normalized);
+ }
getDelegate().put(store, normalized.getKey(), normalized.getValue());
}
+ public final <U extends DataObject> void merge(final LogicalDatastoreType store,
+ final InstanceIdentifier<U> path, final U data,final boolean createParents) {
+
+ final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
+ .toNormalizedNode(path, data);
+
+ if(createParents) {
+ ensureParentsByMerge(store, normalized.getKey(), path);
+ } else {
+ ensureListParentIfNeeded(store,path,normalized);
+ }
+
+ getDelegate().merge(store, normalized.getKey(), normalized.getValue());
+ }
+
+
/**
*
* Ensures list parent if item is list, otherwise noop.
}
}
- protected final void doMerge(final LogicalDatastoreType store,
- final InstanceIdentifier<?> path, final DataObject data) {
-
- final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, NormalizedNode<?, ?>> normalized = getCodec()
- .toNormalizedNode(path, data);
- ensureListParentIfNeeded(store,path,normalized);
- getDelegate().merge(store, normalized.getKey(), normalized.getValue());
- }
+ /**
+ * Subclasses of this class are required to implement creation of parent
+ * nodes based on behaviour of their underlying transaction.
+ *
+ * @param store
+ * @param key
+ * @param path
+ */
+ protected abstract void ensureParentsByMerge(LogicalDatastoreType store,
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier key, InstanceIdentifier<?> path);
protected final void doDelete(final LogicalDatastoreType store,
final InstanceIdentifier<?> path) {
getDelegate().delete(store, normalized);
}
- protected final ListenableFuture<RpcResult<TransactionStatus>> doCommit() {
- return getDelegate().commit();
+ protected final CheckedFuture<Void,TransactionCommitFailedException> doSubmit() {
+ return getDelegate().submit();
}
protected final boolean doCancel() {
}
@Override
- public ListenableFuture<Optional<DataObject>> read(final LogicalDatastoreType store,
- final InstanceIdentifier<?> path) {
+ public <T extends DataObject> ListenableFuture<Optional<T>> read(final LogicalDatastoreType store,
+ final InstanceIdentifier<T> path) {
return doRead(getDelegate(),store, path);
}
}
@Override
- public ListenableFuture<Optional<DataObject>> read(final LogicalDatastoreType store,
- final InstanceIdentifier<?> path) {
+ public <T extends DataObject> ListenableFuture<Optional<T>> read(final LogicalDatastoreType store,
+ final InstanceIdentifier<T> path) {
return doRead(getDelegate(), store, path);
}
}
\ No newline at end of file
*/
package org.opendaylight.controller.md.sal.binding.impl;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
+import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.ListenableFuture;
class BindingDataWriteTransactionImpl<T extends DOMDataWriteTransaction> extends
super(delegateTx, codec);
}
-
+ @Override
+ public <U extends DataObject> void put(final LogicalDatastoreType store, final InstanceIdentifier<U> path,
+ final U data) {
+ put(store, path, data,false);
+ }
@Override
- public void put(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
- doPut(store, path, data);
+ public <T extends DataObject> void merge(final LogicalDatastoreType store, final InstanceIdentifier<T> path,
+ final T data) {
+ merge(store, path, data,false);
}
+
@Override
- public void merge(final LogicalDatastoreType store, final InstanceIdentifier<?> path, final DataObject data) {
- doMerge(store, path, data);
+ protected void ensureParentsByMerge(final LogicalDatastoreType store,
+ final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalizedPath, final InstanceIdentifier<?> path) {
+ List<PathArgument> currentArguments = new ArrayList<>();
+ DataNormalizationOperation<?> currentOp = getCodec().getDataNormalizer().getRootOperation();
+ Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
+ while (iterator.hasNext()) {
+ PathArgument currentArg = iterator.next();
+ try {
+ currentOp = currentOp.getChild(currentArg);
+ } catch (DataNormalizationException e) {
+ throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
+ }
+ currentArguments.add(currentArg);
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier currentPath = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.create(
+ currentArguments);
+
+ getDelegate().merge(store, currentPath, currentOp.createDefault(currentArg));
+ }
}
@Override
@Override
public ListenableFuture<RpcResult<TransactionStatus>> commit() {
- return doCommit();
+ return AbstractDataTransaction.convertToLegacyCommitFuture(submit());
+ }
+
+ @Override
+ public CheckedFuture<Void,TransactionCommitFailedException> submit() {
+ return doSubmit();
}
@Override
*/
public Optional<InstanceIdentifier<? extends DataObject>> toBinding(
final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
- throws DeserializationException {
+ throws DeserializationException {
PathArgument lastArgument = Iterables.getLast(normalized.getPathArguments());
// Used instance-identifier codec do not support serialization of last
private Optional<InstanceIdentifier<? extends DataObject>> toBindingAugmented(
final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
- throws DeserializationException {
+ throws DeserializationException {
Optional<InstanceIdentifier<? extends DataObject>> potential = toBindingImpl(normalized);
// Shorthand check, if codec already supports deserialization
// of AugmentationIdentifier we will return
private Optional<InstanceIdentifier<? extends DataObject>> toBindingImpl(
final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
- throws DeserializationException {
+ throws DeserializationException {
org.opendaylight.yangtools.yang.data.api.InstanceIdentifier legacyPath;
try {
private DataNormalizationOperation<?> findNormalizationOperation(
final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier normalized)
- throws DataNormalizationException {
+ throws DataNormalizationException {
DataNormalizationOperation<?> current = legacyToNormalized.getRootOperation();
for (PathArgument arg : normalized.getPathArguments()) {
current = current.getChild(arg);
public Optional<Entry<org.opendaylight.yangtools.yang.binding.InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
final Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
- throws DeserializationException {
+ throws DeserializationException {
Optional<InstanceIdentifier<? extends DataObject>> potentialPath = toBinding(normalized.getKey());
if (potentialPath.isPresent()) {
InstanceIdentifier<? extends DataObject> bindingPath = potentialPath.get();
return Optional.absent();
}
- private Optional<AugmentationSchema> findAugmentation(final Class targetType,
+ private Optional<AugmentationSchema> findAugmentation(final Class<?> targetType,
final Set<AugmentationSchema> augmentations) {
YangModuleInfo moduleInfo;
try {
if (isAugmentation(arg.getType())) {
count++;
}
+
}
return count;
}
return count;
}
- public Function<Optional<NormalizedNode<?, ?>>, Optional<DataObject>> deserializeFunction(
- final InstanceIdentifier<?> path) {
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public <T extends DataObject> Function<Optional<NormalizedNode<?, ?>>, Optional<T>> deserializeFunction(final InstanceIdentifier<T> path) {
return new DeserializeFunction(this, path);
}
- private static class DeserializeFunction implements Function<Optional<NormalizedNode<?, ?>>, Optional<DataObject>> {
+ private static class DeserializeFunction<T extends DataObject> implements Function<Optional<NormalizedNode<?, ?>>, Optional<T>> {
private final BindingToNormalizedNodeCodec codec;
private final InstanceIdentifier<?> path;
this.path = Preconditions.checkNotNull(path, "Path must not be null");
}
+ @SuppressWarnings("rawtypes")
@Nullable
@Override
- public Optional<DataObject> apply(@Nullable final Optional<NormalizedNode<?, ?>> normalizedNode) {
+ public Optional apply(@Nullable final Optional<NormalizedNode<?, ?>> normalizedNode) {
if (normalizedNode.isPresent()) {
final DataObject dataObject;
try {
/**
* Returns an default object according to YANG schema for supplied path.
*
- * @param path
- * DOM Path
+ * @param path DOM Path
* @return Node with defaults set on.
*/
public NormalizedNode<?, ?> getDefaultNodeFor(final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path) {
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
import org.opendaylight.controller.md.sal.common.api.data.DataReader;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.Delegator;
import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+@SuppressWarnings("deprecation")
public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDataBroker implements DataProviderService, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(ForwardedBackwardsCompatibleDataBroker.class);
@Override
public ListenableFuture<RpcResult<TransactionStatus>> apply(final Boolean requestCommitSuccess) throws Exception {
if(requestCommitSuccess) {
- return tx.getDelegate().commit();
+ return AbstractDataTransaction.convertToLegacyCommitFuture(tx.getDelegate().submit());
}
- return Futures.immediateFuture(Rpcs.getRpcResult(false, TransactionStatus.FAILED, Collections.<RpcError>emptySet()));
+ return Futures.immediateFuture(RpcResultBuilder.<TransactionStatus>failed().withResult(TransactionStatus.FAILED).build());
}
});
@Override
public void putOperationalData(final InstanceIdentifier<? extends DataObject> path, final DataObject data) {
boolean previouslyRemoved = posponedRemovedOperational.remove(path);
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ final InstanceIdentifier<DataObject> castedPath = (InstanceIdentifier) path;
if(previouslyRemoved) {
- doPutWithEnsureParents(LogicalDatastoreType.OPERATIONAL, path, data);
+ put(LogicalDatastoreType.OPERATIONAL, castedPath, data,true);
} else {
- doMergeWithEnsureParents(LogicalDatastoreType.OPERATIONAL, path, data);
+ merge(LogicalDatastoreType.OPERATIONAL, castedPath, data,true);
}
}
created.put(path, data);
}
updated.put(path, data);
+ @SuppressWarnings({"rawtypes","unchecked"})
+ final InstanceIdentifier<DataObject> castedPath = (InstanceIdentifier) path;
if(previouslyRemoved) {
- doPutWithEnsureParents(LogicalDatastoreType.CONFIGURATION, path, data);
+ put(LogicalDatastoreType.CONFIGURATION, castedPath, data,true);
} else {
- doMergeWithEnsureParents(LogicalDatastoreType.CONFIGURATION, path, data);
+ merge(LogicalDatastoreType.CONFIGURATION, castedPath, data,true);
}
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.codegen;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Exception is raised when supplied Bidning Aware
+ * RPCService class is not routed and was used in context
+ * where routed RPCs should only be used.
+ *
+ */
+public class RpcIsNotRoutedException extends IllegalStateException {
+
+ private static final long serialVersionUID = 1L;
+
+ public RpcIsNotRoutedException(final String message, final Throwable cause) {
+ super(Preconditions.checkNotNull(message), cause);
+ }
+
+ public RpcIsNotRoutedException(final String message) {
+ super(Preconditions.checkNotNull(message));
+ }
+}
* - Subclass of RpcService for which Router is to be generated.
* @return Instance of RpcService of provided serviceType which implements
* also {@link RpcRouter}<T> and {@link org.opendaylight.controller.sal.binding.spi.DelegateProxy}
+ * @throws RpcIsNotRoutedException
*/
- <T extends RpcService> RpcRouter<T> getRouterFor(Class<T> serviceType,String name) throws IllegalArgumentException;
+ <T extends RpcService> RpcRouter<T> getRouterFor(Class<T> serviceType,String name) throws IllegalArgumentException, RpcIsNotRoutedException;
NotificationInvokerFactory getInvokerFactory();
}
*/
package org.opendaylight.controller.sal.binding.codegen.impl;
+import com.google.common.base.Supplier;
+
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.xtext.xbase.lib.Extension;
import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
+import org.opendaylight.controller.sal.binding.codegen.RpcIsNotRoutedException;
import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory;
import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.NotificationListener;
import org.opendaylight.yangtools.yang.binding.RpcService;
import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils;
-import com.google.common.base.Supplier;
-
abstract class AbstractRuntimeCodeGenerator implements org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator, NotificationInvokerFactory {
@GuardedBy("this")
private final Map<Class<? extends NotificationListener>, RuntimeGeneratedInvokerPrototype> invokerClasses = new WeakHashMap<>();
protected abstract <T extends RpcService> Supplier<T> directProxySupplier(final Class<T> serviceType);
protected abstract <T extends RpcService> Supplier<T> routerSupplier(final Class<T> serviceType, RpcServiceMetadata metadata);
- private RpcServiceMetadata getRpcMetadata(final CtClass iface) throws ClassNotFoundException, NotFoundException {
+ private RpcServiceMetadata getRpcMetadata(final CtClass iface) throws ClassNotFoundException, NotFoundException, RpcIsNotRoutedException {
final RpcServiceMetadata metadata = new RpcServiceMetadata();
for (CtMethod method : iface.getMethods()) {
- if (iface.equals(method.getDeclaringClass()) && method.getParameterTypes().length == 1) {
+ if (isRpcMethodWithInput(iface, method)) {
final RpcMetadata routingPair = getRpcMetadata(method);
if (routingPair != null) {
metadata.addContext(routingPair.getContext());
* remains to be investigated.
*/
Thread.currentThread().getContextClassLoader().loadClass(routingPair.getInputType().getName());
+ } else {
+ throw new RpcIsNotRoutedException("RPC " + method.getName() + " from "+ iface.getName() +" is not routed");
}
}
}
return metadata;
}
+
+ private boolean isRpcMethodWithInput(final CtClass iface, final CtMethod method) throws NotFoundException {
+ if(iface.equals(method.getDeclaringClass())
+ && method.getParameterTypes().length == 1) {
+ final CtClass onlyArg = method.getParameterTypes()[0];
+ if(onlyArg.isInterface() && onlyArg.getName().endsWith(BindingMapping.RPC_INPUT_SUFFIX)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private RpcMetadata getRpcMetadata(final CtMethod method) throws NotFoundException {
final CtClass inputClass = method.getParameterTypes()[0];
return rpcMethodMetadata(inputClass, inputClass, method.getName());
utils.getLock().lock();
try {
- invoker = ClassLoaderUtils.withClassLoader(cls.getClassLoader(), new Supplier<RuntimeGeneratedInvokerPrototype>() {
- @Override
- public RuntimeGeneratedInvokerPrototype get() {
- return generateListenerInvoker(cls);
- }
- });
+ synchronized (utils) {
+ invoker = ClassLoaderUtils.withClassLoader(cls.getClassLoader(), new Supplier<RuntimeGeneratedInvokerPrototype>() {
+ @Override
+ public RuntimeGeneratedInvokerPrototype get() {
+ return generateListenerInvoker(cls);
+ }
+ });
+ }
invokerClasses.put(cls, invoker);
return invoker;
public final <T extends RpcService> T getDirectProxyFor(final Class<T> serviceType) {
utils.getLock().lock();
try {
- return ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), directProxySupplier(serviceType));
+ synchronized (utils) {
+ return ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), directProxySupplier(serviceType));
+ }
} finally {
utils.getLock().unlock();
}
}
@Override
- public final <T extends RpcService> RpcRouter<T> getRouterFor(final Class<T> serviceType, final String name) {
+ public final <T extends RpcService> RpcRouter<T> getRouterFor(final Class<T> serviceType, final String name) throws RpcIsNotRoutedException {
final RpcServiceMetadata metadata = ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), new Supplier<RpcServiceMetadata>() {
@Override
public RpcServiceMetadata get() {
utils.getLock().lock();
try {
- final T instance = ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), routerSupplier(serviceType, metadata));
- return new RpcRouterCodegenInstance<T>(name, serviceType, instance, metadata.getContexts());
+ synchronized (utils) {
+ final T instance = ClassLoaderUtils.withClassLoader(serviceType.getClassLoader(), routerSupplier(serviceType, metadata));
+ return new RpcRouterCodegenInstance<T>(name, serviceType, instance, metadata.getContexts());
+ }
} finally {
utils.getLock().unlock();
}
private static final Logger LOG = LoggerFactory.getLogger(RpcRouterCodegenInstance.class);
- private T defaultService;
-
private final Class<T> serviceType;
private final T invocationProxy;
private final Map<Class<? extends BaseIdentity>, RpcRoutingTableImpl<? extends BaseIdentity, T>> routingTables;
- private final String name;
-
@SuppressWarnings("unchecked")
public RpcRouterCodegenInstance(final String name,final Class<T> type, final T routerImpl, final Iterable<Class<? extends BaseIdentity>> contexts) {
- this.name = name;
this.listeners = ListenerRegistry.create();
this.serviceType = type;
this.invocationProxy = routerImpl;
@Override
public T getDefaultService() {
- return defaultService;
+ return RuntimeCodeHelper.getDelegate(invocationProxy);
}
@Override
return new RoutedRpcRegistrationImpl(service);
}
+ public void removeDefaultImplementation(final T instance) {
+ RpcService current = RuntimeCodeHelper.getDelegate(invocationProxy);
+ if(instance == current) {
+ RuntimeCodeHelper.setDelegate(invocationProxy, null);
+ }
+ }
+
@Override
public RpcRegistration<T> registerDefaultService(final T service) {
- // TODO Auto-generated method stub
RuntimeCodeHelper.setDelegate(invocationProxy, service);
- return null;
+ return new DefaultRpcImplementationRegistration(service);
}
private class RoutedRpcRegistrationImpl extends AbstractObjectRegistration<T> implements RoutedRpcRegistration<T> {
}
}
+
+ private class DefaultRpcImplementationRegistration extends AbstractObjectRegistration<T> implements RpcRegistration<T> {
+
+
+ protected DefaultRpcImplementationRegistration(final T instance) {
+ super(instance);
+ }
+
+ @Override
+ protected void removeRegistration() {
+ removeDefaultImplementation(this.getInstance());
+ }
+
+ @Override
+ public Class<T> getServiceType() {
+ return serviceType;
+ }
+ }
+
+
}
*/
package org.opendaylight.controller.sal.binding.impl;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.EventListener;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
+import org.opendaylight.controller.sal.binding.codegen.RpcIsNotRoutedException;
import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator;
import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeHelper;
import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.EventListener;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.WeakHashMap;
-
-import static com.google.common.base.Preconditions.checkState;
+import com.google.common.base.Throwables;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.util.concurrent.UncheckedExecutionException;
-public class RpcProviderRegistryImpl implements //
- RpcProviderRegistry, //
- RouteChangePublisher<RpcContextIdentifier, InstanceIdentifier<?>> {
+public class RpcProviderRegistryImpl implements RpcProviderRegistry, RouteChangePublisher<RpcContextIdentifier, InstanceIdentifier<?>> {
private RuntimeCodeGenerator rpcFactory = SingletonHolder.RPC_GENERATOR_IMPL;
- // publicProxies is a cache of proxy objects where each value in the map corresponds to a specific RpcService
- private final Map<Class<? extends RpcService>, RpcService> publicProxies = new WeakHashMap<>();
- private final Map<Class<? extends RpcService>, RpcRouter<?>> rpcRouters = new WeakHashMap<>();
+ // cache of proxy objects where each value in the map corresponds to a specific RpcService
+ private final LoadingCache<Class<? extends RpcService>, RpcService> publicProxies = CacheBuilder.newBuilder().weakKeys().
+ build(new CacheLoader<Class<? extends RpcService>, RpcService>() {
+ @Override
+ public RpcService load(final Class<? extends RpcService> type) {
+ final RpcService proxy = rpcFactory.getDirectProxyFor(type);
+ LOG.debug("Created {} as public proxy for {} in {}", proxy, type.getSimpleName(), this);
+ return proxy;
+ }
+ });
+
+ private final Cache<Class<? extends RpcService>, RpcRouter<?>> rpcRouters = CacheBuilder.newBuilder().weakKeys()
+ .build();
+
private final ListenerRegistry<RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> routeChangeListeners = ListenerRegistry
.create();
private final ListenerRegistry<RouterInstantiationListener> routerInstantiationListener = ListenerRegistry.create();
return name;
}
- public RpcProviderRegistryImpl(String name) {
+ public RpcProviderRegistryImpl(final String name) {
super();
this.name = name;
}
@Override
- public final <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> type,
- T implementation) throws IllegalStateException {
+ public final <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(final Class<T> type,
+ final T implementation) throws IllegalStateException {
return getRpcRouter(type).addRoutedRpcImplementation(implementation);
}
@Override
- public final <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> type, T implementation)
+ public final <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> type, final T implementation)
throws IllegalStateException {
- @SuppressWarnings("unchecked")
- RpcRouter<T> potentialRouter = (RpcRouter<T>) rpcRouters.get(type);
- if (potentialRouter != null) {
+
+ // FIXME: This should be well documented - addRpcImplementation for
+ // routed RPCs
+ try {
+ // Note: If RPC is really global, expected count of registrations
+ // of this method is really low.
+ RpcRouter<T> potentialRouter = getRpcRouter(type);
checkState(potentialRouter.getDefaultService() == null,
- "Default service for routed RPC already registered.");
+ "Default service for routed RPC already registered.");
return potentialRouter.registerDefaultService(implementation);
+ } catch (RpcIsNotRoutedException e) {
+ // NOOP - we could safely continue, since RPC is not routed
+ // so we fallback to global routing.
+ LOG.debug("RPC is not routed. Using global registration.",e);
}
T publicProxy = getRpcService(type);
RpcService currentDelegate = RuntimeCodeHelper.getDelegate(publicProxy);
@SuppressWarnings("unchecked")
@Override
- public final <T extends RpcService> T getRpcService(Class<T> type) {
-
- T potentialProxy = (T) publicProxies.get(type);
- if (potentialProxy != null) {
- return potentialProxy;
- }
- synchronized (this) {
- /**
- * Potential proxy could be instantiated by other thread while we
- * were waiting for the lock.
- */
-
- potentialProxy = (T) publicProxies.get(type);
- if (potentialProxy != null) {
- return potentialProxy;
- }
- T proxy = rpcFactory.getDirectProxyFor(type);
- LOG.debug("Created {} as public proxy for {} in {}", proxy, type.getSimpleName(), this);
- publicProxies.put(type, proxy);
- return proxy;
- }
+ public final <T extends RpcService> T getRpcService(final Class<T> type) {
+ return (T) publicProxies.getUnchecked(type);
}
- @SuppressWarnings("unchecked")
- public <T extends RpcService> RpcRouter<T> getRpcRouter(Class<T> type) {
- RpcRouter<?> potentialRouter = rpcRouters.get(type);
- if (potentialRouter != null) {
- return (RpcRouter<T>) potentialRouter;
- }
- synchronized (this) {
- /**
- * Potential Router could be instantiated by other thread while we
- * were waiting for the lock.
- */
- potentialRouter = rpcRouters.get(type);
- if (potentialRouter != null) {
- return (RpcRouter<T>) potentialRouter;
+
+ public <T extends RpcService> RpcRouter<T> getRpcRouter(final Class<T> type) {
+ try {
+ final AtomicBoolean created = new AtomicBoolean(false);
+ @SuppressWarnings( "unchecked")
+ // LoadingCache is unsuitable for RpcRouter since we need to distinguish
+ // first creation of RPC Router, so that is why
+ // we are using normal cache with load API and shared AtomicBoolean
+ // for this call, which will be set to true if router was created.
+ RpcRouter<T> router = (RpcRouter<T>) rpcRouters.get(type,new Callable<RpcRouter<?>>() {
+
+ @Override
+ public org.opendaylight.controller.sal.binding.api.rpc.RpcRouter<?> call() {
+ RpcRouter<?> router = rpcFactory.getRouterFor(type, name);
+ router.registerRouteChangeListener(new RouteChangeForwarder<T>(type));
+ LOG.debug("Registering router {} as global implementation of {} in {}", router, type.getSimpleName(), this);
+ RuntimeCodeHelper.setDelegate(getRpcService(type), router.getInvocationProxy());
+ created.set(true);
+ return router;
+ }
+ });
+ if(created.get()) {
+ notifyListenersRoutedCreated(router);
}
- RpcRouter<T> router = rpcFactory.getRouterFor(type, name);
- router.registerRouteChangeListener(new RouteChangeForwarder(type));
- LOG.debug("Registering router {} as global implementation of {} in {}", router, type.getSimpleName(), this);
- RuntimeCodeHelper.setDelegate(getRpcService(type), router.getInvocationProxy());
- rpcRouters.put(type, router);
- notifyListenersRoutedCreated(router);
return router;
+ } catch (ExecutionException | UncheckedExecutionException e) {
+ // We rethrow Runtime Exceptions which were wrapped by
+ // Execution Exceptions
+ // otherwise we throw IllegalStateException with original
+ Throwables.propagateIfPossible(e.getCause());
+ throw new IllegalStateException("Could not load RPC Router for "+type.getName(),e);
}
}
- private void notifyGlobalRpcAdded(Class<? extends RpcService> type) {
+ private void notifyGlobalRpcAdded(final Class<? extends RpcService> type) {
for(ListenerRegistration<GlobalRpcRegistrationListener> listener : globalRpcListeners) {
try {
listener.getInstance().onGlobalRpcRegistered(type);
}
- private void notifyListenersRoutedCreated(RpcRouter<?> router) {
+ private void notifyListenersRoutedCreated(final RpcRouter<?> router) {
for (ListenerRegistration<RouterInstantiationListener> listener : routerInstantiationListener) {
try {
}
public ListenerRegistration<RouterInstantiationListener> registerRouterInstantiationListener(
- RouterInstantiationListener listener) {
+ final RouterInstantiationListener listener) {
ListenerRegistration<RouterInstantiationListener> reg = routerInstantiationListener.register(listener);
try {
- for (RpcRouter<?> router : rpcRouters.values()) {
+ for (RpcRouter<?> router : rpcRouters.asMap().values()) {
listener.onRpcRouterCreated(router);
}
} catch (Exception e) {
return reg;
}
+ @SuppressWarnings("unchecked")
@Override
public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
- L listener) {
+ final L listener) {
return (ListenerRegistration<L>) routeChangeListeners.register(listener);
}
return rpcFactory;
}
- public void setRpcFactory(RuntimeCodeGenerator rpcFactory) {
+ public void setRpcFactory(final RuntimeCodeGenerator rpcFactory) {
this.rpcFactory = rpcFactory;
}
void onRpcRouterCreated(RpcRouter<?> router);
}
- public ListenerRegistration<GlobalRpcRegistrationListener> registerGlobalRpcRegistrationListener(GlobalRpcRegistrationListener listener) {
+ public ListenerRegistration<GlobalRpcRegistrationListener> registerGlobalRpcRegistrationListener(final GlobalRpcRegistrationListener listener) {
return globalRpcListeners.register(listener);
}
}
- private class RouteChangeForwarder<T extends RpcService> implements
- RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
+ private class RouteChangeForwarder<T extends RpcService> implements RouteChangeListener<Class<? extends BaseIdentity>, InstanceIdentifier<?>> {
private final Class<T> type;
- public RouteChangeForwarder(Class<T> type) {
+ public RouteChangeForwarder(final Class<T> type) {
this.type = type;
}
@Override
- public void onRouteChange(RouteChange<Class<? extends BaseIdentity>, InstanceIdentifier<?>> change) {
+ public void onRouteChange(final RouteChange<Class<? extends BaseIdentity>, InstanceIdentifier<?>> change) {
Map<RpcContextIdentifier, Set<InstanceIdentifier<?>>> announcements = new HashMap<>();
for (Entry<Class<? extends BaseIdentity>, Set<InstanceIdentifier<?>>> entry : change.getAnnouncements()
.entrySet()) {
try {
listener.getInstance().onRouteChange(toPublish);
} catch (Exception e) {
- e.printStackTrace();
+ LOG.error("Unhandled exception during invoking listener",listener.getInstance(),e);
}
}
}
}
- public static class RpcProxyRegistration<T extends RpcService> extends AbstractObjectRegistration<T> implements
- RpcRegistration<T> {
+ public static class RpcProxyRegistration<T extends RpcService> extends AbstractObjectRegistration<T> implements RpcRegistration<T> {
private final Class<T> serviceType;
private RpcProviderRegistryImpl registry;
- public RpcProxyRegistration(Class<T> type, T service, RpcProviderRegistryImpl registry) {
+ public RpcProxyRegistration(final Class<T> type, final T service, final RpcProviderRegistryImpl registry) {
super(service);
this.serviceType = type;
this.registry = registry;
*/
package org.opendaylight.controller.sal.binding.impl.connect.dom;
-import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
-import java.lang.ref.WeakReference;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.WeakHashMap;
-import java.util.concurrent.Callable;
+
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-
import org.opendaylight.controller.md.sal.binding.impl.AbstractForwardedDataBroker;
-import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
-import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
-import org.opendaylight.controller.md.sal.common.api.data.DataModification;
-import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
-import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.binding.api.NotificationProviderService.NotificationInterestListener;
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.binding.api.data.RuntimeDataProvider;
-import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
-import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
import org.opendaylight.controller.sal.binding.impl.DataBrokerImpl;
import org.opendaylight.controller.sal.binding.impl.MountPointManagerImpl.BindingMountPointImpl;
import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl;
-import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl.GlobalRpcRegistrationListener;
-import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl.RouterInstantiationListener;
-import org.opendaylight.controller.sal.common.util.CommitHandlerTransactions;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
-import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
import org.opendaylight.controller.sal.core.api.Provider;
-import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
-import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
-import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
-import org.opendaylight.yangtools.concepts.CompositeObjectRegistration;
-import org.opendaylight.yangtools.concepts.CompositeObjectRegistration.CompositeObjectRegistrationBuilder;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.concepts.ObjectRegistration;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.binding.Augmentable;
import org.opendaylight.yangtools.yang.binding.Augmentation;
-import org.opendaylight.yangtools.yang.binding.BaseIdentity;
-import org.opendaylight.yangtools.yang.binding.BindingMapping;
-import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.Notification;
-import org.opendaylight.yangtools.yang.binding.RpcService;
-import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
-import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-
public class BindingIndependentConnector implements //
RuntimeDataProvider, //
Provider, //
AutoCloseable {
- private final Logger LOG = LoggerFactory.getLogger(BindingIndependentConnector.class);
-
+ private static final Logger LOG = LoggerFactory.getLogger(BindingIndependentConnector.class);
private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier ROOT_BI = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
.builder().toInstance();
- private final static Method EQUALS_METHOD;
-
private BindingIndependentMappingService mappingService;
-
private org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService;
-
private DataProviderService baDataService;
- private final ConcurrentMap<Object, BindingToDomTransaction> domOpenedTransactions = new ConcurrentHashMap<>();
- private final ConcurrentMap<Object, DomToBindingTransaction> bindingOpenedTransactions = new ConcurrentHashMap<>();
-
- private final BindingToDomCommitHandler bindingToDomCommitHandler = new BindingToDomCommitHandler();
- private final DomToBindingCommitHandler domToBindingCommitHandler = new DomToBindingCommitHandler();
-
- private Registration<DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject>> baCommitHandlerRegistration;
+ private final ConcurrentMap<Object, BindingToDomTransaction> domOpenedTransactions;
+ private final ConcurrentMap<Object, DomToBindingTransaction> bindingOpenedTransactions;
+ private final BindingToDomCommitHandler bindingToDomCommitHandler;
+ private final DomToBindingCommitHandler domToBindingCommitHandler;
private Registration<DataCommitHandler<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode>> biCommitHandlerRegistration;
-
private RpcProvisionRegistry biRpcRegistry;
private RpcProviderRegistry baRpcRegistry;
private ListenerRegistration<DomToBindingRpcForwardingManager> domToBindingRpcManager;
- // private ListenerRegistration<BindingToDomRpcForwardingManager>
- // bindingToDomRpcManager;
-
- private final Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> toDOMInstanceIdentifier = new Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier>() {
- @Override
- public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier apply(final InstanceIdentifier<?> input) {
- return mappingService.toDataDom(input);
- }
-
- };
-
- private boolean rpcForwarding = false;
-
- private boolean dataForwarding = false;
-
- private boolean notificationForwarding = false;
+ private boolean rpcForwarding;
+ private boolean dataForwarding;
+ private boolean notificationForwarding;
private RpcProviderRegistryImpl baRpcRegistryImpl;
private NotificationPublishService domNotificationService;
- static {
- try {
- EQUALS_METHOD = Object.class.getMethod("equals", Object.class);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ public BindingIndependentConnector() {
+ domOpenedTransactions = new ConcurrentHashMap<>();
+ bindingOpenedTransactions = new ConcurrentHashMap<>();
+
+ bindingToDomCommitHandler = new BindingToDomCommitHandler(bindingOpenedTransactions, domOpenedTransactions);
+ domToBindingCommitHandler = new DomToBindingCommitHandler(bindingOpenedTransactions, domOpenedTransactions);
+ rpcForwarding = false;
+ dataForwarding = false;
+ notificationForwarding = false;
}
@Override
}
}
- private DataModificationTransaction createBindingToDomTransaction(
- final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> source) {
- DataModificationTransaction target = biDataService.beginTransaction();
- LOG.debug("Created DOM Transaction {} for {},", target.getIdentifier(), source.getIdentifier());
- for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedConfigurationData()) {
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
- target.removeConfigurationData(biEntry);
- LOG.debug("Delete of Binding Configuration Data {} is translated to {}", entry, biEntry);
- }
- for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedOperationalData()) {
- org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
- target.removeOperationalData(biEntry);
- LOG.debug("Delete of Binding Operational Data {} is translated to {}", entry, biEntry);
- }
- for (Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry : source.getUpdatedConfigurationData()
- .entrySet()) {
- Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> biEntry = mappingService
- .toDataDom(entry);
- target.putConfigurationData(biEntry.getKey(), biEntry.getValue());
- LOG.debug("Update of Binding Configuration Data {} is translated to {}", entry, biEntry);
- }
- for (Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry : source.getUpdatedOperationalData()
- .entrySet()) {
- Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> biEntry = mappingService
- .toDataDom(entry);
- target.putOperationalData(biEntry.getKey(), biEntry.getValue());
- LOG.debug("Update of Binding Operational Data {} is translated to {}", entry, biEntry);
- }
-
- return target;
- }
-
- private org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction createDomToBindingTransaction(
- final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> source) {
- org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction target = baDataService
- .beginTransaction();
- for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedConfigurationData()) {
- try {
-
- InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
- target.removeConfigurationData(baEntry);
- } catch (DeserializationException e) {
- LOG.error("Ommiting from BA transaction: {}.", entry, e);
- }
- }
- for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedOperationalData()) {
- try {
-
- InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
- target.removeOperationalData(baEntry);
- } catch (DeserializationException e) {
- LOG.error("Ommiting from BA transaction: {}.", entry, e);
- }
- }
- for (Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> entry : source
- .getUpdatedConfigurationData().entrySet()) {
- try {
- InstanceIdentifier<?> baKey = mappingService.fromDataDom(entry.getKey());
- DataObject baData = mappingService.dataObjectFromDataDom(baKey, entry.getValue());
- target.putConfigurationData(baKey, baData);
- } catch (DeserializationException e) {
- LOG.error("Ommiting from BA transaction: {}.", entry.getKey(), e);
- }
- }
- for (Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> entry : source
- .getUpdatedOperationalData().entrySet()) {
- try {
-
- InstanceIdentifier<?> baKey = mappingService.fromDataDom(entry.getKey());
- DataObject baData = mappingService.dataObjectFromDataDom(baKey, entry.getValue());
- target.putOperationalData(baKey, baData);
- } catch (DeserializationException e) {
- LOG.error("Ommiting from BA transaction: {}.", entry.getKey(), e);
- }
- }
- return target;
- }
-
public org.opendaylight.controller.sal.core.api.data.DataProviderService getBiDataService() {
return biDataService;
}
protected void setDomDataService(
final org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService) {
this.biDataService = biDataService;
+ bindingToDomCommitHandler.setBindingIndependentDataService(this.biDataService);
}
public DataProviderService getBaDataService() {
protected void setBindingDataService(final DataProviderService baDataService) {
this.baDataService = baDataService;
+ domToBindingCommitHandler.setBindingAwareDataService(this.baDataService);
}
public RpcProviderRegistry getRpcRegistry() {
dataForwarding = true;
}
+ //WTF? - cycle references to biFwdManager - need to solve :-/
public void startRpcForwarding() {
+ checkNotNull(mappingService, "Unable to start Rpc forwarding. Reason: Mapping Service is not initialized properly!");
if (biRpcRegistry != null && baRpcRegistry instanceof RouteChangePublisher<?, ?>) {
checkState(!rpcForwarding, "Connector is already forwarding RPCs");
- final DomToBindingRpcForwardingManager biFwdManager = new DomToBindingRpcForwardingManager();
+ final DomToBindingRpcForwardingManager biFwdManager = new DomToBindingRpcForwardingManager(mappingService, biRpcRegistry, baRpcRegistry);
domToBindingRpcManager = baRpcRegistry.registerRouteChangeListener(biFwdManager);
biRpcRegistry.addRpcRegistrationListener(biFwdManager);
baRpcRegistryImpl = (RpcProviderRegistryImpl) baRpcRegistry;
baRpcRegistryImpl.registerRouterInstantiationListener(domToBindingRpcManager.getInstance());
baRpcRegistryImpl.registerGlobalRpcRegistrationListener(domToBindingRpcManager.getInstance());
+ biFwdManager.setRegistryImpl(baRpcRegistryImpl);
}
rpcForwarding = true;
}
public void startNotificationForwarding() {
checkState(!notificationForwarding, "Connector is already forwarding notifications.");
- if (baNotifyService != null && domNotificationService != null) {
- baNotifyService.registerInterestListener(new DomToBindingNotificationForwarder());
-
+ if (mappingService == null) {
+ LOG.warn("Unable to start Notification forwarding. Reason: Mapping Service is not initialized properly!");
+ } else if (baNotifyService == null) {
+ LOG.warn("Unable to start Notification forwarding. Reason: Binding Aware Notify Service is not initialized properly!");
+ } else if (domNotificationService == null) {
+ LOG.warn("Unable to start Notification forwarding. Reason: DOM Notification Service is not initialized properly!");
+ } else {
+ baNotifyService.registerInterestListener(
+ new DomToBindingNotificationForwarder(mappingService, baNotifyService, domNotificationService));
notificationForwarding = true;
}
}
protected void setMappingService(final BindingIndependentMappingService mappingService) {
this.mappingService = mappingService;
+ bindingToDomCommitHandler.setMappingService(this.mappingService);
+ domToBindingCommitHandler.setMappingService(this.mappingService);
}
@Override
}
- public <T extends RpcService> void onRpcRouterCreated(final Class<T> serviceType, final RpcRouter<T> router) {
-
- }
-
public void setDomRpcRegistry(final RpcProvisionRegistry registry) {
biRpcRegistry = registry;
}
@Override
public void close() throws Exception {
- if (baCommitHandlerRegistration != null) {
- baCommitHandlerRegistration.close();
- }
if (biCommitHandlerRegistration != null) {
biCommitHandlerRegistration.close();
}
-
- }
-
- private class DomToBindingTransaction implements
- DataCommitTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> {
-
- private final org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction backing;
- private final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> modification;
-
- public DomToBindingTransaction(
- final org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction backing,
- final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> modification) {
- super();
- this.backing = backing;
- this.modification = modification;
- bindingOpenedTransactions.put(backing.getIdentifier(), this);
- }
-
- @Override
- public DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> getModification() {
- return modification;
- }
-
- @Override
- public RpcResult<Void> rollback() throws IllegalStateException {
- // backing.cancel();
- return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
- }
-
- @Override
- public RpcResult<Void> finish() throws IllegalStateException {
- Future<RpcResult<TransactionStatus>> result = backing.commit();
- try {
- RpcResult<TransactionStatus> baResult = result.get();
- return Rpcs.<Void> getRpcResult(baResult.isSuccessful(), null, baResult.getErrors());
- } catch (InterruptedException e) {
- throw new IllegalStateException("", e);
- } catch (ExecutionException e) {
- throw new IllegalStateException("", e);
- }
- }
- }
-
- private class BindingToDomTransaction implements
- DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> {
-
- private final DataModificationTransaction backing;
- private final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification;
-
- public BindingToDomTransaction(final DataModificationTransaction backing,
- final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification) {
- this.backing = backing;
- this.modification = modification;
- domOpenedTransactions.put(backing.getIdentifier(), this);
- }
-
- @Override
- public DataModification<InstanceIdentifier<? extends DataObject>, DataObject> getModification() {
- return modification;
- }
-
- @Override
- public RpcResult<Void> finish() throws IllegalStateException {
- Future<RpcResult<TransactionStatus>> result = backing.commit();
- try {
- RpcResult<TransactionStatus> biResult = result.get();
- return Rpcs.<Void> getRpcResult(biResult.isSuccessful(), null, biResult.getErrors());
- } catch (InterruptedException e) {
- throw new IllegalStateException("", e);
- } catch (ExecutionException e) {
- throw new IllegalStateException("", e);
- } finally {
- domOpenedTransactions.remove(backing.getIdentifier());
- }
- }
-
- @Override
- public RpcResult<Void> rollback() throws IllegalStateException {
- domOpenedTransactions.remove(backing.getIdentifier());
- return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());
- }
- }
-
- private class BindingToDomCommitHandler implements
- DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> {
-
- @Override
- public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> requestCommit(
- final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> bindingTransaction) {
-
- /**
- * Transaction was created as DOM transaction, in that case we do
- * not need to forward it back.
- */
- if (bindingOpenedTransactions.containsKey(bindingTransaction.getIdentifier())) {
-
- return CommitHandlerTransactions.allwaysSuccessfulTransaction(bindingTransaction);
- }
- DataModificationTransaction domTransaction = createBindingToDomTransaction(bindingTransaction);
- BindingToDomTransaction wrapped = new BindingToDomTransaction(domTransaction, bindingTransaction);
- LOG.trace("Forwarding Binding Transaction: {} as DOM Transaction: {} .",
- bindingTransaction.getIdentifier(), domTransaction.getIdentifier());
- return wrapped;
- }
- }
-
- private class DomToBindingCommitHandler implements //
- RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>>, //
- DataCommitHandler<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> {
-
- @Override
- public void onRegister(
- final DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
-
- mappingService.toDataDom(registration
- .getPath());
-
- }
-
- @Override
- public void onUnregister(
- final DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
- // NOOP for now
- // FIXME: do registration based on only active commit handlers.
- }
-
- @Override
- public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> requestCommit(
- final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> domTransaction) {
- Object identifier = domTransaction.getIdentifier();
-
- /**
- * We checks if the transcation was originated in this mapper. If it
- * was originated in this mapper we are returing allways success
- * commit hanlder to prevent creating loop in two-phase commit and
- * duplicating data.
- */
- if (domOpenedTransactions.containsKey(identifier)) {
- return CommitHandlerTransactions.allwaysSuccessfulTransaction(domTransaction);
- }
-
- org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction baTransaction = createDomToBindingTransaction(domTransaction);
- DomToBindingTransaction forwardedTransaction = new DomToBindingTransaction(baTransaction, domTransaction);
- LOG.trace("Forwarding DOM Transaction: {} as Binding Transaction: {}.", domTransaction.getIdentifier(),
- baTransaction.getIdentifier());
- return forwardedTransaction;
- }
- }
-
- /**
- * Manager responsible for instantiating forwarders responsible for
- * forwarding of RPC invocations from DOM Broker to Binding Aware Broker
- *
- */
- private class DomToBindingRpcForwardingManager implements
- RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>, RouterInstantiationListener,
- GlobalRpcRegistrationListener, RpcRegistrationListener {
-
- private final Map<Class<? extends RpcService>, DomToBindingRpcForwarder> forwarders = new WeakHashMap<>();
- private RpcProviderRegistryImpl registryImpl;
-
- public RpcProviderRegistryImpl getRegistryImpl() {
- return registryImpl;
- }
-
- public void setRegistryImpl(final RpcProviderRegistryImpl registryImpl) {
- this.registryImpl = registryImpl;
- }
-
- @Override
- public void onGlobalRpcRegistered(final Class<? extends RpcService> cls) {
- getRpcForwarder(cls, null).registerToDOMBroker();
- }
-
- @Override
- public void onGlobalRpcUnregistered(final Class<? extends RpcService> cls) {
- // NOOP
- }
-
- @Override
- public void onRpcRouterCreated(final RpcRouter<?> router) {
- Class<? extends BaseIdentity> ctx = router.getContexts().iterator().next();
- getRpcForwarder(router.getServiceType(), ctx);
- }
-
- @Override
- public void onRouteChange(final RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> change) {
- for (Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry : change.getAnnouncements().entrySet()) {
- bindingRoutesAdded(entry);
- }
- }
-
- private void bindingRoutesAdded(final Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry) {
- Class<? extends BaseIdentity> context = entry.getKey().getRoutingContext();
- Class<? extends RpcService> service = entry.getKey().getRpcService();
- if (context != null) {
- getRpcForwarder(service, context).registerPaths(context, service, entry.getValue());
- }
- }
-
- private DomToBindingRpcForwarder getRpcForwarder(final Class<? extends RpcService> service,
- final Class<? extends BaseIdentity> context) {
- DomToBindingRpcForwarder potential = forwarders.get(service);
- if (potential != null) {
- return potential;
- }
- if (context == null) {
- potential = new DomToBindingRpcForwarder(service);
- } else {
- potential = new DomToBindingRpcForwarder(service, context);
- }
-
- forwarders.put(service, potential);
- return potential;
- }
-
- @Override
- public void onRpcImplementationAdded(final QName name) {
-
- final Optional<Class<? extends RpcService>> rpcInterface = mappingService.getRpcServiceClassFor(
- name.getNamespace().toString(), name.getFormattedRevision());
- if (rpcInterface.isPresent()) {
- getRpcForwarder(rpcInterface.get(), null).registerToBindingBroker();
- }
- }
-
- @Override
- public void onRpcImplementationRemoved(final QName name) {
-
- }
- }
-
- private class DomToBindingRpcForwarder implements RpcImplementation, InvocationHandler {
-
- private final Set<QName> supportedRpcs;
- private final WeakReference<Class<? extends RpcService>> rpcServiceType;
- private Set<org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration> registrations;
- private final Map<QName, RpcInvocationStrategy> strategiesByQName = new HashMap<>();
- private final WeakHashMap<Method, RpcInvocationStrategy> strategiesByMethod = new WeakHashMap<>();
- private final RpcService proxy;
- private ObjectRegistration<?> forwarderRegistration;
- private boolean registrationInProgress = false;
-
- public DomToBindingRpcForwarder(final Class<? extends RpcService> service) {
- this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
- this.supportedRpcs = mappingService.getRpcQNamesFor(service);
-
- Class<?> cls = rpcServiceType.get();
- ClassLoader clsLoader = cls.getClassLoader();
- proxy =(RpcService) Proxy.newProxyInstance(clsLoader, new Class<?>[] { cls }, this);
- createStrategies();
- }
-
- /**
- * Constructor for Routed RPC Forwareder.
- *
- * @param service
- * @param context
- */
- public DomToBindingRpcForwarder(final Class<? extends RpcService> service,
- final Class<? extends BaseIdentity> context) {
- this(service);
- Builder<RoutedRpcRegistration> registrationsBuilder = ImmutableSet
- .<org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration> builder();
- try {
- for (QName rpc : supportedRpcs) {
- registrationsBuilder.add(biRpcRegistry.addRoutedRpcImplementation(rpc, this));
- }
- createDefaultDomForwarder();
- } catch (Exception e) {
- LOG.error("Could not forward Rpcs of type {}", service.getName(), e);
- }
- registrations = registrationsBuilder.build();
- }
-
-
-
- private void createStrategies() {
- try {
- for (QName rpc : supportedRpcs) {
- RpcInvocationStrategy strategy = createInvocationStrategy(rpc, rpcServiceType.get());
- strategiesByMethod.put(strategy.targetMethod, strategy);
- strategiesByQName.put(rpc, strategy);
- }
- } catch (Exception e) {
- LOG.error("Could not forward Rpcs of type {}", rpcServiceType.get(), e);
- }
-
- }
-
- /**
- * Registers RPC Forwarder to DOM Broker,
- * this means Binding Aware Broker has implementation of RPC
- * which is registered to it.
- *
- * If RPC Forwarder was previously registered to DOM Broker
- * or to Bidning Broker this method is noop to prevent
- * creating forwarding loop.
- *
- */
- public void registerToDOMBroker() {
- if(!registrationInProgress && forwarderRegistration == null) {
- registrationInProgress = true;
- CompositeObjectRegistrationBuilder<DomToBindingRpcForwarder> builder = CompositeObjectRegistration.builderFor(this);
- try {
- for (QName rpc : supportedRpcs) {
- builder.add(biRpcRegistry.addRpcImplementation(rpc, this));
- }
- } catch (Exception e) {
- LOG.error("Could not forward Rpcs of type {}", rpcServiceType.get(), e);
- }
- this.forwarderRegistration = builder.toInstance();
- registrationInProgress = false;
- }
- }
-
-
- public void registerPaths(final Class<? extends BaseIdentity> context,
- final Class<? extends RpcService> service, final Set<InstanceIdentifier<?>> set) {
- QName ctx = BindingReflections.findQName(context);
- for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
- toDOMInstanceIdentifier)) {
- for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) {
- reg.registerPath(ctx, path);
- }
- }
- }
-
- @Override
- public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
- if (EQUALS_METHOD.equals(method)) {
- return false;
- }
- RpcInvocationStrategy strategy = strategiesByMethod.get(method);
- checkState(strategy != null);
- checkArgument(args.length <= 2);
- if (args.length == 1) {
- checkArgument(args[0] instanceof DataObject);
- return strategy.forwardToDomBroker((DataObject) args[0]);
- }
- return strategy.forwardToDomBroker(null);
- }
-
- public void removePaths(final Class<? extends BaseIdentity> context, final Class<? extends RpcService> service,
- final Set<InstanceIdentifier<?>> set) {
- QName ctx = BindingReflections.findQName(context);
- for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
- toDOMInstanceIdentifier)) {
- for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) {
- reg.unregisterPath(ctx, path);
- }
- }
- }
-
- @Override
- public Set<QName> getSupportedRpcs() {
- return supportedRpcs;
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- public void createDefaultDomForwarder() {
- if (baRpcRegistryImpl != null) {
- Class<?> cls = rpcServiceType.get();
- ClassLoader clsLoader = cls.getClassLoader();
- RpcService proxy = (RpcService) Proxy.newProxyInstance(clsLoader, new Class<?>[] { cls }, this);
-
- RpcRouter rpcRouter = baRpcRegistryImpl.getRpcRouter(rpcServiceType.get());
- rpcRouter.registerDefaultService(proxy);
- }
- }
-
- @Override
- public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(final QName rpc, final CompositeNode domInput) {
- checkArgument(rpc != null);
- checkArgument(domInput != null);
-
- Class<? extends RpcService> rpcType = rpcServiceType.get();
- checkState(rpcType != null);
- RpcService rpcService = baRpcRegistry.getRpcService(rpcType);
- checkState(rpcService != null);
- CompositeNode domUnwrappedInput = domInput.getFirstCompositeByName(QName.create(rpc, "input"));
-
- try {
- return Futures.immediateFuture(resolveInvocationStrategy(rpc).invokeOn(rpcService, domUnwrappedInput));
- } catch (Exception e) {
- return Futures.immediateFailedFuture(e);
- }
- }
-
- private RpcInvocationStrategy resolveInvocationStrategy(final QName rpc) {
- return strategiesByQName.get(rpc);
- }
-
- private RpcInvocationStrategy createInvocationStrategy(final QName rpc,
- final Class<? extends RpcService> rpcType) throws Exception {
- return ClassLoaderUtils.withClassLoader(rpcType.getClassLoader(), new Callable<RpcInvocationStrategy>() {
- @Override
- public RpcInvocationStrategy call() throws Exception {
- String methodName = BindingMapping.getMethodName(rpc);
- Method targetMethod = null;
- for (Method possibleMethod : rpcType.getMethods()) {
- if (possibleMethod.getName().equals(methodName)
- && BindingReflections.isRpcMethod(possibleMethod)) {
- targetMethod = possibleMethod;
- break;
- }
- }
- checkState(targetMethod != null, "Rpc method not found");
- return new RpcInvocationStrategy(rpc,targetMethod, mappingService, biRpcRegistry);
- }
-
- });
- }
-
- /**
- * Registers RPC Forwarder to Binding Broker,
- * this means DOM Broekr has implementation of RPC
- * which is registered to it.
- *
- * If RPC Forwarder was previously registered to DOM Broker
- * or to Bidning Broker this method is noop to prevent
- * creating forwarding loop.
- *
- */
- public void registerToBindingBroker() {
- if(!registrationInProgress && forwarderRegistration == null) {
- try {
- registrationInProgress = true;
- this.forwarderRegistration = baRpcRegistry.addRpcImplementation((Class)rpcServiceType.get(), proxy);
- } catch (Exception e) {
- LOG.error("Unable to forward RPCs for {}",rpcServiceType.get(),e);
- } finally {
- registrationInProgress = false;
- }
- }
- }
}
public boolean isRpcForwarding() {
public void setDomNotificationService(final NotificationPublishService domService) {
this.domNotificationService = domService;
}
-
- private class DomToBindingNotificationForwarder implements NotificationInterestListener, NotificationListener {
-
- private final ConcurrentMap<QName, WeakReference<Class<? extends Notification>>> notifications = new ConcurrentHashMap<>();
- private final Set<QName> supportedNotifications = new HashSet<>();
-
- @Override
- public Set<QName> getSupportedNotifications() {
- return Collections.unmodifiableSet(supportedNotifications);
- }
-
- @Override
- public void onNotification(final CompositeNode notification) {
- QName qname = notification.getNodeType();
- WeakReference<Class<? extends Notification>> potential = notifications.get(qname);
- if (potential != null) {
- Class<? extends Notification> potentialClass = potential.get();
- if (potentialClass != null) {
- final DataContainer baNotification = mappingService.dataObjectFromDataDom(potentialClass,
- notification);
-
- if (baNotification instanceof Notification) {
- baNotifyService.publish((Notification) baNotification);
- }
- }
- }
- }
-
- @Override
- public void onNotificationSubscribtion(final Class<? extends Notification> notificationType) {
- QName qname = BindingReflections.findQName(notificationType);
- if (qname != null) {
- WeakReference<Class<? extends Notification>> already = notifications.putIfAbsent(qname,
- new WeakReference<Class<? extends Notification>>(notificationType));
- if (already == null) {
- domNotificationService.addNotificationListener(qname, this);
- supportedNotifications.add(qname);
- }
- }
- }
- }
}
--- /dev/null
+package org.opendaylight.controller.sal.binding.impl.connect.dom;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.sal.common.util.CommitHandlerTransactions;
+import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.core.api.data.DataProviderService;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @deprecated This is part of the legacy DataBrokerService
+ */
+@Deprecated
+class BindingToDomCommitHandler implements
+ DataCommitHandler<InstanceIdentifier<? extends DataObject>, DataObject> {
+
+ private final Logger LOG = LoggerFactory.getLogger(BindingToDomCommitHandler.class);
+
+ private final ConcurrentMap<Object, DomToBindingTransaction> bindingOpenedTransactions;
+ private final ConcurrentMap<Object, BindingToDomTransaction> domOpenedTransactions;
+ private org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService;
+ private BindingIndependentMappingService mappingService;
+
+ BindingToDomCommitHandler(final ConcurrentMap<Object, DomToBindingTransaction> bindingOpenedTransactions,
+ final ConcurrentMap<Object, BindingToDomTransaction> domOpenedTransactions) {
+ this.bindingOpenedTransactions = bindingOpenedTransactions;
+ this.domOpenedTransactions = domOpenedTransactions;
+ }
+
+ public void setBindingIndependentDataService(final DataProviderService biDataService) {
+ this.biDataService = biDataService;
+ }
+
+ public void setMappingService(final BindingIndependentMappingService mappingService) {
+ this.mappingService = mappingService;
+ }
+
+ @Override
+ public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> requestCommit(
+ final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> bindingTransaction) {
+
+ /**
+ * Transaction was created as DOM transaction, in that case we do
+ * not need to forward it back.
+ */
+ if (bindingOpenedTransactions.containsKey(bindingTransaction.getIdentifier())) {
+ return CommitHandlerTransactions.allwaysSuccessfulTransaction(bindingTransaction);
+ }
+ DataModificationTransaction domTransaction = createBindingToDomTransaction(bindingTransaction);
+ BindingToDomTransaction wrapped = new BindingToDomTransaction(domTransaction, bindingTransaction, domOpenedTransactions);
+ LOG.trace("Forwarding Binding Transaction: {} as DOM Transaction: {} .",
+ bindingTransaction.getIdentifier(), domTransaction.getIdentifier());
+ return wrapped;
+ }
+
+ private DataModificationTransaction createBindingToDomTransaction(
+ final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> source) {
+ if (biDataService == null) {
+ final String msg = "Binding Independent Service is not initialized correctly! Binding to DOM Transaction cannot be created for ";
+ LOG.error(msg + "{}", source);
+ throw new IllegalStateException(msg + source);
+ }
+ if (mappingService == null) {
+ final String msg = "Mapping Service is not initialized correctly! Binding to DOM Transaction cannot be created for ";
+ LOG.error(msg + "{}", source);
+ throw new IllegalStateException(msg + source);
+ }
+ DataModificationTransaction target = biDataService.beginTransaction();
+ LOG.debug("Created DOM Transaction {} for {},", target.getIdentifier(), source.getIdentifier());
+ for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedConfigurationData()) {
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
+ target.removeConfigurationData(biEntry);
+ LOG.debug("Delete of Binding Configuration Data {} is translated to {}", entry, biEntry);
+ }
+ for (InstanceIdentifier<? extends DataObject> entry : source.getRemovedOperationalData()) {
+ org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biEntry = mappingService.toDataDom(entry);
+ target.removeOperationalData(biEntry);
+ LOG.debug("Delete of Binding Operational Data {} is translated to {}", entry, biEntry);
+ }
+ for (Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry : source.getUpdatedConfigurationData()
+ .entrySet()) {
+ Map.Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> biEntry = mappingService
+ .toDataDom(entry);
+ target.putConfigurationData(biEntry.getKey(), biEntry.getValue());
+ LOG.debug("Update of Binding Configuration Data {} is translated to {}", entry, biEntry);
+ }
+ for (Map.Entry<InstanceIdentifier<? extends DataObject>, DataObject> entry : source.getUpdatedOperationalData()
+ .entrySet()) {
+ Map.Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> biEntry = mappingService
+ .toDataDom(entry);
+ target.putOperationalData(biEntry.getKey(), biEntry.getValue());
+ LOG.debug("Update of Binding Operational Data {} is translated to {}", entry, biEntry);
+ }
+ return target;
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.impl.connect.dom;
+
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+class BindingToDomTransaction implements
+ DataCommitHandler.DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject> {
+
+ private final DataModificationTransaction backing;
+ private final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification;
+ private final ConcurrentMap<Object, BindingToDomTransaction> domOpenedTransactions;
+
+ public BindingToDomTransaction(final DataModificationTransaction backing,
+ final DataModification<InstanceIdentifier<? extends DataObject>, DataObject> modification,
+ ConcurrentMap<Object, BindingToDomTransaction> domOpenedTransactions) {
+ this.backing = backing;
+ this.modification = modification;
+ this.domOpenedTransactions = domOpenedTransactions;
+ this.domOpenedTransactions.put(backing.getIdentifier(), this);
+ }
+
+ @Override
+ public DataModification<InstanceIdentifier<? extends DataObject>, DataObject> getModification() {
+ return modification;
+ }
+
+ @Override
+ public RpcResult<Void> finish() throws IllegalStateException {
+ Future<RpcResult<TransactionStatus>> result = backing.commit();
+ try {
+ RpcResult<TransactionStatus> biResult = result.get();
+ domOpenedTransactions.remove(backing.getIdentifier());
+ return RpcResultBuilder.<Void> status(biResult.isSuccessful())
+ .withRpcErrors(biResult.getErrors()).build();
+ } catch (InterruptedException e) {
+ throw new IllegalStateException("", e);
+ } catch (ExecutionException e) {
+ throw new IllegalStateException("", e);
+ } finally {
+ domOpenedTransactions.remove(backing.getIdentifier());
+ }
+ }
+
+ @Override
+ public RpcResult<Void> rollback() throws IllegalStateException {
+ domOpenedTransactions.remove(backing.getIdentifier());
+ return RpcResultBuilder.<Void> success().build();
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.impl.connect.dom;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.controller.md.sal.common.api.RegistrationListener;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandlerRegistration;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.sal.common.util.CommitHandlerTransactions;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @deprecated This is part of the legacy DataBrokerService
+ */
+@Deprecated
+class DomToBindingCommitHandler implements //
+ RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject>>, //
+ DataCommitHandler<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> {
+
+ private final Logger LOG = LoggerFactory.getLogger(DomToBindingCommitHandler.class);
+
+ private final ConcurrentMap<Object, DomToBindingTransaction> bindingOpenedTransactions;
+ private final ConcurrentMap<Object, BindingToDomTransaction> domOpenedTransactions;
+
+ DomToBindingCommitHandler(final ConcurrentMap<Object, DomToBindingTransaction> bindingOpenedTransactions,
+ final ConcurrentMap<Object, BindingToDomTransaction> domOpenedTransactions) {
+ this.bindingOpenedTransactions = bindingOpenedTransactions;
+ this.domOpenedTransactions = domOpenedTransactions;
+ }
+
+ private DataProviderService baDataService;
+ private BindingIndependentMappingService mappingService;
+
+ public void setBindingAwareDataService(final DataProviderService baDataService) {
+ this.baDataService = baDataService;
+ }
+
+ public void setMappingService(final BindingIndependentMappingService mappingService) {
+ this.mappingService = mappingService;
+ }
+
+ @Override
+ public void onRegister(final DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
+ mappingService.toDataDom(registration.getPath());
+ }
+
+ @Override
+ public void onUnregister(
+ final DataCommitHandlerRegistration<InstanceIdentifier<? extends DataObject>, DataObject> registration) {
+ // NOOP for now
+ // FIXME: do registration based on only active commit handlers.
+ }
+
+ @Override
+ public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> requestCommit(
+ final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> domTransaction) {
+ Object identifier = domTransaction.getIdentifier();
+
+ /**
+ * We checks if the transcation was originated in this mapper. If it
+ * was originated in this mapper we are returing allways success
+ * commit hanlder to prevent creating loop in two-phase commit and
+ * duplicating data.
+ */
+ if (domOpenedTransactions.containsKey(identifier)) {
+ return CommitHandlerTransactions.allwaysSuccessfulTransaction(domTransaction);
+ }
+
+ org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction baTransaction = createDomToBindingTransaction(domTransaction);
+ DomToBindingTransaction forwardedTransaction = new DomToBindingTransaction(baTransaction, domTransaction, bindingOpenedTransactions);
+ LOG.trace("Forwarding DOM Transaction: {} as Binding Transaction: {}.", domTransaction.getIdentifier(),
+ baTransaction.getIdentifier());
+ return forwardedTransaction;
+ }
+
+ private org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction createDomToBindingTransaction(
+ final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> source) {
+ if (baDataService == null) {
+ final String msg = "Binding Aware Service is not initialized correctly! DOM to Binding Transaction cannot be created for ";
+ LOG.error(msg + "{}", source);
+ throw new IllegalStateException(msg + source);
+ }
+ if (mappingService == null) {
+ final String msg = "Mapping Service is not initialized correctly! DOM to Binding Transaction cannot be created for ";
+ LOG.error(msg + "{}", source);
+ throw new IllegalStateException(msg + source);
+ }
+
+ org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction target = baDataService
+ .beginTransaction();
+ for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedConfigurationData()) {
+ try {
+
+ InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
+ target.removeConfigurationData(baEntry);
+ } catch (DeserializationException e) {
+ LOG.error("Ommiting from BA transaction: {}.", entry, e);
+ }
+ }
+ for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier entry : source.getRemovedOperationalData()) {
+ try {
+
+ InstanceIdentifier<?> baEntry = mappingService.fromDataDom(entry);
+ target.removeOperationalData(baEntry);
+ } catch (DeserializationException e) {
+ LOG.error("Ommiting from BA transaction: {}.", entry, e);
+ }
+ }
+ for (Map.Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> entry : source
+ .getUpdatedConfigurationData().entrySet()) {
+ try {
+ InstanceIdentifier<?> baKey = mappingService.fromDataDom(entry.getKey());
+ DataObject baData = mappingService.dataObjectFromDataDom(baKey, entry.getValue());
+ target.putConfigurationData(baKey, baData);
+ } catch (DeserializationException e) {
+ LOG.error("Ommiting from BA transaction: {}.", entry.getKey(), e);
+ }
+ }
+ for (Map.Entry<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> entry : source
+ .getUpdatedOperationalData().entrySet()) {
+ try {
+
+ InstanceIdentifier<?> baKey = mappingService.fromDataDom(entry.getKey());
+ DataObject baData = mappingService.dataObjectFromDataDom(baKey, entry.getValue());
+ target.putOperationalData(baKey, baData);
+ } catch (DeserializationException e) {
+ LOG.error("Ommiting from BA transaction: {}.", entry.getKey(), e);
+ }
+ }
+ return target;
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.impl.connect.dom;
+
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
+import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+
+class DomToBindingNotificationForwarder implements NotificationProviderService.NotificationInterestListener,
+ NotificationListener {
+
+ private final ConcurrentMap<QName, WeakReference<Class<? extends Notification>>> notifications = new ConcurrentHashMap<>();
+ private final Set<QName> supportedNotifications = new HashSet<>();
+
+ private final BindingIndependentMappingService mappingService;
+ private final NotificationProviderService baNotifyService;
+ private final NotificationPublishService domNotificationService;
+
+ DomToBindingNotificationForwarder(final BindingIndependentMappingService mappingService, final NotificationProviderService baNotifyService,
+ final NotificationPublishService domNotificationService) {
+ this.mappingService = mappingService;
+ this.baNotifyService = baNotifyService;
+ this.domNotificationService = domNotificationService;
+ }
+
+ @Override
+ public Set<QName> getSupportedNotifications() {
+ return Collections.unmodifiableSet(supportedNotifications);
+ }
+
+ @Override
+ public void onNotification(final CompositeNode notification) {
+ QName qname = notification.getNodeType();
+ WeakReference<Class<? extends Notification>> potential = notifications.get(qname);
+ if (potential != null) {
+ Class<? extends Notification> potentialClass = potential.get();
+ if (potentialClass != null) {
+ final DataContainer baNotification = mappingService.dataObjectFromDataDom(potentialClass,
+ notification);
+
+ if (baNotification instanceof Notification) {
+ baNotifyService.publish((Notification) baNotification);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onNotificationSubscribtion(final Class<? extends Notification> notificationType) {
+ QName qname = BindingReflections.findQName(notificationType);
+ if (qname != null) {
+ WeakReference<Class<? extends Notification>> already = notifications.putIfAbsent(qname,
+ new WeakReference<Class<? extends Notification>>(notificationType));
+ if (already == null) {
+ domNotificationService.addNotificationListener(qname, this);
+ supportedNotifications.add(qname);
+ }
+ }
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.impl.connect.dom;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.Callable;
+
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
+import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
+import org.opendaylight.yangtools.concepts.CompositeObjectRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+class DomToBindingRpcForwarder implements RpcImplementation, InvocationHandler {
+
+ private final Logger LOG = LoggerFactory.getLogger(DomToBindingRpcForwarder.class);
+
+ private final Set<QName> supportedRpcs;
+ private final WeakReference<Class<? extends RpcService>> rpcServiceType;
+ private Set<org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration> registrations;
+ private final Map<QName, RpcInvocationStrategy> strategiesByQName = new HashMap<>();
+ private final WeakHashMap<Method, RpcInvocationStrategy> strategiesByMethod = new WeakHashMap<>();
+ private final RpcService proxy;
+ private ObjectRegistration<?> forwarderRegistration;
+ private boolean registrationInProgress = false;
+
+ private final RpcProvisionRegistry biRpcRegistry;
+ private final RpcProviderRegistry baRpcRegistry;
+ private final RpcProviderRegistryImpl baRpcRegistryImpl;
+
+ private final Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier> toDOMInstanceIdentifier;
+
+ private final static Method EQUALS_METHOD;
+
+ static {
+ try {
+ EQUALS_METHOD = Object.class.getMethod("equals", Object.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public DomToBindingRpcForwarder(final Class<? extends RpcService> service, final BindingIndependentMappingService mappingService,
+ final RpcProvisionRegistry biRpcRegistry, final RpcProviderRegistry baRpcRegistry, final RpcProviderRegistryImpl registryImpl) {
+ this.rpcServiceType = new WeakReference<Class<? extends RpcService>>(service);
+ this.supportedRpcs = mappingService.getRpcQNamesFor(service);
+
+ toDOMInstanceIdentifier = new Function<InstanceIdentifier<?>, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier>() {
+
+ @Override
+ public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier apply(final InstanceIdentifier<?> input) {
+ return mappingService.toDataDom(input);
+ }
+ };
+
+ this.biRpcRegistry = biRpcRegistry;
+ this.baRpcRegistry = baRpcRegistry;
+ this.baRpcRegistryImpl = registryImpl;
+
+ Class<?> cls = rpcServiceType.get();
+ ClassLoader clsLoader = cls.getClassLoader();
+ proxy =(RpcService) Proxy.newProxyInstance(clsLoader, new Class<?>[] { cls }, this);
+ createStrategies(mappingService);
+ }
+
+ /**
+ * Constructor for Routed RPC Forwareder.
+ *
+ * @param service
+ * @param context
+ * @param registryImpl
+ */
+ public DomToBindingRpcForwarder(final Class<? extends RpcService> service,
+ final Class<? extends BaseIdentity> context, final BindingIndependentMappingService mappingService,
+ final RpcProvisionRegistry biRpcRegistry, final RpcProviderRegistry baRpcRegistry, final RpcProviderRegistryImpl registryImpl) {
+ this(service, mappingService, biRpcRegistry, baRpcRegistry,registryImpl);
+
+ final ImmutableSet.Builder<Broker.RoutedRpcRegistration> registrationsBuilder = ImmutableSet.builder();
+ try {
+ for (QName rpc : supportedRpcs) {
+ registrationsBuilder.add(biRpcRegistry.addRoutedRpcImplementation(rpc, this));
+ }
+ createDefaultDomForwarder();
+ } catch (Exception e) {
+ LOG.error("Could not forward Rpcs of type {}", service.getName(), e);
+ }
+ registrations = registrationsBuilder.build();
+ }
+
+
+
+ private void createStrategies(final BindingIndependentMappingService mappingService) {
+ try {
+ for (QName rpc : supportedRpcs) {
+ RpcInvocationStrategy strategy = createInvocationStrategy(rpc, rpcServiceType.get(), mappingService);
+ strategiesByMethod.put(strategy.targetMethod, strategy);
+ strategiesByQName.put(rpc, strategy);
+ }
+ } catch (Exception e) {
+ LOG.error("Could not forward Rpcs of type {}", rpcServiceType.get(), e);
+ }
+
+ }
+
+ /**
+ * Registers RPC Forwarder to DOM Broker,
+ * this means Binding Aware Broker has implementation of RPC
+ * which is registered to it.
+ *
+ * If RPC Forwarder was previously registered to DOM Broker
+ * or to Bidning Broker this method is noop to prevent
+ * creating forwarding loop.
+ *
+ */
+ public void registerToDOMBroker() {
+ if(!registrationInProgress && forwarderRegistration == null) {
+ registrationInProgress = true;
+ CompositeObjectRegistration.CompositeObjectRegistrationBuilder<DomToBindingRpcForwarder> builder = CompositeObjectRegistration.builderFor(this);
+ try {
+ for (QName rpc : supportedRpcs) {
+ builder.add(biRpcRegistry.addRpcImplementation(rpc, this));
+ }
+ } catch (Exception e) {
+ LOG.error("Could not forward Rpcs of type {}", rpcServiceType.get(), e);
+ }
+ this.forwarderRegistration = builder.toInstance();
+ registrationInProgress = false;
+ }
+ }
+
+
+ public void registerPaths(final Class<? extends BaseIdentity> context,
+ final Class<? extends RpcService> service, final Set<InstanceIdentifier<?>> set) {
+ QName ctx = BindingReflections.findQName(context);
+ for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
+ toDOMInstanceIdentifier)) {
+ for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) {
+ reg.registerPath(ctx, path);
+ }
+ }
+ }
+
+ @Override
+ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+ if (EQUALS_METHOD.equals(method)) {
+ return false;
+ }
+ RpcInvocationStrategy strategy = strategiesByMethod.get(method);
+ checkState(strategy != null);
+ checkArgument(args.length <= 2);
+ if (args.length == 1) {
+ checkArgument(args[0] instanceof DataObject);
+ return strategy.forwardToDomBroker((DataObject) args[0]);
+ }
+ return strategy.forwardToDomBroker(null);
+ }
+
+ public void removePaths(final Class<? extends BaseIdentity> context, final Class<? extends RpcService> service,
+ final Set<InstanceIdentifier<?>> set) {
+ QName ctx = BindingReflections.findQName(context);
+ for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier path : FluentIterable.from(set).transform(
+ toDOMInstanceIdentifier)) {
+ for (org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration reg : registrations) {
+ reg.unregisterPath(ctx, path);
+ }
+ }
+ }
+
+ @Override
+ public Set<QName> getSupportedRpcs() {
+ return supportedRpcs;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public void createDefaultDomForwarder() {
+ if (baRpcRegistryImpl != null) {
+ Class<?> cls = rpcServiceType.get();
+ ClassLoader clsLoader = cls.getClassLoader();
+ RpcService proxy = (RpcService) Proxy.newProxyInstance(clsLoader, new Class<?>[] { cls }, this);
+
+ RpcRouter rpcRouter = baRpcRegistryImpl.getRpcRouter(rpcServiceType.get());
+ rpcRouter.registerDefaultService(proxy);
+ }
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(final QName rpc, final CompositeNode domInput) {
+ checkArgument(rpc != null);
+ checkArgument(domInput != null);
+
+ Class<? extends RpcService> rpcType = rpcServiceType.get();
+ checkState(rpcType != null);
+ RpcService rpcService = baRpcRegistry.getRpcService(rpcType);
+ checkState(rpcService != null);
+ CompositeNode domUnwrappedInput = domInput.getFirstCompositeByName(QName.create(rpc, "input"));
+
+ try {
+ return Futures.immediateFuture(resolveInvocationStrategy(rpc).invokeOn(rpcService, domUnwrappedInput));
+ } catch (Exception e) {
+ return Futures.immediateFailedFuture(e);
+ }
+ }
+
+ private RpcInvocationStrategy resolveInvocationStrategy(final QName rpc) {
+ return strategiesByQName.get(rpc);
+ }
+
+ private RpcInvocationStrategy createInvocationStrategy(final QName rpc,
+ final Class<? extends RpcService> rpcType, final BindingIndependentMappingService mappingService) throws Exception {
+ return ClassLoaderUtils.withClassLoader(rpcType.getClassLoader(), new Callable<RpcInvocationStrategy>() {
+ @Override
+ public RpcInvocationStrategy call() throws Exception {
+ String methodName = BindingMapping.getMethodName(rpc);
+ Method targetMethod = null;
+ for (Method possibleMethod : rpcType.getMethods()) {
+ if (possibleMethod.getName().equals(methodName)
+ && BindingReflections.isRpcMethod(possibleMethod)) {
+ targetMethod = possibleMethod;
+ break;
+ }
+ }
+ checkState(targetMethod != null, "Rpc method not found");
+ return new RpcInvocationStrategy(rpc, targetMethod, mappingService, biRpcRegistry);
+ }
+
+ });
+ }
+
+ /**
+ * Registers RPC Forwarder to Binding Broker,
+ * this means DOM Broekr has implementation of RPC
+ * which is registered to it.
+ *
+ * If RPC Forwarder was previously registered to DOM Broker
+ * or to Bidning Broker this method is noop to prevent
+ * creating forwarding loop.
+ *
+ */
+ public void registerToBindingBroker() {
+ if(!registrationInProgress && forwarderRegistration == null) {
+ try {
+ registrationInProgress = true;
+ this.forwarderRegistration = baRpcRegistry.addRpcImplementation((Class)rpcServiceType.get(), proxy);
+ } catch (Exception e) {
+ LOG.error("Unable to forward RPCs for {}",rpcServiceType.get(),e);
+ } finally {
+ registrationInProgress = false;
+ }
+ }
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.impl.connect.dom;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
+import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
+import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+
+import com.google.common.base.Optional;
+
+/**
+ * Manager responsible for instantiating forwarders responsible for
+ * forwarding of RPC invocations from DOM Broker to Binding Aware Broker
+ *
+ */
+class DomToBindingRpcForwardingManager implements
+ RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>,
+ RpcProviderRegistryImpl.RouterInstantiationListener,
+ RpcProviderRegistryImpl.GlobalRpcRegistrationListener, RpcRegistrationListener {
+
+ private final Map<Class<? extends RpcService>, DomToBindingRpcForwarder> forwarders = new WeakHashMap<>();
+ private final BindingIndependentMappingService mappingService;
+ private final RpcProvisionRegistry biRpcRegistry;
+ private final RpcProviderRegistry baRpcRegistry;
+ private RpcProviderRegistryImpl registryImpl;
+
+ DomToBindingRpcForwardingManager(final BindingIndependentMappingService mappingService, final RpcProvisionRegistry biRpcRegistry,
+ final RpcProviderRegistry baRpcRegistry) {
+ this.mappingService = mappingService;
+ this.biRpcRegistry = biRpcRegistry;
+ this.baRpcRegistry = baRpcRegistry;
+ }
+
+ public RpcProviderRegistryImpl getRegistryImpl() {
+ return registryImpl;
+ }
+
+ public void setRegistryImpl(final RpcProviderRegistryImpl registryImpl) {
+ this.registryImpl = registryImpl;
+ }
+
+ @Override
+ public void onGlobalRpcRegistered(final Class<? extends RpcService> cls) {
+ getRpcForwarder(cls, null).registerToDOMBroker();
+ }
+
+ @Override
+ public void onGlobalRpcUnregistered(final Class<? extends RpcService> cls) {
+ // NOOP
+ }
+
+ @Override
+ public void onRpcRouterCreated(final RpcRouter<?> router) {
+ Class<? extends BaseIdentity> ctx = router.getContexts().iterator().next();
+ getRpcForwarder(router.getServiceType(), ctx);
+ }
+
+ @Override
+ public void onRouteChange(final RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> change) {
+ for (Map.Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry : change.getAnnouncements().entrySet()) {
+ bindingRoutesAdded(entry);
+ }
+ }
+
+ private void bindingRoutesAdded(final Map.Entry<RpcContextIdentifier, Set<InstanceIdentifier<?>>> entry) {
+ Class<? extends BaseIdentity> context = entry.getKey().getRoutingContext();
+ Class<? extends RpcService> service = entry.getKey().getRpcService();
+ if (context != null) {
+ getRpcForwarder(service, context).registerPaths(context, service, entry.getValue());
+ }
+ }
+
+ private DomToBindingRpcForwarder getRpcForwarder(final Class<? extends RpcService> service,
+ final Class<? extends BaseIdentity> context) {
+ DomToBindingRpcForwarder potential = forwarders.get(service);
+ if (potential != null) {
+ return potential;
+ }
+ if (context == null) {
+ potential = new DomToBindingRpcForwarder(service, mappingService, biRpcRegistry, baRpcRegistry,registryImpl);
+ } else {
+ potential = new DomToBindingRpcForwarder(service, context, mappingService, biRpcRegistry, baRpcRegistry,registryImpl);
+ }
+
+ forwarders.put(service, potential);
+ return potential;
+ }
+
+ @Override
+ public void onRpcImplementationAdded(final QName name) {
+
+ final Optional<Class<? extends RpcService>> rpcInterface = mappingService.getRpcServiceClassFor(
+ name.getNamespace().toString(), name.getFormattedRevision());
+ if (rpcInterface.isPresent()) {
+ getRpcForwarder(rpcInterface.get(), null).registerToBindingBroker();
+ }
+ }
+
+ @Override
+ public void onRpcImplementationRemoved(final QName name) {
+
+ }
+}
--- /dev/null
+package org.opendaylight.controller.sal.binding.impl.connect.dom;
+
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+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.InstanceIdentifier;
+
+class DomToBindingTransaction implements
+ DataCommitHandler.DataCommitTransaction<InstanceIdentifier, CompositeNode> {
+
+ private final org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction backing;
+ private final DataModification<InstanceIdentifier, CompositeNode> modification;
+ private final ConcurrentMap<Object, DomToBindingTransaction> bindingOpenedTransactions;
+
+ public DomToBindingTransaction(
+ final org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction backing,
+ final DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> modification,
+ ConcurrentMap<Object, DomToBindingTransaction> bindingOpenedTransactions) {
+ super();
+ this.backing = backing;
+ this.modification = modification;
+ this.bindingOpenedTransactions = bindingOpenedTransactions;
+ this.bindingOpenedTransactions.put(backing.getIdentifier(), this);
+ }
+
+ @Override
+ public DataModification<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, CompositeNode> getModification() {
+ return modification;
+ }
+
+ @Override
+ public RpcResult<Void> rollback() throws IllegalStateException {
+ bindingOpenedTransactions.remove(backing.getIdentifier());
+ return RpcResultBuilder.<Void> success().build();
+ }
+
+ @Override
+ public RpcResult<Void> finish() throws IllegalStateException {
+ Future<RpcResult<TransactionStatus>> result = backing.commit();
+ try {
+ RpcResult<TransactionStatus> baResult = result.get();
+ bindingOpenedTransactions.remove(backing.getIdentifier());
+ return RpcResultBuilder.<Void> status(baResult.isSuccessful())
+ .withRpcErrors(baResult.getErrors()).build();
+ } catch (InterruptedException e) {
+ throw new IllegalStateException("", e);
+ } catch (ExecutionException e) {
+ throw new IllegalStateException("", e);
+ } finally {
+ bindingOpenedTransactions.remove(backing.getIdentifier());
+ }
+ }
+}
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
-import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.Future;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.RpcService;
import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
public ListenableFuture<RpcResult<?>> forwardToDomBroker(final DataObject input) {
if(biRpcRegistry == null) {
- return Futures.<RpcResult<?>> immediateFuture(Rpcs.getRpcResult(false));
+ return Futures.<RpcResult<?>> immediateFuture(RpcResultBuilder.failed().build());
}
CompositeNode inputXml = null;
Function<RpcResult<CompositeNode>, RpcResult<?>> transformationFunction =
new Function<RpcResult<CompositeNode>, RpcResult<?>>() {
+ @SuppressWarnings("rawtypes")
@Override
public RpcResult<?> apply(RpcResult<CompositeNode> result) {
}
}
- return Rpcs.getRpcResult(result.isSuccessful(), output, result.getErrors());
+ return RpcResultBuilder.from( (RpcResult)result ).withResult( output ).build();
}
};
}
if (futureResult == null) {
- return Rpcs.getRpcResult(false);
+ return RpcResultBuilder.<CompositeNode>failed().build();
}
- RpcResult<?> bindingResult = futureResult.get();
-
- Collection<RpcError> errors = bindingResult.getErrors();
- if( errors == null ) {
- errors = Collections.<RpcError>emptySet();
- }
+ @SuppressWarnings("rawtypes")
+ RpcResult bindingResult = futureResult.get();
final Object resultObj = bindingResult.getResult();
- CompositeNode output = null;
+ Object output = null;
if (resultObj instanceof DataObject) {
output = mappingService.toDataDom((DataObject)resultObj);
}
- return Rpcs.getRpcResult( bindingResult.isSuccessful(), output, errors);
+ return RpcResultBuilder.from( bindingResult ).withResult( output ).build();
}
public RpcResult<CompositeNode> invokeOn(final RpcService rpcService, final CompositeNode domInput) throws Exception {
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.sal.binding.impl.util;
-
-import com.google.common.collect.Multimap;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Map.Entry;
-import org.opendaylight.yangtools.concepts.Path;
-
-@SuppressWarnings("all")
-public class MapUtils {
- public static <P extends Path<P>, V extends Object> Collection<Entry<? extends P,? extends V>> getAllChildren(final Multimap<? extends P,? extends V> map, final P path) {
- HashSet<Entry<? extends P,? extends V>> _hashSet = new HashSet<Entry<? extends P, ? extends V>>();
- final HashSet<Entry<? extends P,? extends V>> ret = _hashSet;
- final Collection<? extends Entry<? extends P,? extends V>> entries = map.entries();
- for (final Entry<? extends P,? extends V> entry : entries) {
- {
- final P currentPath = entry.getKey();
- if (path.contains(currentPath)) {
- ret.add(entry);
- } else if (currentPath.contains(path)){
- ret.add(entry);
- }
- }
- }
- return ret;
- }
-}
-
--- /dev/null
+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.test.model.util.ListsBindingUtils.TOP_FOO_KEY;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.path;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+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.augment.rev140709.TreeComplexUsesAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder;
+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.TopBuilder;
+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;
+
+/**
+ * Regression test suite for Bug 1125 - Can't detect switch disconnection
+ * https://bugs.opendaylight.org/show_bug.cgi?id=1125
+ */
+public class Bug1125RegressionTest extends AbstractDataChangeListenerTest {
+
+ private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier
+ .create(Top.class);
+ private static final InstanceIdentifier<TopLevelList> TOP_FOO_PATH = TOP_PATH
+ .child(TopLevelList.class, TOP_FOO_KEY);
+
+ private static final InstanceIdentifier<TreeComplexUsesAugment> FOO_AUGMENT_PATH = TOP_FOO_PATH
+ .augmentation(TreeComplexUsesAugment.class);
+
+ private static final InstanceIdentifier<TreeComplexUsesAugment> WILDCARDED_AUGMENT_PATH = TOP_PATH
+ .child(TopLevelList.class).augmentation(
+ TreeComplexUsesAugment.class);
+
+ private void writeInitialState() {
+ WriteTransaction initialTx = getDataBroker().newWriteOnlyTransaction();
+ initialTx.put(LogicalDatastoreType.OPERATIONAL, TOP_PATH,
+ new TopBuilder().build());
+ TreeComplexUsesAugment fooAugment = new TreeComplexUsesAugmentBuilder()
+ .setContainerWithUses(
+ new ContainerWithUsesBuilder().setLeafFromGrouping(
+ "foo").build()).build();
+ initialTx.put(LogicalDatastoreType.OPERATIONAL, path(TOP_FOO_KEY),
+ topLevelList(TOP_FOO_KEY, fooAugment));
+ assertCommit(initialTx.submit());
+ }
+
+ private void delete(final InstanceIdentifier<?> path) {
+ WriteTransaction tx = getDataBroker().newWriteOnlyTransaction();
+ tx.delete(LogicalDatastoreType.OPERATIONAL, path);
+ assertCommit(tx.submit());
+ }
+
+ private void verifyRemoved(
+ final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> event) {
+ assertEmpty(event.getCreatedData());
+ assertEmpty(event.getUpdatedData());
+ assertContains(event.getRemovedPaths(), FOO_AUGMENT_PATH);
+ }
+
+ private void deleteAndListenAugment(final DataChangeScope scope,
+ final InstanceIdentifier<?> path) {
+ writeInitialState();
+ TestListener listener = createListener(
+ LogicalDatastoreType.OPERATIONAL, WILDCARDED_AUGMENT_PATH,
+ scope);
+ delete(path);
+ verifyRemoved(listener.event());
+ }
+
+ @Test
+ public void deleteAndListenAugment() {
+
+ deleteAndListenAugment(DataChangeScope.ONE, TOP_PATH);
+
+ deleteAndListenAugment(DataChangeScope.BASE, TOP_PATH);
+
+ deleteAndListenAugment(DataChangeScope.SUBTREE, TOP_PATH);
+
+ deleteAndListenAugment(DataChangeScope.BASE, TOP_FOO_PATH);
+
+ deleteAndListenAugment(DataChangeScope.ONE, TOP_FOO_PATH);
+
+ deleteAndListenAugment(DataChangeScope.SUBTREE, TOP_FOO_PATH);
+
+ deleteAndListenAugment(DataChangeScope.BASE, FOO_AUGMENT_PATH);
+
+ deleteAndListenAugment(DataChangeScope.ONE, FOO_AUGMENT_PATH);
+
+ deleteAndListenAugment(DataChangeScope.SUBTREE, FOO_AUGMENT_PATH);
+ }
+}
protected void setupWithDataBroker(final DataBroker dataBroker) {
WriteTransaction initialTx = dataBroker.newWriteOnlyTransaction();
initialTx.put(CONFIGURATION, TOP, top(topLevelList(TOP_FOO_KEY)));
- assertCommit(initialTx.commit());
+ assertCommit(initialTx.submit());
}
@Test
ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
writeTx.put(CONFIGURATION, TOP, top(topLevelList(TOP_BAR_KEY)));
- assertCommit(writeTx.commit());
+ assertCommit(writeTx.submit());
AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> top = topListener.event();
AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> all = allListener.event();
AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> foo = fooListener.event();
ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
writeTx.merge(CONFIGURATION, TOP, top(topLevelList(TOP_BAR_KEY)));
- assertCommit(writeTx.commit());
+ assertCommit(writeTx.submit());
verifyBarOnlyAdded(topListener,allListener,fooListener,barListener);
}
ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
writeTx.put(CONFIGURATION, TOP_BAR, topLevelList(TOP_BAR_KEY));
- assertCommit(writeTx.commit());
+ assertCommit(writeTx.submit());
verifyBarOnlyAdded(topListener,allListener,fooListener,barListener);
}
ReadWriteTransaction writeTx = getDataBroker().newReadWriteTransaction();
writeTx.merge(CONFIGURATION, TOP_BAR, topLevelList(TOP_BAR_KEY));
- assertCommit(writeTx.commit());
+ assertCommit(writeTx.submit());
verifyBarOnlyAdded(topListener,allListener,fooListener,barListener);
}
--- /dev/null
+package org.opendaylight.controller.md.sal.binding.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.TOP_BAR_KEY;
+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.path;
+
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.binding.test.AssertCollections;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcRouter;
+import org.opendaylight.controller.sal.binding.codegen.RpcIsNotRoutedException;
+import org.opendaylight.controller.sal.binding.impl.RpcProviderRegistryImpl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.OpendaylightTestRpcServiceService;
+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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.OpendaylightTestRoutedRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.rpc.routing.rev140701.TestContext;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+
+public class RpcProviderRegistryTest {
+
+ private static InstanceIdentifier<TopLevelList> FOO_PATH = path(TOP_FOO_KEY);
+ private static InstanceIdentifier<TopLevelList> BAR_PATH = path(TOP_BAR_KEY);
+ private static RpcContextIdentifier ROUTING_CONTEXT = RpcContextIdentifier.contextFor(OpendaylightTestRoutedRpcService.class, TestContext.class);
+
+ private RpcProviderRegistryImpl rpcRegistry;
+
+ @Before
+ public void setup() {
+ rpcRegistry = new RpcProviderRegistryImpl("test");
+ }
+
+ private static class TestListener implements RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>> {
+
+ final SettableFuture<RouteChange<RpcContextIdentifier, InstanceIdentifier<?>>> event = SettableFuture.create();
+ @Override
+ public void onRouteChange(
+ final RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> change) {
+ event.set(change);
+ }
+ }
+
+ @Test
+ public void testGlobalRpcRegistrations() throws Exception {
+ OpendaylightTestRpcServiceService one = Mockito.mock(OpendaylightTestRpcServiceService.class);
+ OpendaylightTestRpcServiceService two = Mockito.mock(OpendaylightTestRpcServiceService.class);
+
+ RpcRegistration<OpendaylightTestRpcServiceService> regOne = rpcRegistry.addRpcImplementation(OpendaylightTestRpcServiceService.class, one);
+ assertNotNull(regOne);
+
+ try {
+ rpcRegistry.addRpcImplementation(OpendaylightTestRpcServiceService.class, two);
+ fail("Second call for registration of same RPC must throw IllegalStateException");
+ } catch (IllegalStateException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ regOne.close();
+
+ RpcRegistration<OpendaylightTestRpcServiceService> regTwo = rpcRegistry.addRpcImplementation(OpendaylightTestRpcServiceService.class, two);
+ assertNotNull(regTwo);
+ }
+
+ @Test
+ public void routedRpcRegisteredUsingGlobalAsDefaultInstance() throws Exception {
+ OpendaylightTestRoutedRpcService def = Mockito.mock(OpendaylightTestRoutedRpcService.class);
+ rpcRegistry.addRpcImplementation(OpendaylightTestRoutedRpcService.class, def);
+ RpcRouter<OpendaylightTestRoutedRpcService> router = rpcRegistry.getRpcRouter(OpendaylightTestRoutedRpcService.class);
+ assertEquals(def, router.getDefaultService());
+ }
+
+ @Test
+ public void nonRoutedRegisteredAsRouted() {
+ OpendaylightTestRpcServiceService one = Mockito.mock(OpendaylightTestRpcServiceService.class);
+ try {
+ rpcRegistry.addRoutedRpcImplementation(OpendaylightTestRpcServiceService.class, one);
+ fail("RpcIsNotRoutedException should be thrown");
+ } catch (RpcIsNotRoutedException e) {
+ assertNotNull(e.getMessage());
+ } catch (Exception e) {
+ fail("RpcIsNotRoutedException should be thrown");
+ }
+
+ }
+
+ @Test
+ public void testRpcRouterInstance() throws Exception {
+ OpendaylightTestRoutedRpcService def = Mockito.mock(OpendaylightTestRoutedRpcService.class);
+
+ RpcRouter<OpendaylightTestRoutedRpcService> router = rpcRegistry.getRpcRouter(OpendaylightTestRoutedRpcService.class);
+
+ assertEquals(OpendaylightTestRoutedRpcService.class, router.getServiceType());
+ assertNotNull(router.getInvocationProxy());
+ assertNull(router.getDefaultService());
+
+ AssertCollections.assertContains(router.getContexts(), TestContext.class);
+
+ RpcRegistration<OpendaylightTestRoutedRpcService> regDef = router.registerDefaultService(def);
+ assertNotNull(regDef);
+ assertEquals(OpendaylightTestRoutedRpcService.class,regDef.getServiceType());
+ assertEquals(def,regDef.getInstance());
+ assertEquals(def, router.getDefaultService());
+
+ regDef.close();
+ assertNull("Default instance should be null after closing registration", router.getDefaultService());
+ }
+
+ @Test
+ public void testRoutedRpcPathChangeEvents() throws InterruptedException, TimeoutException, ExecutionException {
+ OpendaylightTestRoutedRpcService one = Mockito.mock(OpendaylightTestRoutedRpcService.class);
+ OpendaylightTestRoutedRpcService two = Mockito.mock(OpendaylightTestRoutedRpcService.class);
+ RoutedRpcRegistration<OpendaylightTestRoutedRpcService> regOne = rpcRegistry.addRoutedRpcImplementation(OpendaylightTestRoutedRpcService.class, one);
+ RoutedRpcRegistration<OpendaylightTestRoutedRpcService> regTwo = rpcRegistry.addRoutedRpcImplementation(OpendaylightTestRoutedRpcService.class, two);
+ assertNotNull(regOne);
+ assertNotNull(regTwo);
+
+ final TestListener addListener = new TestListener();
+ rpcRegistry.registerRouteChangeListener(addListener);
+ regOne.registerPath(TestContext.class, FOO_PATH);
+
+ RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> fooAddEvent = addListener.event.get(500, TimeUnit.MILLISECONDS);
+ Set<InstanceIdentifier<?>> announce = fooAddEvent.getAnnouncements().get(ROUTING_CONTEXT);
+ assertNotNull(announce);
+ AssertCollections.assertContains(announce, FOO_PATH);
+ AssertCollections.assertNotContains(announce, BAR_PATH);
+
+
+
+ final TestListener removeListener = new TestListener();
+ rpcRegistry.registerRouteChangeListener(removeListener);
+
+ regOne.unregisterPath(TestContext.class, FOO_PATH);
+
+ RouteChange<RpcContextIdentifier, InstanceIdentifier<?>> fooRemoveEvent = removeListener.event.get(500, TimeUnit.MILLISECONDS);
+ Set<InstanceIdentifier<?>> removal = fooRemoveEvent.getRemovals().get(ROUTING_CONTEXT);
+ assertNotNull(removal);
+ AssertCollections.assertContains(removal, FOO_PATH);
+ AssertCollections.assertNotContains(removal, BAR_PATH);
+
+
+ }
+
+}
*/
package org.opendaylight.controller.md.sal.binding.impl.test;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import java.util.concurrent.ExecutionException;
import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.binding.test.AbstractDataBrokerTest;
-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;
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.TopBuilder;
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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import com.google.common.base.Optional;
+
public class WriteTransactionTest extends AbstractDataBrokerTest {
private static final InstanceIdentifier<Top> TOP_PATH = InstanceIdentifier.create(Top.class);
private static final TopLevelListKey TOP_LIST_KEY = new TopLevelListKey("foo");
private static final InstanceIdentifier<TopLevelList> NODE_PATH = TOP_PATH.child(TopLevelList.class, TOP_LIST_KEY);
-
+ private static final TopLevelList NODE = new TopLevelListBuilder().setKey(TOP_LIST_KEY).build();
@Test
public void test() throws InterruptedException, ExecutionException {
WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
writeTx.put(LogicalDatastoreType.OPERATIONAL, TOP_PATH, new TopBuilder().build());
- writeTx.put(LogicalDatastoreType.OPERATIONAL, NODE_PATH, new TopLevelListBuilder().setKey(TOP_LIST_KEY).build());
- assertEquals(TransactionStatus.COMMITED, writeTx.commit().get().getResult());
+ writeTx.put(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE);
+ writeTx.submit().get();
+ }
+
+ @Test
+ public void testPutCreateParentsSuccess() throws TransactionCommitFailedException, InterruptedException, ExecutionException {
+
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE,true);
+ writeTx.submit().checkedGet();
+
+ ReadOnlyTransaction readTx = getDataBroker().newReadOnlyTransaction();
+ Optional<Top> topNode = readTx.read(LogicalDatastoreType.OPERATIONAL, TOP_PATH).get();
+ assertTrue("Top node must exists after commit",topNode.isPresent());
+ Optional<TopLevelList> listNode = readTx.read(LogicalDatastoreType.OPERATIONAL, NODE_PATH).get();
+ assertTrue("List node must exists after commit",listNode.isPresent());
+ }
+
+ @Test
+ public void testMergeCreateParentsSuccess() throws TransactionCommitFailedException, InterruptedException, ExecutionException {
+
+ WriteTransaction writeTx = getDataBroker().newWriteOnlyTransaction();
+ writeTx.merge(LogicalDatastoreType.OPERATIONAL, NODE_PATH, NODE,true);
+ writeTx.submit().checkedGet();
+
+ ReadOnlyTransaction readTx = getDataBroker().newReadOnlyTransaction();
+ Optional<Top> topNode = readTx.read(LogicalDatastoreType.OPERATIONAL, TOP_PATH).get();
+ assertTrue("Top node must exists after commit",topNode.isPresent());
+ Optional<TopLevelList> listNode = readTx.read(LogicalDatastoreType.OPERATIONAL, NODE_PATH).get();
+ assertTrue("List node must exists after commit",listNode.isPresent());
}
}
*/
package org.opendaylight.controller.md.sal.binding.test;
-import static org.junit.Assert.assertEquals;
-
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import com.google.common.util.concurrent.ListenableFuture;
return domBroker;
}
- protected static final void assertCommit(final ListenableFuture<RpcResult<TransactionStatus>> commit) {
+ protected static final void assertCommit(final ListenableFuture<Void> commit) {
try {
- assertEquals(TransactionStatus.COMMITED,commit.get(500, TimeUnit.MILLISECONDS).getResult());
+ commit.get(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new IllegalStateException(e);
}
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.RpcService;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
private void setupForForwardToDom(boolean hasOutput, boolean hasInput, int expectedErrorSize) {
- if (expectedErrorSize > 0)
+ if (expectedErrorSize > 0) {
errors.add(rpcError);
- RpcResult<CompositeNode> result = Rpcs.getRpcResult(true, invokeRpcResult, errors);
+ }
+ RpcResult<CompositeNode> result = RpcResultBuilder.<CompositeNode>success(invokeRpcResult)
+ .withRpcErrors( errors ).build();
futureCompNode = Futures.immediateFuture(result);
if( hasInput )
{
* invokeOn Tests
*/
private void setupRpcResultsWithOutput(int expectedErrorSize) {
- if (expectedErrorSize > 0)
+ if (expectedErrorSize > 0) {
errors.add(rpcError);
- RpcResult<CompositeNode> resultCompNode = Rpcs.getRpcResult(true, inputInvokeOn, errors);
+ }
+ RpcResult<CompositeNode> resultCompNode = RpcResultBuilder.<CompositeNode>success(inputInvokeOn)
+ .withRpcErrors(errors).build();
futureCompNode = Futures.immediateFuture(resultCompNode);
- RpcResult<DataObject> resultDataObj = Rpcs.getRpcResult(true, toDataDomInput, errors);
+ RpcResult<DataObject> resultDataObj = RpcResultBuilder.<DataObject>success(toDataDomInput)
+ .withRpcErrors(errors).build();
futureDataObj = Futures.immediateFuture(resultDataObj);
when(mockMappingService.toDataDom(toDataDomInput)).thenReturn(outputInvokeOn);
}
private void setupRpcResultsNoOutput(int expectedErrorSize) {
- if (expectedErrorSize > 0)
+ if (expectedErrorSize > 0) {
errors.add(rpcError);
- RpcResult<CompositeNode> resultCompNode = Rpcs.getRpcResult(true, inputInvokeOn, errors);
+ }
+ RpcResult<CompositeNode> resultCompNode = RpcResultBuilder.<CompositeNode>success(inputInvokeOn)
+ .withRpcErrors(errors).build();
futureCompNode = Futures.immediateFuture(resultCompNode);
- RpcResult<DataObject> resultDataObj = Rpcs.getRpcResult(true, null, errors);
+ RpcResult<DataObject> resultDataObj = RpcResultBuilder.<DataObject>success()
+ .withRpcErrors(errors).build();
futureDataObj = Futures.immediateFuture(resultDataObj);
}
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactory;
import org.opendaylight.controller.sal.binding.test.util.BindingTestContext;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@Override
public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, CompositeNode input) {
CompositeNode result = testContext.getBindingToDomMappingService().toDataDom(output);
- return Futures.immediateFuture(Rpcs.getRpcResult(true, result, ImmutableList.<RpcError>of()));
+ return Futures.immediateFuture(RpcResultBuilder.<CompositeNode>success(result).build());
}
});
registration.registerPath(NodeContext.QNAME, BI_NODE_C_ID);
private Future<RpcResult<AddFlowOutput>> addFlowResult(boolean success, long xid) {
AddFlowOutput output = new AddFlowOutputBuilder() //
.setTransactionId(new TransactionId(BigInteger.valueOf(xid))).build();
- RpcResult<AddFlowOutput> result = Rpcs.getRpcResult(success, output, ImmutableList.<RpcError> of());
+ RpcResult<AddFlowOutput> result = RpcResultBuilder.<AddFlowOutput>status(success).withResult(output).build();
return Futures.immediateFuture(result);
}
import org.opendaylight.controller.sal.binding.api.mount.MountProviderService;
import org.opendaylight.controller.sal.binding.test.util.BindingBrokerTestFactory;
import org.opendaylight.controller.sal.binding.test.util.BindingTestContext;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
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.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(
final QName rpc, final CompositeNode input) {
- return Futures.immediateFuture(Rpcs
- .<CompositeNode> getRpcResult(true));
+ return Futures.immediateFuture(RpcResultBuilder
+ .<CompositeNode> success().build());
}
@Override
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+/**
+ *
+ *
+ * @deprecated Use
+ * {@link org.opendaylight.controller.md.sal.binding.api.ReadTransaction#read(org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType, InstanceIdentifier)}
+ * instead.
+ */
+@Deprecated
public final class TypeSafeDataReader {
private final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate;
return delegate;
}
- public TypeSafeDataReader(DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate) {
+ public TypeSafeDataReader(
+ final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate) {
this.delegate = delegate;
}
@SuppressWarnings("unchecked")
- public <D extends DataObject> D readConfigurationData(InstanceIdentifier<D> path) {
+ public <D extends DataObject> D readConfigurationData(
+ final InstanceIdentifier<D> path) {
return (D) delegate.readConfigurationData(path);
}
@SuppressWarnings("unchecked")
- public <D extends DataObject> D readOperationalData(InstanceIdentifier<D> path) {
+ public <D extends DataObject> D readOperationalData(
+ final InstanceIdentifier<D> path) {
return (D) delegate.readOperationalData(path);
}
- public static TypeSafeDataReader forReader(DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate) {
+ public static TypeSafeDataReader forReader(
+ final DataReader<InstanceIdentifier<? extends DataObject>, DataObject> delegate) {
return new TypeSafeDataReader(delegate);
}
}
import org.opendaylight.yangtools.concepts.Path;
/**
- * Read-only transaction, which provides stable view of data
- * and is {@link AutoCloseable} resource.
+ * Marker interface for a read-only view of the data tree.
*
* @see AsyncReadTransaction
*
-* @param <P>
+ * @param <P>
* Type of path (subtree identifier), which represents location in
* tree
* @param <D>
import org.opendaylight.yangtools.concepts.Path;
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.ListenableFuture;
-
/**
*
- * Provides a stateful read view of the data tree.
+ * Marker interface for stateful read view of the data tree.
*
* <p>
* View of the data tree is a stable point-in-time snapshot of the current data tree state when
* <p>
* <b>Note:</b> example contains blocking calls on future only to illustrate
* that action happened after other asynchronous action. Use of blocking call
- * {@link ListenableFuture#get()} is discouraged for most uses and you should
- * use
- * {@link com.google.common.util.concurrent.Futures#addCallback(ListenableFuture, com.google.common.util.concurrent.FutureCallback)}
+ * {@link com.google.common.util.concurrent.ListenableFuture#get()} is discouraged for most
+ * uses and you should use
+ * {@link com.google.common.util.concurrent.Futures#addCallback(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.FutureCallback)}
* or other functions from {@link com.google.common.util.concurrent.Futures} to
* register more specific listeners.
*
* tree
* @param <D>
* Type of data (payload), which represents data payload
+ *
+ * @see org.opendaylight.controller.md.sal.binding.api.ReadTransaction
+ * @see org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction
*/
public interface AsyncReadTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
- /**
- *
- * Reads data from provided logical data store located at the provided path.
- *<p>
- * If the target is a subtree, then the whole subtree is read (and will be
- * accessible from the returned data object).
- *
- * @param store
- * Logical data store from which read should occur.
- * @param path
- * Path which uniquely identifies subtree which client want to
- * read
- * @return Listenable Future which contains read result
- * <ul>
- * <li>If data at supplied path exists the
- * {@link ListeblaFuture#get()} returns Optional object containing
- * data once read is done.
- * <li>If data at supplied path does not exists the
- * {@link ListenbleFuture#get()} returns {@link Optional#absent()}.
- * </ul>
- */
- ListenableFuture<Optional<D>> read(LogicalDatastoreType store, P path);
-
}
import org.opendaylight.yangtools.concepts.Path;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.ListenableFuture;
/**
* change for the data tree and it is not visible to any other concurrently running
* transaction.
* <p>
- * Applications publish the changes proposed in the transaction by calling {@link #commit}
- * on the transaction. This seals the transaction
+ * Applications make changes to the local data tree in the transaction by via the
+ * <b>put</b>, <b>merge</b>, and <b>delete</b> operations.
+ *
+ * <h2>Put operation</h2>
+ * Stores a piece of data at a specified path. This acts as an add / replace
+ * operation, which is to say that whole subtree will be replaced by the
+ * specified data.
+ * <p>
+ * Performing the following put operations:
+ *
+ * <pre>
+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ * </pre>
+ *
+ * will result in the following data being present:
+ *
+ * <pre>
+ * container { list [ b ] }
+ * </pre>
+ * <h2>Merge operation</h2>
+ * Merges a piece of data with the existing data at a specified path. Any pre-existing data
+ * which is not explicitly overwritten will be preserved. This means that if you store a container,
+ * its child lists will be merged.
+ * <p>
+ * Performing the following merge operations:
+ *
+ * <pre>
+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ * </pre>
+ *
+ * will result in the following data being present:
+ *
+ * <pre>
+ * container { list [ a, b ] }
+ * </pre>
+ *
+ * This also means that storing the container will preserve any
+ * augmentations which have been attached to it.
+ *
+ * <h2>Delete operation</h2>
+ * Removes a piece of data from a specified path.
+ * <p>
+ * After applying changes to the local data tree, applications publish the changes proposed in the
+ * transaction by calling {@link #submit} on the transaction. This seals the transaction
* (preventing any further writes using this transaction) and submits it to be
* processed and applied to global conceptual data tree.
* <p>
* The transaction commit may fail due to a concurrent transaction modifying and committing data in
- * an incompatible way. See {@link #commit()} for more concrete commit failure examples.
- *
- *
+ * an incompatible way. See {@link #submit} for more concrete commit failure examples.
* <p>
* <b>Implementation Note:</b> This interface is not intended to be implemented
* by users of MD-SAL, but only to be consumed by them.
* {@link TransactionStatus#CANCELED} will have no effect, and transaction
* is considered cancelled.
*
- * Invoking cancel() on finished transaction (future returned by {@link #commit()}
+ * Invoking cancel() on finished transaction (future returned by {@link #submit()}
* already completed with {@link TransactionStatus#COMMITED}) will always
* fail (return false).
*
* <tt>true</tt> otherwise
*
*/
- public boolean cancel();
-
- /**
- * Store a piece of data at specified path. This acts as an add / replace
- * operation, which is to say that whole subtree will be replaced by
- * specified path. Performing the following put operations:
- *
- * <pre>
- * 1) container { list [ a ] }
- * 2) container { list [ b ] }
- * </pre>
- *
- * will result in the following data being present:
- *
- * <pre>
- * container { list [ b ] }
- * </pre>
- *
- *
- * If you need to make sure that a parent object exists, but you do not want modify
- * its preexisting state by using put, consider using
- * {@link #merge(LogicalDatastoreType, Path, Object)}
- *
- * @param store
- * Logical data store which should be modified
- * @param path
- * Data object path
- * @param data
- * Data object to be written to specified path
- * @throws IllegalStateException
- * if the transaction is no longer {@link TransactionStatus#NEW}
- */
- public void put(LogicalDatastoreType store, P path, D data);
-
- /**
- * Store a piece of data at the specified path. This acts as a merge operation,
- * which is to say that any pre-existing data which is not explicitly
- * overwritten will be preserved. This means that if you store a container,
- * its child lists will be merged. Performing the following merge
- * operations:
- *
- * <pre>
- * 1) container { list [ a ] }
- * 2) container { list [ b ] }
- * </pre>
- *
- * will result in the following data being present:
- *
- * <pre>
- * container { list [ a, b ] }
- * </pre>
- *
- * This also means that storing the container will preserve any
- * augmentations which have been attached to it.
- *<p>
- * If you require an explicit replace operation, use
- * {@link #put(LogicalDatastoreType, Path, Object)} instead.
- *
- * @param store
- * Logical data store which should be modified
- * @param path
- * Data object path
- * @param data
- * Data object to be written to specified path
- * @throws IllegalStateException
- * if the transaction is no longer {@link TransactionStatus#NEW}
- */
- public void merge(LogicalDatastoreType store, P path, D data);
+ boolean cancel();
/**
- * Remove a piece of data from specified path. This operation does not fail
+ * Removes a piece of data from specified path. This operation does not fail
* if the specified path does not exist.
*
* @param store
* @throws IllegalStateException
* if the transaction is no longer {@link TransactionStatus#NEW}
*/
- public void delete(LogicalDatastoreType store, P path);
+ void delete(LogicalDatastoreType store, P path);
/**
- * Submits transaction to be applied to update logical data tree.
+ * Submits this transaction to be asynchronously applied to update the logical data tree.
+ * The returned CheckedFuture conveys the result of applying the data changes.
+ * <p>
+ * <b>Note:</b> It is strongly recommended to process the CheckedFuture result in an asynchronous
+ * manner rather than using the blocking get() method. See example usage below.
* <p>
* This call logically seals the transaction, which prevents the client from
* further changing data tree using this transaction. Any subsequent calls to
* {@link IllegalStateException}.
*
* The transaction is marked as {@link TransactionStatus#SUBMITED} and
- * enqueued into the data store backed for processing.
+ * enqueued into the data store back-end for processing.
*
* <p>
* Whether or not the commit is successful is determined by versioning
- * of data tree and validation of registered commit participants
- * {@link AsyncConfigurationCommitHandler}
- * if transaction changes {@link LogicalDatastoreType#CONFIGURATION} data tree.
- *<p>
- * The effects of successful commit of data depends on
- * other data change listeners {@link AsyncDataChangeListener} and
- * {@link AsyncConfigurationCommitHandler}, which was registered to the
- * same {@link AsyncDataBroker}, to which this transaction belongs.
- *
+ * of the data tree and validation of registered commit participants
+ * ({@link AsyncConfigurationCommitHandler})
+ * if the transaction changes the data tree.
+ * <p>
+ * The effects of a successful commit of data depends on data change listeners
+ * ({@link AsyncDataChangeListener}) and commit participants
+ * ({@link AsyncConfigurationCommitHandler}) that are registered with the data broker.
+ * <p>
+ * <h3>Example usage:</h3>
+ * <pre>
+ * private void doWrite( final int tries ) {
+ * WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
+ *
+ * MyDataObject data = ...;
+ * InstanceIdentifier<MyDataObject> path = ...;
+ * writeTx.put( LogicalDatastoreType.OPERATIONAL, path, data );
+ *
+ * Futures.addCallback( writeTx.submit(), new FutureCallback<Void>() {
+ * public void onSuccess( Void result ) {
+ * // succeeded
+ * }
+ *
+ * public void onFailure( Throwable t ) {
+ * if( t instanceof OptimisticLockFailedException ) {
+ * if( ( tries - 1 ) > 0 ) {
+ * // do retry
+ * doWrite( tries - 1 );
+ * } else {
+ * // out of retries
+ * }
+ * } else {
+ * // failed due to another type of TransactionCommitFailedException.
+ * }
+ * } );
+ * }
+ * ...
+ * doWrite( 2 );
+ * </pre>
* <h2>Failure scenarios</h2>
* <p>
* Transaction may fail because of multiple reasons, such as
* <ul>
- * <li>Another transaction finished earlier and modified the same node in
- * non-compatible way (see below). In this case the returned future will fail with
+ * <li>Another transaction finished earlier and modified the same node in a
+ * non-compatible way (see below). In this case the returned future will fail with an
* {@link OptimisticLockFailedException}. It is the responsibility of the
* caller to create a new transaction and submit the same modification again in
- * order to update data tree.</li>
+ * order to update data tree. <i><b>Warning</b>: In most cases, retrying after an
+ * OptimisticLockFailedException will result in a high probability of success.
+ * However, there are scenarios, albeit unusual, where any number of retries will
+ * not succeed. Therefore it is strongly recommended to limit the number of retries (2 or 3)
+ * to avoid an endless loop.</i>
+ * </li>
* <li>Data change introduced by this transaction did not pass validation by
* commit handlers or data was incorrectly structured. Returned future will
- * fail with {@link DataValidationFailedException}. User should not retry to
+ * fail with a {@link DataValidationFailedException}. User should not retry to
* create new transaction with same data, since it probably will fail again.
* </li>
* </ul>
* txA.put(CONFIGURATION, PATH, A); // writes to PATH value A
* txB.put(CONFIGURATION, PATH, B) // writes to PATH value B
*
- * ListenableFuture futureA = txA.commit(); // transaction A is sealed and committed
- * ListenebleFuture futureB = txB.commit(); // transaction B is sealed and committed
+ * ListenableFuture futureA = txA.submit(); // transaction A is sealed and submitted
+ * ListenebleFuture futureB = txB.submit(); // transaction B is sealed and submitted
* </pre>
*
* Commit of transaction A will be processed asynchronously and data tree
* with {@link OptimisticLockFailedException} exception, which indicates to
* client that concurrent transaction prevented the submitted transaction from being
* applied.
- *
- * @return Result of the Commit, containing success information or list of
- * encountered errors, if commit was not successful. The Future
- * blocks until {@link TransactionStatus#COMMITED} is reached.
- * Future will fail with {@link TransactionCommitFailedException} if
- * Commit of this transaction failed. TODO: Usability: Consider
- * change from ListenableFuture to
- * {@link com.google.common.util.concurrent.CheckedFuture} which
- * will throw {@link TransactionCommitFailedException}.
+ * <br>
+ * @return a CheckFuture containing the result of the commit. The Future blocks until the
+ * commit operation is complete. A successful commit returns nothing. On failure,
+ * the Future will fail with a {@link TransactionCommitFailedException} or an exception
+ * derived from TransactionCommitFailedException.
*
* @throws IllegalStateException
* if the transaction is not {@link TransactionStatus#NEW}
*/
- public ListenableFuture<RpcResult<TransactionStatus>> commit();
+ CheckedFuture<Void,TransactionCommitFailedException> submit();
+
+ /**
+ * @deprecated Use {@link #submit()} instead.
+ */
+ @Deprecated
+ ListenableFuture<RpcResult<TransactionStatus>> commit();
}
package org.opendaylight.controller.md.sal.common.api.data;
import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import com.google.common.base.Preconditions;
private Class<? extends Path<?>> pathType;
- public <P extends Path<P>> DataValidationFailedException(final Class<P> pathType,final P path, final String message, final Throwable cause) {
- super(message, cause);
+ public <P extends Path<P>> DataValidationFailedException(final Class<P> pathType,final P path,
+ final String message, final Throwable cause) {
+ super(message, cause, RpcResultBuilder.newError(ErrorType.APPLICATION, "invalid-value", message, null,
+ path != null ? path.toString() : null, cause));
this.pathType = Preconditions.checkNotNull(pathType, "path type must not be null");
this.path = Preconditions.checkNotNull(path,"path must not be null.");
}
- public <P extends Path<P>> DataValidationFailedException(final Class<P> pathType,final P path,final String message) {
- this(pathType,path,message,null);
+ public <P extends Path<P>> DataValidationFailedException(final Class<P> pathType,final P path,
+ final String message) {
+ this(pathType, path, message, null);
}
public final Path<?> getPath() {
package org.opendaylight.controller.md.sal.common.api.data;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
/**
*
* Failure of asynchronous transaction commit caused by failure
private static final long serialVersionUID = 1L;
- protected OptimisticLockFailedException(final String message, final Throwable cause, final boolean enableSuppression,
- final boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
- }
-
public OptimisticLockFailedException(final String message, final Throwable cause) {
- super(message, cause);
+ super(message, cause, RpcResultBuilder.newError(ErrorType.APPLICATION, "resource-denied",
+ message, null, null, cause));
}
public OptimisticLockFailedException(final String message) {
- super(message);
+ this(message, null);
}
}
*/
package org.opendaylight.controller.md.sal.common.api.data;
+import java.util.Arrays;
+import java.util.List;
+
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+
+import com.google.common.collect.ImmutableList;
+
/**
*
* Failed commit of asynchronous transaction
*/
public class TransactionCommitFailedException extends Exception {
- private static final long serialVersionUID = -6138306275373237068L;
+ private static final long serialVersionUID = 1L;
- protected TransactionCommitFailedException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) {
- super(message, cause, enableSuppression, writableStackTrace);
+ private final List<RpcError> errorList;
+
+ public TransactionCommitFailedException(final String message, final RpcError... errors) {
+ this(message, null, errors);
}
- public TransactionCommitFailedException(final String message, final Throwable cause) {
+ public TransactionCommitFailedException(final String message, final Throwable cause,
+ final RpcError... errors) {
super(message, cause);
+
+ if( errors != null && errors.length > 0 ) {
+ errorList = ImmutableList.<RpcError>builder().addAll( Arrays.asList( errors ) ).build();
+ }
+ else {
+ // Add a default RpcError.
+ errorList = ImmutableList.of(RpcResultBuilder.newError(ErrorType.APPLICATION, null,
+ getMessage(), null, null, getCause()));
+ }
}
- public TransactionCommitFailedException(final String message) {
- super(message);
+ /**
+ * Returns additional error information about this exception.
+ *
+ * @return a List of RpcErrors. There is always at least one RpcError.
+ */
+ public List<RpcError> getErrorList() {
+ return errorList;
}
+ @Override
+ public String getMessage() {
+ return new StringBuilder( super.getMessage() ).append(", errors: ").append( errorList ).toString();
+ }
}
import java.util.concurrent.TimeUnit;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.common.impl.AbstractDataModification;
import org.opendaylight.yangtools.concepts.Path;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
public abstract class AbstractDataTransaction<P extends Path<P>, D extends Object> extends
AbstractDataModification<P, D> {
@Override
public boolean equals(Object obj) {
- if (this == obj)
+ if (this == obj) {
return true;
- if (obj == null)
+ }
+ if (obj == null) {
return false;
- if (getClass() != obj.getClass())
+ }
+ if (getClass() != obj.getClass()) {
return false;
+ }
AbstractDataTransaction<?, ?> other = (AbstractDataTransaction<?, ?>) obj;
if (identifier == null) {
- if (other.identifier != null)
+ if (other.identifier != null) {
return false;
- } else if (!identifier.equals(other.identifier))
+ }
+ } else if (!identifier.equals(other.identifier)) {
return false;
+ }
return true;
}
this.status = status;
this.onStatusChange(status);
}
+
+ public static ListenableFuture<RpcResult<TransactionStatus>> convertToLegacyCommitFuture(
+ CheckedFuture<Void,TransactionCommitFailedException> from ) {
+ return Futures.transform(from, new AsyncFunction<Void, RpcResult<TransactionStatus>>() {
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> apply(Void input) throws Exception {
+ return Futures.immediateFuture(RpcResultBuilder.<TransactionStatus>
+ success(TransactionStatus.COMMITED).build());
+ }
+ } );
+ }
}
package org.opendaylight.controller.md.sal.common.impl.service;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.opendaylight.controller.md.sal.common.api.data.DataChangeListener;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.yangtools.concepts.Path;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
log.trace("Transaction: {} Finished successfully (no effects).", transactionId);
- return Rpcs.<TransactionStatus> getRpcResult(true, TransactionStatus.COMMITED,
- Collections.<RpcError> emptySet());
+ return RpcResultBuilder.<TransactionStatus> success( TransactionStatus.COMMITED ).build();
}
final ImmutableList.Builder<ListenerStateCapture<P, D, DCL>> listenersBuilder = ImmutableList.builder();
log.trace("Transaction: {} Notifying listeners.", transactionId);
publishDataChangeEvent(listeners);
- return Rpcs.<TransactionStatus> getRpcResult(true, TransactionStatus.COMMITED,
- Collections.<RpcError> emptySet());
+ return RpcResultBuilder.<TransactionStatus> success(TransactionStatus.COMMITED).build();
}
private void captureInitialState(ImmutableList<ListenerStateCapture<P, D, DCL>> listeners) {
for (final DataCommitTransaction<P, D> transaction : transactions) {
transaction.rollback();
}
- Set<RpcError> _emptySet = Collections.<RpcError> emptySet();
- return Rpcs.<TransactionStatus> getRpcResult(false, TransactionStatus.FAILED, _emptySet);
+ return RpcResultBuilder.<TransactionStatus> failed().withResult(TransactionStatus.FAILED).build();
}
}
*/
package org.opendaylight.controller.sal.common.util;
-import java.util.Collections;
-
import org.opendaylight.controller.md.sal.common.api.data.DataModification;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
import org.opendaylight.yangtools.concepts.Path;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
public class CommitHandlerTransactions {
}
@Override
public RpcResult<Void> rollback() throws IllegalStateException {
- return Rpcs.<Void>getRpcResult(true, null, Collections.<RpcError>emptyList());
+ return RpcResultBuilder.<Void>success().build();
}
@Override
public RpcResult<Void> finish() throws IllegalStateException {
- return Rpcs.<Void>getRpcResult(true, null, Collections.<RpcError>emptyList());
+ return RpcResultBuilder.<Void>success().build();
}
@Override
import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
/**
- * @author mirehak
- *
+ * @deprecated Use {@link org.opendaylight.yangtools.yang.common.RpcResultBuilder}
*/
+@Deprecated
public class RpcErrors {
/**
import com.google.common.collect.ImmutableList;
+/**
+ * @deprecated Use {@link org.opendaylight.yangtools.yang.common.RpcResultBuilder}
+ */
+@Deprecated
public class Rpcs {
public static <T> RpcResult<T> getRpcResult(boolean successful) {
*/
public interface DOMDataBroker extends
AsyncDataBroker<InstanceIdentifier, NormalizedNode<?, ?>, DOMDataChangeListener>,
- TransactionChainFactory<InstanceIdentifier, NormalizedNode<?, ?>>, BrokerService {
+ TransactionChainFactory<InstanceIdentifier, NormalizedNode<?, ?>>, BrokerService, DOMService {
/**
* {@inheritDoc}
package org.opendaylight.controller.md.sal.dom.api;
import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * A transaction that provides read access to a logical data store.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncReadTransaction}.
+ */
public interface DOMDataReadTransaction extends AsyncReadTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+ /**
+ * Reads data from provided logical data store located at the provided path.
+ *<p>
+ * If the target is a subtree, then the whole subtree is read (and will be
+ * accessible from the returned data object).
+ *
+ * @param store
+ * Logical data store from which read should occur.
+ * @param path
+ * Path which uniquely identifies subtree which client want to
+ * read
+ * @return Listenable Future which contains read result
+ * <ul>
+ * <li>If data at supplied path exists the
+ * {@link ListeblaFuture#get()} returns Optional object containing
+ * data once read is done.
+ * <li>If data at supplied path does not exists the
+ * {@link ListenbleFuture#get()} returns {@link Optional#absent()}.
+ * </ul>
+ */
+ ListenableFuture<Optional<NormalizedNode<?,?>>> read(LogicalDatastoreType store,InstanceIdentifier path);
}
package org.opendaylight.controller.md.sal.dom.api;
import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+/**
+ * A transaction that provides mutation capabilities on a data tree.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ */
public interface DOMDataWriteTransaction extends AsyncWriteTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+ /**
+ * Stores a piece of data at the specified path. This acts as an add / replace
+ * operation, which is to say that whole subtree will be replaced by the specified data.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ * <p>
+ * If you need to make sure that a parent object exists but you do not want modify
+ * its pre-existing state by using put, consider using {@link #merge} instead.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be written to the specified path
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ void put(LogicalDatastoreType store, InstanceIdentifier path, NormalizedNode<?, ?> data);
+
+ /**
+ * Merges a piece of data with the existing data at a specified path. Any pre-existing data
+ * which is not explicitly overwritten will be preserved. This means that if you store a container,
+ * its child lists will be merged.
+ * <p>
+ * For more information on usage and examples, please see the documentation in {@link AsyncWriteTransaction}.
+ *<p>
+ * If you require an explicit replace operation, use {@link #put} instead.
+ *
+ * @param store
+ * the logical data store which should be modified
+ * @param path
+ * the data object path
+ * @param data
+ * the data object to be merged to the specified path
+ * @throws IllegalStateException
+ * if the transaction has already been submitted
+ */
+ void merge(LogicalDatastoreType store, InstanceIdentifier path, NormalizedNode<?, ?> data);
}
--- /dev/null
+/*
+ * 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.api;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public interface DOMMountPoint extends Identifiable<InstanceIdentifier> {
+
+ <T extends DOMService> Optional<T> getService(Class<T> cls);
+
+ SchemaContext getSchemaContext();
+}
--- /dev/null
+/*
+ * 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.api;
+
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+
+
+public interface DOMMountPointService extends BrokerService {
+
+ Optional<DOMMountPoint> getMountPoint(InstanceIdentifier path);
+
+ DOMMountPointBuilder createMountPoint(InstanceIdentifier path);
+
+ ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
+
+ public interface DOMMountPointBuilder {
+
+ <T extends DOMService> DOMMountPointBuilder addService(Class<T> type,T impl);
+
+ DOMMountPointBuilder addInitialSchemaContext(SchemaContext ctx);
+
+ ObjectRegistration<DOMMountPoint> register();
+ }
+}
--- /dev/null
+/*
+ * 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.api;
+
+public interface DOMService {
+
+}
package org.opendaylight.controller.sal.core.api;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangePublisher;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-public interface RpcProvisionRegistry extends RpcImplementation, BrokerService, RouteChangePublisher<RpcRoutingContext, InstanceIdentifier> {
+public interface RpcProvisionRegistry extends RpcImplementation, BrokerService, RouteChangePublisher<RpcRoutingContext, InstanceIdentifier>, DOMService {
/**
* Registers an implementation of the rpc.
* Interface representing a single mount instance and represents a way for
* clients to access underlying data, RPCs and notifications.
*/
+@Deprecated
public interface MountInstance extends //
NotificationService, //
DataBrokerService {
import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+@Deprecated
public interface MountProvisionInstance extends //
MountInstance,//
NotificationPublishService, //
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html.
+ */
+
+package org.opendaylight.controller.sal.core.api.mount;
+
+import java.util.EventListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+public interface MountProvisionListener extends EventListener {
+
+ void onMountPointCreated(InstanceIdentifier path);
+
+ void onMountPointRemoved(InstanceIdentifier path);
+
+}
*/
package org.opendaylight.controller.sal.core.api.mount;
-import java.util.EventListener;
-
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+/**
+ * @deprecated Use org.opendaylight.controller.md.sal.dom.api.DOMMountPointService instead
+ */
+@Deprecated
public interface MountProvisionService extends MountService {
@Override
ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
- public interface MountProvisionListener extends EventListener {
-
- void onMountPointCreated(InstanceIdentifier path);
-
- void onMountPointRemoved(InstanceIdentifier path);
-
- }
}
/**
* Client-level interface for interacting with mount points. It provides access
* to {@link MountInstance} instances based on their path.
+ *
+ * @deprecated Use org.opendaylight.controller.md.sal.dom.api.DOMMountPointService instead
*/
+@Deprecated
public interface MountService extends BrokerService {
/**
* Obtain access to a mount instance registered at the specified path.
*/
package org.opendaylight.controller.sal.core.api.notify;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
/**
* {@link NotificationListener#onNotification(CompositeNode)}
* </ol>
*/
-public interface NotificationPublishService extends NotificationService {
+public interface NotificationPublishService extends NotificationService, DOMService {
/**
* Publishes a notification.
*
* <li> {@link DOMDataWriteTransaction#commit()} - results in invoking
* {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts
* and then invoking finalized implementation callback
- * {@link #commit(DOMDataWriteTransaction, Iterable)} with transaction which
+ * {@link #submit(DOMDataWriteTransaction, Iterable)} with transaction which
* was commited and gathered results.
* </ul>
*
* <li> {@link DOMDataWriteTransaction#commit()} - results in invoking
* {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts
* and then invoking finalized implementation callback
- * {@link #commit(DOMDataWriteTransaction, Iterable)} with transaction which
+ * {@link #submit(DOMDataWriteTransaction, Iterable)} with transaction which
* was commited and gathered results.
* <li>
* </ul>
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicLong;
-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.TransactionChainListener;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
public class DOMDataBrokerImpl extends AbstractDOMForwardedTransactionFactory<DOMStore> implements DOMDataBroker,
}
@Override
- public ListenableFuture<RpcResult<TransactionStatus>> commit(final DOMDataWriteTransaction transaction,
+ public CheckedFuture<Void,TransactionCommitFailedException> submit(final DOMDataWriteTransaction transaction,
final Iterable<DOMStoreThreePhaseCommitCohort> cohorts) {
LOG.debug("Transaction: {} submitted with cohorts {}.", transaction.getIdentifier(), cohorts);
return coordinator.submit(transaction, cohorts, Optional.<DOMDataCommitErrorListener> absent());
import javax.annotation.concurrent.GuardedBy;
-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.TransactionChainListener;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.CheckedFuture;
/**
* NormalizedNode implementation of {@link org.opendaylight.controller.md.sal.common.api.data.TransactionChain} which is backed
}
@Override
- public synchronized ListenableFuture<RpcResult<TransactionStatus>> commit(
+ public synchronized CheckedFuture<Void,TransactionCommitFailedException> submit(
final DOMDataWriteTransaction transaction, final Iterable<DOMStoreThreePhaseCommitCohort> cohorts) {
return coordinator.submit(transaction, cohorts, Optional.<DOMDataCommitErrorListener> of(this));
}
*/
package org.opendaylight.controller.md.sal.dom.broker.impl;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import javax.annotation.concurrent.GuardedBy;
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
-import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
}
@Override
- public ListenableFuture<RpcResult<TransactionStatus>> submit(final DOMDataWriteTransaction transaction,
+ public CheckedFuture<Void,TransactionCommitFailedException> submit(final DOMDataWriteTransaction transaction,
final Iterable<DOMStoreThreePhaseCommitCohort> cohorts, final Optional<DOMDataCommitErrorListener> listener) {
Preconditions.checkArgument(transaction != null, "Transaction must not be null.");
Preconditions.checkArgument(cohorts != null, "Cohorts must not be null.");
Preconditions.checkArgument(listener != null, "Listener must not be null");
LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier());
- ListenableFuture<RpcResult<TransactionStatus>> commitFuture = executor.submit(new CommitCoordinationTask(
+ ListenableFuture<Void> commitFuture = executor.submit(new CommitCoordinationTask(
transaction, cohorts, listener));
if (listener.isPresent()) {
Futures.addCallback(commitFuture, new DOMDataCommitErrorInvoker(transaction, listener.get()));
}
- return commitFuture;
+
+ return Futures.makeChecked(commitFuture, TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER);
}
/**
* support of cancelation.
*
*/
- private static class CommitCoordinationTask implements Callable<RpcResult<TransactionStatus>> {
+ private static class CommitCoordinationTask implements Callable<Void> {
private final DOMDataWriteTransaction tx;
private final Iterable<DOMStoreThreePhaseCommitCohort> cohorts;
}
@Override
- public RpcResult<TransactionStatus> call() throws TransactionCommitFailedException {
+ public Void call() throws TransactionCommitFailedException {
try {
canCommitBlocking();
preCommitBlocking();
- return commitBlocking();
+ commitBlocking();
+ return null;
} catch (TransactionCommitFailedException e) {
LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), currentPhase, e);
abortBlocking(e);
* If one of cohorts failed preCommit
*
*/
- private RpcResult<TransactionStatus> commitBlocking() throws TransactionCommitFailedException {
+ private void commitBlocking() throws TransactionCommitFailedException {
commitAll().checkedGet();
- return Rpcs.getRpcResult(true, TransactionStatus.COMMITED, Collections.<RpcError> emptySet());
}
/**
*/
package org.opendaylight.controller.md.sal.dom.broker.impl;
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.FutureCallback;
* callback is invoked with associated transaction and throwable is invoked on listener.
*
*/
-class DOMDataCommitErrorInvoker implements FutureCallback<RpcResult<TransactionStatus>> {
+class DOMDataCommitErrorInvoker implements FutureCallback<Void> {
private final DOMDataWriteTransaction tx;
private final DOMDataCommitErrorListener listener;
}
@Override
- public void onSuccess(RpcResult<TransactionStatus> result) {
+ public void onSuccess(Void result) {
// NOOP
}
}
\ No newline at end of file
*/
package org.opendaylight.controller.md.sal.dom.broker.impl;
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-
import com.google.common.base.Optional;
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.CheckedFuture;
/**
* Executor of Three Phase Commit coordination for
* subtransactoins.
* @param listener
* Error listener which should be notified if transaction failed.
- * @return ListenableFuture which contains RpcResult with
- * {@link TransactionStatus#COMMITED} if commit coordination on
- * cohorts finished successfully.
+ * @return a CheckedFuture. if commit coordination on cohorts finished successfully,
+ * nothing is returned from the Future, On failure,
+ * the Future fails with a {@link TransactionCommitFailedException}.
*
*/
- ListenableFuture<RpcResult<TransactionStatus>> submit(DOMDataWriteTransaction tx,
+ CheckedFuture<Void,TransactionCommitFailedException> submit(DOMDataWriteTransaction tx,
Iterable<DOMStoreThreePhaseCommitCohort> cohort, Optional<DOMDataCommitErrorListener> listener);
}
*/
package org.opendaylight.controller.md.sal.dom.broker.impl;
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.CheckedFuture;
/**
*
public interface DOMDataCommitImplementation {
/**
- * User-supplied implementation of {@link DOMDataWriteTransaction#commit()}
+ * User-supplied implementation of {@link DOMDataWriteTransaction#submit()}
* for transaction.
*
- * Callback invoked when {@link DOMDataWriteTransaction#commit()} is invoked
+ * Callback invoked when {@link DOMDataWriteTransaction#submit()} is invoked
* on transaction created by this factory.
*
* @param transaction
* commited transaction.
*
*/
- ListenableFuture<RpcResult<TransactionStatus>> commit(final DOMDataWriteTransaction transaction,
+ CheckedFuture<Void,TransactionCommitFailedException> submit(final DOMDataWriteTransaction transaction,
final Iterable<DOMStoreThreePhaseCommitCohort> cohorts);
}
* <li>{@link #merge(LogicalDatastoreType, InstanceIdentifier, NormalizedNode)}
* </ul>
* {@link #commit()} will result in invocation of
- * {@link DOMDataCommitImplementation#commit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
+ * {@link DOMDataCommitImplementation#submit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
* invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying
* transactions.
*
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;
+import org.opendaylight.controller.md.sal.common.impl.service.AbstractDataTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.ListenableFuture;
/**
* </ul>
* <p>
* {@link #commit()} will result in invocation of
- * {@link DOMDataCommitImplementation#commit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
+ * {@link DOMDataCommitImplementation#submit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
* invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying
* transactions.
*
*
*/
@GuardedBy("this")
- private volatile ListenableFuture<RpcResult<TransactionStatus>> commitFuture;
+ private volatile CheckedFuture<Void, TransactionCommitFailedException> commitFuture;
protected DOMForwardedWriteTransaction(final Object identifier,
final ImmutableMap<LogicalDatastoreType, T> backingTxs, final DOMDataCommitImplementation commitImpl) {
@Override
public synchronized ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ return AbstractDataTransaction.convertToLegacyCommitFuture(submit());
+ }
+
+ @Override
+ public CheckedFuture<Void,TransactionCommitFailedException> submit() {
checkNotReady();
ImmutableList.Builder<DOMStoreThreePhaseCommitCohort> cohortsBuilder = ImmutableList.builder();
cohortsBuilder.add(subTx.ready());
}
ImmutableList<DOMStoreThreePhaseCommitCohort> cohorts = cohortsBuilder.build();
- commitFuture = commitImpl.commit(this, cohorts);
+ commitFuture = commitImpl.submit(this, cohorts);
/*
*We remove reference to Commit Implementation in order
private void checkNotCommited() {
checkState(commitFuture == null, "Transaction was already submited.");
}
-
}
\ No newline at end of file
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.impl.service.AbstractDataTransaction;
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
public Future<RpcResult<TransactionStatus>> commit() {
Preconditions.checkState(status == TransactionStatus.NEW);
status = TransactionStatus.SUBMITED;
- return getDelegate().commit();
+ return AbstractDataTransaction.convertToLegacyCommitFuture(getDelegate().submit());
}
@Override
--- /dev/null
+/*
+ * 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.mount;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.controller.md.sal.dom.broker.spi.mount.SimpleDOMMountPoint;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.MutableClassToInstanceMap;
+
+public class DOMMountPointServiceImpl implements DOMMountPointService {
+
+ private final Map<InstanceIdentifier, SimpleDOMMountPoint> mountPoints = new HashMap<>();
+
+ private final ListenerRegistry<MountProvisionListener> listeners = ListenerRegistry.create();
+
+ @Override
+ public Optional<DOMMountPoint> getMountPoint(final InstanceIdentifier path) {
+ return Optional.<DOMMountPoint>fromNullable(mountPoints.get(path));
+ }
+
+ @Override
+ public DOMMountPointBuilder createMountPoint(final InstanceIdentifier path) {
+ Preconditions.checkState(!mountPoints.containsKey(path), "Mount point already exists");
+ return new DOMMountPointBuilderImpl(path);
+ }
+
+ public void notifyMountCreated(final InstanceIdentifier identifier) {
+ for (final ListenerRegistration<MountProvisionListener> listener : listeners
+ .getListeners()) {
+ listener.getInstance().onMountPointCreated(identifier);
+ }
+ }
+
+ @Override
+ public ListenerRegistration<MountProvisionListener> registerProvisionListener(
+ final MountProvisionListener listener) {
+ return listeners.register(listener);
+ }
+
+ public ObjectRegistration<DOMMountPoint> registerMountPoint(final SimpleDOMMountPoint mountPoint) {
+ synchronized (mountPoints) {
+ Preconditions.checkState(!mountPoints.containsKey(mountPoint.getIdentifier()), "Mount point already exists");
+ mountPoints.put(mountPoint.getIdentifier(), mountPoint);
+ }
+ notifyMountCreated(mountPoint.getIdentifier());
+
+ // FIXME this shouldnt be null
+ return null;
+ }
+
+ public class DOMMountPointBuilderImpl implements DOMMountPointBuilder {
+
+ ClassToInstanceMap<DOMService> services = MutableClassToInstanceMap.create();
+ private SimpleDOMMountPoint mountPoint;
+ private final InstanceIdentifier path;
+ private SchemaContext schemaContext;
+
+ public DOMMountPointBuilderImpl(final InstanceIdentifier path) {
+ this.path = path;
+ }
+
+ @Override
+ public <T extends DOMService> DOMMountPointBuilder addService(final Class<T> type, final T impl) {
+ services.putInstance(type, impl);
+ return this;
+ }
+
+ @Override
+ public DOMMountPointBuilder addInitialSchemaContext(final SchemaContext ctx) {
+ schemaContext = ctx;
+ return this;
+ }
+
+ @Override
+ public ObjectRegistration<DOMMountPoint> register() {
+ Preconditions.checkState(mountPoint == null, "Mount point is already built.");
+ mountPoint = SimpleDOMMountPoint.create(path, services,schemaContext);
+ return registerMountPoint(mountPoint);
+ }
+ }
+}
import com.google.common.util.concurrent.ListenableFuture;
+@Deprecated
public class MountPointImpl implements MountProvisionInstance, SchemaContextProvider {
private final SchemaAwareRpcBroker rpcs;
import org.opendaylight.controller.sal.core.api.data.DataProviderService;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+@Deprecated
public class MountPointManagerImpl implements MountProvisionService {
private final ListenerRegistry<MountProvisionListener> listeners =
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.dom.broker;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
-import org.opendaylight.controller.sal.core.api.BrokerService;
-import org.opendaylight.controller.sal.core.api.Consumer.ConsumerFunctionality;
-import org.opendaylight.controller.sal.core.api.Provider.ProviderFunctionality;
-import org.opendaylight.controller.sal.core.api.notify.NotificationListener;
-import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
-import org.opendaylight.controller.sal.core.api.notify.NotificationService;
-import org.opendaylight.controller.sal.core.spi.BrokerModule;
-import org.opendaylight.yangtools.concepts.Registration;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-
-public class NotificationModule implements BrokerModule {
- private static Logger log = LoggerFactory
- .getLogger(NotificationModule.class);
-
- private final Multimap<QName, NotificationListener> listeners = HashMultimap
- .create();
-
- private static final Set<Class<? extends BrokerService>> PROVIDED_SERVICE_TYPE = ImmutableSet
- .<Class<? extends BrokerService>>of(NotificationService.class,
- NotificationPublishService.class);
-
- private static final Set<Class<? extends ConsumerFunctionality>> SUPPORTED_CONSUMER_FUNCTIONALITY = ImmutableSet
- .of((Class<? extends ConsumerFunctionality>) NotificationListener.class,
- NotificationListener.class); // Workaround: if we use the
- // version of method with only
- // one argument, the generics
- // inference will not work
-
- @Override
- public Set<Class<? extends BrokerService>> getProvidedServices() {
- return PROVIDED_SERVICE_TYPE;
- }
-
- @Override
- public Set<Class<? extends ConsumerFunctionality>> getSupportedConsumerFunctionality() {
- return SUPPORTED_CONSUMER_FUNCTIONALITY;
- }
-
- @Override
- public <T extends BrokerService> T getServiceForSession(Class<T> service,
- ConsumerSession session) {
- if (NotificationPublishService.class.equals(service)
- && session instanceof ProviderSession) {
- @SuppressWarnings("unchecked")
- T ret = (T) newNotificationPublishService(session);
- return ret;
- } else if (NotificationService.class.equals(service)) {
-
- @SuppressWarnings("unchecked")
- T ret = (T) newNotificationConsumerService(session);
- return ret;
- }
-
- throw new IllegalArgumentException(
- "The requested session-specific service is not provided by this module.");
- }
-
- private void sendNotification(CompositeNode notification) {
- QName type = notification.getNodeType();
- Collection<NotificationListener> toNotify = listeners.get(type);
- log.trace("Publishing notification " + type);
-
- if (toNotify == null) {
- // No listeners were registered - returns.
- return;
- }
-
- for (NotificationListener listener : toNotify) {
- try {
- // FIXME: ensure that notification is immutable
- listener.onNotification(notification);
- } catch (Exception e) {
- log.error("Uncaught exception in NotificationListener", e);
- }
- }
-
- }
-
- private NotificationService newNotificationConsumerService(
- ConsumerSession session) {
- return new NotificationConsumerSessionImpl();
- }
-
- private NotificationPublishService newNotificationPublishService(
- ConsumerSession session) {
- return new NotificationProviderSessionImpl();
- }
-
- private class NotificationConsumerSessionImpl implements
- NotificationService {
-
- private final Multimap<QName, NotificationListener> consumerListeners = HashMultimap
- .create();
- private boolean closed = false;
-
-
- @Override
- public Registration<NotificationListener> addNotificationListener(QName notification,
- NotificationListener listener) {
- checkSessionState();
- if (notification == null) {
- throw new IllegalArgumentException(
- "Notification type must not be null.");
- }
- if (listener == null) {
- throw new IllegalArgumentException("Listener must not be null.");
- }
-
- consumerListeners.put(notification, listener);
- listeners.put(notification, listener);
- log.trace("Registered listener for notification: " + notification);
- return null; // Return registration Object.
- }
-
- public void removeNotificationListener(QName notification,
- NotificationListener listener) {
- checkSessionState();
- if (notification == null) {
- throw new IllegalArgumentException(
- "Notification type must not be null.");
- }
- if (listener == null) {
- throw new IllegalArgumentException("Listener must not be null.");
- }
- consumerListeners.remove(notification, listener);
- listeners.remove(notification, listener);
- }
-
- public void closeSession() {
- closed = true;
- Map<QName, Collection<NotificationListener>> toRemove = consumerListeners
- .asMap();
- for (Entry<QName, Collection<NotificationListener>> entry : toRemove
- .entrySet()) {
- listeners.remove(entry.getKey(), entry.getValue());
- }
- }
-
- protected void checkSessionState() {
- if (closed)
- throw new IllegalStateException("Session is closed");
- }
- }
-
- private class NotificationProviderSessionImpl extends
- NotificationConsumerSessionImpl implements
- NotificationPublishService {
-
- @Override
- public void publish(CompositeNode notification) {
- checkSessionState();
- if (notification == null)
- throw new IllegalArgumentException(
- "Notification must not be null.");
- NotificationModule.this.sendNotification(notification);
- }
- }
-
- @Override
- public Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality() {
- return Collections.emptySet();
- }
-}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.impl;
+
+import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
+
+class GlobalRpcRegistration extends AbstractObjectRegistration<RpcImplementation> implements
+ RpcRegistration {
+ private final QName type;
+ private SchemaAwareRpcBroker router;
+
+ public GlobalRpcRegistration(final QName type, final RpcImplementation instance, final SchemaAwareRpcBroker router) {
+ super(instance);
+ this.type = type;
+ this.router = router;
+ }
+
+ @Override
+ public QName getType() {
+ return type;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ if (router != null) {
+ router.remove(this);
+ router = null;
+ }
+ }
+}
\ No newline at end of file
*/
package org.opendaylight.controller.sal.dom.broker.impl;
-import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
import org.opendaylight.controller.md.sal.common.api.data.DataModification;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.api.data.DataStore;
-import org.opendaylight.yangtools.yang.common.RpcError;
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.InstanceIdentifier;
import org.slf4j.Logger;
}
public RpcResult<Void> rollback(HashMapDataStoreTransaction transaction) {
- return Rpcs.<Void> getRpcResult(true, null,
- Collections.<RpcError> emptySet());
+ return RpcResultBuilder.<Void> success().build();
}
public RpcResult<Void> finish(HashMapDataStoreTransaction transaction) {
configuration.putAll(modification.getUpdatedConfigurationData());
operational.putAll(modification.getUpdatedOperationalData());
- return Rpcs.<Void> getRpcResult(true, null,
- Collections.<RpcError> emptySet());
+ return RpcResultBuilder.<Void> success().build();
}
public void remove(final Map<InstanceIdentifier, CompositeNode> map,
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.impl;
+
+import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+class RoutedRpcRegImpl extends AbstractObjectRegistration<RpcImplementation> implements
+ RoutedRpcRegistration {
+
+ private final QName type;
+ private final RoutedRpcSelector router;
+
+ public RoutedRpcRegImpl(final QName rpcType, final RpcImplementation implementation, final RoutedRpcSelector routedRpcSelector) {
+ super(implementation);
+ this.type = rpcType;
+ router = routedRpcSelector;
+ }
+
+ @Override
+ public void registerPath(final QName context, final InstanceIdentifier path) {
+ router.addPath(context, path, this);
+ }
+
+ @Override
+ public void unregisterPath(final QName context, final InstanceIdentifier path) {
+ router.removePath(context, path, this);
+ }
+
+ @Override
+ protected void removeRegistration() {
+
+ }
+
+ @Override
+ public QName getType() {
+ return type;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.dom.broker.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.opendaylight.controller.md.sal.dom.broker.spi.rpc.RpcRoutingStrategy;
+import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
+
+class RoutedRpcSelector implements RpcImplementation, AutoCloseable, Identifiable<RpcRoutingContext> {
+
+ private final RpcRoutingStrategy strategy;
+ private final Set<QName> supportedRpcs;
+ private final RpcRoutingContext identifier;
+ final ConcurrentMap<InstanceIdentifier, RoutedRpcRegImpl> implementations = new ConcurrentHashMap<>();
+ private final SchemaAwareRpcBroker router;
+
+ public RoutedRpcSelector(final RpcRoutingStrategy strategy, final SchemaAwareRpcBroker router) {
+ super();
+ this.strategy = strategy;
+ supportedRpcs = ImmutableSet.of(strategy.getIdentifier());
+ identifier = RpcRoutingContext.create(strategy.getContext(), strategy.getIdentifier());
+ this.router = router;
+ }
+
+ @Override
+ public RpcRoutingContext getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public void close() throws Exception {
+
+ }
+
+ @Override
+ public Set<QName> getSupportedRpcs() {
+ return supportedRpcs;
+ }
+
+ public RoutedRpcRegistration addRoutedRpcImplementation(final QName rpcType, final RpcImplementation implementation) {
+ return new RoutedRpcRegImpl(rpcType, implementation, this);
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(final QName rpc, final CompositeNode input) {
+ CompositeNode inputContainer = input.getFirstCompositeByName(QName.create(rpc,"input"));
+ checkArgument(inputContainer != null, "Rpc payload must contain input element");
+ SimpleNode<?> routeContainer = inputContainer.getFirstSimpleByName(strategy.getLeaf());
+ checkArgument(routeContainer != null, "Leaf %s must be set with value", strategy.getLeaf());
+ Object route = routeContainer.getValue();
+ checkArgument(route instanceof InstanceIdentifier,
+ "The routed node %s is not an instance identifier", route);
+ RpcImplementation potential = null;
+ if (route != null) {
+ RoutedRpcRegImpl potentialReg = implementations.get(route);
+ if (potentialReg != null) {
+ potential = potentialReg.getInstance();
+ }
+ }
+ if (potential == null) {
+ return router.invokeRpc(rpc, (InstanceIdentifier) route, input);
+ }
+ checkState(potential != null, "No implementation is available for rpc:%s path:%s", rpc, route);
+ return potential.invokeRpc(rpc, input);
+ }
+
+ public void addPath(final QName context, final InstanceIdentifier path, final RoutedRpcRegImpl routedRpcRegImpl) {
+ //checkArgument(strategy.getContext().equals(context),"Supplied context is not supported.");
+ RoutedRpcRegImpl previous = implementations.put(path, routedRpcRegImpl);
+ if (previous == null) {
+ router.notifyPathAnnouncement(context,strategy.getIdentifier(), path);
+ }
+
+ }
+
+ public void removePath(final QName context, final InstanceIdentifier path, final RoutedRpcRegImpl routedRpcRegImpl) {
+ boolean removed = implementations.remove(path, routedRpcRegImpl);
+ if (removed) {
+ router.notifyPathWithdrawal(context, strategy.getIdentifier(), path);
+ }
+ }
+}
\ No newline at end of file
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import com.google.common.base.Preconditions;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils;
+import org.opendaylight.controller.md.sal.dom.broker.spi.rpc.RpcRoutingStrategy;
import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
import org.opendaylight.controller.sal.dom.broker.spi.RpcRouter;
-import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.Identifiable;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.SimpleNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
private static final Logger LOG = LoggerFactory.getLogger(SchemaAwareRpcBroker.class);
- private static final QName CONTEXT_REFERENCE = QName.create("urn:opendaylight:yang:extension:yang-ext",
- "2013-07-09", "context-reference");
+
private final ListenerRegistry<RpcRegistrationListener> rpcRegistrationListeners = new ListenerRegistry<>();
private final ListenerRegistry<RouteChangeListener<RpcRoutingContext, InstanceIdentifier>> routeChangeListeners = new ListenerRegistry<>();
private SchemaContextProvider schemaProvider;
private RoutedRpcDefaultImplementation defaultDelegate;
- public SchemaAwareRpcBroker(String identifier, SchemaContextProvider schemaProvider) {
+ public SchemaAwareRpcBroker(final String identifier, final SchemaContextProvider schemaProvider) {
super();
this.identifier = identifier;
this.schemaProvider = schemaProvider;
return defaultImplementation;
}
- public void setDefaultImplementation(RpcImplementation defaultImplementation) {
+ public void setDefaultImplementation(final RpcImplementation defaultImplementation) {
this.defaultImplementation = defaultImplementation;
}
return schemaProvider;
}
- public void setSchemaProvider(SchemaContextProvider schemaProvider) {
+ public void setSchemaProvider(final SchemaContextProvider schemaProvider) {
this.schemaProvider = schemaProvider;
}
}
@Override
- public void setRoutedRpcDefaultDelegate(RoutedRpcDefaultImplementation defaultDelegate) {
+ public void setRoutedRpcDefaultDelegate(final RoutedRpcDefaultImplementation defaultDelegate) {
this.defaultDelegate = defaultDelegate;
}
@Override
- public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
+ public RoutedRpcRegistration addRoutedRpcImplementation(final QName rpcType, final RpcImplementation implementation) {
checkArgument(rpcType != null, "RPC Type should not be null");
checkArgument(implementation != null, "RPC Implementatoin should not be null");
return getOrCreateRoutedRpcRouter(rpcType).addRoutedRpcImplementation(rpcType, implementation);
}
- private RoutedRpcSelector getOrCreateRoutedRpcRouter(QName rpcType) {
+ private RoutedRpcSelector getOrCreateRoutedRpcRouter(final QName rpcType) {
RoutedRpcSelector potential = getRoutedRpcRouter(rpcType);
if (potential != null) {
return potential;
return potential;
}
RpcDefinition definition = findRpcDefinition(rpcType);
- RoutingStrategy strategy = getRoutingStrategy(definition);
- checkState(strategy instanceof RoutedRpcStrategy, "Rpc %s is not routed.", rpcType);
- potential = new RoutedRpcSelector((RoutedRpcStrategy) strategy, this);
+ RpcRoutingStrategy strategy = RpcRoutingStrategy.from(definition);
+ checkState(strategy.isContextBasedRouted(), "Rpc %s is not routed.", rpcType);
+ potential = new RoutedRpcSelector( strategy, this);
implementations.put(rpcType, potential);
return potential;
}
}
- private RoutedRpcSelector getRoutedRpcRouter(QName rpcType) {
+ private RoutedRpcSelector getRoutedRpcRouter(final QName rpcType) {
RpcImplementation potential = implementations.get(rpcType);
if (potential != null) {
checkState(potential instanceof RoutedRpcSelector, "Rpc %s is not routed.", rpcType);
}
@Override
- public RpcRegistration addRpcImplementation(QName rpcType, RpcImplementation implementation)
+ public RpcRegistration addRpcImplementation(final QName rpcType, final RpcImplementation implementation)
throws IllegalArgumentException {
checkArgument(rpcType != null, "RPC Type should not be null");
checkArgument(implementation != null, "RPC Implementatoin should not be null");
checkState(!hasRpcImplementation(rpcType), "Implementation already registered");
RpcDefinition definition = findRpcDefinition(rpcType);
- checkArgument(!isRoutedRpc(definition), "RPC Type must not be routed.");
+ checkArgument(!RpcRoutingStrategy.from(definition).isContextBasedRouted(), "RPC Type must not be content routed.");
GlobalRpcRegistration reg = new GlobalRpcRegistration(rpcType, implementation, this);
final RpcImplementation previous = implementations.putIfAbsent(rpcType, implementation);
Preconditions.checkState(previous == null, "Rpc %s is already registered.",rpcType);
return reg;
}
- private void notifyRpcAdded(QName rpcType) {
+ private void notifyRpcAdded(final QName rpcType) {
for (ListenerRegistration<RpcRegistrationListener> listener : rpcRegistrationListeners) {
try {
listener.getInstance().onRpcImplementationAdded(rpcType);
}
}
- private boolean isRoutedRpc(RpcDefinition definition) {
- return getRoutingStrategy(definition) instanceof RoutedRpcStrategy;
- }
-
@Override
- public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(RpcRegistrationListener listener) {
+ public ListenerRegistration<RpcRegistrationListener> addRpcRegistrationListener(final RpcRegistrationListener listener) {
ListenerRegistration<RpcRegistrationListener> reg = rpcRegistrationListeners.register(listener);
for (QName impl : implementations.keySet()) {
listener.onRpcImplementationAdded(impl);
}
@Override
- public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, CompositeNode input) {
+ public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(final QName rpc, final CompositeNode input) {
return findRpcImplemention(rpc).invokeRpc(rpc, input);
}
- private RpcImplementation findRpcImplemention(QName rpc) {
+ private RpcImplementation findRpcImplemention(final QName rpc) {
checkArgument(rpc != null, "Rpc name should not be null");
RpcImplementation potentialImpl = implementations.get(rpc);
if (potentialImpl != null) {
return potentialImpl;
}
- private boolean hasRpcImplementation(QName rpc) {
+ private boolean hasRpcImplementation(final QName rpc) {
return implementations.containsKey(rpc);
}
- private RpcDefinition findRpcDefinition(QName rpcType) {
+ private RpcDefinition findRpcDefinition(final QName rpcType) {
checkArgument(rpcType != null, "Rpc name must be supplied.");
checkState(schemaProvider != null, "Schema Provider is not available.");
SchemaContext ctx = schemaProvider.getSchemaContext();
return findRpcDefinition(rpcType, module.getRpcs());
}
- static private RpcDefinition findRpcDefinition(QName rpcType, Set<RpcDefinition> rpcs) {
+ static private RpcDefinition findRpcDefinition(final QName rpcType, final Set<RpcDefinition> rpcs) {
checkState(rpcs != null, "Rpc schema is not available.");
for (RpcDefinition rpc : rpcs) {
if (rpcType.equals(rpc.getQName())) {
throw new IllegalArgumentException("Supplied Rpc Type is not defined.");
}
- private RoutingStrategy getRoutingStrategy(RpcDefinition rpc) {
- ContainerSchemaNode input = rpc.getInput();
- if (input != null) {
- for (DataSchemaNode schemaNode : input.getChildNodes()) {
- Optional<QName> context = getRoutingContext(schemaNode);
- if (context.isPresent()) {
- return createRoutedStrategy(rpc, context.get(), schemaNode.getQName());
- }
- }
- }
- return createGlobalStrategy(rpc);
- }
-
- private static RoutingStrategy createRoutedStrategy(RpcDefinition rpc, QName context, QName leafNode) {
- return new RoutedRpcStrategy(rpc.getQName(), context, leafNode);
- }
-
- private Optional<QName> getRoutingContext(DataSchemaNode schemaNode) {
- for (UnknownSchemaNode extension : schemaNode.getUnknownSchemaNodes()) {
- if (CONTEXT_REFERENCE.equals(extension.getNodeType())) {
- return Optional.fromNullable(extension.getQName());
- }
- }
- return Optional.absent();
- }
-
- private static RoutingStrategy createGlobalStrategy(RpcDefinition rpc) {
- GlobalRpcStrategy ret = new GlobalRpcStrategy(rpc.getQName());
- return ret;
- }
-
@Override
- public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, InstanceIdentifier identifier, CompositeNode input) {
- checkState(defaultDelegate != null);
- return defaultDelegate.invokeRpc(rpc, identifier, input);
- }
-
- private static abstract class RoutingStrategy implements Identifiable<QName> {
-
- private final QName identifier;
-
- public RoutingStrategy(QName identifier) {
- super();
- this.identifier = identifier;
- }
-
- @Override
- public QName getIdentifier() {
- return identifier;
- }
- }
-
- private static class GlobalRpcStrategy extends RoutingStrategy {
-
- public GlobalRpcStrategy(QName identifier) {
- super(identifier);
- }
- }
-
- private static class RoutedRpcStrategy extends RoutingStrategy {
-
- private final QName context;
- private final QName leaf;
-
- public RoutedRpcStrategy(QName identifier, QName ctx, QName leaf) {
- super(identifier);
- this.context = ctx;
- this.leaf = leaf;
- }
-
- public QName getContext() {
- return context;
- }
-
- public QName getLeaf() {
- return leaf;
- }
+ public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(final QName rpc, final InstanceIdentifier route, final CompositeNode input) {
+ checkState(defaultDelegate != null, "No implementation is available for rpc:%s path:%s", rpc, route);
+ return defaultDelegate.invokeRpc(rpc, route, input);
}
- private static class RoutedRpcSelector implements RpcImplementation, AutoCloseable, Identifiable<RpcRoutingContext> {
-
- private final RoutedRpcStrategy strategy;
- private final Set<QName> supportedRpcs;
- private final RpcRoutingContext identifier;
- private RpcImplementation defaultDelegate;
- private final ConcurrentMap<InstanceIdentifier, RoutedRpcRegImpl> implementations = new ConcurrentHashMap<>();
- private final SchemaAwareRpcBroker router;
-
- public RoutedRpcSelector(RoutedRpcStrategy strategy, SchemaAwareRpcBroker router) {
- super();
- this.strategy = strategy;
- supportedRpcs = ImmutableSet.of(strategy.getIdentifier());
- identifier = RpcRoutingContext.create(strategy.context, strategy.getIdentifier());
- this.router = router;
- }
-
- @Override
- public RpcRoutingContext getIdentifier() {
- return identifier;
- }
-
- @Override
- public void close() throws Exception {
-
- }
-
- @Override
- public Set<QName> getSupportedRpcs() {
- return supportedRpcs;
- }
-
- public RoutedRpcRegistration addRoutedRpcImplementation(QName rpcType, RpcImplementation implementation) {
- return new RoutedRpcRegImpl(rpcType, implementation, this);
- }
-
- @Override
- public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, CompositeNode input) {
- CompositeNode inputContainer = input.getFirstCompositeByName(QName.create(rpc,"input"));
- checkArgument(inputContainer != null, "Rpc payload must contain input element");
- SimpleNode<?> routeContainer = inputContainer.getFirstSimpleByName(strategy.getLeaf());
- checkArgument(routeContainer != null, "Leaf %s must be set with value", strategy.getLeaf());
- Object route = routeContainer.getValue();
- checkArgument(route instanceof InstanceIdentifier,
- "The routed node %s is not an instance identifier", route);
- RpcImplementation potential = null;
- if (route != null) {
- RoutedRpcRegImpl potentialReg = implementations.get(route);
- if (potentialReg != null) {
- potential = potentialReg.getInstance();
- }
- }
- if (potential == null) {
- return router.invokeRpc(rpc, (InstanceIdentifier) route, input);
- }
- checkState(potential != null, "No implementation is available for rpc:%s path:%s", rpc, route);
- return potential.invokeRpc(rpc, input);
- }
-
- public void addPath(QName context, InstanceIdentifier path, RoutedRpcRegImpl routedRpcRegImpl) {
- //checkArgument(strategy.getContext().equals(context),"Supplied context is not supported.");
- RoutedRpcRegImpl previous = implementations.put(path, routedRpcRegImpl);
- if (previous == null) {
- router.notifyPathAnnouncement(context,strategy.getIdentifier(), path);
- }
-
- }
-
- public void removePath(QName context, InstanceIdentifier path, RoutedRpcRegImpl routedRpcRegImpl) {
- boolean removed = implementations.remove(path, routedRpcRegImpl);
- if (removed) {
- router.notifyPathWithdrawal(context, strategy.getIdentifier(), path);
- }
- }
- }
-
- private static class GlobalRpcRegistration extends AbstractObjectRegistration<RpcImplementation> implements
- RpcRegistration {
- private final QName type;
- private SchemaAwareRpcBroker router;
-
- public GlobalRpcRegistration(QName type, RpcImplementation instance, SchemaAwareRpcBroker router) {
- super(instance);
- this.type = type;
- this.router = router;
- }
-
- @Override
- public QName getType() {
- return type;
- }
-
- @Override
- protected void removeRegistration() {
- if (router != null) {
- router.remove(this);
- router = null;
- }
- }
- }
-
- private static class RoutedRpcRegImpl extends AbstractObjectRegistration<RpcImplementation> implements
- RoutedRpcRegistration {
-
- private final QName type;
- private final RoutedRpcSelector router;
-
- public RoutedRpcRegImpl(QName rpcType, RpcImplementation implementation, RoutedRpcSelector routedRpcSelector) {
- super(implementation);
- this.type = rpcType;
- router = routedRpcSelector;
- }
-
- @Override
- public void registerPath(QName context, InstanceIdentifier path) {
- router.addPath(context, path, this);
- }
-
- @Override
- public void unregisterPath(QName context, InstanceIdentifier path) {
- router.removePath(context, path, this);
- }
-
- @Override
- protected void removeRegistration() {
-
- }
-
- @Override
- public QName getType() {
- return type;
- }
-
- }
-
- private void remove(GlobalRpcRegistration registration) {
+ void remove(final GlobalRpcRegistration registration) {
implementations.remove(registration.getType(), registration);
}
- private void notifyPathAnnouncement(QName context, QName identifier, InstanceIdentifier path) {
+ void notifyPathAnnouncement(final QName context, final QName identifier, final InstanceIdentifier path) {
RpcRoutingContext contextWrapped = RpcRoutingContext.create(context, identifier);
RouteChange<RpcRoutingContext, InstanceIdentifier> change = RoutingUtils.announcementChange(contextWrapped , path);
for(ListenerRegistration<RouteChangeListener<RpcRoutingContext, InstanceIdentifier>> routeListener : routeChangeListeners) {
routeListener.getInstance().onRouteChange(change);
} catch (Exception e) {
LOG.error("Unhandled exception during invoking onRouteChange for {}",routeListener.getInstance(),e);
-
}
}
}
-
-
- private void notifyPathWithdrawal(QName context,QName identifier, InstanceIdentifier path) {
+ void notifyPathWithdrawal(final QName context,final QName identifier, final InstanceIdentifier path) {
RpcRoutingContext contextWrapped = RpcRoutingContext.create(context, identifier);
RouteChange<RpcRoutingContext, InstanceIdentifier> change = RoutingUtils.removalChange(contextWrapped , path);
for(ListenerRegistration<RouteChangeListener<RpcRoutingContext, InstanceIdentifier>> routeListener : routeChangeListeners) {
@Override
public <L extends RouteChangeListener<RpcRoutingContext, InstanceIdentifier>> ListenerRegistration<L> registerRouteChangeListener(
- L listener) {
+ final L listener) {
ListenerRegistration<L> reg = routeChangeListeners.registerWithType(listener);
RouteChange<RpcRoutingContext, InstanceIdentifier> initial = createInitialRouteChange();
try {
package org.opendaylight.controller.sal.dom.broker.osgi;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
public List<ListenableFuture<?>> call() throws Exception {
List<ListenableFuture<?>> builder = new ArrayList<>(txNum);
for (DOMDataReadWriteTransaction tx :transactions) {
- builder.add(tx.commit());
+ builder.add(tx.submit());
}
return builder;
}
measure("Txs:1 Submit", new Callable<ListenableFuture<?>>() {
@Override
public ListenableFuture<?> call() throws Exception {
- return writeTx.commit();
+ return writeTx.submit();
}
}).get();
return null;
TestModel.TEST_PATH);
assertTrue(writeTxContainer.get().isPresent());
- writeTx.commit().get();
+ writeTx.submit().get();
Optional<NormalizedNode<?, ?>> afterCommitRead = domBroker.newReadOnlyTransaction()
.read(OPERATIONAL, TestModel.TEST_PATH).get();
*/
package org.opendaylight.controller.md.sal.dom.broker.impl;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Test;
-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.dom.api.DOMDataReadTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
import org.opendaylight.controller.sal.core.spi.data.DOMStore;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
* First transaction is marked as ready, we are able to allocate chained
* transactions
*/
- ListenableFuture<RpcResult<TransactionStatus>> firstWriteTxFuture = firstTx.commit();
+ ListenableFuture<Void> firstWriteTxFuture = firstTx.submit();
/**
* We alocate chained transaction - read transaction.
/**
* third transaction is sealed and commited
*/
- ListenableFuture<RpcResult<TransactionStatus>> thirdDeleteTxFuture = thirdDeleteTx.commit();
+ ListenableFuture<Void> thirdDeleteTxFuture = thirdDeleteTx.submit();
assertCommitSuccessful(thirdDeleteTxFuture);
/**
return tx;
}
- private static void assertCommitSuccessful(final ListenableFuture<RpcResult<TransactionStatus>> future)
+ private static void assertCommitSuccessful(final ListenableFuture<Void> future)
throws InterruptedException, ExecutionException {
- RpcResult<TransactionStatus> rpcResult = future.get();
- assertTrue(rpcResult.isSuccessful());
- assertEquals(TransactionStatus.COMMITED, rpcResult.getResult());
+ future.get();
}
private static void assertTestContainerExists(final DOMDataReadTransaction readTx) throws InterruptedException,
--- /dev/null
+/*
+ * 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;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.base.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService.DOMMountPointBuilder;
+import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+public class MountPointServiceTest {
+
+ private DOMMountPointService mountService;
+ private static final InstanceIdentifier PATH = InstanceIdentifier.of(QName.create("namespace", "12-12-2012", "top"));
+
+ @Before
+ public void setup() {
+ mountService = new DOMMountPointServiceImpl();
+ }
+
+ @Test
+ public void createSimpleMountPoint() {
+ Optional<DOMMountPoint> mountNotPresent = mountService.getMountPoint(PATH);
+ assertFalse(mountNotPresent.isPresent());
+ DOMMountPointBuilder mountBuilder = mountService.createMountPoint(PATH);
+ mountBuilder.register();
+
+ Optional<DOMMountPoint> mountPresent = mountService.getMountPoint(PATH);
+ assertTrue(mountPresent.isPresent());
+ }
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.dom.broker.spi.mount;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMService;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ClassToInstanceMap;
+import com.google.common.collect.ImmutableClassToInstanceMap;
+
+public class SimpleDOMMountPoint implements DOMMountPoint {
+
+ private final InstanceIdentifier identifier;
+ private final ClassToInstanceMap<DOMService> services;
+ private final SchemaContext schemaContext;
+
+ public static final SimpleDOMMountPoint create(final InstanceIdentifier identifier, final ClassToInstanceMap<DOMService> services, final SchemaContext ctx) {
+ return new SimpleDOMMountPoint(identifier, services, ctx);
+ }
+ private SimpleDOMMountPoint(final InstanceIdentifier identifier, final ClassToInstanceMap<DOMService> services, final SchemaContext ctx) {
+ this.identifier = identifier;
+ this.services = ImmutableClassToInstanceMap.copyOf(services);
+ this.schemaContext = ctx;
+ }
+
+ @Override
+ public InstanceIdentifier getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public SchemaContext getSchemaContext() {
+ return schemaContext;
+ }
+
+ @Override
+ public <T extends DOMService> Optional<T> getService(final Class<T> cls) {
+ return Optional.fromNullable(services.getInstance(cls));
+ }
+}
--- /dev/null
+/*
+ * 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.spi.rpc;
+
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+
+import com.google.common.base.Optional;
+
+public abstract class RpcRoutingStrategy implements Identifiable<QName> {
+
+ private final QName identifier;
+ private static final QName CONTEXT_REFERENCE = QName.create("urn:opendaylight:yang:extension:yang-ext",
+ "2013-07-09", "context-reference");
+
+ private RpcRoutingStrategy(final QName identifier) {
+ super();
+ this.identifier = identifier;
+ }
+
+ /**
+ * Returns leaf QName in which RPC Route is stored
+ *
+ *
+ * @return leaf QName in which RPC Route is stored
+ * @throws UnsupportedOperationException If RPC is not content routed.
+ * ({@link #isContextBasedRouted()} returned <code>false</code>)
+ */
+ public abstract QName getLeaf();
+
+ /**
+ * Returns identity QName which represents RPC Routing context
+ *
+ * @return identity QName which represents RPC Routing context
+ * @throws UnsupportedOperationException If RPC is not content routed.
+ * ({@link #isContextBasedRouted()} returned <code>false</code>)
+ */
+ public abstract QName getContext();
+
+ @Override
+ public QName getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * Returns true if RPC is routed by context.
+ *
+ * @return true if RPC is routed by content.
+ */
+ public abstract boolean isContextBasedRouted();
+
+ public static RpcRoutingStrategy from(final RpcDefinition rpc) {
+ ContainerSchemaNode input = rpc.getInput();
+ if (input != null) {
+ for (DataSchemaNode schemaNode : input.getChildNodes()) {
+ Optional<QName> context = getRoutingContext(schemaNode);
+ if (context.isPresent()) {
+ return createRoutedStrategy(rpc, context.get(), schemaNode.getQName());
+ }
+ }
+ }
+ return createGlobalStrategy(rpc);
+ }
+
+ public static Optional<QName> getRoutingContext(final DataSchemaNode schemaNode) {
+ for (UnknownSchemaNode extension : schemaNode.getUnknownSchemaNodes()) {
+ if (CONTEXT_REFERENCE.equals(extension.getNodeType())) {
+ return Optional.fromNullable(extension.getQName());
+ }
+ }
+ return Optional.absent();
+ }
+
+ private static RpcRoutingStrategy createRoutedStrategy(final RpcDefinition rpc, final QName context, final QName leafNode) {
+ return new RoutedRpcStrategy(rpc.getQName(), context, leafNode);
+ }
+
+
+
+ private static RpcRoutingStrategy createGlobalStrategy(final RpcDefinition rpc) {
+ GlobalRpcStrategy ret = new GlobalRpcStrategy(rpc.getQName());
+ return ret;
+ }
+
+ private static class RoutedRpcStrategy extends RpcRoutingStrategy {
+
+ final QName context;
+ private final QName leaf;
+
+ private RoutedRpcStrategy(final QName identifier, final QName ctx, final QName leaf) {
+ super(identifier);
+ this.context = ctx;
+ this.leaf = leaf;
+ }
+
+ @Override
+ public QName getContext() {
+ return context;
+ }
+
+ @Override
+ public QName getLeaf() {
+ return leaf;
+ }
+
+ @Override
+ public boolean isContextBasedRouted() {
+ return true;
+ }
+ }
+
+ private static class GlobalRpcStrategy extends RpcRoutingStrategy {
+
+ public GlobalRpcStrategy(final QName identifier) {
+ super(identifier);
+ }
+
+ @Override
+ public boolean isContextBasedRouted() {
+ return false;
+ }
+
+ @Override
+ public QName getContext() {
+ throw new UnsupportedOperationException("Not routed strategy does not have context.");
+ }
+
+ @Override
+ public QName getLeaf() {
+ throw new UnsupportedOperationException("Not routed strategy does not have context.");
+ }
+ }
+}
\ No newline at end of file
package org.opendaylight.controller.config.yang.inmemory_datastore_provider;
-import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.Executors;
+
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import com.google.common.util.concurrent.MoreExecutors;
+
public class InMemoryConfigDataStoreProviderModule extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryConfigDataStoreProviderModule {
- public InMemoryConfigDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ public InMemoryConfigDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
}
- public InMemoryConfigDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryConfigDataStoreProviderModule oldModule, java.lang.AutoCloseable oldInstance) {
+ public InMemoryConfigDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryConfigDataStoreProviderModule oldModule, final java.lang.AutoCloseable oldInstance) {
super(identifier, dependencyResolver, oldModule, oldInstance);
}
@Override
public java.lang.AutoCloseable createInstance() {
- InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-CFG", MoreExecutors.sameThreadExecutor());
+ InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-CFG", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
getSchemaServiceDependency().registerSchemaServiceListener(ids);
return ids;
}
package org.opendaylight.controller.config.yang.inmemory_datastore_provider;
-import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.Executors;
+
import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import com.google.common.util.concurrent.MoreExecutors;
+
public class InMemoryOperationalDataStoreProviderModule extends org.opendaylight.controller.config.yang.inmemory_datastore_provider.AbstractInMemoryOperationalDataStoreProviderModule {
- public InMemoryOperationalDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ public InMemoryOperationalDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
super(identifier, dependencyResolver);
}
- public InMemoryOperationalDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryOperationalDataStoreProviderModule oldModule, java.lang.AutoCloseable oldInstance) {
+ public InMemoryOperationalDataStoreProviderModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.inmemory_datastore_provider.InMemoryOperationalDataStoreProviderModule oldModule, final java.lang.AutoCloseable oldInstance) {
super(identifier, dependencyResolver, oldModule, oldInstance);
}
@Override
public java.lang.AutoCloseable createInstance() {
- InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-OPER", MoreExecutors.sameThreadExecutor());
+ InMemoryDOMDataStore ids = new InMemoryDOMDataStore("DOM-OPER", MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()));
getSchemaServiceDependency().registerSchemaServiceListener(ids);
return ids;
}
--- /dev/null
+/*
+ * 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.store.impl;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.bi.ba.rpcservice.rev140701.RockTheHouseInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class SchemaUpdateForTransactionTest {
+
+ private static final InstanceIdentifier TOP_PATH = InstanceIdentifier.of(Top.QNAME);
+ private SchemaContext schemaContext;
+ private InMemoryDOMDataStore domStore;
+
+ @Before
+ public void setupStore() {
+ domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor());
+ loadSchemas(RockTheHouseInput.class);
+ }
+
+ public void loadSchemas(final Class<?>... classes) {
+ YangModuleInfo moduleInfo;
+ try {
+ ModuleInfoBackedContext context = ModuleInfoBackedContext.create();
+ for (Class<?> clz : classes) {
+ moduleInfo = BindingReflections.getModuleInfo(clz);
+
+ context.registerModuleInfo(moduleInfo);
+ }
+ schemaContext = context.tryToCreateSchemaContext().get();
+ domStore.onGlobalContextUpdated(schemaContext);
+ } catch (Exception e) {
+ Throwables.propagateIfPossible(e);
+ }
+ }
+
+ /**
+ * Test suite tests allocating transaction when schema context
+ * does not contain module necessary for client write,
+ * then triggering update of global schema context
+ * and then performing write (according to new module).
+ *
+ * If transaction between allocation and schema context was
+ * unmodified, it is safe to change its schema context
+ * to new one (e.g. it will be same as if allocated after
+ * schema context update.)
+ *
+ * @throws InterruptedException
+ * @throws ExecutionException
+ */
+ @Test
+ public void testTransactionSchemaUpdate() throws InterruptedException, ExecutionException {
+
+ assertNotNull(domStore);
+
+ // We allocate transaction, initial schema context does not
+ // contain Lists model
+ DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+ assertNotNull(writeTx);
+
+ // we trigger schema context update to contain Lists model
+ loadSchemas(RockTheHouseInput.class, Top.class);
+
+ /**
+ *
+ * Writes /test in writeTx, this write should not fail
+ * with IllegalArgumentException since /test is in
+ * schema context.
+ *
+ */
+ writeTx.write(TOP_PATH, ImmutableNodes.containerNode(Top.QNAME));
+
+ }
+
+}
*/
package org.opendaylight.controller.sal.connect.netconf;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.io.InputStream;
+import java.util.LinkedList;
+import java.util.List;
import java.util.concurrent.ExecutorService;
-
import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.controller.sal.connect.api.MessageTransformer;
import org.opendaylight.controller.sal.connect.api.RemoteDevice;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
/**
* This is a mediator between NetconfDeviceCommunicator and NetconfDeviceSalFacade
*/
private final MessageTransformer<NetconfMessage> messageTransformer;
private final SchemaContextProviderFactory schemaContextProviderFactory;
private final SchemaSourceProviderFactory<InputStream> sourceProviderFactory;
+ private final NotificationHandler notificationHandler;
public static NetconfDevice createNetconfDevice(final RemoteDeviceId id,
final AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider,
this.sourceProviderFactory = sourceProviderFactory;
this.processingExecutor = MoreExecutors.listeningDecorator(processingExecutor);
this.schemaContextProviderFactory = schemaContextProviderFactory;
+ this.notificationHandler = new NotificationHandler(salFacade, messageTransformer, id);
}
@Override
final SchemaContextProvider schemaContextProvider = setUpSchemaContext(delegate, remoteSessionCapabilities);
updateMessageTransformer(schemaContextProvider);
salFacade.onDeviceConnected(schemaContextProvider, remoteSessionCapabilities, deviceRpc);
+ notificationHandler.onRemoteSchemaUp();
}
});
@Override
public void onNotification(final NetconfMessage notification) {
- final CompositeNode parsedNotification = messageTransformer.toNotification(notification);
- salFacade.onNotification(parsedNotification);
+ notificationHandler.handleNotification(notification);
+ }
+
+ /**
+ * Handles incoming notifications. Either caches them(until onRemoteSchemaUp is called) or passes to sal Facade.
+ */
+ private final static class NotificationHandler {
+
+ private final RemoteDeviceHandler<?> salFacade;
+ private final List<NetconfMessage> cache = new LinkedList<>();
+ private final MessageTransformer<NetconfMessage> messageTransformer;
+ private boolean passNotifications = false;
+ private final RemoteDeviceId id;
+
+ NotificationHandler(final RemoteDeviceHandler<?> salFacade, final MessageTransformer<NetconfMessage> messageTransformer, final RemoteDeviceId id) {
+ this.salFacade = salFacade;
+ this.messageTransformer = messageTransformer;
+ this.id = id;
+ }
+
+ synchronized void handleNotification(final NetconfMessage notification) {
+ if(passNotifications) {
+ passNotification(messageTransformer.toNotification(notification));
+ } else {
+ cacheNotification(notification);
+ }
+ }
+
+ /**
+ * Forward all cached notifications and pass all notifications from this point directly to sal facade.
+ */
+ synchronized void onRemoteSchemaUp() {
+ passNotifications = true;
+
+ for (final NetconfMessage cachedNotification : cache) {
+ passNotification(messageTransformer.toNotification(cachedNotification));
+ }
+
+ cache.clear();
+ }
+
+ private void cacheNotification(final NetconfMessage notification) {
+ Preconditions.checkState(passNotifications == false);
+
+ logger.debug("{}: Caching notification {}, remote schema not yet fully built", id, notification);
+ if(logger.isTraceEnabled()) {
+ logger.trace("{}: Caching notification {}", id, XmlUtil.toString(notification.getDocument()));
+ }
+
+ cache.add(notification);
+ }
+
+ private void passNotification(final CompositeNode parsedNotification) {
+ logger.debug("{}: Forwarding notification {}", id, parsedNotification);
+ Preconditions.checkNotNull(parsedNotification);
+ salFacade.onNotification(parsedNotification);
+ }
+
}
}
package org.opendaylight.controller.sal.connect.netconf.listener;
import java.util.ArrayDeque;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
-import org.opendaylight.controller.sal.common.util.RpcErrors;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.connect.api.RemoteDevice;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
-import org.opendaylight.controller.sal.connect.util.FailedRpcResult;
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final NetconfClientConfiguration config) {
if(config instanceof NetconfReconnectingClientConfiguration) {
dispatch.createReconnectingClient((NetconfReconnectingClientConfiguration) config);
+ } else {
+ dispatch.createClient(config);
}
-
- dispatch.createClient(config);
}
private void tearDown( String reason ) {
private RpcResult<NetconfMessage> createErrorRpcResult( RpcError.ErrorType errorType, String message )
{
- return new FailedRpcResult<NetconfMessage>( RpcErrors.getRpcError( null,
- NetconfDocumentedException.ErrorTag.operation_failed.getTagValue(),
- null, RpcError.ErrorSeverity.ERROR, message, errorType, null ) );
+ return RpcResultBuilder.<NetconfMessage>failed()
+ .withError( errorType, NetconfDocumentedException.ErrorTag.operation_failed.getTagValue(),
+ message )
+ .build();
}
@Override
logger.warn( "{}: Invalid request-reply match, reply message contains different message-id, request: {}, response: {}",
id, msgToS( request.request ), msgToS( message ), e );
- request.future.set( new FailedRpcResult<NetconfMessage>(
- NetconfMessageTransformUtil.toRpcError( e ) ) );
+ request.future.set( RpcResultBuilder.<NetconfMessage>failed()
+ .withRpcError( NetconfMessageTransformUtil.toRpcError( e ) ).build() );
return;
}
logger.warn( "{}: Error reply from remote device, request: {}, response: {}", id,
msgToS( request.request ), msgToS( message ), e );
- request.future.set( new FailedRpcResult<NetconfMessage>(
- NetconfMessageTransformUtil.toRpcError( e ) ) );
+ request.future.set( RpcResultBuilder.<NetconfMessage>failed()
+ .withRpcError( NetconfMessageTransformUtil.toRpcError( e ) ).build() );
return;
}
- request.future.set(Rpcs.getRpcResult( true, message, Collections.<RpcError>emptySet() ) );
+ request.future.set( RpcResultBuilder.<NetconfMessage>success( message ).build() );
}
}
package org.opendaylight.controller.sal.connect.netconf.listener;
-import java.util.Arrays;
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+
import java.util.Collection;
+import java.util.HashSet;
import java.util.Set;
import org.opendaylight.controller.netconf.client.NetconfClientSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Objects;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-
public final class NetconfSessionCapabilities {
+ private static final class ParameterMatcher {
+ private final Predicate<String> predicate;
+ private final int skipLength;
+
+ ParameterMatcher(final String name) {
+ predicate = new Predicate<String>() {
+ @Override
+ public boolean apply(final String input) {
+ return input.startsWith(name);
+ }
+ };
- private static final Logger logger = LoggerFactory.getLogger(NetconfSessionCapabilities.class);
+ this.skipLength = name.length();
+ }
- private final Set<String> capabilities;
+ private String from(final Iterable<String> params) {
+ final Optional<String> o = Iterables.tryFind(params, predicate);
+ if (!o.isPresent()) {
+ return null;
+ }
+
+ return o.get().substring(skipLength);
+ }
+ }
+
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfSessionCapabilities.class);
+ private static final ParameterMatcher MODULE_PARAM = new ParameterMatcher("module=");
+ private static final ParameterMatcher REVISION_PARAM = new ParameterMatcher("revision=");
+ private static final ParameterMatcher BROKEN_REVISON_PARAM = new ParameterMatcher("amp;revision=");
+ private static final Splitter AMP_SPLITTER = Splitter.on('&');
+ private static final Predicate<String> CONTAINS_REVISION = new Predicate<String>() {
+ @Override
+ public boolean apply(final String input) {
+ return input.contains("revision=");
+ }
+ };
private final Set<QName> moduleBasedCaps;
+ private final Set<String> capabilities;
private NetconfSessionCapabilities(final Set<String> capabilities, final Set<QName> moduleBasedCaps) {
- this.capabilities = capabilities;
- this.moduleBasedCaps = moduleBasedCaps;
+ this.capabilities = Preconditions.checkNotNull(capabilities);
+ this.moduleBasedCaps = Preconditions.checkNotNull(moduleBasedCaps);
}
public Set<QName> getModuleBasedCaps() {
}
public static NetconfSessionCapabilities fromStrings(final Collection<String> capabilities) {
- final Set<QName> moduleBasedCaps = Sets.newHashSet();
+ final Set<QName> moduleBasedCaps = new HashSet<>();
for (final String capability : capabilities) {
- if(isModuleBasedCapability(capability)) {
- final String[] parts = capability.split("\\?");
- final String namespace = parts[0];
- final FluentIterable<String> queryParams = FluentIterable.from(Arrays.asList(parts[1].split("&")));
-
- String revision = getStringAndTransform(queryParams, "revision=", "revision=");
-
- final String moduleName = getStringAndTransform(queryParams, "module=", "module=");
+ final int qmark = capability.indexOf('?');
+ if (qmark == -1) {
+ continue;
+ }
- if (revision == null) {
- logger.debug("Netconf device was not reporting revision correctly, trying to get amp;revision=");
- revision = getStringAndTransform(queryParams, "amp;revision=", "amp;revision=");
+ final String namespace = capability.substring(0, qmark);
+ final Iterable<String> queryParams = AMP_SPLITTER.split(capability.substring(qmark + 1));
+ final String moduleName = MODULE_PARAM.from(queryParams);
+ if (moduleName == null) {
+ continue;
+ }
- if (revision == null) {
- logger.warn("Netconf device returned revision incorrectly escaped for {}", capability);
- }
- }
+ String revision = REVISION_PARAM.from(queryParams);
+ if (revision != null) {
moduleBasedCaps.add(QName.create(namespace, revision, moduleName));
+ continue;
}
- }
-
- return new NetconfSessionCapabilities(Sets.newHashSet(capabilities), moduleBasedCaps);
- }
- private static boolean isModuleBasedCapability(final String capability) {
- return capability.contains("?") && capability.contains("module=") && capability.contains("revision=");
- }
+ /*
+ * We have seen devices which mis-escape revision, but the revision may not
+ * even be there. First check if there is a substring that matches revision.
+ */
+ if (!Iterables.any(queryParams, CONTAINS_REVISION)) {
+ continue;
+ }
- private static String getStringAndTransform(final Iterable<String> queryParams, final String match,
- final String substringToRemove) {
- final Optional<String> found = Iterables.tryFind(queryParams, new Predicate<String>() {
- @Override
- public boolean apply(final String input) {
- return input.startsWith(match);
+ LOG.debug("Netconf device was not reporting revision correctly, trying to get amp;revision=");
+ revision = BROKEN_REVISON_PARAM.from(queryParams);
+ if (revision == null) {
+ LOG.warn("Netconf device returned revision incorrectly escaped for {}, ignoring it", capability);
}
- });
- return found.isPresent() ? found.get().replaceAll(substringToRemove, "") : null;
- }
+ // FIXME: do we really want to continue here?
+ moduleBasedCaps.add(QName.create(namespace, revision, moduleName));
+ }
+ return new NetconfSessionCapabilities(ImmutableSet.copyOf(capabilities), ImmutableSet.copyOf(moduleBasedCaps));
+ }
}
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
import org.opendaylight.controller.md.sal.common.api.data.DataModification;
-import org.opendaylight.controller.sal.common.util.RpcErrors;
-import org.opendaylight.controller.sal.connect.util.FailedRpcResult;
import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.yangtools.yang.common.RpcError;
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.InstanceIdentifier;
import org.slf4j.Logger;
@Override
public RpcResult<Void> finish() throws IllegalStateException {
- return new FailedRpcResult<>(RpcErrors.getRpcError(null, null, null, RpcError.ErrorSeverity.ERROR,
- id + ": Unexpected operation error during pre-commit operations", RpcError.ErrorType.APPLICATION, e));
+ return RpcResultBuilder.<Void>failed().withError( RpcError.ErrorType.APPLICATION,
+ id + ": Unexpected operation error during pre-commit operations", e ).build();
}
@Override
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
+
import java.util.Collections;
import java.util.Set;
import javax.annotation.Nullable;
+
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.connect.api.MessageTransformer;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
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 com.google.common.util.concurrent.ListenableFuture;
if (netconfMessageRpcResult.isSuccessful()) {
return transformer.toRpcResult(netconfMessageRpcResult.getResult(), rpc);
} else {
- return Rpcs.getRpcResult(false, netconfMessageRpcResult.getErrors());
+ return RpcResultBuilder.<CompositeNode> failed()
+ .withRpcErrors(netconfMessageRpcResult.getErrors()).build();
}
}
*/
package org.opendaylight.controller.sal.connect.netconf.sal;
+import com.google.common.base.Preconditions;
import java.util.Collection;
import java.util.Collections;
-
import java.util.concurrent.ExecutorService;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Preconditions;
-
final class NetconfDeviceSalProvider implements AutoCloseable, Provider, BindingAwareProvider {
private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceSalProvider.class);
public MountProvisionInstance getMountInstance() {
Preconditions.checkState(mountInstance != null,
- "%s: Sal provider was not initialized by sal. Cannot publish notification", id);
+ "%s: Sal provider was not initialized by sal. Cannot get mount instance", id);
return mountInstance;
}
public NetconfDeviceDatastoreAdapter getDatastoreAdapter() {
Preconditions.checkState(datastoreAdapter != null,
- "%s: Sal provider %s was not initialized by sal. Cannot publish notification", id);
+ "%s: Sal provider %s was not initialized by sal. Cannot get datastore adapter", id);
return datastoreAdapter;
}
import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
import org.opendaylight.controller.md.sal.common.api.data.DataModification;
-import org.opendaylight.controller.sal.common.util.RpcErrors;
import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
-import org.opendaylight.controller.sal.connect.util.FailedRpcResult;
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.RpcError;
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.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.api.SimpleNode;
import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
}
private void sendMerge(final InstanceIdentifier key, final CompositeNode value) throws InterruptedException, ExecutionException {
- sendEditRpc(createEditConfigStructure(key, Optional.<String>absent(), Optional.of(value)), Optional.<String>absent());
+ sendEditRpc(createEditConfigStructure(key, Optional.<ModifyAction>absent(), Optional.of(value)), Optional.<ModifyAction>absent());
}
private void sendDelete(final InstanceIdentifier toDelete) throws InterruptedException, ExecutionException {
- // FIXME use org.opendaylight.yangtools.yang.data.api.ModifyAction instead of strings
- // TODO add string lowercase value to ModifyAction enum entries
- sendEditRpc(createEditConfigStructure(toDelete, Optional.of("delete"), Optional.<CompositeNode>absent()), Optional.of("none"));
+ sendEditRpc(createEditConfigStructure(toDelete, Optional.of(ModifyAction.DELETE), Optional.<CompositeNode>absent()), Optional.of(ModifyAction.NONE));
}
- private void sendEditRpc(final CompositeNode editStructure, final Optional<String> defaultOperation) throws InterruptedException, ExecutionException {
+ private void sendEditRpc(final CompositeNode editStructure, final Optional<ModifyAction> defaultOperation) throws InterruptedException, ExecutionException {
final ImmutableCompositeNode editConfigRequest = createEditConfigRequest(editStructure, defaultOperation);
final RpcResult<CompositeNode> rpcResult = rpc.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, editConfigRequest).get();
}
}
- private ImmutableCompositeNode createEditConfigRequest(final CompositeNode editStructure, final Optional<String> defaultOperation) {
+ private ImmutableCompositeNode createEditConfigRequest(final CompositeNode editStructure, final Optional<ModifyAction> defaultOperation) {
final CompositeNodeBuilder<ImmutableCompositeNode> ret = ImmutableCompositeNode.builder();
// Target
// Default operation
if(defaultOperation.isPresent()) {
- SimpleNode<String> defOp = NodeFactory.createImmutableSimpleNode(NETCONF_DEFAULT_OPERATION_QNAME, null, defaultOperation.get());
+ final SimpleNode<String> defOp = NodeFactory.createImmutableSimpleNode(NETCONF_DEFAULT_OPERATION_QNAME, null, modifyOperationToXmlString(defaultOperation.get()));
ret.add(defOp);
}
return ret.toInstance();
}
- private CompositeNode createEditConfigStructure(final InstanceIdentifier dataPath, final Optional<String> operation,
+ private CompositeNode createEditConfigStructure(final InstanceIdentifier dataPath, final Optional<ModifyAction> operation,
final Optional<CompositeNode> lastChildOverride) {
Preconditions.checkArgument(Iterables.isEmpty(dataPath.getPathArguments()) == false, "Instance identifier with empty path %s", dataPath);
return predicates;
}
- private CompositeNode getDeepestEditElement(final PathArgument arg, final Optional<String> operation, final Optional<CompositeNode> lastChildOverride) {
+ private CompositeNode getDeepestEditElement(final PathArgument arg, final Optional<ModifyAction> operation, final Optional<CompositeNode> lastChildOverride) {
final CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
builder.setQName(arg.getNodeType());
addPredicatesToCompositeNodeBuilder(predicates, builder);
if (operation.isPresent()) {
- builder.setAttribute(NETCONF_OPERATION_QNAME, operation.get());
+ builder.setAttribute(NETCONF_OPERATION_QNAME, modifyOperationToXmlString(operation.get()));
}
if (lastChildOverride.isPresent()) {
final List<Node<?>> children = lastChildOverride.get().getValue();
return builder.toInstance();
}
+ private String modifyOperationToXmlString(final ModifyAction operation) {
+ return operation.name().toLowerCase();
+ }
+
/**
* Send commit rpc to finish the transaction
* In case of failure or unexpected error response, ExecutionException is thrown
throw new RuntimeException(id + ": Interrupted while waiting for response", e);
} catch (final ExecutionException e) {
LOG.warn("{}: Failed to finish commit operation", id, e);
- return new FailedRpcResult<>(RpcErrors.getRpcError(null, null, null, RpcError.ErrorSeverity.ERROR,
- id + ": Unexpected operation error during commit operation", RpcError.ErrorType.APPLICATION, e));
+ return RpcResultBuilder.<Void>failed().withError( RpcError.ErrorType.APPLICATION,
+ id + ": Unexpected operation error during commit operation", e ).build();
}
}
package org.opendaylight.controller.sal.connect.netconf.schema.mapping;
import com.google.common.base.Optional;
-import java.util.Collections;
+
import java.util.List;
import java.util.Set;
+
import javax.activation.UnsupportedDataTypeException;
+
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.connect.api.MessageTransformer;
import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
import org.opendaylight.controller.sal.connect.util.MessageCounter;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
return toRpcResult(message, rpc, schemaContext.get());
} else {
final CompositeNode node = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument());
- return Rpcs.getRpcResult(true, node, Collections.<RpcError>emptySet());
+ return RpcResultBuilder.success( node ).build();
}
}
compositeNode = (CompositeNode) XmlDocumentUtils.toDomNode(message.getDocument());
}
- return Rpcs.getRpcResult(true, compositeNode, Collections.<RpcError> emptySet());
+ return RpcResultBuilder.success( compositeNode ).build();
}
@Override
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.net.URI;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
-import org.opendaylight.controller.sal.common.util.RpcErrors;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.Node;
return null;
}
- for (final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument component : Lists
- .reverse(identifier.getPath())) {
+ for (final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument component : identifier.getReversePathArguments()) {
if (component instanceof InstanceIdentifier.NodeIdentifierWithPredicates) {
previous = toNode((InstanceIdentifier.NodeIdentifierWithPredicates)component, previous);
} else {
}
}
- return RpcErrors.getRpcError( null, ex.getErrorTag().getTagValue(), infoBuilder.toString(),
- toRpcErrorSeverity( ex.getErrorSeverity() ), ex.getLocalizedMessage(),
- toRpcErrorType( ex.getErrorType() ), ex.getCause() );
+ ErrorSeverity severity = toRpcErrorSeverity( ex.getErrorSeverity() );
+ return severity == ErrorSeverity.ERROR ?
+ RpcResultBuilder.newError(
+ toRpcErrorType( ex.getErrorType() ), ex.getErrorTag().getTagValue(),
+ ex.getLocalizedMessage(), null, infoBuilder.toString(), ex.getCause() ) :
+ RpcResultBuilder.newWarning(
+ toRpcErrorType( ex.getErrorType() ), ex.getErrorTag().getTagValue(),
+ ex.getLocalizedMessage(), null, infoBuilder.toString(), ex.getCause() );
}
private static ErrorSeverity toRpcErrorSeverity( final NetconfDocumentedException.ErrorSeverity severity ) {
.addAll(Collections2.filter(node.getValue(), new Predicate<Node<?>>() {
@Override
public boolean apply(@Nullable final Node<?> input) {
- return input.getNodeType() != inputQName;
+ return !inputQName.equals(input.getNodeType());
}
})) //
.build();
+++ /dev/null
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.sal.connect.util;
-
-import java.util.Collection;
-import java.util.Collections;
-
-import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcResult;
-
-public final class FailedRpcResult<T> implements RpcResult<T> {
-
- private final RpcError rpcError;
-
- public FailedRpcResult(final RpcError rpcError) {
- this.rpcError = rpcError;
- }
-
- @Override
- public boolean isSuccessful() {
- return false;
- }
-
- @Override
- public T getResult() {
- return null;
- }
-
- @Override
- public Collection<RpcError> getErrors() {
- return Collections.singletonList(rpcError);
- }
-}
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.io.InputStream;
import org.mockito.Mockito;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.connect.api.MessageTransformer;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
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.RpcError;
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.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
}
}
- private static final RpcResult<NetconfMessage> rpcResult = Rpcs.getRpcResult(true, netconfMessage, Collections.<RpcError>emptySet());
- private static final RpcResult<CompositeNode> rpcResultC = Rpcs.getRpcResult(true, compositeNode, Collections.<RpcError>emptySet());
+ private static final RpcResult<NetconfMessage> rpcResult = RpcResultBuilder.success(netconfMessage).build();
+ private static final RpcResult<CompositeNode> rpcResultC = RpcResultBuilder.success(compositeNode).build();
public static final String TEST_NAMESPACE = "test:namespace";
public static final String TEST_MODULE = "test-module";
Mockito.verify(facade, Mockito.timeout(5000)).onDeviceDisconnected();
}
+ @Test
+ public void testNotificationBeforeSchema() throws Exception {
+ final RemoteDeviceHandler<NetconfSessionCapabilities> facade = getFacade();
+ final RemoteDeviceCommunicator<NetconfMessage> listener = getListener();
+
+ final MessageTransformer<NetconfMessage> messageTransformer = getMessageTransformer();
+ final NetconfDevice device = new NetconfDevice(getId(), facade, getExecutor(), messageTransformer, getSchemaContextProviderFactory(), getSourceProviderFactory());
+
+ device.onNotification(netconfMessage);
+ device.onNotification(netconfMessage);
+
+ verify(facade, times(0)).onNotification(any(CompositeNode.class));
+
+ final NetconfSessionCapabilities sessionCaps = getSessionCaps(true,
+ Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION));
+
+ device.onRemoteSessionUp(sessionCaps, listener);
+
+ verify(messageTransformer, timeout(10000).times(2)).toNotification(netconfMessage);
+ verify(facade, timeout(10000).times(2)).onNotification(compositeNode);
+
+ device.onNotification(netconfMessage);
+ verify(messageTransformer, timeout(10000).times(3)).toNotification(netconfMessage);
+ verify(facade, timeout(10000).times(3)).onNotification(compositeNode);
+ }
+
@Test
public void testNetconfDeviceReconnect() throws Exception {
final RemoteDeviceHandler<NetconfSessionCapabilities> facade = getFacade();
final RemoteDeviceHandler<NetconfSessionCapabilities> remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class);
doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContextProvider.class), any(NetconfSessionCapabilities.class), any(RpcImplementation.class));
doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
+ doNothing().when(remoteDeviceHandler).onNotification(any(CompositeNode.class));
return remoteDeviceHandler;
}
final MessageTransformer<NetconfMessage> messageTransformer = mockClass(MessageTransformer.class);
doReturn(netconfMessage).when(messageTransformer).toRpcRequest(any(QName.class), any(CompositeNode.class));
doReturn(rpcResultC).when(messageTransformer).toRpcResult(any(NetconfMessage.class), any(QName.class));
+ doReturn(compositeNode).when(messageTransformer).toNotification(any(NetconfMessage.class));
doNothing().when(messageTransformer).onGlobalContextUpdated(any(SchemaContext.class));
return messageTransformer;
}
doReturn(Futures.immediateFuture(rpcResult)).when(remoteDeviceCommunicator).sendRequest(any(NetconfMessage.class), any(QName.class));
return remoteDeviceCommunicator;
}
-}
\ No newline at end of file
+}
--- /dev/null
+package org.opendaylight.controller.sal.connect.netconf;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+/**
+ * Test case for reported bug 1355
+ *
+ * @author Lukas Sedlak
+ * @see <a
+ * https://bugs.opendaylight.org/show_bug.cgi?id=1355</a>
+ */
+public class NetconfToRpcRequestTest {
+
+ private String TEST_MODEL_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:rpc-test";
+ private String REVISION = "2014-07-14";
+ private QName INPUT_QNAME = QName.create(TEST_MODEL_NAMESPACE, REVISION, "input");
+ private QName STREAM_NAME = QName.create(TEST_MODEL_NAMESPACE, REVISION, "stream-name");
+ private QName RPC_NAME = QName.create(TEST_MODEL_NAMESPACE, REVISION, "subscribe");
+
+ NetconfMessageTransformer messageTransformer;
+
+ @SuppressWarnings("deprecation")
+ @Before
+ public void setup() throws Exception {
+ final List<InputStream> modelsToParse = Collections
+ .singletonList(getClass().getResourceAsStream("/schemas/rpc-notification-subscription.yang"));
+ final YangContextParser parser = new YangParserImpl();
+ final Set<Module> modules = parser.parseYangModelsFromStreams(modelsToParse);
+ assertTrue(!modules.isEmpty());
+ final SchemaContext schemaContext = parser.resolveSchemaContext(modules);
+ assertNotNull(schemaContext);
+
+ messageTransformer = new NetconfMessageTransformer();
+ messageTransformer.onGlobalContextUpdated(schemaContext);
+ }
+
+ @Test
+ public void test() throws Exception {
+ final CompositeNodeBuilder<ImmutableCompositeNode> rootBuilder = ImmutableCompositeNode.builder();
+ rootBuilder.setQName(RPC_NAME);
+
+ final CompositeNodeBuilder<ImmutableCompositeNode> inputBuilder = ImmutableCompositeNode.builder();
+ inputBuilder.setQName(INPUT_QNAME);
+ inputBuilder.addLeaf(STREAM_NAME, "NETCONF");
+
+ rootBuilder.add(inputBuilder.toInstance());
+ final ImmutableCompositeNode root = rootBuilder.toInstance();
+
+ final CompositeNode flattenedNode = NetconfMessageTransformUtil.flattenInput(root);
+ assertNotNull(flattenedNode);
+ assertEquals(1, flattenedNode.size());
+
+ final List<CompositeNode> inputNode = flattenedNode.getCompositesByName(INPUT_QNAME);
+ assertNotNull(inputNode);
+ assertTrue(inputNode.isEmpty());
+
+ final NetconfMessage message = messageTransformer.toRpcRequest(RPC_NAME, root);
+ assertNotNull(message);
+ }
+}
--- /dev/null
+module rpc-notification-subscription {
+
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:rpc-test";
+ prefix "rpc";
+
+ organization
+ "Cisco Systems, Inc.";
+
+ contact
+ "lsedlak@cisco.com";
+
+ description
+ "Test model for testing of rpc INPUT parameter during subscription call.";
+
+ revision 2014-07-14 {
+ description
+ "Initial revision.";
+ }
+
+ rpc subscribe {
+ description
+ "Test rpc to init subscription";
+
+ input {
+ leaf stream-name {
+ type string;
+ default "NETCONF";
+ description
+ "Optional stream name param.";
+ }
+
+ anyxml data {
+ description
+ "Optional additional data.";
+ }
+ }
+ }
+}
\ No newline at end of file
*/
package org.opendaylight.controller.cluster.datastore.util;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.Test;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.TransformerFactoryConfigurationError;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.net.URI;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
/**
}
}
- public static DataSchemaNode getSchemaNode(SchemaContext context,
- String moduleName, String childNodeName) {
+ public static DataSchemaNode getSchemaNode(final SchemaContext context,
+ final String moduleName, final String childNodeName) {
for (Module module : context.getModules()) {
if (module.getName().equals(moduleName)) {
DataSchemaNode found =
+ childNodeName);
}
- static DataSchemaNode findChildNode(Set<DataSchemaNode> children, String name) {
+ static DataSchemaNode findChildNode(final Set<DataSchemaNode> children, final String name) {
List<DataNodeContainer> containers = Lists.newArrayList();
for (DataSchemaNode dataSchemaNode : children) {
- if (dataSchemaNode.getQName().getLocalName().equals(name))
+ if (dataSchemaNode.getQName().getLocalName().equals(name)) {
return dataSchemaNode;
+ }
if (dataSchemaNode instanceof DataNodeContainer) {
containers.add((DataNodeContainer) dataSchemaNode);
} else if (dataSchemaNode instanceof ChoiceNode) {
}
private static InstanceIdentifier.NodeIdentifier getNodeIdentifier(
- String localName) {
- return new InstanceIdentifier.NodeIdentifier(new QName(
+ final String localName) {
+ return new InstanceIdentifier.NodeIdentifier(QName.create(
URI.create(NAMESPACE), revision, localName));
}
public static InstanceIdentifier.AugmentationIdentifier getAugmentIdentifier(
- String... childNames) {
+ final String... childNames) {
Set<QName> qn = Sets.newHashSet();
for (String childName : childNames) {
- public void init(String yangPath, String xmlPath, ContainerNode expectedNode)
+ public void init(final String yangPath, final String xmlPath, final ContainerNode expectedNode)
throws Exception {
SchemaContext schema = parseTestSchema(yangPath);
this.xmlPath = xmlPath;
this.expectedNode = expectedNode;
}
- SchemaContext parseTestSchema(String yangPath) throws Exception {
+ SchemaContext parseTestSchema(final String yangPath) throws Exception {
YangParserImpl yangParserImpl = new YangParserImpl();
InputStream stream =
.parse(Collections.singletonList(doc.getDocumentElement()),
containerNode);
- if (expectedNode != null)
- junit.framework.Assert.assertEquals(expectedNode, built);
+ if (expectedNode != null) {
+ junit.framework.Assert.assertEquals(expectedNode, built);
+ }
logger.info("{}", built);
System.out.println(toString(doc.getDocumentElement()));
System.out.println(toString(el));
- boolean diff =
- new Diff(
- XMLUnit.buildControlDocument(toString(doc.getDocumentElement())),
- XMLUnit.buildTestDocument(toString(el))).similar();
+ new Diff(
+ XMLUnit.buildControlDocument(toString(doc.getDocumentElement())),
+ XMLUnit.buildTestDocument(toString(el))).similar();
}
private static ContainerNode listLeafListWithAttributes() {
.parse(Collections.singletonList(doc.getDocumentElement()),
containerNode);
- if (expectedNode != null)
- junit.framework.Assert.assertEquals(expectedNode, built);
+ if (expectedNode != null) {
+ junit.framework.Assert.assertEquals(expectedNode, built);
+ }
logger.info("{}", built);
System.out.println(toString(doc.getDocumentElement()));
System.out.println(toString(el));
- boolean diff =
- new Diff(
- XMLUnit.buildControlDocument(toString(doc.getDocumentElement())),
- XMLUnit.buildTestDocument(toString(el))).similar();
+ new Diff(
+ XMLUnit.buildControlDocument(toString(doc.getDocumentElement())),
+ XMLUnit.buildTestDocument(toString(el))).similar();
}
- private Document loadDocument(String xmlPath) throws Exception {
+ private Document loadDocument(final String xmlPath) throws Exception {
InputStream resourceAsStream =
NormalizedNodeXmlConverterTest.class.getResourceAsStream(xmlPath);
BUILDERFACTORY = factory;
}
- private Document readXmlToDocument(InputStream xmlContent)
+ private Document readXmlToDocument(final InputStream xmlContent)
throws IOException, SAXException {
DocumentBuilder dBuilder;
try {
return doc;
}
- public static String toString(Element xml) {
+ public static String toString(final Element xml) {
try {
Transformer transformer =
TransformerFactory.newInstance().newTransformer();
System.out.println(toString(convertedDoc.getDocumentElement()));
XMLUnit.setIgnoreWhitespace(true);
XMLUnit.setIgnoreComments(true);
- boolean diff =
- new Diff(XMLUnit.buildControlDocument(toString(expectedDoc
- .getDocumentElement())),
- XMLUnit.buildTestDocument(toString(convertedDoc
- .getDocumentElement()))).similar();
+ new Diff(XMLUnit.buildControlDocument(toString(expectedDoc
+ .getDocumentElement())),
+ XMLUnit.buildTestDocument(toString(convertedDoc
+ .getDocumentElement()))).similar();
System.out.println(toString(expectedDoc.getDocumentElement()));
}
System.out.println(toString(convertedDoc.getDocumentElement()));
XMLUnit.setIgnoreWhitespace(true);
XMLUnit.setIgnoreComments(true);
- boolean diff =
- new Diff(XMLUnit.buildControlDocument(toString(expectedDoc
- .getDocumentElement())),
- XMLUnit.buildTestDocument(toString(convertedDoc
- .getDocumentElement()))).similar();
+ new Diff(XMLUnit.buildControlDocument(toString(expectedDoc
+ .getDocumentElement())),
+ XMLUnit.buildTestDocument(toString(convertedDoc
+ .getDocumentElement()))).similar();
System.out.println(toString(expectedDoc.getDocumentElement()));
// now we will try to convert xml back to normalize node.
package org.opendaylight.controller.sal.restconf.impl;
import com.google.common.util.concurrent.Futures;
-import java.util.Collections;
+
import java.util.concurrent.Future;
+
import javax.ws.rs.core.Response.Status;
+
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.DataReader;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.slf4j.Logger;
LOG.info("Delete Configuration via Restconf: {}", path);
CompositeNode redDataAtPath = transaction.readConfigurationData(path);
if (redDataAtPath == null) {
- return Futures.immediateFuture(Rpcs.<TransactionStatus> getRpcResult(true, TransactionStatus.COMMITED,
- Collections.<RpcError> emptyList()));
+ return Futures.immediateFuture(RpcResultBuilder.<TransactionStatus>
+ success(TransactionStatus.COMMITED).build());
}
transaction.removeConfigurationData(path);
return transaction.commit();
if (input instanceof IdentityValuesDTO) {
return identityrefCodec.deserialize(input);
}
- logger.info(
+ logger.debug(
"Value is not instance of IdentityrefTypeDefinition but is {}. Therefore NULL is used as translation of - {}",
input == null ? "null" : input.getClass(), String.valueOf(input));
return null;
}
final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
- RpcDefinition rpc = controllerContext.getRpcDefinition(identifierDecoded);
+
+ RpcDefinition rpc = null;
+ if (mountPoint == null) {
+ rpc = controllerContext.getRpcDefinition(identifierDecoded);
+ } else {
+ rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
+ }
if (rpc == null) {
throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
}
+ private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
+ final String[] splittedIdentifier = identifierDecoded.split(":");
+ if (splittedIdentifier.length != 2) {
+ throw new RestconfDocumentedException(identifierDecoded
+ + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION,
+ ErrorTag.INVALID_VALUE);
+ }
+ for (Module module : schemaContext.getModules()) {
+ if (module.getName().equals(splittedIdentifier[0])) {
+ for (RpcDefinition rpcDefinition : module.getRpcs()) {
+ if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) {
+ return rpcDefinition;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload, boolean prettyPrint) {
if (rpcExecutor == null) {
throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
throw new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
} catch (UnsupportedOperationException e) {
throw new RestconfDocumentedException(e.getMessage(), ErrorType.RPC, ErrorTag.OPERATION_NOT_SUPPORTED);
+ } catch (RestconfDocumentedException e) {
+ throw e;
} catch (Exception e) {
throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.", e);
}
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
-import org.opendaylight.controller.sal.common.util.RpcErrors;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
import org.opendaylight.controller.sal.restconf.impl.StructuredData;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
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.ModifyAction;
import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
CompositeNode payload = preparePayload();
when(mockedBrokerFacade.invokeRpc(any(QName.class), any(CompositeNode.class))).thenReturn(
- Futures.<RpcResult<CompositeNode>> immediateFuture(Rpcs.<CompositeNode> getRpcResult(true)));
+ Futures.<RpcResult<CompositeNode>> immediateFuture(RpcResultBuilder.<CompositeNode>success().build()));
StructuredData structData = restconf.invokeRpc("invoke-rpc-module:rpc-test", payload, uriInfo);
assertTrue(structData == null);
@Test
public void testInvokeRpcWithNoPayloadRpc_FailNoErrors() {
- RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(false);
+ RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>failed().build();
BrokerFacade brokerFacade = mock(BrokerFacade.class);
when(
@Test
public void testInvokeRpcWithNoPayloadRpc_FailWithRpcError() {
- List<RpcError> rpcErrors = Arrays.asList(RpcErrors.getRpcError(null, "bogusTag", null, ErrorSeverity.ERROR,
- "foo", RpcError.ErrorType.TRANSPORT, null), RpcErrors.getRpcError("app-tag", "in-use", null,
- ErrorSeverity.WARNING, "bar", RpcError.ErrorType.RPC, null));
+ List<RpcError> rpcErrors = Arrays.asList(
+ RpcResultBuilder.newError( RpcError.ErrorType.TRANSPORT, "bogusTag", "foo" ),
+ RpcResultBuilder.newWarning( RpcError.ErrorType.RPC, "in-use", "bar",
+ "app-tag", null, null ) );
- RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(false, rpcErrors);
+ RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>failed()
+ .withRpcErrors(rpcErrors).build();
BrokerFacade brokerFacade = mock(BrokerFacade.class);
when(
@Test
public void testInvokeRpcWithNoPayload_Success() {
- RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(true);
+ RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
BrokerFacade brokerFacade = mock(BrokerFacade.class);
when(
@Test
public void testInvokeRpcMethodWithInput() {
- RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(true);
+ RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
CompositeNode payload = mock(CompositeNode.class);
@Test
public void testInvokeRpcWithNoPayloadWithOutput_Success() {
CompositeNode compositeNode = mock(CompositeNode.class);
- RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(true, compositeNode,
- Collections.<RpcError> emptyList());
+ RpcResult<CompositeNode> rpcResult =
+ RpcResultBuilder.<CompositeNode>success(compositeNode).build();
BrokerFacade brokerFacade = mock(BrokerFacade.class);
when(
assertNotNull(output.getSchema());
}
+ /**
+ *
+ * Tests calling of RestConfImpl method invokeRpc. In the method there is searched rpc in remote schema context.
+ * This rpc is then executed.
+ *
+ * I wasn't able to simulate calling of rpc on remote device therefore this testing method raise method when rpc is
+ * invoked.
+ */
@Test
public void testMountedRpcCallNoPayload_Success() throws Exception {
- RpcResult<CompositeNode> rpcResult = Rpcs.<CompositeNode> getRpcResult(true);
+ RpcResult<CompositeNode> rpcResult = RpcResultBuilder.<CompositeNode>success().build();
ListenableFuture<RpcResult<CompositeNode>> mockListener = mock(ListenableFuture.class);
when(mockListener.get()).thenReturn(rpcResult);
MountInstance mockMountPoint = mock(MountInstance.class);
when(mockMountPoint.rpc(eq(cancelToastQName), any(CompositeNode.class))).thenReturn(mockListener);
+ when(mockMountPoint.getSchemaContext()).thenReturn(TestUtils.loadSchemaContext("/invoke-rpc"));
+
InstanceIdWithSchemaNode mockedInstanceId = mock(InstanceIdWithSchemaNode.class);
when(mockedInstanceId.getMountPoint()).thenReturn(mockMountPoint);
ControllerContext mockedContext = mock(ControllerContext.class);
- String cancelToastStr = "toaster:cancel-toast";
- when(mockedContext.urlPathArgDecode(cancelToastStr)).thenReturn(cancelToastStr);
- when(mockedContext.getRpcDefinition(cancelToastStr)).thenReturn(mockRpc);
+ String rpcNoop = "invoke-rpc-module:rpc-noop";
+ when(mockedContext.urlPathArgDecode(rpcNoop)).thenReturn(rpcNoop);
+ when(mockedContext.getRpcDefinition(rpcNoop)).thenReturn(mockRpc);
when(
- mockedContext.toMountPointIdentifier("opendaylight-inventory:nodes/node/"
- + "REMOTE_HOST/yang-ext:mount/toaster:cancel-toast")).thenReturn(mockedInstanceId);
+ mockedContext.toMountPointIdentifier(eq("opendaylight-inventory:nodes/node/"
+ + "REMOTE_HOST/yang-ext:mount/invoke-rpc-module:rpc-noop"))).thenReturn(mockedInstanceId);
restconfImpl.setControllerContext(mockedContext);
- StructuredData output = restconfImpl.invokeRpc(
- "opendaylight-inventory:nodes/node/REMOTE_HOST/yang-ext:mount/toaster:cancel-toast", "", uriInfo);
- assertEquals(null, output);
+ try {
+ restconfImpl.invokeRpc(
+ "opendaylight-inventory:nodes/node/REMOTE_HOST/yang-ext:mount/invoke-rpc-module:rpc-noop", "",
+ uriInfo);
+ fail("RestconfDocumentedException wasn't raised");
+ } catch (RestconfDocumentedException e) {
+ List<RestconfError> errors = e.getErrors();
+ assertNotNull(errors);
+ assertEquals(1, errors.size());
+ assertEquals(ErrorType.APPLICATION, errors.iterator().next().getErrorType());
+ assertEquals(ErrorTag.OPERATION_FAILED, errors.iterator().next().getErrorTag());
+ }
// additional validation in the fact that the restconfImpl does not
// throw an exception.
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.BeforeClass;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
public class RestGetOperationTest extends JerseyTest {
static class NodeData {
private static SchemaContext schemaContextModules;
private static SchemaContext schemaContextBehindMountPoint;
+ private static final String RESTCONF_NS = "urn:ietf:params:xml:ns:yang:ietf-restconf";
+
@BeforeClass
public static void init() throws FileNotFoundException {
schemaContextYangsIetf = TestUtils.loadSchemaContext("/full-versions/yangs");
validateModulesResponseJson(response);
response = target(uri).request("application/yang.api+xml").get();
- validateModulesResponseXml(response);
+ validateModulesResponseXml(response,schemaContextModules);
}
// /streams/
assertTrue(responseBody.contains("streams"));
response = target(uri).request("application/yang.api+xml").get();
- responseBody = response.readEntity(String.class);
- assertNotNull(responseBody);
- assertTrue(responseBody.contains("<streams xmlns=\"urn:ietf:params:xml:ns:yang:ietf-restconf\""));
+ Document responseXmlBody = response.readEntity(Document.class);
+ assertNotNull(responseXmlBody);
+ Element rootNode = responseXmlBody.getDocumentElement();
+
+ assertEquals("streams", rootNode.getLocalName());
+ assertEquals(RESTCONF_NS, rootNode.getNamespaceURI());
}
// /modules/module
Response response = target(uri).request("application/yang.api+xml").get();
assertEquals(200, response.getStatus());
- String responseBody = response.readEntity(String.class);
- assertTrue("Module2 in xml wasn't found", prepareXmlRegex("module2", "2014-01-02", "module:2", responseBody)
- .find());
- String[] split = responseBody.split("<module");
- assertEquals("<module element is returned more then once", 2, split.length);
+ Document responseXml = response.readEntity(Document.class);
+
+
+
+ QName qname = assertedModuleXmlToModuleQName(responseXml.getDocumentElement());
+ assertNotNull(qname);
+
+ assertEquals("module2", qname.getLocalName());
+ assertEquals("module:2", qname.getNamespace().toString());
+ assertEquals("2014-01-02", qname.getFormattedRevision());
response = target(uri).request("application/yang.api+json").get();
assertEquals(200, response.getStatus());
- responseBody = response.readEntity(String.class);
+ String responseBody = response.readEntity(String.class);
assertTrue("Module2 in json wasn't found", prepareJsonRegex("module2", "2014-01-02", "module:2", responseBody)
.find());
- split = responseBody.split("\"module\"");
+ String[] split = responseBody.split("\"module\"");
assertEquals("\"module\" element is returned more then once", 2, split.length);
}
Response response = target(uri).request("application/yang.api+xml").get();
assertEquals(200, response.getStatus());
- String responseBody = response.readEntity(String.class);
- assertTrue("Xml response for /operations dummy-rpc1-module1 is incorrect",
- validateOperationsResponseXml(responseBody, "dummy-rpc1-module1", "module:1").find());
- assertTrue("Xml response for /operations dummy-rpc2-module1 is incorrect",
- validateOperationsResponseXml(responseBody, "dummy-rpc2-module1", "module:1").find());
- assertTrue("Xml response for /operations dummy-rpc1-module2 is incorrect",
- validateOperationsResponseXml(responseBody, "dummy-rpc1-module2", "module:2").find());
- assertTrue("Xml response for /operations dummy-rpc2-module2 is incorrect",
- validateOperationsResponseXml(responseBody, "dummy-rpc2-module2", "module:2").find());
+ Document responseDoc = response.readEntity(Document.class);
+ validateOperationsResponseXml(responseDoc, schemaContextModules);
response = target(uri).request("application/yang.api+json").get();
assertEquals(200, response.getStatus());
- responseBody = response.readEntity(String.class);
+ String responseBody = response.readEntity(String.class);
assertTrue("Json response for /operations dummy-rpc1-module1 is incorrect",
validateOperationsResponseJson(responseBody, "dummy-rpc1-module1", "module1").find());
assertTrue("Json response for /operations dummy-rpc2-module1 is incorrect",
}
+ private void validateOperationsResponseXml(final Document responseDoc, final SchemaContext schemaContext) {
+ Element operationsElem = responseDoc.getDocumentElement();
+ assertEquals(RESTCONF_NS, operationsElem.getNamespaceURI());
+ assertEquals("operations", operationsElem.getLocalName());
+
+
+ HashSet<QName> foundOperations = new HashSet<>();
+
+ NodeList operationsList = operationsElem.getChildNodes();
+ for(int i = 0;i < operationsList.getLength();i++) {
+ org.w3c.dom.Node operation = operationsList.item(i);
+
+ String namespace = operation.getNamespaceURI();
+ String name = operation.getLocalName();
+ QName opQName = QName.create(URI.create(namespace), null, name);
+ foundOperations.add(opQName);
+ }
+
+ for(RpcDefinition schemaOp : schemaContext.getOperations()) {
+ assertTrue(foundOperations.contains(schemaOp.getQName().withoutRevision()));
+ }
+
+ }
+
// /operations/pathToMountPoint/yang-ext:mount
@Test
public void getOperationsBehindMountPointTest() throws FileNotFoundException, UnsupportedEncodingException {
Response response = target(uri).request("application/yang.api+xml").get();
assertEquals(200, response.getStatus());
- String responseBody = response.readEntity(String.class);
- assertTrue("Xml response for /operations/mount_point rpc-behind-module1 is incorrect",
- validateOperationsResponseXml(responseBody, "rpc-behind-module1", "module:1:behind:mount:point").find());
- assertTrue("Xml response for /operations/mount_point rpc-behind-module2 is incorrect",
- validateOperationsResponseXml(responseBody, "rpc-behind-module2", "module:2:behind:mount:point").find());
+
+ Document responseDoc = response.readEntity(Document.class);
+ validateOperationsResponseXml(responseDoc, schemaContextBehindMountPoint);
response = target(uri).request("application/yang.api+json").get();
assertEquals(200, response.getStatus());
- responseBody = response.readEntity(String.class);
+ String responseBody = response.readEntity(String.class);
assertTrue("Json response for /operations/mount_point rpc-behind-module1 is incorrect",
validateOperationsResponseJson(responseBody, "rpc-behind-module1", "module1-behind-mount-point").find());
assertTrue("Json response for /operations/mount_point rpc-behind-module2 is incorrect",
response = target(uri).request("application/yang.api+xml").get();
assertEquals(200, response.getStatus());
- responseBody = response.readEntity(String.class);
- assertTrue(
- "module1-behind-mount-point in json wasn't found",
- prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody)
- .find());
- assertTrue(
- "module2-behind-mount-point in json wasn't found",
- prepareXmlRegex("module2-behind-mount-point", "2014-02-04", "module:2:behind:mount:point", responseBody)
- .find());
+ validateModulesResponseXml(response, schemaContextBehindMountPoint);
}
response = target(uri).request("application/yang.api+xml").get();
assertEquals(200, response.getStatus());
- responseBody = response.readEntity(String.class);
- assertTrue(
- "module1-behind-mount-point in json wasn't found",
- prepareXmlRegex("module1-behind-mount-point", "2014-02-03", "module:1:behind:mount:point", responseBody)
- .find());
- split = responseBody.split("<module");
- assertEquals("<module element is returned more then once", 2, split.length);
+ Document responseXml = response.readEntity(Document.class);
+
+ QName module = assertedModuleXmlToModuleQName(responseXml.getDocumentElement());
+
+ assertEquals("module1-behind-mount-point", module.getLocalName());
+ assertEquals("2014-02-03", module.getFormattedRevision());
+ assertEquals("module:1:behind:mount:point", module.getNamespace().toString());
+
}
- private void validateModulesResponseXml(final Response response) {
+ private void validateModulesResponseXml(final Response response, final SchemaContext schemaContext) {
assertEquals(200, response.getStatus());
- String responseBody = response.readEntity(String.class);
+ Document responseBody = response.readEntity(Document.class);
+ NodeList moduleNodes = responseBody.getDocumentElement().getElementsByTagNameNS(RESTCONF_NS, "module");
- assertTrue("Module1 in xml wasn't found", prepareXmlRegex("module1", "2014-01-01", "module:1", responseBody)
- .find());
- assertTrue("Module2 in xml wasn't found", prepareXmlRegex("module2", "2014-01-02", "module:2", responseBody)
- .find());
- assertTrue("Module3 in xml wasn't found", prepareXmlRegex("module3", "2014-01-03", "module:3", responseBody)
- .find());
+ assertTrue(moduleNodes.getLength() > 0);
+
+ HashSet<QName> foundModules = new HashSet<>();
+
+ for(int i=0;i < moduleNodes.getLength();i++) {
+ org.w3c.dom.Node module = moduleNodes.item(i);
+
+ QName name = assertedModuleXmlToModuleQName(module);
+ foundModules.add(name);
+ }
+
+ assertAllModules(foundModules,schemaContext);
+ }
+
+ private void assertAllModules(final Set<QName> foundModules, final SchemaContext schemaContext) {
+ for(Module module : schemaContext.getModules()) {
+ QName current = QName.create(module.getQNameModule(),module.getName());
+ assertTrue("Module not found in response.",foundModules.contains(current));
+ }
+
+ }
+
+ private QName assertedModuleXmlToModuleQName(final org.w3c.dom.Node module) {
+ assertEquals("module", module.getLocalName());
+ assertEquals(RESTCONF_NS, module.getNamespaceURI());
+ String revision = null;
+ String namespace = null;
+ String name = null;
+
+
+ NodeList childNodes = module.getChildNodes();
+
+ for(int i =0;i < childNodes.getLength(); i++) {
+ org.w3c.dom.Node child = childNodes.item(i);
+ assertEquals(RESTCONF_NS, child.getNamespaceURI());
+
+ switch(child.getLocalName()) {
+ case "name":
+ assertNull("Name element appeared multiple times",name);
+ name = child.getTextContent().trim();
+ break;
+ case "revision":
+ assertNull("Revision element appeared multiple times",revision);
+ revision = child.getTextContent().trim();
+ break;
+
+ case "namespace":
+ assertNull("Namespace element appeared multiple times",namespace);
+ namespace = child.getTextContent().trim();
+ break;
+ }
+ }
+
+ assertNotNull("Revision was not part of xml",revision);
+ assertNotNull("Module namespace was not part of xml",namespace);
+ assertNotNull("Module identiffier was not part of xml",name);
+
+
+ // TODO Auto-generated method stub
+
+ return QName.create(namespace,revision,name);
}
private void validateModulesResponseJson(final Response response) {
}
- private Matcher prepareXmlRegex(final String module, final String revision, final String namespace,
- final String searchIn) {
- StringBuilder regex = new StringBuilder();
- regex.append("^");
-
- regex.append(".*<module.*");
- regex.append(".*>");
-
- regex.append(".*<name>");
- regex.append(".*" + module);
- regex.append(".*<\\/name>");
-
- regex.append(".*<revision>");
- regex.append(".*" + revision);
- regex.append(".*<\\/revision>");
-
- regex.append(".*<namespace>");
- regex.append(".*" + namespace);
- regex.append(".*<\\/namespace>");
-
- regex.append(".*<\\/module.*>");
-
- regex.append(".*");
- regex.append("$");
-
- Pattern ptrn = Pattern.compile(regex.toString(), Pattern.DOTALL);
- return ptrn.matcher(searchIn);
- }
private void prepareMockForModulesTest(final ControllerContext mockedControllerContext)
throws FileNotFoundException {
getDataWithUriIncludeWhiteCharsParameter("operational");
}
- private void getDataWithUriIncludeWhiteCharsParameter(String target) throws UnsupportedEncodingException {
+ private void getDataWithUriIncludeWhiteCharsParameter(final String target) throws UnsupportedEncodingException {
mockReadConfigurationDataMethod();
String uri = "/" + target + "/ietf-interfaces:interfaces/interface/eth0";
Response response = target(uri).queryParam("prettyPrint", "false").request("application/xml").get();
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
-import org.opendaylight.controller.sal.common.util.RpcErrors;
import org.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.controller.sal.core.api.mount.MountService;
import org.opendaylight.controller.sal.rest.api.Draft02;
import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataRpcInput));
List<RpcError> rpcErrors = new ArrayList<>();
- rpcErrors.add(RpcErrors.getRpcError("applicationTag1", "tag1", "info1", ErrorSeverity.ERROR, "message1",
- ErrorType.RPC, null));
- rpcErrors.add(RpcErrors.getRpcError("applicationTag2", "tag2", "info2", ErrorSeverity.WARNING, "message2",
- ErrorType.PROTOCOL, null));
+ rpcErrors.add( RpcResultBuilder.newError( ErrorType.RPC, "tag1", "message1",
+ "applicationTag1", "info1", null ) );
+ rpcErrors.add( RpcResultBuilder.newWarning( ErrorType.PROTOCOL, "tag2", "message2",
+ "applicationTag2", "info2", null ) );
mockInvokeRpc(null, false, rpcErrors);
assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataRpcInput));
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
-import static org.opendaylight.controller.sal.common.util.RpcErrors.getRpcError;
import java.util.HashMap;
import java.util.Map;
import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
/**
* Unit tests for RestconfError.
public void testRestConfErrorWithRpcError() {
// All fields set
- RpcError rpcError = getRpcError("mock app-tag", ErrorTag.BAD_ATTRIBUTE.getTagValue(), "mock error-info",
- RpcError.ErrorSeverity.ERROR, "mock error-message", RpcError.ErrorType.PROTOCOL, new Exception(
- "mock cause"));
+ RpcError rpcError = RpcResultBuilder.newError(
+ RpcError.ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE.getTagValue(), "mock error-message",
+ "mock app-tag", "mock error-info", new Exception( "mock cause" ) );
validateRestConfError("mock error-message", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock app-tag",
"mock error-info", new RestconfError(rpcError));
// All fields set except 'info' - expect error-info set to 'cause'
- rpcError = getRpcError("mock app-tag", ErrorTag.BAD_ATTRIBUTE.getTagValue(), null,
- RpcError.ErrorSeverity.ERROR, "mock error-message", RpcError.ErrorType.PROTOCOL, new Exception(
- "mock cause"));
+ rpcError = RpcResultBuilder.newError(
+ RpcError.ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE.getTagValue(), "mock error-message",
+ "mock app-tag", null, new Exception( "mock cause" ) );
validateRestConfError("mock error-message", ErrorType.PROTOCOL, ErrorTag.BAD_ATTRIBUTE, "mock app-tag",
new Contains("mock cause"), new RestconfError(rpcError));
// Some fields set - expect error-info set to ErrorSeverity
- rpcError = getRpcError(null, ErrorTag.ACCESS_DENIED.getTagValue(), null, RpcError.ErrorSeverity.ERROR, null,
- RpcError.ErrorType.RPC, null);
+ rpcError = RpcResultBuilder.newError(
+ RpcError.ErrorType.RPC, ErrorTag.ACCESS_DENIED.getTagValue(), null, null, null, null );
validateRestConfError(null, ErrorType.RPC, ErrorTag.ACCESS_DENIED, null, "<severity>error</severity>",
new RestconfError(rpcError));
// 'tag' field not mapped to ErrorTag - expect error-tag set to
// OPERATION_FAILED
- rpcError = getRpcError(null, "not mapped", null, RpcError.ErrorSeverity.WARNING, null,
- RpcError.ErrorType.TRANSPORT, null);
+ rpcError = RpcResultBuilder.newWarning(
+ RpcError.ErrorType.TRANSPORT, "not mapped", null, null, null, null );
validateRestConfError(null, ErrorType.TRANSPORT, ErrorTag.OPERATION_FAILED, null,
"<severity>warning</severity>", new RestconfError(rpcError));
// No fields set - edge case
- rpcError = getRpcError(null, null, null, null, null, null, null);
+ rpcError = RpcResultBuilder.newError( null, null, null, null, null, null );
- validateRestConfError(null, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, null, (String) null,
- new RestconfError(rpcError));
+ validateRestConfError( null, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
+ null, "<severity>error</severity>", new RestconfError( rpcError ) );
}
private void validateRestConfError(String expectedMessage, ErrorType expectedErrorType, ErrorTag expectedErrorTag,
}
}
}
-
+
+ rpc rpc-noop {
+ }
+
}
\ No newline at end of file
import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
import org.opendaylight.controller.sal.rest.doc.impl.ApiDocGenerator;
import org.opendaylight.controller.sal.rest.doc.mountpoints.MountPointSwagger;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
import org.opendaylight.controller.sal.rest.doc.impl.BaseYangSwaggerGenerator;
import org.opendaylight.controller.sal.rest.doc.swagger.Api;
import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
--- /dev/null
+module opendaylight-test-routed-rpc {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:rpc:routing";
+ prefix "rpc";
+ import yang-ext { prefix ext; }
+
+ description
+ "Test model for testing of registering rpc service on binding independent mount point
+ and retrieving rpc service via binding aware mount point.";
+
+ revision "2014-07-01" {
+ description
+ "Initial revision";
+ }
+
+ identity test-context {
+ description "Test Context";
+ }
+
+ typedef encapsulated-route {
+ type instance-identifier;
+ }
+
+ grouping route-in-grouping {
+ leaf route {
+ type instance-identifier;
+ ext:context-reference test-context;
+ }
+ }
+
+ grouping encapsulated-route-in-grouping {
+ leaf route {
+ type encapsulated-route;
+ ext:context-reference test-context;
+ }
+ }
+
+ rpc routed-simple-route {
+ input {
+ leaf route {
+ type instance-identifier;
+ ext:context-reference test-context;
+ }
+ }
+ }
+}
package org.opendaylight.controller.sample.kitchen.impl;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+
import org.opendaylight.controller.config.yang.config.kitchen_service.impl.KitchenServiceRuntimeMXBean;
-import org.opendaylight.controller.sal.common.util.RpcErrors;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sample.kitchen.api.EggsType;
import org.opendaylight.controller.sample.kitchen.api.KitchenService;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInput;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterRestocked;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.WheatBread;
-import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
}
return Futures.immediateFuture(
- Rpcs.<Void> getRpcResult( atLeastOneSucceeded, errorList.build() ) );
+ RpcResultBuilder.<Void> status( atLeastOneSucceeded )
+ .withRpcErrors( errorList.build() ).build() );
}
} );
}
public RpcResult<Void> call() throws Exception {
// We don't actually do anything here - just return a successful result.
- return Rpcs.<Void> getRpcResult( true, Collections.<RpcError>emptyList() );
+ return RpcResultBuilder.<Void> success().build();
}
} );
}
if( toasterOutOfBread )
{
log.info( "We're out of toast but we can make eggs" );
- return Futures.immediateFuture( Rpcs.<Void> getRpcResult( true,
- Arrays.asList( RpcErrors.getRpcError( "", "partial-operation", null,
- ErrorSeverity.WARNING,
- "Toaster is out of bread but we can make you eggs",
- ErrorType.APPLICATION, null ) ) ) );
+ return Futures.immediateFuture( RpcResultBuilder.<Void> success()
+ .withWarning( ErrorType.APPLICATION, "partial-operation",
+ "Toaster is out of bread but we can make you eggs" ).build() );
}
// Access the ToasterService to make the toast.
*/
package org.opendaylight.controller.sample.toaster.provider;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.common.util.RpcErrors;
-import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.DisplayString;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInput;
import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.RestockToasterInput;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
executor.shutdown();
if (dataProvider != null) {
- WriteTransaction t = dataProvider.newWriteOnlyTransaction();
- t.delete(LogicalDatastoreType.OPERATIONAL,TOASTER_IID);
- ListenableFuture<RpcResult<TransactionStatus>> future = t.commit();
- Futures.addCallback( future, new FutureCallback<RpcResult<TransactionStatus>>() {
+ WriteTransaction tx = dataProvider.newWriteOnlyTransaction();
+ tx.delete(LogicalDatastoreType.OPERATIONAL,TOASTER_IID);
+ Futures.addCallback( tx.submit(), new FutureCallback<Void>() {
@Override
- public void onSuccess( RpcResult<TransactionStatus> result ) {
+ public void onSuccess( final Void result ) {
LOG.debug( "Delete Toaster commit result: " + result );
}
@Override
- public void onFailure( Throwable t ) {
+ public void onFailure( final Throwable t ) {
LOG.error( "Delete of Toaster failed", t );
}
} );
}
}
- private Toaster buildToaster( ToasterStatus status ) {
+ private Toaster buildToaster( final ToasterStatus status ) {
// note - we are simulating a device whose manufacture and model are
// fixed (embedded) into the hardware.
}
// Always return success from the cancel toast call.
- return Futures.immediateFuture( Rpcs.<Void> getRpcResult( true,
- Collections.<RpcError>emptyList() ) );
+ return Futures.immediateFuture( RpcResultBuilder.<Void> success().build() );
}
/**
final SettableFuture<RpcResult<Void>> futureResult = SettableFuture.create();
- checkStatusAndMakeToast( input, futureResult );
+ checkStatusAndMakeToast( input, futureResult, 2 );
return futureResult;
}
- private List<RpcError> makeToasterOutOfBreadError() {
- return Arrays.asList(
- RpcErrors.getRpcError( "out-of-stock", "resource-denied", null, null,
- "Toaster is out of bread",
- ErrorType.APPLICATION, null ) );
+ private RpcError makeToasterOutOfBreadError() {
+ return RpcResultBuilder.newError( ErrorType.APPLICATION, "resource-denied",
+ "Toaster is out of bread", "out-of-stock", null, null );
}
- private List<RpcError> makeToasterInUseError() {
- return Arrays.asList(
- RpcErrors.getRpcError( "", "in-use", null, ErrorSeverity.WARNING,
- "Toaster is busy", ErrorType.APPLICATION, null ) );
+ private RpcError makeToasterInUseError() {
+ return RpcResultBuilder.newWarning( ErrorType.APPLICATION, "in-use",
+ "Toaster is busy", null, null, null );
}
private void checkStatusAndMakeToast( final MakeToastInput input,
- final SettableFuture<RpcResult<Void>> futureResult ) {
+ final SettableFuture<RpcResult<Void>> futureResult,
+ final int tries ) {
// Read the ToasterStatus and, if currently Up, try to write the status to Down.
// If that succeeds, then we essentially have an exclusive lock and can proceed
// to make toast.
final ReadWriteTransaction tx = dataProvider.newReadWriteTransaction();
- ListenableFuture<Optional<DataObject>> readFuture =
+ ListenableFuture<Optional<Toaster>> readFuture =
tx.read( LogicalDatastoreType.OPERATIONAL, TOASTER_IID );
- final ListenableFuture<RpcResult<TransactionStatus>> commitFuture =
- Futures.transform( readFuture, new AsyncFunction<Optional<DataObject>,
- RpcResult<TransactionStatus>>() {
+ final ListenableFuture<Void> commitFuture =
+ Futures.transform( readFuture, new AsyncFunction<Optional<Toaster>,Void>() {
@Override
- public ListenableFuture<RpcResult<TransactionStatus>> apply(
- Optional<DataObject> toasterData ) throws Exception {
+ public ListenableFuture<Void> apply(
+ final Optional<Toaster> toasterData ) throws Exception {
ToasterStatus toasterStatus = ToasterStatus.Up;
if( toasterData.isPresent() ) {
- toasterStatus = ((Toaster)toasterData.get()).getToasterStatus();
+ toasterStatus = toasterData.get().getToasterStatus();
}
LOG.debug( "Read toaster status: {}", toasterStatus );
if( outOfBread() ) {
LOG.debug( "Toaster is out of bread" );
- return Futures.immediateFuture( Rpcs.<TransactionStatus>getRpcResult(
- false, null, makeToasterOutOfBreadError() ) );
+ return Futures.immediateFailedCheckedFuture(
+ new TransactionCommitFailedException( "", makeToasterOutOfBreadError() ) );
}
LOG.debug( "Setting Toaster status to Down" );
// concurrent toasting.
tx.put( LogicalDatastoreType.OPERATIONAL, TOASTER_IID,
buildToaster( ToasterStatus.Down ) );
- return tx.commit();
+ return tx.submit();
}
LOG.debug( "Oops - already making toast!" );
// Return an error since we are already making toast. This will get
// propagated to the commitFuture below which will interpret the null
// TransactionStatus in the RpcResult as an error condition.
- return Futures.immediateFuture( Rpcs.<TransactionStatus>getRpcResult(
- false, null, makeToasterInUseError() ) );
+ return Futures.immediateFailedCheckedFuture(
+ new TransactionCommitFailedException( "", makeToasterInUseError() ) );
}
} );
- Futures.addCallback( commitFuture, new FutureCallback<RpcResult<TransactionStatus>>() {
+ Futures.addCallback( commitFuture, new FutureCallback<Void>() {
@Override
- public void onSuccess( RpcResult<TransactionStatus> result ) {
- if( result.getResult() == TransactionStatus.COMMITED ) {
-
- // OK to make toast
- currentMakeToastTask.set( executor.submit(
- new MakeToastTask( input, futureResult ) ) );
- } else {
-
- LOG.debug( "Setting error result" );
-
- // Either the transaction failed to commit for some reason or, more likely,
- // the read above returned ToasterStatus.Down. Either way, fail the
- // futureResult and copy the errors.
-
- futureResult.set( Rpcs.<Void>getRpcResult( false, null, result.getErrors() ) );
- }
+ public void onSuccess( final Void result ) {
+ // OK to make toast
+ currentMakeToastTask.set( executor.submit( new MakeToastTask( input, futureResult ) ) );
}
@Override
- public void onFailure( Throwable ex ) {
+ public void onFailure( final Throwable ex ) {
if( ex instanceof OptimisticLockFailedException ) {
// Another thread is likely trying to make toast simultaneously and updated the
// status before us. Try reading the status again - if another make toast is
// now in progress, we should get ToasterStatus.Down and fail.
- LOG.debug( "Got OptimisticLockFailedException - trying again" );
+ if( ( tries - 1 ) > 0 ) {
+ LOG.debug( "Got OptimisticLockFailedException - trying again" );
- checkStatusAndMakeToast( input, futureResult );
+ checkStatusAndMakeToast( input, futureResult, tries - 1 );
+ }
+ else {
+ futureResult.set( RpcResultBuilder.<Void> failed()
+ .withError( ErrorType.APPLICATION, ex.getMessage() ).build() );
+ }
} else {
- LOG.error( "Failed to commit Toaster status", ex );
+ LOG.debug( "Failed to commit Toaster status", ex );
- // Got some unexpected error so fail.
- futureResult.set( Rpcs.<Void> getRpcResult( false, null, Arrays.asList(
- RpcErrors.getRpcError( null, null, null, ErrorSeverity.ERROR,
- ex.getMessage(),
- ErrorType.APPLICATION, ex ) ) ) );
+ // Probably already making toast.
+ futureResult.set( RpcResultBuilder.<Void> failed()
+ .withRpcErrors( ((TransactionCommitFailedException)ex).getErrorList() )
+ .build() );
}
}
} );
notificationProvider.publish( reStockedNotification );
}
- return Futures.immediateFuture(Rpcs.<Void> getRpcResult(true, Collections.<RpcError>emptyList()));
+ return Futures.immediateFuture( RpcResultBuilder.<Void> success().build() );
}
/**
WriteTransaction tx = dataProvider.newWriteOnlyTransaction();
tx.put( LogicalDatastoreType.OPERATIONAL,TOASTER_IID, buildToaster( ToasterStatus.Up ) );
- ListenableFuture<RpcResult<TransactionStatus>> commitFuture = tx.commit();
-
- Futures.addCallback( commitFuture, new FutureCallback<RpcResult<TransactionStatus>>() {
+ Futures.addCallback( tx.submit(), new FutureCallback<Void>() {
@Override
- public void onSuccess( RpcResult<TransactionStatus> result ) {
- if( result.getResult() != TransactionStatus.COMMITED ) {
- LOG.error( "Failed to update toaster status: " + result.getErrors() );
- }
-
- notifyCallback( result.getResult() == TransactionStatus.COMMITED );
+ public void onSuccess( final Void result ) {
+ notifyCallback( true );
}
@Override
- public void onFailure( Throwable t ) {
+ public void onFailure( final Throwable t ) {
// We shouldn't get an OptimisticLockFailedException (or any ex) as no
// other component should be updating the operational state.
LOG.error( "Failed to update toaster status", t );
notifyCallback( false );
}
- void notifyCallback( boolean result ) {
+ void notifyCallback( final boolean result ) {
if( resultCallback != null ) {
resultCallback.apply( result );
}
setToasterStatusUp( new Function<Boolean,Void>() {
@Override
- public Void apply( Boolean result ) {
+ public Void apply( final Boolean result ) {
currentMakeToastTask.set( null );
LOG.debug("Toast done");
- futureResult.set( Rpcs.<Void>getRpcResult( true, null,
- Collections.<RpcError>emptyList() ) );
+ futureResult.set( RpcResultBuilder.<Void>success().build() );
return null;
}
if (statsFlow == storedFlow) {
return true;
}
+ if (storedFlow == null && statsFlow != null) return false;
+ if (statsFlow == null && storedFlow != null) return false;
if (storedFlow.getClass() != statsFlow.getClass()) {
return false;
}
package org.opendaylight.controller.netconf.confignetconfconnector.osgi;
import java.lang.ref.SoftReference;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import java.util.concurrent.atomic.AtomicReference;
-import javax.annotation.concurrent.GuardedBy;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class YangStoreServiceImpl implements YangStoreService {
+ private static final Logger LOG = LoggerFactory.getLogger(YangStoreServiceImpl.class);
+
+ /**
+ * This is a rather interesting locking model. We need to guard against both the
+ * cache expiring from GC and being invalidated by schema context change. The
+ * context can change while we are doing processing, so we do not want to block
+ * it, so no synchronization can happen on the methods.
+ *
+ * So what we are doing is the following:
+ *
+ * We synchronize with GC as usual, using a SoftReference.
+ *
+ * The atomic reference is used to synchronize with {@link #refresh()}, e.g. when
+ * refresh happens, it will push a SoftReference(null), e.g. simulate the GC. Now
+ * that may happen while the getter is already busy acting on the old schema context,
+ * so it needs to understand that a refresh has happened and retry. To do that, it
+ * attempts a CAS operation -- if it fails, in knows that the SoftReference has
+ * been replaced and thus it needs to retry.
+ *
+ * Note that {@link #getYangStoreSnapshot()} will still use synchronize() internally
+ * to stop multiple threads doing the same work.
+ */
+ private final AtomicReference<SoftReference<YangStoreSnapshotImpl>> ref = new AtomicReference<>(new SoftReference<YangStoreSnapshotImpl>(null));
private final SchemaContextProvider service;
- @GuardedBy("this")
- private SoftReference<YangStoreSnapshotImpl> cache = new SoftReference<>(null);
- public YangStoreServiceImpl(SchemaContextProvider service) {
+ public YangStoreServiceImpl(final SchemaContextProvider service) {
this.service = service;
}
@Override
public synchronized YangStoreSnapshotImpl getYangStoreSnapshot() throws YangStoreException {
- YangStoreSnapshotImpl yangStoreSnapshot = cache.get();
- if (yangStoreSnapshot == null) {
- yangStoreSnapshot = new YangStoreSnapshotImpl(service.getSchemaContext());
- cache = new SoftReference<>(yangStoreSnapshot);
+ SoftReference<YangStoreSnapshotImpl> r = ref.get();
+ YangStoreSnapshotImpl ret = r.get();
+
+ while (ret == null) {
+ // We need to be compute a new value
+ ret = new YangStoreSnapshotImpl(service.getSchemaContext());
+
+ if (!ref.compareAndSet(r, new SoftReference<>(ret))) {
+ LOG.debug("Concurrent refresh detected, recomputing snapshot");
+ r = ref.get();
+ ret = null;
+ }
}
- return yangStoreSnapshot;
+
+ return ret;
}
/**
* Called when schema context changes, invalidates cache.
*/
- public synchronized void refresh() {
- cache.clear();
+ public void refresh() {
+ ref.set(new SoftReference<YangStoreSnapshotImpl>(null));
}
}
*/
package org.opendaylight.controller.netconf.cli.commands.local;
-import com.google.common.collect.Lists;
import org.opendaylight.controller.netconf.cli.NetconfDeviceConnectionManager;
import org.opendaylight.controller.netconf.cli.commands.AbstractCommand;
import org.opendaylight.controller.netconf.cli.commands.Command;
import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import com.google.common.collect.Lists;
+
/**
* Local disconnect command
*/
connectionManager.disconnect();
return new Output(new CompositeNodeTOImpl(getCommandId(), null,
- Lists.<Node<?>> newArrayList(new SimpleNodeTOImpl<>(new QName(getCommandId(), "status"), null,
+ Lists.<Node<?>> newArrayList(new SimpleNodeTOImpl<>(QName.create(getCommandId(), "status"), null,
"Connection disconnected"))));
}
*/
package org.opendaylight.controller.netconf.cli.io;
-import com.google.common.collect.Maps;
import java.util.Map;
+
import junit.framework.Assert;
+
import org.junit.Test;
import org.opendaylight.controller.netconf.cli.commands.CommandConstants;
import org.opendaylight.yangtools.yang.common.QName;
+import com.google.common.collect.Maps;
+
public class IOUtilTest {
@Test
public void testQNameFromKeyStringNew() throws Exception {
final String s = IOUtil.qNameToKeyString(CommandConstants.HELP_QNAME, "module");
final Map<String, QName> modulesMap = Maps.newHashMap();
- modulesMap.put("module", new QName(CommandConstants.HELP_QNAME, "module"));
+ modulesMap.put("module", QName.create(CommandConstants.HELP_QNAME, "module"));
final QName qName = IOUtil.qNameFromKeyString(s, modulesMap);
Assert.assertEquals(CommandConstants.HELP_QNAME, qName);
}
import io.netty.util.concurrent.Promise;
import java.io.IOException;
import org.opendaylight.controller.netconf.nettyutil.AbstractChannelInitializer;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.SshHandler;
import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.Invoker;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshHandler;
import org.opendaylight.protocol.framework.SessionListenerFactory;
final class SshClientChannelInitializer extends AbstractChannelInitializer<NetconfClientSession> {
@Override
public void initialize(final Channel ch, final Promise<NetconfClientSession> promise) {
try {
- final Invoker invoker = Invoker.subsystem("netconf");
- ch.pipeline().addFirst(new SshHandler(authenticationHandler, invoker));
+ ch.pipeline().addFirst(SshHandler.createForNetconfSubsystem(authenticationHandler));
super.initialize(ch,promise);
} catch (final IOException e) {
throw new RuntimeException(e);
this.label = clientLabel;
sessionListener = config.getSessionListener();
Future<NetconfClientSession> clientFuture = netconfClientDispatcher.createClient(config);
- clientSession = get(clientFuture);
+ clientSession = get(clientFuture);//TODO: make static
this.sessionId = clientSession.getSessionId();
}
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
}
private State state = State.IDLE;
+ private final Promise<S> promise;
private final Timer timer;
private final long connectionTimeoutMillis;
L sessionListener, long connectionTimeoutMillis) {
super(promise, channel);
this.sessionPreferences = sessionPreferences;
+ this.promise = promise;
this.timer = timer;
this.sessionListener = sessionListener;
this.connectionTimeoutMillis = connectionTimeoutMillis;
private void start() {
final NetconfMessage helloMessage = this.sessionPreferences.getHelloMessage();
- logger.debug("Session negotiation started with hello message {}", XmlUtil.toString(helloMessage.getDocument()));
+ logger.debug("Session negotiation started with hello message {} on channel {}", XmlUtil.toString(helloMessage.getDocument()), channel);
channel.pipeline().addLast(NAME_OF_EXCEPTION_HANDLER, new ExceptionHandlingInboundChannelHandler());
+ // FIXME, make sessionPreferences return HelloMessage, move NetconfHelloMessage to API
+ sendMessage((NetconfHelloMessage)helloMessage);
+
+ replaceHelloMessageOutboundHandler();
+ changeState(State.OPEN_WAIT);
+
timeout = this.timer.newTimeout(new TimerTask() {
@Override
public void run(final Timeout timeout) {
synchronized (this) {
if (state != State.ESTABLISHED) {
+
logger.debug("Connection timeout after {}, session is in state {}", timeout, state);
- final IllegalStateException cause = new IllegalStateException(
- "Session was not established after " + timeout);
- negotiationFailed(cause);
- changeState(State.FAILED);
+
+ // Do not fail negotiation if promise is done or canceled
+ // It would result in setting result of the promise second time and that throws exception
+ if (isPromiseFinished() == false) {
+ negotiationFailed(new IllegalStateException("Session was not established after " + timeout));
+ changeState(State.FAILED);
+
+ channel.closeFuture().addListener(new GenericFutureListener<ChannelFuture>() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if(future.isSuccess()) {
+ logger.debug("Channel {} closed: success", future.channel());
+ } else {
+ logger.warn("Channel {} closed: fail", future.channel());
+ }
+ }
+ });
+ }
} else if(channel.isOpen()) {
channel.pipeline().remove(NAME_OF_EXCEPTION_HANDLER);
}
}
}
- }, connectionTimeoutMillis, TimeUnit.MILLISECONDS);
- // FIXME, make sessionPreferences return HelloMessage, move NetconfHelloMessage to API
- sendMessage((NetconfHelloMessage)helloMessage);
+ private boolean isPromiseFinished() {
+ return promise.isDone() || promise.isCancelled();
+ }
- replaceHelloMessageOutboundHandler();
- changeState(State.OPEN_WAIT);
+ }, connectionTimeoutMillis, TimeUnit.MILLISECONDS);
}
private void cancelTimeout() {
protected abstract S getSession(L sessionListener, Channel channel, NetconfHelloMessage message) throws NetconfDocumentedException;
private synchronized void changeState(final State newState) {
- logger.debug("Changing state from : {} to : {}", state, newState);
- Preconditions.checkState(isStateChangePermitted(state, newState), "Cannot change state from %s to %s", state,
- newState);
+ logger.debug("Changing state from : {} to : {} for channel: {}", state, newState, channel);
+ Preconditions.checkState(isStateChangePermitted(state, newState), "Cannot change state from %s to %s for chanel %s", state,
+ newState, channel);
this.state = newState;
}
package org.opendaylight.controller.netconf.nettyutil.handler;
import io.netty.buffer.ByteBuf;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.handler.codec.ByteToMessageDecoder;
-
-import java.util.List;
-
+import io.netty.buffer.Unpooled;
+import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import org.opendaylight.controller.netconf.util.messages.NetconfMessageConstants;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import com.google.common.base.Charsets;
+public class NetconfEOMAggregator extends DelimiterBasedFrameDecoder {
-public class NetconfEOMAggregator extends ByteToMessageDecoder {
- private final static Logger logger = LoggerFactory.getLogger(NetconfEOMAggregator.class);
+ public static final ByteBuf DELIMITER = Unpooled.wrappedBuffer(NetconfMessageConstants.END_OF_MESSAGE);
- @Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
- int index = indexOfSequence(in, NetconfMessageConstants.END_OF_MESSAGE);
- if (index == -1) {
- logger.debug("Message is not complete, read again.");
- if (logger.isTraceEnabled()) {
- String str = in.toString(Charsets.UTF_8);
- logger.trace("Message read so far: {}", str);
- }
- ctx.read();
- } else {
- ByteBuf msg = in.readBytes(index);
- in.readBytes(NetconfMessageConstants.END_OF_MESSAGE.length);
- in.discardReadBytes();
- logger.debug("Message is complete.");
- out.add(msg);
- }
+ public NetconfEOMAggregator() {
+ super(Integer.MAX_VALUE, DELIMITER);
}
-
- private int indexOfSequence(ByteBuf in, byte[] sequence) {
- int index = -1;
- for (int i = 0; i < in.readableBytes() - sequence.length + 1; i++) {
- if (in.getByte(i) == sequence[0]) {
- index = i;
- for (int j = 1; j < sequence.length; j++) {
- if (in.getByte(i + j) != sequence[j]) {
- index = -1;
- break;
- }
- }
- if (index != -1) {
- return index;
- }
- }
- }
- return index;
- }
-
}
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
+import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
- StreamResult result = new StreamResult(new OutputStreamWriter(os));
+ // Wrap OutputStreamWriter with BufferedWriter as suggested in javadoc for OutputStreamWriter
+ StreamResult result = new StreamResult(new BufferedWriter(new OutputStreamWriter(os)));
DOMSource source = new DOMSource(msg.getDocument());
transformer.transform(source, result);
}
*/
package org.opendaylight.controller.netconf.nettyutil.handler.exi;
-import org.opendaylight.controller.netconf.api.NetconfMessage;
+import com.google.common.base.Preconditions;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
import org.openexi.proc.common.AlignmentType;
import org.openexi.proc.common.EXIOptions;
import org.openexi.proc.common.EXIOptionsException;
-import com.google.common.base.Preconditions;
-
public final class EXIParameters {
private static final String EXI_PARAMETER_ALIGNMENT = "alignment";
private static final String EXI_PARAMETER_BYTE_ALIGNED = "byte-aligned";
private static final String EXI_FIDELITY_PIS = "pis";
private static final String EXI_FIDELITY_PREFIXES = "prefixes";
- private static final String EXI_PARAMETER_SCHEMA = "schema";
- private static final String EXI_PARAMETER_SCHEMA_NONE = "none";
- private static final String EXI_PARAMETER_SCHEMA_BUILT_IN = "builtin";
- private static final String EXI_PARAMETER_SCHEMA_BASE_1_1 = "base:1.1";
-
private final EXIOptions options;
private EXIParameters(final EXIOptions options) {
this.options = Preconditions.checkNotNull(options);
}
- public static EXIParameters fromNetconfMessage(final NetconfMessage root) throws EXIOptionsException {
- return fromXmlElement(XmlElement.fromDomDocument(root.getDocument()));
- }
public static EXIParameters fromXmlElement(final XmlElement root) throws EXIOptionsException {
final EXIOptions options = new EXIOptions();
options.setPreserveNS(true);
}
}
-
- if (root.getElementsByTagName(EXI_PARAMETER_SCHEMA).getLength() > 0) {
-/*
- GrammarFactory grammarFactory = GrammarFactory.newInstance();
- if (operationElement
- .getElementsByTagName(EXI_PARAMETER_SCHEMA_NONE)
- .getLength() > 0) {
- this.grammars = grammarFactory.createSchemaLessGrammars();
- }
-
- if (operationElement.getElementsByTagName(
- EXI_PARAMETER_SCHEMA_BUILT_IN).getLength() > 0) {
- this.grammars = grammarFactory.createXSDTypesOnlyGrammars();
- }
-
- if (operationElement.getElementsByTagName(
- EXI_PARAMETER_SCHEMA_BASE_1_1).getLength() > 0) {
- this.grammars = grammarFactory
- .createGrammars(NETCONF_XSD_LOCATION);
- }
-*/
-
- }
-
return new EXIParameters(options);
}
Element startExiElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0,
START_EXI);
- addAlignemnt(exiOptions, doc, startExiElement);
+ addAlignment(exiOptions, doc, startExiElement);
addFidelity(exiOptions, doc, startExiElement);
rpcElement.appendChild(startExiElement);
}
}
- private static void addAlignemnt(EXIOptions exiOptions, Document doc, Element startExiElement) {
+ private static void addAlignment(EXIOptions exiOptions, Document doc, Element startExiElement) {
Element alignmentElement = doc.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_EXI_1_0,
ALIGNMENT_KEY);
alignmentElement.setTextContent(exiOptions.getAlignmentType().toString());
/**
* Class Providing username/password authentication option to
- * {@link org.opendaylight.controller.netconf.nettyutil.handler.ssh.SshHandler}
+ * {@link org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshHandler}
*/
public class LoginPassword extends AuthenticationHandler {
private final String username;
* Abstract class providing mechanism of invoking various SSH level services.
* Class is not allowed to be extended, as it provides its own implementations via instance initiators.
*/
-public abstract class Invoker {
+abstract class Invoker {
private boolean invoked = false;
- private Invoker(){}
+ private Invoker() {
+ }
protected boolean isInvoked() {
- // TODO invoked is always false
return invoked;
}
+ public void setInvoked() {
+ this.invoked = true;
+ }
+
abstract void invoke(SshSession session) throws IOException;
- /**
- * Invoker implementation to invokes subsystem SSH service.
- *
- * @param subsystem
- * @return
- */
+ public static Invoker netconfSubsystem(){
+ return subsystem("netconf");
+ }
+
public static Invoker subsystem(final String subsystem) {
return new Invoker() {
@Override
- void invoke(SshSession session) throws IOException {
+ synchronized void invoke(SshSession session) throws IOException {
if (isInvoked()) {
throw new IllegalStateException("Already invoked.");
}
-
- session.startSubSystem(subsystem);
+ try {
+ session.startSubSystem(subsystem);
+ } finally {
+ setInvoked();
+ }
}
};
}
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
-import ch.ethz.ssh2.channel.Channel;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocket;
-
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocket;
/**
* Wrapper class around GANYMED SSH java library.
*/
-public class SshClient {
+class SshClient {
private final VirtualSocket socket;
private final Map<Integer, SshSession> openSessions = new HashMap<>();
private final AuthenticationHandler authenticationHandler;
authenticationHandler.authenticate(connection);
}
- public void closeSession(SshSession session) {
- if (session.getState() == Channel.STATE_OPEN || session.getState() == Channel.STATE_OPENING) {
- session.close();
- }
- }
public void close() {
for (SshSession session : openSessions.values()){
- closeSession(session);
+ session.close();
}
openSessions.clear();
connection.close();
}
}
+
+ @Override
+ public String toString() {
+ return "SshClient{" +
+ "socket=" + socket +
+ '}';
+ }
}
package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocketException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
* Worker thread class. Handles all downstream and upstream events in SSH Netty
* pipeline.
*/
-public class SshClientAdapter implements Runnable {
+class SshClientAdapter implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(SshClientAdapter.class);
private static final int BUFFER_SIZE = 1024;
this.invoker = invoker;
}
+ // TODO: refactor
public void run() {
try {
SshSession session = sshClient.openSession();
byteBuf.writeBytes(tranBuff);
ctx.fireChannelRead(byteBuf);
}
-
- } catch (VirtualSocketException e) {
- // Netty closed connection prematurely.
- // Or maybe tried to open ganymed connection without having initialized session
- // (ctx.channel().remoteAddress() is null)
- // Just pass and move on.
} catch (Exception e) {
logger.error("Unexpected exception", e);
} finally {
}
}
- public void start(ChannelHandlerContext ctx) {
- if (this.ctx != null) {
- // context is already associated.
- return;
+ public Thread start(ChannelHandlerContext ctx, ChannelFuture channelFuture) {
+ checkArgument(channelFuture.isSuccess());
+ checkNotNull(ctx.channel().remoteAddress());
+ synchronized (this) {
+ checkState(this.ctx == null);
+ this.ctx = ctx;
}
- this.ctx = ctx;
- new Thread(this).start();
+ String threadName = toString();
+ Thread thread = new Thread(this, threadName);
+ thread.start();
+ return thread;
+ }
+
+ @Override
+ public String toString() {
+ return "SshClientAdapter{" +
+ "sshClient=" + sshClient +
+ '}';
}
}
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.nettyutil.handler.ssh;
+package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
-
import java.io.IOException;
import java.net.SocketAddress;
-
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshClient;
import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.Invoker;
-import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshClientAdapter;
import org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket.VirtualSocket;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Netty SSH handler class. Acts as interface between Netty and SSH library. All standard Netty message handling
* stops at instance of this class. All downstream events are handed of to wrapped {@link org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshClientAdapter};
*/
public class SshHandler extends ChannelOutboundHandlerAdapter {
+ private static final Logger logger = LoggerFactory.getLogger(SshHandler.class);
private static final String SOCKET = "socket";
private final VirtualSocket virtualSocket = new VirtualSocket();
private final SshClientAdapter sshClientAdapter;
+
+ public static SshHandler createForNetconfSubsystem(AuthenticationHandler authenticationHandler) throws IOException {
+ return new SshHandler(authenticationHandler, Invoker.netconfSubsystem());
+ }
+
+
public SshHandler(AuthenticationHandler authenticationHandler, Invoker invoker) throws IOException {
SshClient sshClient = new SshClient(virtualSocket, authenticationHandler);
this.sshClientAdapter = new SshClientAdapter(sshClient, invoker);
promise.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture channelFuture) {
- sshClientAdapter.start(ctx);
+ if (channelFuture.isSuccess()) {
+ sshClientAdapter.start(ctx, channelFuture);
+ } else {
+ logger.debug("Failed to connect to remote host");
+ }
}}
);
}
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
+import ch.ethz.ssh2.channel.Channel;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
/**
* Wrapper class for proprietary SSH sessions implementations
*/
-public class SshSession implements Closeable {
+class SshSession implements Closeable {
private final Session session;
public SshSession(Session session) {
this.session = session;
}
- public void execCommand(String cmd) throws IOException {
- session.execCommand(cmd);
- }
-
- public void execCommand(String cmd, String charsetName) throws IOException {
- session.execCommand(cmd, charsetName);
- }
-
- public void startShell() throws IOException {
- session.startShell();
- }
public void startSubSystem(String name) throws IOException {
session.startSubSystem(name);
}
- public int getState() {
- return session.getState();
- }
-
public InputStream getStdout() {
return new StreamGobbler(session.getStdout());
}
return session.getStdin();
}
- public int waitUntilDataAvailable(long timeout) throws IOException {
- return session.waitUntilDataAvailable(timeout);
- }
-
- public int waitForCondition(int conditionSet, long timeout) {
- return session.waitForCondition(conditionSet, timeout);
- }
-
- public Integer getExitStatus() {
- return session.getExitStatus();
- }
-
- public String getExitSignal() {
- return session.getExitSignal();
- }
-
@Override
public void close() {
- session.close();
+ if (session.getState() == Channel.STATE_OPEN || session.getState() == Channel.STATE_OPENING) {
+ session.close();
+ }
}
}
* use OIO application in asynchronous environment and NIO EventLoop. Using VirtualSocket OIO applications
* are able to use full potential of NIO environment.
*/
+//TODO: refactor - socket should be created when connection is established
public class VirtualSocket extends Socket implements ChannelHandler {
private static final String INPUT_STREAM = "inputStream";
private static final String OUTPUT_STREAM = "outputStream";
- private final ChannelInputStream chis = new ChannelInputStream();
- private final ChannelOutputStream chos = new ChannelOutputStream();
+ private final ChannelInputStream chais = new ChannelInputStream();
+ private final ChannelOutputStream chaos = new ChannelOutputStream();
private ChannelHandlerContext ctx;
public InputStream getInputStream() {
- return this.chis;
+ return this.chais;
}
public OutputStream getOutputStream() {
- return this.chos;
+ return this.chaos;
}
public void handlerAdded(ChannelHandlerContext ctx) {
this.ctx = ctx;
if (ctx.channel().pipeline().get(OUTPUT_STREAM) == null) {
- ctx.channel().pipeline().addFirst(OUTPUT_STREAM, chos);
+ ctx.channel().pipeline().addFirst(OUTPUT_STREAM, chaos);
}
if (ctx.channel().pipeline().get(INPUT_STREAM) == null) {
- ctx.channel().pipeline().addFirst(INPUT_STREAM, chis);
+ ctx.channel().pipeline().addFirst(INPUT_STREAM, chais);
}
}
ctx.fireExceptionCaught(throwable);
}
- public VirtualSocket() {super();}
@Override
public void connect(SocketAddress endpoint) throws IOException {}
@Override
public InetAddress getInetAddress() {
InetSocketAddress isa = getInetSocketAddress();
-
- if (isa == null) {
- throw new VirtualSocketException();
- }
-
- return getInetSocketAddress().getAddress();
+ return isa.getAddress();
}
@Override
@Override
public String toString() {
- return "Virtual socket InetAdress["+getInetAddress()+"], Port["+getPort()+"]";
+ return "VirtualSocket{" + getInetAddress() + ":" + getPort() + "}";
}
@Override
+++ /dev/null
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.netconf.nettyutil.handler.ssh.virtualsocket;
-
-/**
- * Exception class which provides notification about exceptional situations at the virtual socket layer.
- */
-// FIXME: Switch to checked exception, create a runtime exception to workaround Socket API
-public class VirtualSocketException extends RuntimeException {
- private static final long serialVersionUID = 1L;
-}
<artifactId>mockito-configuration</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-netty-util</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-client</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
*/
package org.opendaylight.controller.netconf.ssh;
+import com.google.common.annotations.VisibleForTesting;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.local.LocalAddress;
import java.io.IOException;
+import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
logger.trace("SSH server socket closed.");
}
+ @VisibleForTesting
+ public InetSocketAddress getLocalSocketAddress() {
+ return (InetSocketAddress) serverSocket.getLocalSocketAddress();
+ }
+
@Override
public void run() {
while (up) {
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.handler.stream.ChunkedStream;
+import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
class SSHClientHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SSHClientHandler.class);
private final AutoCloseable remoteConnection;
- private final OutputStream remoteOutputStream;
+ private final BufferedOutputStream remoteOutputStream;
private final String session;
private ChannelHandlerContext channelHandlerContext;
public SSHClientHandler(AutoCloseable remoteConnection, OutputStream remoteOutputStream,
String session) {
this.remoteConnection = remoteConnection;
- this.remoteOutputStream = remoteOutputStream;
+ this.remoteOutputStream = new BufferedOutputStream(remoteOutputStream);
this.session = session;
}
}
@Override
- public void channelRead(ChannelHandlerContext ctx, Object msg) {
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws IOException {
ByteBuf bb = (ByteBuf) msg;
// we can block the server here so that slow client does not cause memory pressure
try {
* traffic between the echo client and server by sending the first message to
* the server.
*/
-public class EchoClient implements Runnable {
+public class EchoClient extends Thread {
private static final Logger logger = LoggerFactory.getLogger(EchoClient.class);
- private final ChannelHandler clientHandler;
+ private final ChannelInitializer<LocalChannel> channelInitializer;
- public EchoClient(ChannelHandler clientHandler) {
- this.clientHandler = clientHandler;
+
+ public EchoClient(final ChannelHandler clientHandler) {
+ channelInitializer = new ChannelInitializer<LocalChannel>() {
+ @Override
+ public void initChannel(LocalChannel ch) throws Exception {
+ ch.pipeline().addLast(clientHandler);
+ }
+ };
+ }
+
+ public EchoClient(ChannelInitializer<LocalChannel> channelInitializer) {
+ this.channelInitializer = channelInitializer;
}
+ @Override
public void run() {
// Configure the client.
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
+
b.group(group)
.channel(LocalChannel.class)
- .handler(new ChannelInitializer<LocalChannel>() {
- @Override
- public void initChannel(LocalChannel ch) throws Exception {
- ch.pipeline().addLast(clientHandler);
- }
- });
+ .handler(channelInitializer);
// Start the client.
LocalAddress localAddress = new LocalAddress("foo");
import com.google.common.base.Charsets;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
* traffic between the echo client and server by sending the first message to
* the server.
*/
-public class EchoClientHandler extends ChannelInboundHandlerAdapter {
+public class EchoClientHandler extends ChannelInboundHandlerAdapter implements ChannelFutureListener {
private static final Logger logger = LoggerFactory.getLogger(EchoClientHandler.class);
private ChannelHandlerContext ctx;
+ private final StringBuilder fromServer = new StringBuilder();
+
+ public static enum State {CONNECTING, CONNECTED, FAILED_TO_CONNECT, CONNECTION_CLOSED}
+
+
+ private State state = State.CONNECTING;
@Override
- public void channelActive(ChannelHandlerContext ctx) {
+ public synchronized void channelActive(ChannelHandlerContext ctx) {
checkState(this.ctx == null);
- logger.info("client active");
+ logger.info("channelActive");
this.ctx = ctx;
+ state = State.CONNECTED;
}
@Override
- public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
- ByteBuf bb = (ByteBuf) msg;
- logger.info(">{}", bb.toString(Charsets.UTF_8));
- bb.release();
+ public synchronized void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ state = State.CONNECTION_CLOSED;
}
@Override
- public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
+ public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ ByteBuf bb = (ByteBuf) msg;
+ String string = bb.toString(Charsets.UTF_8);
+ fromServer.append(string);
+ logger.info(">{}", string);
+ bb.release();
}
@Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
+ public synchronized void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// Close the connection when an exception is raised.
logger.warn("Unexpected exception from downstream.", cause);
checkState(this.ctx.equals(ctx));
this.ctx = null;
}
- public void write(String message) {
+ public synchronized void write(String message) {
ByteBuf byteBuf = Unpooled.copiedBuffer(message.getBytes());
ctx.writeAndFlush(byteBuf);
}
+
+ public synchronized boolean isConnected() {
+ return state == State.CONNECTED;
+ }
+
+ public synchronized String read() {
+ return fromServer.toString();
+ }
+
+ @Override
+ public synchronized void operationComplete(ChannelFuture future) throws Exception {
+ checkState(state == State.CONNECTING);
+ if (future.isSuccess()) {
+ logger.trace("Successfully connected, state will be switched in channelActive");
+ } else {
+ state = State.FAILED_TO_CONNECT;
+ }
+ }
+
+ public State getState() {
+ return state;
+ }
}
package org.opendaylight.controller.netconf.netty;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import com.google.common.base.Stopwatch;
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.util.HashedWheelTimer;
+import java.net.InetSocketAddress;
+import java.util.concurrent.TimeUnit;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
+import org.opendaylight.controller.netconf.netty.EchoClientHandler.State;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.LoginPassword;
+import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshHandler;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
public class SSHTest {
public static final Logger logger = LoggerFactory.getLogger(SSHTest.class);
+ public static final String AHOJ = "ahoj\n";
+ private EventLoopGroup nettyGroup;
+ HashedWheelTimer hashedWheelTimer;
+
+ @Before
+ public void setUp() throws Exception {
+ hashedWheelTimer = new HashedWheelTimer();
+ nettyGroup = new NioEventLoopGroup();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ hashedWheelTimer.stop();
+ nettyGroup.shutdownGracefully();
+ }
@Test
public void test() throws Exception {
AuthProvider authProvider = mock(AuthProvider.class);
doReturn(PEMGenerator.generate().toCharArray()).when(authProvider).getPEMAsCharArray();
doReturn(true).when(authProvider).authenticated(anyString(), anyString());
- NetconfSSHServer thread = NetconfSSHServer.start(10831, NetconfConfigUtil.getNetconfLocalAddress(), authProvider, new NioEventLoopGroup());
- Thread.sleep(2000);
- logger.info("Closing socket");
- thread.close();
- thread.join();
+ NetconfSSHServer netconfSSHServer = NetconfSSHServer.start(10831, NetconfConfigUtil.getNetconfLocalAddress(),
+ authProvider, new NioEventLoopGroup());
+
+ InetSocketAddress address = netconfSSHServer.getLocalSocketAddress();
+ final EchoClientHandler echoClientHandler = connectClient(address);
+ Stopwatch stopwatch = new Stopwatch().start();
+ while(echoClientHandler.isConnected() == false && stopwatch.elapsed(TimeUnit.SECONDS) < 5) {
+ Thread.sleep(100);
+ }
+ assertTrue(echoClientHandler.isConnected());
+ logger.info("connected, writing to client");
+ echoClientHandler.write(AHOJ);
+ // check that server sent back the same string
+ stopwatch = stopwatch.reset().start();
+ while (echoClientHandler.read().endsWith(AHOJ) == false && stopwatch.elapsed(TimeUnit.SECONDS) < 5) {
+ Thread.sleep(100);
+ }
+ try {
+ String read = echoClientHandler.read();
+ assertTrue(read + " should end with " + AHOJ, read.endsWith(AHOJ));
+ } finally {
+ logger.info("Closing socket");
+ netconfSSHServer.close();
+ netconfSSHServer.join();
+ }
}
+
+ public EchoClientHandler connectClient(InetSocketAddress address) {
+ final EchoClientHandler echoClientHandler = new EchoClientHandler();
+ ChannelInitializer<NioSocketChannel> channelInitializer = new ChannelInitializer<NioSocketChannel>() {
+ @Override
+ public void initChannel(NioSocketChannel ch) throws Exception {
+ ch.pipeline().addFirst(SshHandler.createForNetconfSubsystem(new LoginPassword("a", "a")));
+ ch.pipeline().addLast(echoClientHandler);
+ }
+ };
+ Bootstrap b = new Bootstrap();
+
+ b.group(nettyGroup)
+ .channel(NioSocketChannel.class)
+ .handler(channelInitializer);
+
+ // Start the client.
+ b.connect(address).addListener(echoClientHandler);
+ return echoClientHandler;
+ }
+
+ @Test
+ public void testClientWithoutServer() throws Exception {
+ InetSocketAddress address = new InetSocketAddress(12345);
+ final EchoClientHandler echoClientHandler = connectClient(address);
+ Stopwatch stopwatch = new Stopwatch().start();
+ while(echoClientHandler.getState() == State.CONNECTING && stopwatch.elapsed(TimeUnit.SECONDS) < 5) {
+ Thread.sleep(100);
+ }
+ assertFalse(echoClientHandler.isConnected());
+ assertEquals(State.FAILED_TO_CONNECT, echoClientHandler.getState());
+ }
+
}
<module>opendaylight/dummy-console</module>
<module>opendaylight/karaf-branding</module>
<module>opendaylight/distribution/opendaylight-karaf</module>
+ <module>features</module>
</modules>
<scm>
<connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>