From: Moiz Raja Date: Mon, 25 Aug 2014 04:56:54 +0000 (+0000) Subject: Merge "Fix for possible NPE if Bundle is stopped." X-Git-Tag: release/helium~224 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=30faeb35260541c273a81b8f126b40da94daa825;hp=de2a9ebcf8bdcc8424980878c6c4feda55376f66 Merge "Fix for possible NPE if Bundle is stopped." --- diff --git a/features/adsal/pom.xml b/features/adsal/pom.xml index 0ccb17a664..213e500458 100644 --- a/features/adsal/pom.xml +++ b/features/adsal/pom.xml @@ -9,7 +9,7 @@ features-adsal ${sal.version} - pom + jar Features :: AD-SAL Features AD-SAL Features POM @@ -18,11 +18,21 @@ org.opendaylight.controller - base-features - ${project.parent.version} + features-base features xml - runtime + + + + org.opendaylight.yangtools + features-test + + + + org.opendaylight.controller + opendaylight-karaf-empty + 1.4.2-SNAPSHOT + zip @@ -68,6 +78,20 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + org.opendaylight.controller + opendaylight-karaf-empty + ${commons.opendaylight.version} + + + org.opendaylight.yangtools:features-test + + + diff --git a/features/adsal/src/main/resources/features.xml b/features/adsal/src/main/resources/features.xml index f955315164..e12ca8e5e9 100644 --- a/features/adsal/src/main/resources/features.xml +++ b/features/adsal/src/main/resources/features.xml @@ -1,6 +1,7 @@ + mvn:org.opendaylight.controller/features-base/${commons.opendaylight.version}/xml/features odl-adsal-core odl-adsal-networkconfiguration @@ -9,15 +10,16 @@ odl-adsal-configuration - base-felix-dm - base-dummy-console + odl-base-felix-dm + odl-base-dummy-console odl-adsal-thirdparty - mvn:org.apache.commons/commons-lang3/${commons.lang3.version} + mvn:org.apache.commons/commons-lang3/${commons.lang3.version} mvn:org.opendaylight.controller/sal/${sal.version} mvn:org.opendaylight.controller/sal.implementation/${sal.implementation.version} + odl-adsal-core mvn:org.opendaylight.controller/sal.networkconfiguration/${sal.networkconfiguration.version} mvn:org.opendaylight.controller/sal.networkconfiguration.implementation/${sal.networkconfiguration.version} @@ -28,8 +30,8 @@ transaction - base-felix-dm - base-eclipselink-persistence + odl-base-felix-dm + odl-base-eclipselink-persistence odl-adsal-core mvn:org.opendaylight.controller/clustering.services/${clustering.services.version} mvn:org.opendaylight.controller/clustering.services-implementation/${clustering.services_implementation.version} diff --git a/features/base/pom.xml b/features/base/pom.xml index d925f51b94..5dabfe5a44 100644 --- a/features/base/pom.xml +++ b/features/base/pom.xml @@ -7,13 +7,461 @@ 1.4.2-SNAPSHOT ../../opendaylight/commons/opendaylight - base-features - pom + features-base + jar ${project.artifactId} Base Features POM features.xml + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + + com.fasterxml.jackson.core + jackson-annotations + + + + com.fasterxml.jackson.core + jackson-core + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.datatype + jackson-datatype-json-org + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-base + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + + + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + + + + com.google.code.gson + gson + + + com.google.guava + guava + + + com.sun.jersey + jersey-client + + + + com.sun.jersey + jersey-core + + + com.sun.jersey + jersey-server + + + commons-codec + commons-codec + + + commons-fileupload + commons-fileupload + + + commons-io + commons-io + + + commons-net + commons-net + + + eclipselink + javax.persistence + + + eclipselink + javax.resource + + + equinoxSDK381 + javax.servlet + + + equinoxSDK381 + javax.servlet.jsp + + + equinoxSDK381 + org.apache.felix.gogo.command + + + equinoxSDK381 + org.apache.felix.gogo.runtime + + + equinoxSDK381 + org.apache.felix.gogo.shell + + + equinoxSDK381 + org.eclipse.equinox.cm + + + equinoxSDK381 + org.eclipse.equinox.console + + + equinoxSDK381 + org.eclipse.equinox.ds + + + equinoxSDK381 + org.eclipse.equinox.launcher + + + equinoxSDK381 + org.eclipse.equinox.util + + + equinoxSDK381 + org.eclipse.osgi + + + equinoxSDK381 + org.eclipse.osgi.services + + + + geminiweb + org.eclipse.gemini.web.core + + + geminiweb + org.eclipse.gemini.web.extender + + + geminiweb + org.eclipse.gemini.web.tomcat + + + geminiweb + org.eclipse.virgo.kernel.equinox.extensions + + + geminiweb + org.eclipse.virgo.util.common + + + geminiweb + org.eclipse.virgo.util.io + + + geminiweb + org.eclipse.virgo.util.math + + + geminiweb + org.eclipse.virgo.util.osgi + + + geminiweb + org.eclipse.virgo.util.osgi.manifest + + + geminiweb + org.eclipse.virgo.util.parser.manifest + + + io.netty + netty-buffer + + + io.netty + netty-codec + + + io.netty + netty-codec-http + + + io.netty + netty-common + + + + + io.netty + netty-handler + + + io.netty + netty-transport + + + orbit + javax.activation + + + orbit + javax.annotation + + + orbit + javax.ejb + + + orbit + javax.el + + + orbit + javax.mail.glassfish + + + orbit + javax.servlet.jsp.jstl + + + orbit + javax.servlet.jsp.jstl.impl + + + orbit + javax.xml.rpc + + + orbit + org.apache.catalina + 7.0.53.v201406061610 + + + orbit + org.apache.catalina.ha + 7.0.53.v201406070630 + + + orbit + org.apache.catalina.tribes + 7.0.53.v201406070630 + + + orbit + org.apache.coyote + 7.0.53.v201406070630 + + + orbit + org.apache.el + 7.0.53.v201406060720 + + + orbit + org.apache.jasper + 7.0.53.v201406070630 + + + orbit + org.apache.juli.extras + 7.0.53.v201406060720 + + + orbit + org.apache.tomcat.api + 7.0.53.v201406060720 + + + orbit + org.apache.tomcat.util + 7.0.53.v201406070630 + + + org.aopalliance + com.springsource.org.aopalliance + + + org.apache.commons + commons-lang3 + + + org.apache.felix + org.apache.felix.dependencymanager + + + org.apache.felix + org.apache.felix.dependencymanager.shell + + + org.apache.felix + org.apache.felix.fileinstall + + + + org.apache.felix + org.apache.felix.webconsole + all + + + + org.codehaus.jettison + jettison + + + + org.eclipse.equinox.http + servlet + + + org.eclipse.persistence + org.eclipse.persistence.antlr + + + org.eclipse.persistence + org.eclipse.persistence.core + + + org.eclipse.persistence + org.eclipse.persistence.moxy + + + org.javassist + javassist + + + org.jboss.spec.javax.transaction + jboss-transaction-api_1.1_spec + + + org.jolokia + jolokia-osgi + + + + org.json + json + + + + org.ow2.asm + asm-all + + + + org.ow2.chameleon.management + chameleon-mbeans + + + + + org.slf4j + jcl-over-slf4j + + + org.slf4j + log4j-over-slf4j + + + org.slf4j + slf4j-api + + + org.springframework + org.springframework.aop + + + + org.springframework + org.springframework.asm + + + org.springframework + org.springframework.beans + + + org.springframework + org.springframework.context + + + org.springframework + org.springframework.context.support + + + org.springframework + org.springframework.core + + + org.springframework + org.springframework.expression + + + org.springframework + org.springframework.transaction + + + org.springframework + org.springframework.web + + + org.springframework + org.springframework.web.servlet + + + + org.springframework.security + spring-security-config + + + org.springframework.security + spring-security-core + + + org.springframework.security + spring-security-taglibs + + + org.springframework.security + spring-security-web + + + virgomirror + org.eclipse.jdt.core.compiler.batch + + + + + org.opendaylight.yangtools + features-test + 0.6.2-SNAPSHOT + + + + org.opendaylight.controller + opendaylight-karaf-empty + 1.4.2-SNAPSHOT + zip + + @@ -75,6 +523,20 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + org.opendaylight.controller + opendaylight-karaf-empty + ${commons.opendaylight.version} + + + org.opendaylight.yangtools:features-test + + + diff --git a/features/base/src/main/resources/features.xml b/features/base/src/main/resources/features.xml index b9107b9f62..999cf704d2 100644 --- a/features/base/src/main/resources/features.xml +++ b/features/base/src/main/resources/features.xml @@ -1,34 +1,35 @@ - + - + http transaction - base-felix-dm - base-aries-spi-fly - base-dummy-console - base-apache-commons - base-eclipselink-persistence - base-gemini-web - base-tomcat - base-netty - base-jersey - base-spring-security + odl-base-felix-dm + odl-base-aries-spi-fly + odl-base-dummy-console + odl-base-apache-commons + odl-base-eclipselink-persistence + odl-base-gemini-web + odl-base-tomcat + odl-base-netty + odl-base-jersey + odl-base-jackson + odl-base-spring-security - + mvn:org.opendaylight.controller/dummy-console/1.1.0-SNAPSHOT - - mvn:org.osgi/org.osgi.compendium/${osgi.compendium.version} - mvn:org.apache.felix/org.apache.felix.dependencymanager/${felix.dependencymanager.version} - mvn:org.apache.felix/org.apache.felix.dependencymanager.shell/${felix.dependencymanager.shell.version} + + mvn:org.osgi/org.osgi.compendium/${osgi.compendium.version} + mvn:org.apache.felix/org.apache.felix.dependencymanager/${felix.dependencymanager.version} + mvn:org.apache.felix/org.apache.felix.dependencymanager.shell/${felix.dependencymanager.shell.version} - - mvn:org.apache.aries/org.apache.aries.util/1.1.0 - mvn:org.apache.aries.spifly/org.apache.aries.spifly.dynamic.bundle/${spifly.version} - mvn:org.ow2.asm/asm-all/4.0 + + mvn:org.apache.aries/org.apache.aries.util/1.1.0 + mvn:org.apache.aries.spifly/org.apache.aries.spifly.dynamic.bundle/${spifly.version} + mvn:org.ow2.asm/asm-all/4.0 - + wrap:mvn:io.netty/netty-buffer/${netty.version} wrap:mvn:io.netty/netty-codec/${netty.version} wrap:mvn:io.netty/netty-transport/${netty.version} @@ -37,95 +38,96 @@ wrap:mvn:io.netty/netty-codec-http/${netty.version} mvn:org.opendaylight.controller.thirdparty/ganymed/1.1-SNAPSHOT - - base-gemini-web - mvn:org.opendaylight.controller.thirdparty/com.sun.jersey.jersey-servlet/${jersey.version} + + odl-base-gemini-web mvn:com.sun.jersey/jersey-server/${jersey.version} mvn:com.sun.jersey/jersey-core/${jersey.version} mvn:com.sun.jersey/jersey-client/${jersey.version} mvn:com.sun.jersey/jersey-servlet/${jersey.version} - mvn:javax.ws.rs/javax.ws.rs-api/2.0 - + http mvn:com.eclipsesource.jaxrs/jersey-all/${jersey2.version} mvn:com.eclipsesource.jaxrs/publisher/${jersey2.publisher.version} - mvn:javax.ws.rs/javax.ws.rs-api/${jsr311.v2.api.version} + mvn:javax.ws.rs/javax.ws.rs-api/${jsr311.v2.api.version} mvn:javax.annotation/javax.annotation-api/${javax.annotation.version} - - mvn:com.fasterxml.jackson.core/jackson-annotations/${jackson.version} - mvn:com.fasterxml.jackson.core/jackson-core/${jackson.version} - mvn:com.fasterxml.jackson.core/jackson-databind/${jackson.version} - mvn:org.codehaus.jettison/jettison/${jettison.version} - mvn:com.fasterxml.jackson.module/jackson-module-jaxb-annotations/${jackson.version} - mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-base/${jackson.version} - mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider/${jackson.version} + + mvn:com.sun.jersey/jersey-core/${jersey.version} + mvn:com.sun.jersey/jersey-client/${jersey.version} + mvn:com.fasterxml.jackson.core/jackson-annotations/${jackson.version} + mvn:com.fasterxml.jackson.core/jackson-core/${jackson.version} + mvn:com.fasterxml.jackson.core/jackson-databind/${jackson.version} + mvn:org.codehaus.jettison/jettison/${jettison.version} + mvn:com.fasterxml.jackson.module/jackson-module-jaxb-annotations/${jackson.version} + mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-base/${jackson.version} + mvn:com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider/${jackson.version} - - mvn:org.slf4j/slf4j-jdk14/1.7.2 - mvn:org.slf4j/slf4j-nop/1.7.2 - mvn:org.slf4j/slf4j-simple/1.7.2 - mvn:org.slf4j/slf4j-api/1.7.2 + + mvn:org.slf4j/slf4j-jdk14/1.7.2 + mvn:org.slf4j/slf4j-nop/1.7.2 + mvn:org.slf4j/slf4j-simple/1.7.2 + mvn:org.slf4j/slf4j-api/1.7.2 - - mvn:com.google.guava/guava/${guava.version} - mvn:org.javassist/javassist/${javassist.version} - mvn:commons-io/commons-io/${commons.io.version} - mvn:commons-codec/commons-codec/${commons.codec.version} - mvn:org.apache.commons/commons-lang3/${commons.lang3.version} - mvn:commons-net/commons-net/${commons.net.version} + + mvn:com.google.guava/guava/${guava.version} + mvn:org.javassist/javassist/${javassist.version} + mvn:commons-io/commons-io/${commons.io.version} + mvn:commons-codec/commons-codec/${commons.codec.version} + mvn:org.apache.commons/commons-lang3/${commons.lang3.version} + mvn:commons-net/commons-net/${commons.net.version} - - mvn:eclipselink/javax.persistence/2.0.4.v201112161009 - mvn:eclipselink/javax.resource/1.5.0.v200906010428 - mvn:org.eclipse.persistence/org.eclipse.persistence.moxy/2.5.0 - mvn:org.eclipse.persistence/org.eclipse.persistence.core/2.5.0 + + mvn:eclipselink/javax.persistence/2.0.4.v201112161009 + mvn:eclipselink/javax.resource/1.5.0.v200906010428 + mvn:org.eclipse.persistence/org.eclipse.persistence.moxy/2.5.0 + mvn:org.eclipse.persistence/org.eclipse.persistence.core/2.5.0 - + http transaction - base-slf4j - base-felix-dm - base-jackson - base-apache-commons - mvn:com.google.code.gson/gson/${gson.version} - mvn:commons-fileupload/commons-fileupload/${commons.fileupload.version} - mvn:geminiweb/org.eclipse.gemini.web.core/${geminiweb.version} - mvn:geminiweb/org.eclipse.gemini.web.extender/${geminiweb.version} - mvn:geminiweb/org.eclipse.virgo.util.common/${virgo.version} - mvn:geminiweb/org.eclipse.virgo.util.io/${virgo.version} - mvn:geminiweb/org.eclipse.virgo.util.math/${virgo.version} - mvn:geminiweb/org.eclipse.virgo.util.osgi/${virgo.version} - mvn:geminiweb/org.eclipse.virgo.util.osgi.manifest/${virgo.version} - mvn:geminiweb/org.eclipse.virgo.util.parser.manifest/${virgo.version} - mvn:org.apache.felix/org.apache.felix.fileinstall/3.1.6 - mvn:orbit/javax.activation/1.1.0.v201211130549 - mvn:orbit/javax.annotation/1.1.0.v201209060031 - mvn:orbit/javax.ejb/3.1.1.v201204261316 - mvn:orbit/javax.el/2.2.0.v201108011116 - mvn:orbit/javax.mail.glassfish/1.4.1.v201108011116 - mvn:orbit/javax.xml.rpc/1.1.0.v201005080400 - mvn:org.eclipse.jetty.orbit/javax.servlet.jsp/2.2.0.v201112011158 - mvn:orbit/javax.servlet.jsp.jstl/1.2.0.v201105211821 - mvn:orbit/javax.servlet.jsp.jstl.impl/1.2.0.v201210211230 + odl-base-slf4j + odl-base-felix-dm + odl-base-jackson + odl-base-apache-commons + mvn:com.google.code.gson/gson/${gson.version} + mvn:commons-fileupload/commons-fileupload/${commons.fileupload.version} + mvn:geminiweb/org.eclipse.gemini.web.core/${geminiweb.version} + mvn:geminiweb/org.eclipse.gemini.web.extender/${geminiweb.version} + mvn:geminiweb/org.eclipse.virgo.util.common/${virgo.version} + mvn:geminiweb/org.eclipse.virgo.util.io/${virgo.version} + mvn:geminiweb/org.eclipse.virgo.util.math/${virgo.version} + mvn:geminiweb/org.eclipse.virgo.util.osgi/${virgo.version} + mvn:geminiweb/org.eclipse.virgo.util.osgi.manifest/${virgo.version} + mvn:geminiweb/org.eclipse.virgo.util.parser.manifest/${virgo.version} + mvn:org.apache.felix/org.apache.felix.fileinstall/3.1.6 + mvn:orbit/javax.activation/1.1.0.v201211130549 + mvn:orbit/javax.annotation/1.1.0.v201209060031 + mvn:orbit/javax.ejb/3.1.1.v201204261316 + mvn:orbit/javax.el/2.2.0.v201108011116 + mvn:orbit/javax.mail.glassfish/1.4.1.v201108011116 + mvn:orbit/javax.xml.rpc/1.1.0.v201005080400 + mvn:org.eclipse.jetty.orbit/javax.servlet.jsp/2.2.0.v201112011158 + mvn:orbit/javax.servlet.jsp.jstl/1.2.0.v201105211821 + mvn:orbit/javax.servlet.jsp.jstl.impl/1.2.0.v201210211230 - - base-gemini-web - base-eclipselink-persistence - mvn:orbit/org.apache.catalina/7.0.32.v201211201336 - mvn:geminiweb/org.eclipse.gemini.web.tomcat/${geminiweb.version} - mvn:orbit/org.apache.catalina.ha/7.0.32.v201211201952 - mvn:orbit/org.apache.catalina.tribes/7.0.32.v201211201952 - mvn:orbit/org.apache.coyote/7.0.32.v201211201952 - mvn:orbit/org.apache.el/7.0.32.v201211081135 - mvn:orbit/org.apache.jasper/7.0.32.v201211201952 - mvn:orbit/org.apache.juli.extras/7.0.32.v201211081135 - mvn:orbit/org.apache.tomcat.api/7.0.32.v201211081135 - mvn:orbit/org.apache.tomcat.util/7.0.32.v201211201952 - wrap:mvn:virgomirror/org.eclipse.jdt.core.compiler.batch/3.8.0.I20120518-2145 + + odl-base-gemini-web + odl-base-eclipselink-persistence + mvn:orbit/org.apache.catalina/${commons.karaf.catalina} + mvn:geminiweb/org.eclipse.gemini.web.tomcat/${geminiweb.version} + mvn:orbit/org.apache.catalina.ha/${commons.karaf.catalina.ha} + mvn:orbit/org.apache.catalina.tribes/${commons.karaf.catalina.tribes} + mvn:orbit/org.apache.coyote/${commons.karaf.coyote} + mvn:orbit/org.apache.el/${commons.karaf.el} + mvn:orbit/org.apache.jasper/${commons.karaf.jasper} + mvn:orbit/org.apache.juli.extras/${commons.karaf.juli.version} + mvn:orbit/org.apache.tomcat.api/${commons.karaf.tomcat.api} + mvn:orbit/org.apache.tomcat.util/${commons.karaf.tomcat.util} + mvn:org.opendaylight.controller/karaf-tomcat-security/${karaf.security.version} + wrap:mvn:virgomirror/org.eclipse.jdt.core.compiler.batch/${eclipse.jdt.core.compiler.batch.version} - + mvn:org.ow2.asm/asm-all/${asm.version} mvn:org.aopalliance/com.springsource.org.aopalliance/${aopalliance.version} mvn:org.springframework/org.springframework.aop/${spring.version} @@ -137,14 +139,14 @@ mvn:org.springframework/org.springframework.expression/${spring.version} mvn:org.springframework/org.springframework.transaction/${spring.version} - - base-spring - base-gemini-web + + odl-base-spring + odl-base-gemini-web mvn:org.springframework/org.springframework.web/${spring.version} mvn:org.springframework/org.springframework.web.servlet/${spring.version} - - base-spring-web + + odl-base-spring-web mvn:org.springframework.security/spring-security-config/${spring-security.version} mvn:org.springframework.security/spring-security-core/${spring-security.version} mvn:org.springframework.security/spring-security-taglibs/${spring-security.version} diff --git a/features/config-netty/pom.xml b/features/config-netty/pom.xml index 2f4b4b1e21..bf036979cf 100644 --- a/features/config-netty/pom.xml +++ b/features/config-netty/pom.xml @@ -9,7 +9,7 @@ features-config-netty - pom + jar features.xml @@ -21,7 +21,6 @@ features-config-persister features xml - runtime org.opendaylight.controller @@ -46,6 +45,19 @@ org.opendaylight.controller config-netty-config + + ${config.version} + xml + config + + + + org.opendaylight.yangtools + features-test @@ -92,6 +104,20 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + org.opendaylight.controller + opendaylight-karaf-empty + ${commons.opendaylight.version} + + + org.opendaylight.yangtools:features-test + + + diff --git a/features/config-netty/src/main/resources/features.xml b/features/config-netty/src/main/resources/features.xml index 7f57d8cb84..f8df1aa58c 100644 --- a/features/config-netty/src/main/resources/features.xml +++ b/features/config-netty/src/main/resources/features.xml @@ -4,7 +4,7 @@ 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"> mvn:org.opendaylight.controller/features-config-persister/${config.version}/xml/features - + odl-config-netty-config-api mvn:org.opendaylight.controller/netty-event-executor-config/${project.version} mvn:org.opendaylight.controller/netty-threadgroup-config/${project.version} diff --git a/features/config-persister/pom.xml b/features/config-persister/pom.xml index 6dc8941345..3346c754d6 100644 --- a/features/config-persister/pom.xml +++ b/features/config-persister/pom.xml @@ -9,7 +9,7 @@ features-config-persister - pom + jar features.xml @@ -22,21 +22,18 @@ ${yangtools.version} features xml - runtime org.opendaylight.controller features-netconf features xml - runtime org.opendaylight.controller features-config features xml - runtime org.opendaylight.controller @@ -82,6 +79,11 @@ org.eclipse.persistence org.eclipse.persistence.moxy + + + org.opendaylight.yangtools + features-test + @@ -127,6 +129,20 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + org.opendaylight.controller + opendaylight-karaf-empty + ${commons.opendaylight.version} + + + org.opendaylight.yangtools:features-test + + + diff --git a/features/config-persister/src/main/resources/features.xml b/features/config-persister/src/main/resources/features.xml index a3c005b3bd..20fb19f5ff 100644 --- a/features/config-persister/src/main/resources/features.xml +++ b/features/config-persister/src/main/resources/features.xml @@ -6,11 +6,11 @@ mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features mvn:org.opendaylight.controller/features-netconf/${netconf.version}/xml/features mvn:org.opendaylight.controller/features-config/${config.version}/xml/features - + odl-config-persister odl-config-startup - + odl-netconf-api odl-config-api odl-yangtools-binding-generator @@ -27,7 +27,7 @@ mvn:org.eclipse.persistence/org.eclipse.persistence.core/${eclipse.persistence.version} mvn:org.eclipse.persistence/org.eclipse.persistence.moxy/${eclipse.persistence.version} - + odl-config-netconf-connector odl-config-persister odl-netconf-impl diff --git a/features/config/pom.xml b/features/config/pom.xml index c69e11bed2..20feceb360 100644 --- a/features/config/pom.xml +++ b/features/config/pom.xml @@ -9,20 +9,26 @@ features-config - pom + jar features.xml + + + org.opendaylight.controller + opendaylight-karaf-empty + 1.4.2-SNAPSHOT + zip + org.opendaylight.yangtools features-yangtools ${yangtools.version} features xml - runtime org.opendaylight.controller @@ -92,6 +98,11 @@ org.opendaylight.controller config-manager + + + org.opendaylight.yangtools + features-test + @@ -137,6 +148,20 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + org.opendaylight.controller + opendaylight-karaf-empty + ${commons.opendaylight.version} + + + org.opendaylight.yangtools:features-test + + + diff --git a/features/config/src/main/resources/features.xml b/features/config/src/main/resources/features.xml index 5027588acb..b4dd03f491 100644 --- a/features/config/src/main/resources/features.xml +++ b/features/config/src/main/resources/features.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0"> mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features - + odl-mdsal-common odl-config-api odl-config-netty-config-api @@ -13,7 +13,7 @@ odl-config-manager - + odl-yangtools-data-binding mvn:org.opendaylight.controller/sal-common/${mdsal.version} mvn:org.opendaylight.controller/sal-common-api/${mdsal.version} @@ -21,13 +21,13 @@ mvn:org.opendaylight.controller/sal-common-util/${mdsal.version} - + mvn:org.opendaylight.controller/config-api/${project.version} odl-yangtools-common odl-yangtools-binding - + odl-config-api mvn:org.opendaylight.controller/netty-config-api/${project.version} mvn:io.netty/netty-transport/${netty.version} @@ -35,7 +35,7 @@ mvn:io.netty/netty-buffer/${netty.version} - + odl-yangtools-common odl-yangtools-binding odl-yangtools-binding-generator @@ -49,7 +49,7 @@ mvn:com.google.guava/guava/${guava.version} mvn:org.javassist/javassist/${javassist.version} - + odl-config-core mvn:org.opendaylight.controller/config-manager/${project.version} diff --git a/features/flow/pom.xml b/features/flow/pom.xml index 09bb6c91e6..ac189737d9 100644 --- a/features/flow/pom.xml +++ b/features/flow/pom.xml @@ -9,7 +9,7 @@ features-flow - pom + jar features.xml @@ -22,7 +22,6 @@ ${mdsal.version} features xml - runtime org.opendaylight.controller.model @@ -64,6 +63,11 @@ org.opendaylight.controller.md forwardingrules-manager + + + org.opendaylight.yangtools + features-test + @@ -109,6 +113,20 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + org.opendaylight.controller + opendaylight-karaf-empty + ${commons.opendaylight.version} + + + org.opendaylight.yangtools:features-test + + + diff --git a/features/flow/src/main/resources/features.xml b/features/flow/src/main/resources/features.xml index 3f914be4ae..0540458630 100644 --- a/features/flow/src/main/resources/features.xml +++ b/features/flow/src/main/resources/features.xml @@ -4,7 +4,7 @@ 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"> mvn:org.opendaylight.controller/features-mdsal/${mdsal.version}/xml/features - + odl-yangtools-models mvn:org.opendaylight.controller.model/model-flow-base/${project.version} mvn:org.opendaylight.controller.model/model-flow-service/${project.version} @@ -12,7 +12,7 @@ mvn:org.opendaylight.controller.model/model-inventory/${project.version} mvn:org.opendaylight.controller.model/model-topology/${project.version} - + odl-mdsal-broker odl-flow-model mvn:org.opendaylight.controller.md/topology-manager/${project.version} diff --git a/features/mdsal/pom.xml b/features/mdsal/pom.xml index 4f1ba98e5c..5be0061698 100644 --- a/features/mdsal/pom.xml +++ b/features/mdsal/pom.xml @@ -9,7 +9,7 @@ features-mdsal - pom + jar features.xml @@ -21,28 +21,24 @@ features-yangtools features xml - runtime org.opendaylight.controller features-config features xml - runtime org.opendaylight.controller features-config-persister features xml - runtime org.opendaylight.controller features-config-netty features xml - runtime org.opendaylight.controller @@ -87,6 +83,9 @@ org.opendaylight.controller md-sal-config + ${mdsal.version} + xml + config org.opendaylight.controller @@ -103,6 +102,9 @@ org.opendaylight.controller netconf-connector-config + ${netconf.version} + xml + config org.opendaylight.controller @@ -121,8 +123,8 @@ jersey-server - org.opendaylight.controller.thirdparty - com.sun.jersey.jersey-servlet + com.sun.jersey + jersey-servlet io.netty @@ -155,6 +157,9 @@ org.opendaylight.controller sal-rest-connector-config + ${mdsal.version} + xml + config org.opendaylight.controller.samples @@ -171,6 +176,15 @@ org.opendaylight.controller.samples toaster-config + ${mdsal.version} + xml + config + + + + org.opendaylight.yangtools + features-test + 0.6.2-SNAPSHOT @@ -217,6 +231,20 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + org.opendaylight.controller + opendaylight-karaf-empty + ${commons.opendaylight.version} + + + org.opendaylight.yangtools:features-test + + + diff --git a/features/mdsal/src/main/resources/features.xml b/features/mdsal/src/main/resources/features.xml index a3d7ed0f83..754e97dee3 100644 --- a/features/mdsal/src/main/resources/features.xml +++ b/features/mdsal/src/main/resources/features.xml @@ -7,13 +7,13 @@ mvn:org.opendaylight.controller/features-config/${config.version}/xml/features mvn:org.opendaylight.controller/features-config-persister/${config.version}/xml/features mvn:org.opendaylight.controller/features-config-netty/${config.version}/xml/features - + odl-mdsal-broker odl-mdsal-netconf-connector odl-restconf odl-toaster - + odl-yangtools-common odl-yangtools-binding odl-mdsal-common @@ -30,7 +30,7 @@ mvn:org.opendaylight.controller/sal-inmemory-datastore/${project.version} mvn:org.opendaylight.controller/md-sal-config/${mdsal.version}/xml/config - + odl-mdsal-broker odl-netconf-client odl-yangtools-models @@ -39,14 +39,14 @@ mvn:org.opendaylight.controller/netconf-config-dispatcher/${config.version} mvn:org.opendaylight.controller/netconf-connector-config/${netconf.version}/xml/config - + odl-mdsal-broker war mvn:org.opendaylight.controller/sal-rest-connector/${project.version} mvn:com.google.code.gson/gson/${gson.version} mvn:com.sun.jersey/jersey-core/${jersey.version} mvn:com.sun.jersey/jersey-server/${jersey.version} - mvn:org.opendaylight.controller.thirdparty/com.sun.jersey.jersey-servlet/${jersey.version} + mvn:com.sun.jersey/jersey-servlet/${jersey.version} mvn:io.netty/netty-buffer/${netty.version} mvn:io.netty/netty-codec/${netty.version} mvn:io.netty/netty-codec-http/${netty.version} @@ -56,7 +56,7 @@ mvn:org.opendaylight.controller/sal-remote/${project.version} mvn:org.opendaylight.controller/sal-rest-connector-config/${mdsal.version}/xml/config - + odl-yangtools-common odl-yangtools-binding odl-mdsal-broker diff --git a/features/netconf/pom.xml b/features/netconf/pom.xml index 956a67e28b..106e54a338 100644 --- a/features/netconf/pom.xml +++ b/features/netconf/pom.xml @@ -105,6 +105,9 @@ org.opendaylight.controller netconf-config + ${config.version} + xml + config org.opendaylight.controller diff --git a/features/netconf/src/main/resources/features.xml b/features/netconf/src/main/resources/features.xml index 0033b0d83c..f1631a5340 100644 --- a/features/netconf/src/main/resources/features.xml +++ b/features/netconf/src/main/resources/features.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0"> mvn:org.opendaylight.controller/features-protocol-framework/${protocol-framework.version}/xml/features mvn:org.opendaylight.controller/features-config/${config.version}/xml/features - + odl-netconf-api odl-netconf-mapping-api odl-netconf-util @@ -16,7 +16,7 @@ odl-netconf-monitoring - + odl-protocol-framework mvn:org.opendaylight.controller/netconf-api/${project.version} mvn:org.opendaylight.controller/ietf-netconf-monitoring/${project.version} @@ -24,7 +24,7 @@ mvn:org.opendaylight.yangtools.model/ietf-inet-types/${ietf-inet-types.version} mvn:org.opendaylight.yangtools.model/ietf-yang-types/${ietf-yang-types.version} - + odl-netconf-api mvn:org.opendaylight.controller/netconf-mapping-api/${project.version} @@ -32,21 +32,21 @@ odl-netconf-mapping-api mvn:org.opendaylight.controller/netconf-util/${project.version} - + odl-netconf-api odl-netconf-mapping-api odl-netconf-util odl-netconf-netty-util mvn:org.opendaylight.controller/netconf-impl/${project.version} - + odl-config-manager odl-netconf-api odl-netconf-mapping-api odl-netconf-util mvn:org.opendaylight.controller/config-netconf-connector/${project.version} - + odl-netconf-api odl-netconf-mapping-api odl-netconf-util @@ -59,12 +59,12 @@ mvn:io.netty/netty-buffer/${netty.version} mvn:io.netty/netty-transport/${netty.version} - + odl-netconf-netty-util mvn:org.opendaylight.controller/netconf-client/${project.version} - mvn:org.opendaylight.controller/netconf-config/${netconf.version}/xml/config + mvn:org.opendaylight.controller/netconf-config/${netconf.version}/xml/config - + odl-netconf-util mvn:org.opendaylight.controller/netconf-monitoring/${project.version} diff --git a/features/nsf/pom.xml b/features/nsf/pom.xml index 224aef1dac..62b790b0da 100644 --- a/features/nsf/pom.xml +++ b/features/nsf/pom.xml @@ -8,12 +8,27 @@ ../../opendaylight/commons/opendaylight features-nsf - pom + ${nsf.version} + jar OpenDaylight :: Features :: Network Service Functions Feature for Network Service Functions features.xml + + + + org.opendaylight.yangtools + features-test + + + + org.opendaylight.controller + opendaylight-karaf-empty + 1.4.2-SNAPSHOT + zip + + @@ -57,6 +72,20 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + org.opendaylight.controller + opendaylight-karaf-empty + ${commons.opendaylight.version} + + + org.opendaylight.yangtools:features-test + + + diff --git a/features/nsf/src/main/resources/features.xml b/features/nsf/src/main/resources/features.xml index 130d72e01a..e48bfc1410 100644 --- a/features/nsf/src/main/resources/features.xml +++ b/features/nsf/src/main/resources/features.xml @@ -1,5 +1,7 @@ + mvn:org.opendaylight.controller/features-base/${commons.opendaylight.version}/xml/features + mvn:org.opendaylight.controller/features-adsal/${sal.version}/xml/features odl-adsal-all odl-nsf-managers @@ -11,7 +13,7 @@ - base-all + odl-base-all odl-adsal-all mvn:org.opendaylight.controller/usermanager/${usermanager.version} mvn:org.opendaylight.controller/usermanager.implementation/${usermanager.version} @@ -50,9 +52,9 @@ - base-all + odl-base-all odl-nsf-managers - mvn:org.ow2.asm/asm-all/${asm.version} + mvn:org.ow2.asm/asm-all/${asm.version} + + org.opendaylight.yangtools + features-test + @@ -72,6 +76,20 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + org.opendaylight.controller + opendaylight-karaf-empty + ${commons.opendaylight.version} + + + org.opendaylight.yangtools:features-test + + + diff --git a/features/protocol-framework/src/main/resources/features.xml b/features/protocol-framework/src/main/resources/features.xml index 6daa3432c1..46510bca46 100644 --- a/features/protocol-framework/src/main/resources/features.xml +++ b/features/protocol-framework/src/main/resources/features.xml @@ -4,7 +4,7 @@ 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"> mvn:org.opendaylight.controller/features-config/${config.version}/xml/features - + odl-config-api odl-config-netty-config-api mvn:org.opendaylight.controller/protocol-framework/${protocol-framework.version} diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 1064afd82d..2f1cd8b925 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -42,14 +42,27 @@ 7.0.32.v201211201336 7.0.32.v201211201952 7.0.32.v201211201952 - 0.0.3-SNAPSHOT 7.0.32.v201211201952 7.0.32.v201211081135 + 7.0.32.v201211201952 + 7.0.32.v201211081135 + 7.0.32.v201211081135 + 7.0.32.v201211201952 + + 7.0.53.v201406061610 + 7.0.53.v201406070630 + 7.0.53.v201406070630 + 7.0.53.v201406070630 + 7.0.53.v201406060720 + 7.0.53.v201406070630 + 7.0.53.v201406060720 + 7.0.53.v201406060720 + 7.0.53.v201406070630 + + 0.0.3-SNAPSHOT 1.2.2 0.1.2-SNAPSHOT 2.4 - 7.0.32.v201211201952 - 7.0.32.v201211081135 3.1 0.0.2-SNAPSHOT 3.0.1 @@ -57,8 +70,6 @@ 0.5.2-SNAPSHOT 1.4.2-SNAPSHOT 1.0.2-SNAPSHOT - 7.0.32.v201211081135 - 7.0.32.v201211201952 2.3.2 0.1.2-SNAPSHOT 0.5.2-SNAPSHOT @@ -82,6 +93,7 @@ 0.4.2-SNAPSHOT 1.1.0-SNAPSHOT 2.5.0 + 3.8.0.I20120518-2145 1.3.1 1.28 @@ -162,6 +174,7 @@ 2.10 4 0.4.2-SNAPSHOT + 0.4.2-SNAPSHOT 1.2.4 dav:http://nexus.opendaylight.org/content/sites/site ${user.name}-private-view @@ -197,6 +210,7 @@ 0.4.2-SNAPSHOT 0.0.2-SNAPSHOT 0.4.2-SNAPSHOT + 0.4.2-SNAPSHOT 0.4.2-SNAPSHOT src/main/xtend-gen 2013.09.07.4-SNAPSHOT @@ -306,12 +320,16 @@ jersey-core ${jersey.version} - com.sun.jersey jersey-server ${jersey.version} + + com.sun.jersey + jersey-servlet + ${jersey-servlet.version} + com.typesafe.akka @@ -1913,6 +1931,14 @@ xml runtime + + org.opendaylight.controller + features-base + ${commons.opendaylight.version} + features + xml + runtime + org.opendaylight.controller features-adsal @@ -1921,6 +1947,14 @@ xml runtime + + org.opendaylight.controller + features-nsf + ${nsf.version} + features + xml + runtime + org.opendaylight.controller features-mdsal diff --git a/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ConfigurationService.java b/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ConfigurationService.java index dd73675070..de7621110e 100644 --- a/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ConfigurationService.java +++ b/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ConfigurationService.java @@ -44,13 +44,13 @@ import org.slf4j.LoggerFactory; * */ -public class ConfigurationService implements IConfigurationService, ICacheUpdateAware { +public class ConfigurationService implements IConfigurationService, ICacheUpdateAware { private static final Logger logger = LoggerFactory .getLogger(ConfigurationService.class); public static final String SAVE_EVENT_CACHE = "config.event.save"; private static final String ROOT = GlobalConstants.STARTUPHOME.toString(); private IClusterGlobalServices clusterServices; - private ConcurrentMap configEvent; + private ConcurrentMap configEvent; private Set configurationAwareList = Collections .synchronizedSet(new HashSet()); private ObjectReader objReader; @@ -105,7 +105,7 @@ public class ConfigurationService implements IConfigurationService, ICacheUpdate @Override public Status saveConfigurations() { if (configEvent != null) { - configEvent.put(ConfigurationEvent.SAVE, ""); + configEvent.put(ConfigurationEvent.SAVE.toString(), ""); } return saveConfigurationsInternal(); } @@ -183,7 +183,7 @@ public class ConfigurationService implements IConfigurationService, ICacheUpdate } @Override - public void entryCreated(ConfigurationEvent key, String cacheName, + public void entryCreated(String key, String cacheName, boolean originLocal) { if (originLocal) { return; @@ -191,18 +191,18 @@ public class ConfigurationService implements IConfigurationService, ICacheUpdate } @Override - public void entryUpdated(ConfigurationEvent key, String new_value, + public void entryUpdated(String key, String new_value, String cacheName, boolean originLocal) { if (originLocal) { return; } - if (key == ConfigurationEvent.SAVE) { + if (key.equals(ConfigurationEvent.SAVE.toString())) { saveConfigurationsInternal(); } } @Override - public void entryDeleted(ConfigurationEvent key, String cacheName, + public void entryDeleted(String key, String cacheName, boolean originLocal) { if (originLocal) { return; @@ -230,7 +230,7 @@ public class ConfigurationService implements IConfigurationService, ICacheUpdate logger.error("uninitialized clusterServices, can't retrieve cache"); return; } - configEvent = (ConcurrentMap) this.clusterServices.getCache(SAVE_EVENT_CACHE); + configEvent = (ConcurrentMap) this.clusterServices.getCache(SAVE_EVENT_CACHE); if (configEvent == null) { logger.error("Failed to retrieve configuration Cache"); } diff --git a/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ContainerConfigurationService.java b/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ContainerConfigurationService.java index a36d4cc6d7..dcab1f63fb 100644 --- a/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ContainerConfigurationService.java +++ b/opendaylight/configuration/implementation/src/main/java/org/opendaylight/controller/configuration/internal/ContainerConfigurationService.java @@ -47,11 +47,12 @@ import org.slf4j.LoggerFactory; */ public class ContainerConfigurationService implements IConfigurationContainerService, - IConfigurationAware, ICacheUpdateAware { + IConfigurationAware, + ICacheUpdateAware { public static final String CONTAINER_SAVE_EVENT_CACHE = "config.container.event.save"; private static final Logger logger = LoggerFactory.getLogger(ContainerConfigurationService.class); private IClusterContainerServices clusterServices; - private ConcurrentMap containerConfigEvent; + private ConcurrentMap containerConfigEvent; // Directory which contains the startup files for this container private String root; private Set configurationAwareList = Collections @@ -142,12 +143,12 @@ public class ContainerConfigurationService implements IConfigurationContainerSer @Override public Status saveConfigurations() { - containerConfigEvent.put(ConfigurationEvent.SAVE, ""); + containerConfigEvent.put(ConfigurationEvent.SAVE.toString(), ""); return saveConfiguration(); } @Override - public void entryCreated(ConfigurationEvent key, String cacheName, + public void entryCreated(String key, String cacheName, boolean originLocal) { if (originLocal) { return; @@ -155,19 +156,19 @@ public class ContainerConfigurationService implements IConfigurationContainerSer } @Override - public void entryUpdated(ConfigurationEvent key, String new_value, + public void entryUpdated(String key, String new_value, String cacheName, boolean originLocal) { if (originLocal) { return; } logger.debug("Processing {} event", key); - if (key == ConfigurationEvent.SAVE) { + if (key.equals(ConfigurationEvent.SAVE.toString())) { saveConfiguration(); } } @Override - public void entryDeleted(ConfigurationEvent key, String cacheName, + public void entryDeleted(String key, String cacheName, boolean originLocal) { if (originLocal) { return; @@ -195,7 +196,8 @@ public class ContainerConfigurationService implements IConfigurationContainerSer logger.error("uninitialized clusterServices, can't retrieve cache"); return; } - containerConfigEvent = (ConcurrentMap) this.clusterServices.getCache(CONTAINER_SAVE_EVENT_CACHE); + containerConfigEvent = + (ConcurrentMap) this.clusterServices.getCache(CONTAINER_SAVE_EVENT_CACHE); if (containerConfigEvent == null) { logger.error("Failed to retrieve configuration Cache"); } diff --git a/opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/configuration/tomcat-server.xml b/opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/configuration/tomcat-server.xml index f6f923e0d2..fbcd0a4c77 100644 --- a/opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/configuration/tomcat-server.xml +++ b/opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/configuration/tomcat-server.xml @@ -48,9 +48,8 @@ - + + + org.opendaylight.controller + features-base + features + xml + + + org.opendaylight.controller + features-adsal + features + xml + + + org.opendaylight.controller + features-nsf + features + xml + org.opendaylight.controller @@ -242,6 +261,11 @@ + + + + + diff --git a/opendaylight/karaf-tomcat-security/pom.xml b/opendaylight/karaf-tomcat-security/pom.xml new file mode 100644 index 0000000000..817e0faeec --- /dev/null +++ b/opendaylight/karaf-tomcat-security/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.2-SNAPSHOT + ../commons/opendaylight + + + karaf-tomcat-security + 0.4.2-SNAPSHOT + bundle + + + orbit + org.apache.catalina + + + + + + org.apache.felix + maven-bundle-plugin + true + + + org.apache.catalina + org.slf4j, + javax.servlet, + org.apache.catalina, + org.apache.catalina.connector, + org.apache.catalina.valves, + org.apache.catalina.realm + + org.opendaylight.controller.karafsecurity + + ${project.basedir}/META-INF + + + + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + HEAD + https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main + + diff --git a/opendaylight/karaf-tomcat-security/src/main/java/org/opendaylight/controller/karafsecurity/ControllerCustomRealm.java b/opendaylight/karaf-tomcat-security/src/main/java/org/opendaylight/controller/karafsecurity/ControllerCustomRealm.java new file mode 100644 index 0000000000..316067c3a6 --- /dev/null +++ b/opendaylight/karaf-tomcat-security/src/main/java/org/opendaylight/controller/karafsecurity/ControllerCustomRealm.java @@ -0,0 +1,42 @@ +/* + * 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.karafsecurity; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; + +import org.apache.catalina.realm.GenericPrincipal; +import org.apache.catalina.realm.RealmBase; + +public class ControllerCustomRealm extends RealmBase { + + private static final String name = "ControllerCustomRealm"; + + @Override + protected String getName() { + return name; + } + + @Override + protected String getPassword(String username) { + return "admin"; + } + + @Override + protected Principal getPrincipal(String username) { + List controllerRoles = new ArrayList(); + controllerRoles.add("System-Admin"); + return new GenericPrincipal(username, "", controllerRoles); + } + + @Override + public Principal authenticate(String username, String credentials) { + return this.getPrincipal(username); + } +} diff --git a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/reconil/FlowNodeReconcilListener.java b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/reconil/FlowNodeReconcilListener.java index eb5ae4a9d3..6308f2a9b0 100644 --- a/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/reconil/FlowNodeReconcilListener.java +++ b/opendaylight/md-sal/forwardingrules-manager/src/main/java/org/opendaylight/controller/frm/reconil/FlowNodeReconcilListener.java @@ -8,11 +8,14 @@ package org.opendaylight.controller.frm.reconil; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.ListenableFuture; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ExecutionException; - import org.opendaylight.controller.frm.AbstractChangeListener; import org.opendaylight.controller.frm.FlowCookieProducer; import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; @@ -38,10 +41,6 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.ListenableFuture; - /** * forwardingrules-manager * org.opendaylight.controller.frm @@ -65,7 +64,7 @@ public class FlowNodeReconcilListener extends AbstractChangeListener { } @Override - public void onDataChanged(AsyncDataChangeEvent, DataObject> changeEvent) { + public void onDataChanged(final AsyncDataChangeEvent, DataObject> changeEvent) { /* FlowCapableNode DataObjects for reconciliation */ final Set, DataObject>> createdEntries = changeEvent.getCreatedData().entrySet(); @@ -118,34 +117,46 @@ public class FlowNodeReconcilListener extends AbstractChangeListener { final InstanceIdentifier nodeIdent = identifier.firstIdentifierOf(Node.class); final NodeRef nodeRef = new NodeRef(nodeIdent); /* Groups - have to be first */ - for (Group group : flowCapNode.get().getGroup()) { - final GroupRef groupRef = new GroupRef(flowNodeIdent.child(Group.class, group.getKey())); - final AddGroupInputBuilder groupBuilder = new AddGroupInputBuilder(group); - groupBuilder.setGroupRef(groupRef); - groupBuilder.setNode(nodeRef); - this.provider.getSalGroupService().addGroup(groupBuilder.build()); + List groups = flowCapNode.get().getGroup(); + if(groups != null) { + for (Group group : groups) { + final GroupRef groupRef = new GroupRef(flowNodeIdent.child(Group.class, group.getKey())); + final AddGroupInputBuilder groupBuilder = new AddGroupInputBuilder(group); + groupBuilder.setGroupRef(groupRef); + groupBuilder.setNode(nodeRef); + this.provider.getSalGroupService().addGroup(groupBuilder.build()); + } } /* Meters */ - for (Meter meter : flowCapNode.get().getMeter()) { - final MeterRef meterRef = new MeterRef(flowNodeIdent.child(Meter.class, meter.getKey())); - final AddMeterInputBuilder meterBuilder = new AddMeterInputBuilder(meter); - meterBuilder.setMeterRef(meterRef); - meterBuilder.setNode(nodeRef); - this.provider.getSalMeterService().addMeter(meterBuilder.build()); + List meters = flowCapNode.get().getMeter(); + if(meters != null) { + for (Meter meter : meters) { + final MeterRef meterRef = new MeterRef(flowNodeIdent.child(Meter.class, meter.getKey())); + final AddMeterInputBuilder meterBuilder = new AddMeterInputBuilder(meter); + meterBuilder.setMeterRef(meterRef); + meterBuilder.setNode(nodeRef); + this.provider.getSalMeterService().addMeter(meterBuilder.build()); + } } /* Flows */ - for (Table flowTable : flowCapNode.get().getTable()) { - final InstanceIdentifier tableIdent = flowNodeIdent.child(Table.class, flowTable.getKey()); - for (Flow flow : flowTable.getFlow()) { - final FlowCookie flowCookie = new FlowCookie(FlowCookieProducer.INSTANCE.getNewCookie(tableIdent)); - final FlowRef flowRef = new FlowRef(tableIdent.child(Flow.class, flow.getKey())); - final FlowTableRef flowTableRef = new FlowTableRef(tableIdent); - final AddFlowInputBuilder flowBuilder = new AddFlowInputBuilder(flow); - flowBuilder.setCookie(flowCookie); - flowBuilder.setNode(nodeRef); - flowBuilder.setFlowTable(flowTableRef); - flowBuilder.setFlowRef(flowRef); - this.provider.getSalFlowService().addFlow(flowBuilder.build()); + List
tables = flowCapNode.get().getTable(); + if(tables != null) { + for (Table flowTable : tables) { + final InstanceIdentifier
tableIdent = flowNodeIdent.child(Table.class, flowTable.getKey()); + List flows = flowTable.getFlow(); + if(flows != null) { + for (Flow flow : flows) { + final FlowCookie flowCookie = new FlowCookie(FlowCookieProducer.INSTANCE.getNewCookie(tableIdent)); + final FlowRef flowRef = new FlowRef(tableIdent.child(Flow.class, flow.getKey())); + final FlowTableRef flowTableRef = new FlowTableRef(tableIdent); + final AddFlowInputBuilder flowBuilder = new AddFlowInputBuilder(flow); + flowBuilder.setCookie(flowCookie); + flowBuilder.setNode(nodeRef); + flowBuilder.setFlowTable(flowTableRef); + flowBuilder.setFlowRef(flowRef); + this.provider.getSalFlowService().addFlow(flowBuilder.build()); + } + } } } } @@ -153,7 +164,7 @@ public class FlowNodeReconcilListener extends AbstractChangeListener { @Override protected void update(final InstanceIdentifier identifier, - final DataObject original, DataObject update) { + final DataObject original, final DataObject update) { // NOOP - Listener is registered for DataChangeScope.BASE only } diff --git a/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/FlowCapableInventoryProvider.java b/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/FlowCapableInventoryProvider.java index 29ac12393a..1e77a5554f 100644 --- a/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/FlowCapableInventoryProvider.java +++ b/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/FlowCapableInventoryProvider.java @@ -13,15 +13,19 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; +import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction; +import org.opendaylight.controller.md.sal.common.api.data.TransactionChain; +import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.sal.binding.api.NotificationProviderService; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -class FlowCapableInventoryProvider implements AutoCloseable, Runnable { +class FlowCapableInventoryProvider implements AutoCloseable, Runnable, TransactionChainListener { private static final Logger LOG = LoggerFactory.getLogger(FlowCapableInventoryProvider.class); private static final int QUEUE_DEPTH = 500; private static final int MAX_BATCH = 100; @@ -30,6 +34,7 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { private final NotificationProviderService notificationService; private final DataBroker dataBroker; + private BindingTransactionChain txChain; private ListenerRegistration listenerRegistration; private Thread thread; @@ -42,6 +47,7 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { final NodeChangeCommiter changeCommiter = new NodeChangeCommiter(FlowCapableInventoryProvider.this); this.listenerRegistration = this.notificationService.registerNotificationListener(changeCommiter); + this.txChain = dataBroker.createTransactionChain(this); thread = new Thread(this); thread.setDaemon(true); thread.setName("FlowCapableInventoryProvider"); @@ -75,6 +81,10 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { thread.join(); thread = null; } + if(txChain != null) { + txChain.close(); + txChain = null; + } } @@ -85,7 +95,7 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { for (; ; ) { InventoryOperation op = queue.take(); - final ReadWriteTransaction tx = dataBroker.newReadWriteTransaction(); + final ReadWriteTransaction tx = txChain.newReadWriteTransaction(); LOG.debug("New operations available, starting transaction {}", tx.getIdentifier()); int ops = 0; @@ -105,12 +115,12 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { final CheckedFuture result = tx.submit(); Futures.addCallback(result, new FutureCallback() { @Override - public void onSuccess(Void aVoid) { + public void onSuccess(final Void aVoid) { //NOOP } @Override - public void onFailure(Throwable throwable) { + public void onFailure(final Throwable throwable) { LOG.error("Transaction {} failed.", tx.getIdentifier(), throwable); } }); @@ -124,4 +134,16 @@ class FlowCapableInventoryProvider implements AutoCloseable, Runnable { queue.poll(); } } + + @Override + public void onTransactionChainFailed(final TransactionChain chain, final AsyncTransaction transaction, + final Throwable cause) { + LOG.error("Failed to export Flow Capable Inventory, Transaction {} failed.",transaction.getIdentifier(),cause); + + } + + @Override + public void onTransactionChainSuccessful(final TransactionChain chain) { + // NOOP + } } diff --git a/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/NodeChangeCommiter.java b/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/NodeChangeCommiter.java index 57ec893076..b14bfd429c 100644 --- a/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/NodeChangeCommiter.java +++ b/opendaylight/md-sal/inventory-manager/src/main/java/org/opendaylight/controller/md/inventory/manager/NodeChangeCommiter.java @@ -32,8 +32,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.No import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder; @@ -81,7 +79,7 @@ class NodeChangeCommiter implements OpendaylightInventoryListener { InstanceIdentifier value = (InstanceIdentifier) ref.getValue(); LOG.debug("updating node connector : {}.", value); NodeConnector build = data.build(); - tx.put(LogicalDatastoreType.OPERATIONAL, value, build); + tx.merge(LogicalDatastoreType.OPERATIONAL, value, build, true); } }); } @@ -139,13 +137,9 @@ class NodeChangeCommiter implements OpendaylightInventoryListener { manager.enqueue(new InventoryOperation() { @Override public void applyOperation(final ReadWriteTransaction tx) { - final NodeBuilder nodeBuilder = new NodeBuilder(node); - nodeBuilder.setKey(new NodeKey(node.getId())); - final FlowCapableNode augment = InventoryMapping.toInventoryAugment(flowNode); - nodeBuilder.addAugmentation(FlowCapableNode.class, augment); LOG.debug("updating node :{} ", path); - tx.put(LogicalDatastoreType.OPERATIONAL, path, augment); + tx.merge(LogicalDatastoreType.OPERATIONAL, path, augment, true); } }); } diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingTransactionChain.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingTransactionChain.java index 24ec89ebb2..c71aa049c0 100644 --- a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingTransactionChain.java +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingTransactionChain.java @@ -1,18 +1,41 @@ +/* + * 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.TransactionChain; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +/** + * A chain of transactions. + *

+ * For more information about transaction chaining and transaction chains + * see {@link TransactionChain}. + * + * @see TransactionChain + * + */ public interface BindingTransactionChain extends TransactionFactory, TransactionChain, DataObject> { - + /** + * {@inheritDoc} + */ @Override ReadOnlyTransaction newReadOnlyTransaction(); + /** + * {@inheritDoc} + */ @Override ReadWriteTransaction newReadWriteTransaction(); + /** + * {@inheritDoc} + */ @Override WriteTransaction newWriteOnlyTransaction(); - } diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataBroker.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataBroker.java index 619a08eac5..cc8deb81b3 100644 --- a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataBroker.java +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataBroker.java @@ -20,22 +20,39 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; * subscribe for changes to data under a given branch of the tree. *

* For more information on usage, please see the documentation in {@link AsyncDataBroker}. + * + * @see AsyncDataBroker + * @see TransactionChainFactory */ public interface DataBroker extends TransactionFactory, AsyncDataBroker, DataObject, DataChangeListener>, BindingService, TransactionChainFactory, DataObject> { - + /** + * {@inheritDoc} + */ @Override ReadOnlyTransaction newReadOnlyTransaction(); + /** + * {@inheritDoc} + */ @Override ReadWriteTransaction newReadWriteTransaction(); + /** + * {@inheritDoc} + */ @Override WriteTransaction newWriteOnlyTransaction(); + /** + * {@inheritDoc} + */ @Override ListenerRegistration registerDataChangeListener(LogicalDatastoreType store, InstanceIdentifier path, DataChangeListener listener, DataChangeScope triggeringScope); + /** + * {@inheritDoc} + */ @Override BindingTransactionChain createTransactionChain(TransactionChainListener listener); } diff --git a/opendaylight/md-sal/sal-binding-broker/pom.xml b/opendaylight/md-sal/sal-binding-broker/pom.xml index 74cceb1cbd..539f9d45c8 100644 --- a/opendaylight/md-sal/sal-binding-broker/pom.xml +++ b/opendaylight/md-sal/sal-binding-broker/pom.xml @@ -67,6 +67,11 @@ org.opendaylight.yangtools binding-generator-impl + + org.opendaylight.yangtools + binding-data-codec + 0.6.2-SNAPSHOT + org.opendaylight.yangtools yang-data-impl diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java index 018e26878c..93d99c832f 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java @@ -2,14 +2,13 @@ package org.opendaylight.controller.config.yang.md.sal.binding.impl; import java.util.Collection; import java.util.Collections; - +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBindingDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; import org.opendaylight.controller.sal.core.api.Provider; import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; public class BindingAsyncDataBrokerImplModule extends org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingAsyncDataBrokerImplModule implements @@ -36,7 +35,7 @@ public class BindingAsyncDataBrokerImplModule extends @Override public java.lang.AutoCloseable createInstance() { Broker domBroker = getDomAsyncBrokerDependency(); - BindingIndependentMappingService mappingService = getBindingMappingServiceDependency(); + BindingToNormalizedNodeCodec mappingService = getBindingMappingServiceDependency(); // FIXME: Switch this to DOM Broker registration which would not require // BundleContext when API are updated. diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModule.java deleted file mode 100644 index 4a4e800078..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModule.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.controller.config.yang.md.sal.binding.impl; - -import java.util.concurrent.ExecutorService; - -import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder; -import org.opendaylight.controller.sal.binding.impl.RootDataBrokerImpl; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingDomConnectorDeployer; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector; -import org.opendaylight.controller.sal.binding.impl.forward.DomForwardedDataBrokerImpl; -import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; - -/** -* -*/ -public final class DataBrokerImplModule extends - org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractDataBrokerImplModule { - - public DataBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, - final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { - super(identifier, dependencyResolver); - } - - public DataBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, - final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, - final DataBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) { - super(identifier, dependencyResolver, oldModule, oldInstance); - } - - @Override - public void validate() { - super.validate(); - } - - @Override - public java.lang.AutoCloseable createInstance() { - RootDataBrokerImpl dataBindingBroker; - - - ExecutorService listeningExecutor = SingletonHolder.getDefaultCommitExecutor(); - BindingIndependentMappingService potentialMapping = getMappingServiceDependency(); - if (getDomBrokerDependency() != null && potentialMapping != null) { - - dataBindingBroker = createDomConnectedBroker(listeningExecutor,potentialMapping); - } else { - dataBindingBroker = createStandAloneBroker(listeningExecutor); - } - dataBindingBroker.registerRuntimeBean(getRootRuntimeBeanRegistratorWrapper()); - dataBindingBroker.setNotificationExecutor(SingletonHolder.getDefaultChangeEventExecutor()); - return dataBindingBroker; - } - - - private RootDataBrokerImpl createStandAloneBroker(final ExecutorService listeningExecutor) { - RootDataBrokerImpl broker = new RootDataBrokerImpl(); - broker.setExecutor(listeningExecutor); - return broker; - } - - private RootDataBrokerImpl createDomConnectedBroker(final ExecutorService listeningExecutor, final BindingIndependentMappingService mappingService) { - DomForwardedDataBrokerImpl forwardedBroker = new DomForwardedDataBrokerImpl(); - forwardedBroker.setExecutor(listeningExecutor); - BindingIndependentConnector connector = BindingDomConnectorDeployer.createConnector(mappingService); - getDomBrokerDependency().registerProvider(forwardedBroker, null); - ProviderSession domContext = forwardedBroker.getDomProviderContext(); - forwardedBroker.setConnector(connector); - forwardedBroker.setDomProviderContext(domContext); - forwardedBroker.startForwarding(); - return forwardedBroker; - } - -} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModuleFactory.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModuleFactory.java deleted file mode 100644 index d3fc5ac215..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/DataBrokerImplModuleFactory.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.controller.config.yang.md.sal.binding.impl; - - -/** -* -*/ -public class DataBrokerImplModuleFactory extends - org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractDataBrokerImplModuleFactory { - -} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java index 0ea30f7e66..2bc673adff 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java @@ -7,9 +7,10 @@ */ package org.opendaylight.controller.config.yang.md.sal.binding.impl; +import com.google.common.util.concurrent.ListeningExecutorService; import java.util.Collection; import java.util.Collections; - +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBackwardsCompatibleDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder; @@ -18,9 +19,6 @@ import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; import org.opendaylight.controller.sal.core.api.Provider; import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; - -import com.google.common.util.concurrent.ListeningExecutorService; /** * @@ -51,7 +49,7 @@ public final class ForwardedCompatibleDataBrokerImplModule extends @Override public java.lang.AutoCloseable createInstance() { ListeningExecutorService listeningExecutor = SingletonHolder.getDefaultCommitExecutor(); - BindingIndependentMappingService mappingService = getBindingMappingServiceDependency(); + BindingToNormalizedNodeCodec mappingService = getBindingMappingServiceDependency(); Broker domBroker = getDomAsyncBrokerDependency(); ProviderSession session = domBroker.registerProvider(this, null); @@ -60,7 +58,7 @@ public final class ForwardedCompatibleDataBrokerImplModule extends ForwardedBackwardsCompatibleDataBroker dataBroker = new ForwardedBackwardsCompatibleDataBroker(domDataBroker, mappingService, schemaService,listeningExecutor); - dataBroker.setConnector(BindingDomConnectorDeployer.createConnector(getBindingMappingServiceDependency())); + dataBroker.setConnector(BindingDomConnectorDeployer.createConnector(mappingService.getLegacy())); dataBroker.setDomProviderContext(session); return dataBroker; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java index b0c2d742e2..a15b1d746c 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/RuntimeMappingModule.java @@ -7,12 +7,18 @@ */ package org.opendaylight.controller.config.yang.md.sal.binding.impl; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; import java.util.Hashtable; import java.util.Map.Entry; import java.util.Set; - +import javassist.ClassPool; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.sal.binding.codegen.impl.SingletonHolder; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; import org.opendaylight.yangtools.concepts.Delegator; +import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy; import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl; import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -29,9 +35,6 @@ import org.osgi.framework.ServiceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; - /** * */ @@ -42,14 +45,14 @@ public final class RuntimeMappingModule extends private BundleContext bundleContext; - public RuntimeMappingModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, - org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + public RuntimeMappingModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, + final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); } - public RuntimeMappingModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, - org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, - RuntimeMappingModule oldModule, java.lang.AutoCloseable oldInstance) { + public RuntimeMappingModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, + final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, + final RuntimeMappingModule oldModule, final java.lang.AutoCloseable oldInstance) { super(identifier, dependencyResolver, oldModule, oldInstance); } @@ -61,41 +64,48 @@ public final class RuntimeMappingModule extends } @Override - public boolean canReuseInstance(AbstractRuntimeMappingModule oldModule) { + public boolean canReuseInstance(final AbstractRuntimeMappingModule oldModule) { return true; } @Override public java.lang.AutoCloseable createInstance() { + final GeneratedClassLoadingStrategy classLoading = getGlobalClassLoadingStrategy(); + final BindingIndependentMappingService legacyMapping = getGlobalLegacyMappingService(classLoading); + BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(new StreamWriterGenerator(SingletonHolder.JAVASSIST)); + BindingToNormalizedNodeCodec instance = new BindingToNormalizedNodeCodec(classLoading, legacyMapping, codecRegistry); + bundleContext.registerService(SchemaContextListener.class, instance, new Hashtable()); + return instance; + } - RuntimeGeneratedMappingServiceProxy potential = tryToReuseGlobalInstance(); - if(potential != null) { - return potential; + private BindingIndependentMappingService getGlobalLegacyMappingService(final GeneratedClassLoadingStrategy classLoading) { + BindingIndependentMappingService potential = tryToReuseGlobalMappingServiceInstance(); + if(potential == null) { + potential = new RuntimeGeneratedMappingServiceImpl(ClassPool.getDefault(),classLoading); + bundleContext.registerService(SchemaContextListener.class, (SchemaContextListener) potential, new Hashtable()); } + return potential; + } - final RuntimeGeneratedMappingServiceImpl service = new RuntimeGeneratedMappingServiceImpl(SingletonHolder.CLASS_POOL); - bundleContext.registerService(SchemaContextListener.class, service, new Hashtable()); - return service; + private GeneratedClassLoadingStrategy getGlobalClassLoadingStrategy() { + ServiceReference ref = bundleContext.getServiceReference(GeneratedClassLoadingStrategy.class); + return bundleContext.getService(ref); } - private RuntimeGeneratedMappingServiceProxy tryToReuseGlobalInstance() { + private BindingIndependentMappingService tryToReuseGlobalMappingServiceInstance() { ServiceReference serviceRef = getBundleContext().getServiceReference(BindingIndependentMappingService.class); if(serviceRef == null) { return null; } + return bundleContext.getService(serviceRef); - BindingIndependentMappingService delegate = bundleContext.getService(serviceRef); - if (delegate == null) { - return null; - } - return new RuntimeGeneratedMappingServiceProxy(getBundleContext(),serviceRef,delegate); } private BundleContext getBundleContext() { return bundleContext; } - public void setBundleContext(BundleContext bundleContext) { + public void setBundleContext(final BundleContext bundleContext) { this.bundleContext = bundleContext; } @@ -108,9 +118,9 @@ public final class RuntimeMappingModule extends private ServiceReference reference; private BundleContext bundleContext; - public RuntimeGeneratedMappingServiceProxy(BundleContext bundleContext, - ServiceReference serviceRef, - BindingIndependentMappingService delegate) { + public RuntimeGeneratedMappingServiceProxy(final BundleContext bundleContext, + final ServiceReference serviceRef, + final BindingIndependentMappingService delegate) { this.bundleContext = Preconditions.checkNotNull(bundleContext); this.reference = Preconditions.checkNotNull(serviceRef); this.delegate = Preconditions.checkNotNull(delegate); @@ -122,47 +132,47 @@ public final class RuntimeMappingModule extends } @Override - public CompositeNode toDataDom(DataObject data) { + public CompositeNode toDataDom(final DataObject data) { return delegate.toDataDom(data); } @Override public Entry toDataDom( - Entry, DataObject> entry) { + final Entry, DataObject> entry) { return delegate.toDataDom(entry); } @Override public YangInstanceIdentifier toDataDom( - org.opendaylight.yangtools.yang.binding.InstanceIdentifier path) { + final org.opendaylight.yangtools.yang.binding.InstanceIdentifier path) { return delegate.toDataDom(path); } @Override public DataObject dataObjectFromDataDom( - org.opendaylight.yangtools.yang.binding.InstanceIdentifier path, - CompositeNode result) throws DeserializationException { + final org.opendaylight.yangtools.yang.binding.InstanceIdentifier path, + final CompositeNode result) throws DeserializationException { return delegate.dataObjectFromDataDom(path, result); } @Override - public org.opendaylight.yangtools.yang.binding.InstanceIdentifier fromDataDom(YangInstanceIdentifier entry) + public org.opendaylight.yangtools.yang.binding.InstanceIdentifier fromDataDom(final YangInstanceIdentifier entry) throws DeserializationException { return delegate.fromDataDom(entry); } @Override - public Set getRpcQNamesFor(Class service) { + public Set getRpcQNamesFor(final Class service) { return delegate.getRpcQNamesFor(service); } @Override - public Optional> getRpcServiceClassFor(String namespace, String revision) { + public Optional> getRpcServiceClassFor(final String namespace, final String revision) { return delegate.getRpcServiceClassFor(namespace,revision); } @Override - public DataContainer dataObjectFromDataDom(Class inputClass, CompositeNode domInput) { + public DataContainer dataObjectFromDataDom(final Class inputClass, final CompositeNode domInput) { return delegate.dataObjectFromDataDom(inputClass, domInput); } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java index e632e6336a..f843b23f9b 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java @@ -9,7 +9,6 @@ package org.opendaylight.controller.md.sal.binding.impl; import com.google.common.base.Objects; import com.google.common.base.Optional; - import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -19,7 +18,6 @@ import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; - import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; @@ -37,33 +35,28 @@ import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public abstract class AbstractForwardedDataBroker implements Delegator, DomForwardedBroker, SchemaContextListener, AutoCloseable { +public abstract class AbstractForwardedDataBroker implements Delegator, DomForwardedBroker, + SchemaContextListener, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedDataBroker.class); // The Broker to whom we do all forwarding private final DOMDataBroker domDataBroker; - // Mapper to convert from Binding Independent objects to Binding Aware - // objects - private final BindingIndependentMappingService mappingService; - private final BindingToNormalizedNodeCodec codec; private BindingIndependentConnector connector; private ProviderSession context; private final ListenerRegistration schemaListenerRegistration; - protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker, - final BindingIndependentMappingService mappingService,final SchemaService schemaService) { + protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker, final BindingToNormalizedNodeCodec codec, + final SchemaService schemaService) { this.domDataBroker = domDataBroker; - this.mappingService = mappingService; - this.codec = new BindingToNormalizedNodeCodec(mappingService); + this.codec = codec; this.schemaListenerRegistration = schemaService.registerSchemaContextListener(this); } @@ -71,10 +64,6 @@ public abstract class AbstractForwardedDataBroker implements Delegator registerDataChangeListener(final LogicalDatastoreType store, - final InstanceIdentifier path, final DataChangeListener listener, - final DataChangeScope triggeringScope) { + final InstanceIdentifier path, final DataChangeListener listener, final DataChangeScope triggeringScope) { DOMDataChangeListener domDataChangeListener = new TranslatingDataChangeInvoker(store, path, listener, triggeringScope); YangInstanceIdentifier domPath = codec.toNormalized(path); @@ -96,23 +84,16 @@ public abstract class AbstractForwardedDataBroker implements Delegator, DataObject> toBinding( - InstanceIdentifier path, + protected Map, DataObject> toBinding(final InstanceIdentifier path, final Map> normalized) { Map, DataObject> newMap = new HashMap<>(); for (Map.Entry> entry : sortedEntries(normalized)) { try { - Optional, DataObject>> potential = getCodec().toBinding( - entry); + Optional, DataObject>> potential = getCodec().toBinding(entry); if (potential.isPresent()) { Entry, DataObject> binding = potential.get(); newMap.put(binding.getKey(), binding.getValue()); - } else if (entry.getKey().getLastPathArgument() instanceof YangInstanceIdentifier.AugmentationIdentifier) { - DataObject bindingDataObject = getCodec().toBinding(path, entry.getValue()); - if (bindingDataObject != null) { - newMap.put(path, bindingDataObject); - } } } catch (DeserializationException e) { LOG.warn("Failed to transform {}, omitting it", entry, e); @@ -123,8 +104,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator> MAP_ENTRY_COMPARATOR = new Comparator>() { @Override - public int compare(final Entry left, - final Entry right) { + public int compare(final Entry left, final Entry right) { final Iterator li = left.getKey().getPathArguments().iterator(); final Iterator ri = right.getKey().getPathArguments().iterator(); @@ -144,7 +124,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator Iterable> sortedEntries(final Map map) { + private static Iterable> sortedEntries(final Map map) { if (!map.isEmpty()) { ArrayList> entries = new ArrayList<>(map.entrySet()); Collections.sort(entries, MAP_ENTRY_COMPARATOR); @@ -154,7 +134,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator> toBinding(InstanceIdentifier path, + protected Set> toBinding(final InstanceIdentifier path, final Set normalized) { Set> hashSet = new HashSet<>(); for (YangInstanceIdentifier normalizedPath : normalized) { @@ -177,12 +157,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator> of(data)); } private class TranslatingDataChangeInvoker implements DOMDataChangeListener { @@ -200,8 +175,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator> change) { + public void onDataChanged(final AsyncDataChangeEvent> change) { bindingDataChangeListener.onDataChanged(new TranslatedDataChangeEvent(change, path)); } } @@ -261,7 +235,7 @@ public abstract class AbstractForwardedDataBroker implements Delegator binding) { - - // Used instance-identifier codec do not support serialization of last - // path - // argument if it is Augmentation (behaviour expected by old datastore) - // in this case, we explicitly check if last argument is augmentation - // to process it separately - if (isAugmentationIdentifier(binding)) { - return toNormalizedAugmented(binding); - } - return toNormalizedImpl(binding); + return codecRegistry.toYangInstanceIdentifier(binding); } + @SuppressWarnings({ "unchecked", "rawtypes" }) public Entry> toNormalizedNode( final InstanceIdentifier bindingPath, final DataObject bindingObject) { - return toNormalizedNode(toBindingEntry(bindingPath, bindingObject)); + return codecRegistry.toNormalizedNode((InstanceIdentifier) bindingPath, bindingObject); } public Entry> toNormalizedNode( final Entry, DataObject> binding) { - Entry legacyEntry = bindingToLegacy - .toDataDom(binding); - Entry> normalizedEntry = legacyToNormalized - .toNormalized(legacyEntry); - LOG.trace("Serialization of {}, Legacy Representation: {}, Normalized Representation: {}", binding, - legacyEntry, normalizedEntry); - if (isAugmentation(binding.getKey().getTargetType())) { - - for (DataContainerChild child : ((DataContainerNode) normalizedEntry - .getValue()).getValue()) { - if (child instanceof AugmentationNode) { - ImmutableList childArgs = ImmutableList. builder() - .addAll(normalizedEntry.getKey().getPathArguments()).add(child.getIdentifier()).build(); - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier childPath = org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier - .create(childArgs); - return toDOMEntry(childPath, child); - } - } - - } - return normalizedEntry; - + return toNormalizedNode(binding.getKey(),binding.getValue()); } /** @@ -125,156 +72,26 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { public Optional> toBinding( final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized) throws DeserializationException { - - PathArgument lastArgument = Iterables.getLast(normalized.getPathArguments()); - // Used instance-identifier codec do not support serialization of last - // path - // argument if it is AugmentationIdentifier (behaviour expected by old - // datastore) - // in this case, we explicitly check if last argument is augmentation - // to process it separately - if (lastArgument instanceof AugmentationIdentifier) { - return toBindingAugmented(normalized); - } - return toBindingImpl(normalized); - } - - private Optional> toBindingAugmented( - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized) - throws DeserializationException { - Optional> potential = toBindingImpl(normalized); - // Shorthand check, if codec already supports deserialization - // of AugmentationIdentifier we will return - if (potential.isPresent() && isAugmentationIdentifier(potential.get())) { - return potential; - } - - int normalizedCount = getAugmentationCount(normalized); - AugmentationIdentifier lastArgument = (AugmentationIdentifier) Iterables.getLast(normalized.getPathArguments()); - - // Here we employ small trick - Binding-aware Codec injects an pointer - // to augmentation class - // if child is referenced - so we will reference child and then shorten - // path. - LOG.trace("Looking for candidates to match {}", normalized); - for (QName child : lastArgument.getPossibleChildNames()) { - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier childPath = normalized.node(child); - try { - if (isNotRepresentable(childPath)) { - LOG.trace("Path {} is not BI-representable, skipping it", childPath); - continue; - } - } catch (DataNormalizationException e) { - LOG.warn("Failed to denormalize path {}, skipping it", childPath, e); - continue; - } - - Optional> baId = toBindingImpl(childPath); - if (!baId.isPresent()) { - LOG.debug("No binding-aware identifier found for path {}, skipping it", childPath); - continue; - } - - InstanceIdentifier potentialPath = shortenToLastAugment(baId.get()); - int potentialAugmentCount = getAugmentationCount(potentialPath); - if (potentialAugmentCount == normalizedCount) { - LOG.trace("Found matching path {}", potentialPath); - return Optional.> of(potentialPath); - } - - LOG.trace("Skipping mis-matched potential path {}", potentialPath); - } - - LOG.trace("Failed to find augmentation matching {}", normalized); - return Optional.absent(); - } - - private Optional> toBindingImpl( - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized) - throws DeserializationException { - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier legacyPath; - try { - if (isNotRepresentable(normalized)) { - return Optional.absent(); - } - legacyPath = legacyToNormalized.toLegacy(normalized); - } catch (DataNormalizationException e) { - throw new IllegalStateException("Could not denormalize path.", e); - } - LOG.trace("InstanceIdentifier Path Deserialization: Legacy representation {}, Normalized representation: {}", - legacyPath, normalized); - return Optional.> of(bindingToLegacy.fromDataDom(legacyPath)); - } - - private boolean isNotRepresentable(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized) - throws DataNormalizationException { - DataNormalizationOperation op = findNormalizationOperation(normalized); - if (op.isMixin() && op.getIdentifier() instanceof NodeIdentifier) { - return true; - } - if (op.isLeaf()) { - return true; - } - return false; - } - - private DataNormalizationOperation findNormalizationOperation( - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized) - throws DataNormalizationException { - DataNormalizationOperation current = legacyToNormalized.getRootOperation(); - for (PathArgument arg : normalized.getPathArguments()) { - current = current.getChild(arg); - } - return current; - } - - private static final Entry, DataObject> toBindingEntry( - final org.opendaylight.yangtools.yang.binding.InstanceIdentifier key, - final DataObject value) { - return new SimpleEntry, DataObject>( - key, value); - } - - private static final Entry> toDOMEntry( - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier key, final NormalizedNode value) { - return new SimpleEntry>(key, - value); - } - - public DataObject toBinding(final InstanceIdentifier path, final NormalizedNode normalizedNode) - throws DeserializationException { - CompositeNode legacy = null; - if (isAugmentationIdentifier(path) && normalizedNode instanceof AugmentationNode) { - QName augIdentifier = BindingReflections.findQName(path.getTargetType()); - ContainerNode virtualNode = Builders.containerBuilder() // - .withNodeIdentifier(new NodeIdentifier(augIdentifier)) // - .withChild((DataContainerChild) normalizedNode) // - .build(); - legacy = (CompositeNode) DataNormalizer.toLegacy(virtualNode); - } else { - legacy = (CompositeNode) DataNormalizer.toLegacy(normalizedNode); + return Optional.>of(codecRegistry.fromYangInstanceIdentifier(normalized)); + } catch (IllegalArgumentException e) { + return Optional.absent(); } - - return bindingToLegacy.dataObjectFromDataDom(path, legacy); } public DataNormalizer getDataNormalizer() { return legacyToNormalized; } + @SuppressWarnings("unchecked") public Optional, DataObject>> toBinding( final Entry> normalized) throws DeserializationException { - Optional> potentialPath = toBinding(normalized.getKey()); - if (potentialPath.isPresent()) { - InstanceIdentifier bindingPath = potentialPath.get(); - DataObject bindingData = toBinding(bindingPath, normalized.getValue()); - if (bindingData == null) { - LOG.warn("Failed to deserialize {} to Binding format. Binding path is: {}", normalized, bindingPath); - } - return Optional.of(toBindingEntry(bindingPath, bindingData)); - } else { + try { + @SuppressWarnings("rawtypes") + Entry binding = codecRegistry.fromNormalizedNode(normalized.getKey(), normalized.getValue()); + return Optional., DataObject>>fromNullable(binding); + } catch (IllegalArgumentException e) { return Optional.absent(); } } @@ -282,269 +99,11 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { @Override public void onGlobalContextUpdated(final SchemaContext arg0) { legacyToNormalized = new DataNormalizer(arg0); + codecRegistry.onBindingRuntimeContextUpdated(BindingRuntimeContext.create(classLoadingStrategy, arg0)); } - private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier toNormalizedAugmented( - final InstanceIdentifier augPath) { - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier processed = toNormalizedImpl(augPath); - // If used instance identifier codec added supports for deserialization - // of last AugmentationIdentifier we will just reuse it - if (isAugmentationIdentifier(processed)) { - return processed; - } - Optional additionalSerialized; - additionalSerialized = toNormalizedAugmentedUsingChildContainers(augPath, processed); - - if (additionalSerialized.isPresent()) { - return additionalSerialized.get(); - } - additionalSerialized = toNormalizedAugmentedUsingChildLeafs(augPath, processed); - if (additionalSerialized.isPresent()) { - return additionalSerialized.get(); - } - throw new IllegalStateException("Unabled to construct augmentation identfier for " + augPath); - } - - /** - * Tries to find correct augmentation identifier using children leafs - * - * This method uses normalized Instance Identifier of parent node to fetch - * schema and {@link BindingReflections#getModuleInfo(Class)} to learn about - * augmentation namespace, specificly, in which module it was defined. - * - * Then it uses it to filter all available augmentations for parent by - * module. After that it walks augmentations in particular module and - * pick-up first which at least one leaf name matches supplied augmentation. - * We could do this safely since YANG explicitly states that no any existing - * augmentations must differ in leaf fully qualified names. - * - * - * @param augPath - * Binding Aware Path which ends with augment - * @param parentPath - * Processed path - * @return - */ - private Optional toNormalizedAugmentedUsingChildLeafs( - final InstanceIdentifier augPath, - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) { - try { - DataNormalizationOperation parentOp = legacyToNormalized.getOperation(parentPath); - if(!parentOp.getDataSchemaNode().isPresent()) { - return Optional.absent(); - } - DataSchemaNode parentSchema = parentOp.getDataSchemaNode().get(); - if (parentSchema instanceof AugmentationTarget) { - Set augmentations = ((AugmentationTarget) parentSchema).getAvailableAugmentations(); - LOG.info("Augmentations for {}, {}", augPath, augmentations); - Optional schema = findAugmentation(augPath.getTargetType(), augmentations); - if (schema.isPresent()) { - AugmentationIdentifier augmentationIdentifier = DataNormalizationOperation - .augmentationIdentifierFrom(schema.get()); - return Optional.of(parentPath.node(augmentationIdentifier)); - } - } - } catch (DataNormalizationException e) { - throw new IllegalArgumentException(e); - } - return Optional.absent(); - } - - /** - * Creates instance identifier for augmentation child, tries to serialize it - * Instance Identifier is then shortened to last augmentation. - * - * This is for situations, where underlying codec is implementing hydrogen - * style DOM APIs (which did not supported {@link AugmentationIdentifier}.) - * - * @param augPath - * @param parentPath - * Path to parent node - * @return - */ - @SuppressWarnings("rawtypes") - private Optional toNormalizedAugmentedUsingChildContainers( - final InstanceIdentifier augPath, - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) { - for (Class augChild : BindingReflections.getChildrenClasses(augPath.getTargetType())) { - @SuppressWarnings("unchecked") - InstanceIdentifier childPath = augPath.child(augChild); - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized = toNormalizedImpl(childPath); - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier potentialDiscovered = shortenToLastAugmentation( - normalized, parentPath); - if (potentialDiscovered != null) { - return Optional.of(potentialDiscovered); - } - } - return Optional.absent(); - } - - private Optional findAugmentation(final Class targetType, - final Set augmentations) { - YangModuleInfo moduleInfo; - try { - moduleInfo = BindingReflections.getModuleInfo(targetType); - } catch (Exception e) { - throw new IllegalStateException(e); - } - Iterable filtered = filteredByModuleInfo(augmentations, - BindingReflections.getModuleQName(moduleInfo).getModule()); - filtered.toString(); - Set targetTypeGetters = getYangModeledGetters(targetType); - for (AugmentationSchema schema : filtered) { - for (DataSchemaNode child : schema.getChildNodes()) { - String getterName = "get" + BindingMapping.getClassName(child.getQName()); - if (targetTypeGetters.contains(getterName)) { - return Optional.of(schema); - } - } - } - return Optional.absent(); - } - - private static Iterable filteredByModuleInfo(final Iterable augmentations, - final QNameModule module) { - return Iterables.filter(augmentations, new Predicate() { - @Override - public boolean apply(final AugmentationSchema schema) { - final Collection childNodes = schema.getChildNodes(); - return !childNodes.isEmpty() && module.equals(Iterables.get(childNodes, 0).getQName().getModule()); - } - }); - } - - public static final Set getYangModeledGetters(final Class targetType) { - HashSet ret = new HashSet(); - for (Method method : targetType.getMethods()) { - if (isYangModeledGetter(method)) { - ret.add(method.getName()); - } - } - return ret; - } - - /** - * - * Returns true if supplied method represent getter for YANG modeled value - * - * @param method - * Method to be tested - * @return true if method represent getter for YANG Modeled value. - */ - private static final boolean isYangModeledGetter(final Method method) { - return !method.getName().equals("getClass") && !method.getName().equals("getImplementedInterface") - && method.getName().startsWith("get") && method.getParameterTypes().length == 0; - } - - private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier shortenToLastAugmentation( - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized, - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier parentPath) { - int parentSize = Iterables.size(parentPath.getPathArguments()); - int position = 0; - int foundPosition = -1; - for (PathArgument arg : normalized.getPathArguments()) { - position++; - if (arg instanceof AugmentationIdentifier) { - foundPosition = position; - } - } - if (foundPosition > 0 && foundPosition > parentSize) { - Iterable shortened = Iterables.limit(normalized.getPathArguments(), foundPosition); - return org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.create(shortened); - } - return null; - } - - private InstanceIdentifier shortenToLastAugment( - final InstanceIdentifier binding) { - int position = 0; - int foundPosition = -1; - for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : binding.getPathArguments()) { - position++; - if (isAugmentation(arg.getType())) { - foundPosition = position; - } - } - return InstanceIdentifier.create(Iterables.limit(binding.getPathArguments(), foundPosition)); - } - - private org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier toNormalizedImpl( - final InstanceIdentifier binding) { - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier legacyPath = bindingToLegacy - .toDataDom(binding); - final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier normalized = legacyToNormalized - .toNormalized(legacyPath); - return normalized; - } - - private static boolean isAugmentation(final Class type) { - return Augmentation.class.isAssignableFrom(type); - } - - private static boolean isAugmentationIdentifier(final InstanceIdentifier potential) { - return Augmentation.class.isAssignableFrom(potential.getTargetType()); - } - - private boolean isAugmentationIdentifier(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier processed) { - return Iterables.getLast(processed.getPathArguments()) instanceof AugmentationIdentifier; - } - - private static int getAugmentationCount(final InstanceIdentifier potential) { - int count = 0; - for (org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument arg : potential.getPathArguments()) { - if (isAugmentation(arg.getType())) { - count++; - } - - } - return count; - } - - private static int getAugmentationCount(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier potential) { - int count = 0; - for (PathArgument arg : potential.getPathArguments()) { - if (arg instanceof AugmentationIdentifier) { - count++; - } - } - return count; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) public Function>, Optional> deserializeFunction(final InstanceIdentifier path) { - return new DeserializeFunction(this, path); - } - - private static class DeserializeFunction implements Function>, Optional> { - - private final BindingToNormalizedNodeCodec codec; - private final InstanceIdentifier path; - - public DeserializeFunction(final BindingToNormalizedNodeCodec codec, final InstanceIdentifier path) { - super(); - this.codec = Preconditions.checkNotNull(codec, "Codec must not be null"); - this.path = Preconditions.checkNotNull(path, "Path must not be null"); - } - - @SuppressWarnings("rawtypes") - @Nullable - @Override - public Optional apply(@Nullable final Optional> normalizedNode) { - if (normalizedNode.isPresent()) { - final DataObject dataObject; - try { - dataObject = codec.toBinding(path, normalizedNode.get()); - } catch (DeserializationException e) { - LOG.warn("Failed to create dataobject from node {}", normalizedNode.get(), e); - throw new IllegalStateException("Failed to create dataobject", e); - } - - if (dataObject != null) { - return Optional.of(dataObject); - } - } - return Optional.absent(); - } + return codecRegistry.deserializeFunction(path); } /** @@ -566,4 +125,13 @@ public class BindingToNormalizedNodeCodec implements SchemaContextListener { } return currentOp.createDefault(path.getLastPathArgument()); } + + public BindingIndependentMappingService getLegacy() { + return bindingToLegacy; + } + + @Override + public void close() throws Exception { + // NOOP Intentionally + } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java index 237d9678f9..52e114b0ea 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java @@ -7,6 +7,12 @@ */ package org.opendaylight.controller.md.sal.binding.impl; +import com.google.common.base.Function; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -17,7 +23,6 @@ import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; - 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.AsyncDataBroker.DataChangeScope; @@ -44,17 +49,9 @@ 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; -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.util.concurrent.AsyncFunction; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; - @SuppressWarnings("deprecation") public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDataBroker implements DataProviderService, AutoCloseable { @@ -64,7 +61,7 @@ public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDat private final ListeningExecutorService executorService; public ForwardedBackwardsCompatibleDataBroker(final DOMDataBroker domDataBroker, - final BindingIndependentMappingService mappingService, final SchemaService schemaService,final ListeningExecutorService executor) { + final BindingToNormalizedNodeCodec mappingService, final SchemaService schemaService,final ListeningExecutorService executor) { super(domDataBroker, mappingService,schemaService); executorService = executor; LOG.info("ForwardedBackwardsCompatibleBroker started."); diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java index 6359b60684..ef66d80ed4 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java @@ -16,7 +16,6 @@ import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; /** * The DataBrokerImpl simply defers to the DOMDataBroker for all its operations. @@ -30,8 +29,8 @@ import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMapping */ public class ForwardedBindingDataBroker extends AbstractForwardedDataBroker implements DataBroker { - public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingIndependentMappingService mappingService, final SchemaService schemaService) { - super(domDataBroker, mappingService,schemaService); + public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingToNormalizedNodeCodec codec, final SchemaService schemaService) { + super(domDataBroker, codec,schemaService); } @Override diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java index 1ec4aa2d30..f037e679be 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/SingletonHolder.java @@ -7,6 +7,10 @@ */ package org.opendaylight.controller.sal.binding.codegen.impl; +import com.google.common.util.concurrent.ForwardingBlockingQueue; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -16,24 +20,19 @@ import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; - import javassist.ClassPool; - import org.apache.commons.lang3.StringUtils; import org.opendaylight.controller.sal.binding.codegen.RuntimeCodeGenerator; import org.opendaylight.controller.sal.binding.spi.NotificationInvokerFactory; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.util.concurrent.ForwardingBlockingQueue; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import com.google.common.util.concurrent.ThreadFactoryBuilder; - public class SingletonHolder { private static final Logger logger = LoggerFactory.getLogger(SingletonHolder.class); public static final ClassPool CLASS_POOL = ClassPool.getDefault(); + public static final JavassistUtils JAVASSIST = JavassistUtils.forClassPool(CLASS_POOL); public static final org.opendaylight.controller.sal.binding.codegen.impl.RuntimeCodeGenerator RPC_GENERATOR_IMPL = new org.opendaylight.controller.sal.binding.codegen.impl.RuntimeCodeGenerator( CLASS_POOL); public static final RuntimeCodeGenerator RPC_GENERATOR = RPC_GENERATOR_IMPL; diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootDataBrokerImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootDataBrokerImpl.java deleted file mode 100644 index a1cae266c1..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootDataBrokerImpl.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.controller.sal.binding.impl; - -import org.opendaylight.controller.config.yang.md.sal.binding.impl.Data; -import org.opendaylight.controller.config.yang.md.sal.binding.impl.DataBrokerImplRuntimeMXBean; -import org.opendaylight.controller.config.yang.md.sal.binding.impl.DataBrokerImplRuntimeRegistration; -import org.opendaylight.controller.config.yang.md.sal.binding.impl.DataBrokerImplRuntimeRegistrator; -import org.opendaylight.controller.config.yang.md.sal.binding.impl.Transactions; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector; - -public class RootDataBrokerImpl extends DataBrokerImpl implements DataBrokerImplRuntimeMXBean { - - private final Transactions transactions = new Transactions(); - private final Data data = new Data(); - private BindingIndependentConnector bindingIndependentConnector; - private DataBrokerImplRuntimeRegistration runtimeBeanRegistration; - - public BindingIndependentConnector getBindingIndependentConnector() { - return bindingIndependentConnector; - } - - public Transactions getTransactions() { - transactions.setCreated(getCreatedTransactionsCount().get()); - transactions.setSubmitted(getSubmittedTransactionsCount().get()); - transactions.setSuccessful(getFinishedTransactionsCount().get()); - transactions.setFailed(getFailedTransactionsCount().get()); - return transactions; - } - - @Override - public Data getData() { - data.setTransactions(getTransactions()); - return data; - } - - public void setBindingIndependentConnector(BindingIndependentConnector runtimeMapping) { - this.bindingIndependentConnector = runtimeMapping; - } - - public void registerRuntimeBean(DataBrokerImplRuntimeRegistrator rootRegistrator) { - runtimeBeanRegistration = rootRegistrator.register(this); - } - -} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/forward/DomForwardedDataBrokerImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/forward/DomForwardedDataBrokerImpl.java deleted file mode 100644 index 3d0e4deb65..0000000000 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/forward/DomForwardedDataBrokerImpl.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.controller.sal.binding.impl.forward; - -import java.util.Collection; -import java.util.Collections; - -import org.opendaylight.controller.sal.binding.impl.RootDataBrokerImpl; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingDomConnectorDeployer; -import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector; -import org.opendaylight.controller.sal.core.api.Provider; -import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; - -public class DomForwardedDataBrokerImpl extends RootDataBrokerImpl implements Provider, DomForwardedBroker { - - private BindingIndependentConnector connector; - private ProviderSession domProviderContext; - - public void setConnector(BindingIndependentConnector connector) { - this.connector = connector; - } - - @Override - public void onSessionInitiated(ProviderSession session) { - this.setDomProviderContext(session); - } - - @Override - public Collection getProviderFunctionality() { - return Collections.emptySet(); - } - - @Override - public BindingIndependentConnector getConnector() { - return connector; - } - - @Override - public ProviderSession getDomProviderContext() { - return domProviderContext; - } - - public void setDomProviderContext(ProviderSession domProviderContext) { - this.domProviderContext = domProviderContext; - } - - @Override - public void startForwarding() { - BindingDomConnectorDeployer.startDataForwarding(getConnector(), this, getDomProviderContext()); - } -} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang b/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang index cee4b1efb3..aec2723591 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang +++ b/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang @@ -18,10 +18,9 @@ module opendaylight-sal-binding-broker-impl { identity binding-dom-mapping-service { base config:service-type; - config:java-class "org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService"; + config:java-class "org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec"; } - identity binding-broker-impl { base config:module-type; config:provided-service sal:binding-broker-osgi-registry; @@ -29,13 +28,6 @@ module opendaylight-sal-binding-broker-impl { config:java-name-prefix BindingBrokerImpl; } - identity binding-data-broker { - base config:module-type; - config:provided-service sal:binding-data-broker; - config:provided-service sal:binding-data-consumer-broker; - config:java-name-prefix DataBrokerImpl; - } - identity binding-data-compatible-broker { base config:module-type; config:provided-service sal:binding-data-broker; @@ -131,29 +123,6 @@ module opendaylight-sal-binding-broker-impl { } } - augment "/config:modules/config:module/config:configuration" { - case binding-data-broker { - when "/config:modules/config:module/config:type = 'binding-data-broker'"; - container dom-broker { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity dom:dom-broker-osgi-registry; - } - } - } - - container mapping-service { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity binding-dom-mapping-service; - } - } - } - } - } - augment "/config:modules/config:module/config:configuration" { case binding-data-compatible-broker { when "/config:modules/config:module/config:type = 'binding-data-compatible-broker'"; @@ -178,14 +147,6 @@ module opendaylight-sal-binding-broker-impl { } } - augment "/config:modules/config:module/config:state" { - case binding-data-broker { - when "/config:modules/config:module/config:type = 'binding-data-broker'"; - container data { - uses common:data-state; - } - } - } augment "/config:modules/config:module/config:state" { case binding-rpc-broker { when "/config:modules/config:module/config:type = 'binding-rpc-broker'"; diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingNormalizedCodecTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingNormalizedCodecTest.java index fd0a169694..3e6c4c072d 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingNormalizedCodecTest.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/impl/test/BindingNormalizedCodecTest.java @@ -11,7 +11,12 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controll import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.Top; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelList; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.list.rev140701.two.level.list.TopLevelListKey; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy; import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; @@ -34,7 +39,9 @@ public class BindingNormalizedCodecTest extends AbstractSchemaAwareTest { @Override protected void setupWithSchema(final SchemaContext context) { mappingService = new RuntimeGeneratedMappingServiceImpl(ClassPool.getDefault()); - codec = new BindingToNormalizedNodeCodec(mappingService); + DataObjectSerializerGenerator streamWriter = StreamWriterGenerator.create(JavassistUtils.forClassPool(ClassPool.getDefault())); + BindingNormalizedNodeCodecRegistry registry = new BindingNormalizedNodeCodecRegistry(streamWriter); + codec = new BindingToNormalizedNodeCodec(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(), mappingService, registry); mappingService.onGlobalContextUpdated(context); codec.onGlobalContextUpdated(context); }; diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java index 60eec55ca5..ca04f99ba2 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/md/sal/binding/test/DataBrokerTestCustomizer.java @@ -7,9 +7,14 @@ */ package org.opendaylight.controller.md.sal.binding.test; +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + import javassist.ClassPool; import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBackwardsCompatibleDataBroker; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBindingDataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -19,20 +24,21 @@ import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; import org.opendaylight.controller.sal.binding.test.util.MockSchemaService; import org.opendaylight.controller.sal.core.api.model.SchemaService; import org.opendaylight.controller.sal.core.spi.data.DOMStore; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy; import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl; -import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import com.google.common.collect.ImmutableMap; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - public class DataBrokerTestCustomizer { private DOMDataBroker domDataBroker; private final RuntimeGeneratedMappingServiceImpl mappingService; private final MockSchemaService schemaService; private ImmutableMap datastores; + private final BindingToNormalizedNodeCodec bindingToNormalized ; public ImmutableMap createDatastores() { return ImmutableMap.builder() @@ -43,7 +49,13 @@ public class DataBrokerTestCustomizer { public DataBrokerTestCustomizer() { schemaService = new MockSchemaService(); - mappingService = new RuntimeGeneratedMappingServiceImpl(ClassPool.getDefault()); + ClassPool pool = ClassPool.getDefault(); + mappingService = new RuntimeGeneratedMappingServiceImpl(pool); + DataObjectSerializerGenerator generator = StreamWriterGenerator.create(JavassistUtils.forClassPool(pool)); + BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(generator); + GeneratedClassLoadingStrategy loading = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(); + bindingToNormalized = new BindingToNormalizedNodeCodec(loading, mappingService, codecRegistry); + schemaService.registerSchemaContextListener(bindingToNormalized); } public DOMStore createConfigurationDatastore() { @@ -69,22 +81,17 @@ public class DataBrokerTestCustomizer { } public DataBroker createDataBroker() { - return new ForwardedBindingDataBroker(getDOMDataBroker(), getMappingService(), getSchemaService()); + return new ForwardedBindingDataBroker(getDOMDataBroker(), bindingToNormalized, schemaService ); } public ForwardedBackwardsCompatibleDataBroker createBackwardsCompatibleDataBroker() { - return new ForwardedBackwardsCompatibleDataBroker(getDOMDataBroker(), getMappingService(), getSchemaService(), MoreExecutors.sameThreadExecutor()); + return new ForwardedBackwardsCompatibleDataBroker(getDOMDataBroker(), bindingToNormalized, getSchemaService(), MoreExecutors.sameThreadExecutor()); } - private SchemaService getSchemaService() { return schemaService; } - private BindingIndependentMappingService getMappingService() { - return mappingService; - } - private DOMDataBroker getDOMDataBroker() { if(domDataBroker == null) { domDataBroker = createDOMDataBroker(); @@ -92,8 +99,8 @@ public class DataBrokerTestCustomizer { return domDataBroker; } - private ImmutableMap getDatastores() { - if(datastores == null) { + private synchronized ImmutableMap getDatastores() { + if (datastores == null) { datastores = createDatastores(); } return datastores; diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/compat/MultipleAugmentationPutsTest.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/compat/MultipleAugmentationPutsTest.java index 7b67d3b10f..63e0e2290a 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/compat/MultipleAugmentationPutsTest.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/compat/MultipleAugmentationPutsTest.java @@ -16,7 +16,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; - import org.junit.Test; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent; @@ -76,7 +75,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem * * @throws Exception */ - @Test( timeout = 15000) + @Test() public void testAugmentSerialization() throws Exception { baDataService.registerDataChangeListener(NODES_INSTANCE_ID_BA, this); @@ -122,7 +121,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem testNodeRemove(); } - private > Node createTestNode(Class augmentationClass, T augmentation) { + private > Node createTestNode(final Class augmentationClass, final T augmentation) { NodeBuilder nodeBuilder = new NodeBuilder(); nodeBuilder.setId(new NodeId(NODE_ID)); nodeBuilder.setKey(NODE_KEY); @@ -130,7 +129,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem return nodeBuilder.build(); } - private DataModificationTransaction commitNodeAndVerifyTransaction(Node original) throws Exception { + private DataModificationTransaction commitNodeAndVerifyTransaction(final Node original) throws Exception { DataModificationTransaction transaction = baDataService.beginTransaction(); transaction.putOperationalData(NODE_INSTANCE_ID_BA, original); RpcResult result = transaction.commit().get(); @@ -148,7 +147,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem assertNull(node); } - private AugmentationVerifier verifyNode(Nodes nodes, Node original) { + private AugmentationVerifier verifyNode(final Nodes nodes, final Node original) { assertNotNull(nodes); assertNotNull(nodes.getNode()); assertEquals(1, nodes.getNode().size()); @@ -158,7 +157,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem return new AugmentationVerifier(readedNode); } - private void assertBindingIndependentVersion(org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier nodeId) { + private void assertBindingIndependentVersion(final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier nodeId) { CompositeNode node = biDataService.readOperationalData(nodeId); assertNotNull(node); } @@ -171,7 +170,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem return nodeMeterStatistics(10, false); } - private NodeMeterStatistics nodeMeterStatistics(int count, boolean setDuration) { + private NodeMeterStatistics nodeMeterStatistics(final int count, final boolean setDuration) { NodeMeterStatisticsBuilder nmsb = new NodeMeterStatisticsBuilder(); MeterStatisticsBuilder meterStats = new MeterStatisticsBuilder(); @@ -207,7 +206,7 @@ public class MultipleAugmentationPutsTest extends AbstractDataServiceTest implem } @Override - public void onDataChanged(DataChangeEvent, DataObject> change) { + public void onDataChanged(final DataChangeEvent, DataObject> change) { receivedChangeEvent = change; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java index fef5715f50..d0a326adff 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java @@ -9,12 +9,19 @@ package org.opendaylight.controller.sal.binding.test.util; import static com.google.common.base.Preconditions.checkState; +import com.google.common.annotations.Beta; +import com.google.common.collect.ClassToInstanceMap; +import com.google.common.collect.ImmutableClassToInstanceMap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.MutableClassToInstanceMap; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; import java.util.Set; import java.util.concurrent.Future; - import javassist.ClassPool; - import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBackwardsCompatibleDataBroker; import org.opendaylight.controller.md.sal.binding.impl.ForwardedBindingDataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -43,9 +50,14 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStore; import org.opendaylight.controller.sal.dom.broker.BrokerImpl; import org.opendaylight.controller.sal.dom.broker.MountPointManagerImpl; import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareRpcBroker; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy; import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; import org.opendaylight.yangtools.yang.binding.YangModuleInfo; import org.opendaylight.yangtools.yang.binding.util.BindingReflections; import org.opendaylight.yangtools.yang.common.QName; @@ -56,15 +68,6 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.annotations.Beta; -import com.google.common.collect.ClassToInstanceMap; -import com.google.common.collect.ImmutableClassToInstanceMap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.MutableClassToInstanceMap; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - @Beta public class BindingTestContext implements AutoCloseable { @@ -74,6 +77,7 @@ public class BindingTestContext implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(BindingTestContext.class); private RuntimeGeneratedMappingServiceImpl mappingServiceImpl; + private BindingToNormalizedNodeCodec codec; private DomForwardedBindingBrokerImpl baBrokerImpl; private DataBrokerImpl baDataImpl; @@ -129,7 +133,7 @@ public class BindingTestContext implements AutoCloseable { public void startNewDataBroker() { checkState(executor != null, "Executor needs to be set"); checkState(newDOMDataBroker != null, "DOM Data Broker must be set"); - dataBroker = new ForwardedBindingDataBroker(newDOMDataBroker, mappingServiceImpl, mockSchemaService); + dataBroker = new ForwardedBindingDataBroker(newDOMDataBroker, codec, mockSchemaService); } public void startNewDomDataBroker() { @@ -250,6 +254,12 @@ public class BindingTestContext implements AutoCloseable { checkState(classPool != null, "ClassPool needs to be present"); mappingServiceImpl = new RuntimeGeneratedMappingServiceImpl(classPool); mockSchemaService.registerSchemaContextListener(mappingServiceImpl); + + DataObjectSerializerGenerator generator = StreamWriterGenerator.create(JavassistUtils.forClassPool(classPool)); + BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(generator); + GeneratedClassLoadingStrategy loading = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(); + codec = new BindingToNormalizedNodeCodec(loading, mappingServiceImpl, codecRegistry); + mockSchemaService.registerSchemaContextListener(codec); } private void updateYangSchema(final ImmutableSet moduleInfos) { @@ -280,7 +290,7 @@ public class BindingTestContext implements AutoCloseable { } public void startNewBindingDataBroker() { - ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl,mockSchemaService, executor); + ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, codec,mockSchemaService, executor); baData = forwarded; } diff --git a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java index 83a69969b7..344694381a 100644 --- a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java +++ b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java @@ -112,6 +112,7 @@ public class TestHelper { mavenBundle(YANGTOOLS, "binding-generator-api").versionAsInProject(), mavenBundle(YANGTOOLS, "binding-generator-spi").versionAsInProject(), // mavenBundle(YANGTOOLS, "binding-generator-impl").versionAsInProject(), + mavenBundle(YANGTOOLS, "binding-data-codec").versionAsInProject(), mavenBundle(YANGTOOLS + ".thirdparty", "antlr4-runtime-osgi-nohead").versionAsInProject(), // // mavenBundle(CONTROLLER, "sal-core-api").versionAsInProject().update(), // diff --git a/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/DataServiceTest.java b/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/DataServiceTest.java index 8a390b337e..33039ea231 100644 --- a/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/DataServiceTest.java +++ b/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/DataServiceTest.java @@ -11,9 +11,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import com.google.inject.Inject; import java.util.concurrent.Future; - import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext; @@ -22,7 +23,6 @@ import org.opendaylight.controller.sal.binding.api.data.DataBrokerService; import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; -import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; @@ -31,8 +31,6 @@ import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; -import com.google.inject.Inject; - public class DataServiceTest extends AbstractTest { protected DataBrokerService consumerDataService; @@ -45,12 +43,20 @@ public class DataServiceTest extends AbstractTest { public void setUp() throws Exception { } + /* + * + * Ignored this, because classes here are constructed from + * very different class loader as MD-SAL is run into, + * this is code is run from different classloader. + * + */ @Test + @Ignore public void test() throws Exception { BindingAwareConsumer consumer1 = new BindingAwareConsumer() { @Override - public void onSessionInitialized(ConsumerContext session) { + public void onSessionInitialized(final ConsumerContext session) { consumerDataService = session.getSALService(DataBrokerService.class); } }; @@ -62,12 +68,12 @@ public class DataServiceTest extends AbstractTest { DataModificationTransaction transaction = consumerDataService.beginTransaction(); assertNotNull(transaction); - NodeRef node1 = createNodeRef("0"); - DataObject node = consumerDataService.readConfigurationData(node1.getValue()); + InstanceIdentifier node1 = createNodeRef("0"); + DataObject node = consumerDataService.readConfigurationData(node1); assertNull(node); Node nodeData1 = createNode("0"); - transaction.putConfigurationData(node1.getValue(), nodeData1); + transaction.putConfigurationData(node1, nodeData1); Future> commitResult = transaction.commit(); assertNotNull(commitResult); @@ -77,7 +83,7 @@ public class DataServiceTest extends AbstractTest { assertNotNull(result.getResult()); assertEquals(TransactionStatus.COMMITED, result.getResult()); - Node readedData = (Node) consumerDataService.readConfigurationData(node1.getValue()); + Node readedData = (Node) consumerDataService.readConfigurationData(node1); assertNotNull(readedData); assertEquals(nodeData1.getKey(), readedData.getKey()); @@ -85,7 +91,7 @@ public class DataServiceTest extends AbstractTest { DataModificationTransaction transaction2 = consumerDataService.beginTransaction(); assertNotNull(transaction); - transaction2.removeConfigurationData(node1.getValue()); + transaction2.removeConfigurationData(node1); Future> commitResult2 = transaction2.commit(); assertNotNull(commitResult2); @@ -96,21 +102,19 @@ public class DataServiceTest extends AbstractTest { assertNotNull(result2.getResult()); assertEquals(TransactionStatus.COMMITED, result2.getResult()); - DataObject readedData2 = consumerDataService.readConfigurationData(node1.getValue()); + DataObject readedData2 = consumerDataService.readConfigurationData(node1); assertNull(readedData2); } - private static NodeRef createNodeRef(String string) { + private static InstanceIdentifier createNodeRef(final String string) { NodeKey key = new NodeKey(new NodeId(string)); - InstanceIdentifier path = InstanceIdentifier.builder(Nodes.class).child(Node.class, key).build(); - - return new NodeRef(path); + return InstanceIdentifier.builder(Nodes.class).child(Node.class, key).build(); } - private static Node createNode(String string) { + private static Node createNode(final String string) { NodeBuilder ret = new NodeBuilder(); NodeId id = new NodeId(string); ret.setKey(new NodeKey(id)); diff --git a/opendaylight/md-sal/sal-binding-it/src/test/resources/controller.xml b/opendaylight/md-sal/sal-binding-it/src/test/resources/controller.xml index 63a921d6f3..5e37f36a2c 100644 --- a/opendaylight/md-sal/sal-binding-it/src/test/resources/controller.xml +++ b/opendaylight/md-sal/sal-binding-it/src/test/resources/controller.xml @@ -38,124 +38,104 @@ - - prefix:schema-service-singleton - + prefix:schema-service-singleton yang-schema-service - - prefix:hash-map-data-store - - hash-map-data-store + prefix:runtime-generated-mapping + runtime-mapping-singleton - - prefix:dom-broker-impl - - dom-broker - - - dom:dom-data-store - - ref_hash-map-data-store - + prefix:binding-notification-broker + binding-notification-broker - - prefix:binding-broker-impl - + prefix:binding-broker-impl binding-broker-impl - - - binding:binding-notification-service - - ref_binding-notification-broker + + binding:binding-notification-service + binding-notification-broker - - binding:binding-data-broker - - ref_binding-data-broker + binding:binding-data-broker + binding-data-broker + - - prefix:runtime-generated-mapping - - runtime-mapping-singleton + prefix:dom-inmemory-data-broker + inmemory-data-broker + + dom:schema-service + yang-schema-service + - - prefix:binding-notification-broker - - binding-notification-broker + prefix:dom-broker-impl + inmemory-dom-broker + + dom:dom-async-data-broker + inmemory-data-broker + - - prefix:binding-data-broker - - binding-data-broker - - - dom:dom-broker-osgi-registry - - ref_dom-broker - - - - binding:binding-dom-mapping-service - - ref_runtime-mapping-singleton - + prefix:binding-data-compatible-broker + inmemory-binding-data-broker + + dom:dom-broker-osgi-registry + dom-broker + + + binding:binding-dom-mapping-service + runtime-mapping-singleton + + + + prefix:binding-forwarded-data-broker + binding-async-data-broker + + + dom:dom-broker-osgi-registry + dom-broker + + + binding:binding-dom-mapping-service + runtime-mapping-singleton + + - - dom:schema-service - + dom:schema-service - ref_yang-schema-service - - /config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service'] - + yang-schema-service + /modules/module[type='schema-service-singleton'][name='yang-schema-service'] - - binding:binding-notification-service - + binding-impl:binding-dom-mapping-service - ref_binding-notification-broker - - /config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker'] - + runtime-mapping-singleton + /modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton'] - - dom:dom-data-store - + binding:binding-notification-service - ref_hash-map-data-store - - /config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store'] - + binding-notification-broker + /modules/module[type='binding-notification-broker'][name='binding-notification-broker'] - - - binding:binding-broker-osgi-registry - + binding:binding-broker-osgi-registry - ref_binding-broker-impl - - /config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl'] - + binding-osgi-broker + /modules/module[type='binding-broker-impl'][name='binding-broker-impl'] @@ -165,36 +145,36 @@ /modules/module[type='binding-broker-impl'][name='binding-broker-impl'] + + + dom:dom-broker-osgi-registry + + dom-broker + /modules/module[type='dom-broker-impl'][name='inmemory-dom-broker'] + + + - - binding-impl:binding-dom-mapping-service - + binding:binding-data-broker - ref_runtime-mapping-singleton - - /config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton'] - + binding-data-broker + /modules/module[type='binding-data-compatible-broker'][name='inmemory-binding-data-broker'] + - - dom:dom-broker-osgi-registry - + binding:binding-async-data-broker - ref_dom-broker - /config/modules/module[name='dom-broker-impl']/instance[name='dom-broker'] - + binding-data-broker + /modules/module[type='binding-forwarded-data-broker'][name='binding-async-data-broker'] + - - binding:binding-data-broker - + dom:dom-async-data-broker - ref_binding-data-broker - - /config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker'] - + inmemory-data-broker + /modules/module[type='dom-inmemory-data-broker'][name='inmemory-data-broker'] diff --git a/opendaylight/md-sal/sal-clustering-commons/pom.xml b/opendaylight/md-sal/sal-clustering-commons/pom.xml index 6db4d3a094..4419d19f52 100644 --- a/opendaylight/md-sal/sal-clustering-commons/pom.xml +++ b/opendaylight/md-sal/sal-clustering-commons/pom.xml @@ -64,6 +64,10 @@ org.opendaylight.yangtools yang-parser-impl + + org.opendaylight.controller + netconf-util + xmlunit @@ -156,6 +160,12 @@ jsr305 2.0.1 + + + com.codahale.metrics + metrics-core + 3.0.1 + diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/QNameFactory.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/QNameFactory.java index 002b9ff82e..5a8f522861 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/QNameFactory.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/QNameFactory.java @@ -10,25 +10,28 @@ package org.opendaylight.controller.cluster.datastore.node.utils; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import org.opendaylight.yangtools.yang.common.QName; -import java.util.HashMap; -import java.util.Map; - public class QNameFactory { - private static final Map cache = new HashMap<>(); - public static QName create(String name){ - QName value = cache.get(name); - if(value == null){ - synchronized (cache){ - value = cache.get(name); - if(value == null) { - value = QName.create(name); - cache.put(name, value); + private static final int MAX_QNAME_CACHE_SIZE = 10000; + + private static LoadingCache cache = CacheBuilder.newBuilder() + .maximumSize(MAX_QNAME_CACHE_SIZE) + .softValues() + .build( + new CacheLoader() { + public QName load(String key) { + return QName.create(key); } } - } - return value; + ); + + + public static QName create(String name){ + return cache.getUnchecked(name); } } diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/util/InstanceIdentifierUtils.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/util/InstanceIdentifierUtils.java index 55cb341086..0bb0d4fe87 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/util/InstanceIdentifierUtils.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/util/InstanceIdentifierUtils.java @@ -11,6 +11,7 @@ package org.opendaylight.controller.cluster.datastore.util; import org.opendaylight.controller.cluster.datastore.node.utils.NodeIdentifierFactory; +import org.opendaylight.controller.cluster.datastore.node.utils.QNameFactory; import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -222,7 +223,7 @@ public class InstanceIdentifierUtils { YangInstanceIdentifier.NodeWithValue nodeWithValue = new YangInstanceIdentifier.NodeWithValue( - QName.create(pathArgument.getNodeType().getValue()), + QNameFactory.create(pathArgument.getNodeType().getValue()), parseAttribute(pathArgument.getAttributes(0))); return nodeWithValue; @@ -232,7 +233,7 @@ public class InstanceIdentifierUtils { YangInstanceIdentifier.NodeIdentifierWithPredicates nodeIdentifierWithPredicates = new YangInstanceIdentifier.NodeIdentifierWithPredicates( - QName.create(pathArgument.getNodeType().getValue()), toAttributesMap(pathArgument.getAttributesList())); + QNameFactory.create(pathArgument.getNodeType().getValue()), toAttributesMap(pathArgument.getAttributesList())); return nodeIdentifierWithPredicates; @@ -241,7 +242,7 @@ public class InstanceIdentifierUtils { Set qNameSet = new HashSet<>(); for(NormalizedNodeMessages.Attribute attribute : pathArgument.getAttributesList()){ - qNameSet.add(QName.create(attribute.getValue())); + qNameSet.add(QNameFactory.create(attribute.getValue())); } return new YangInstanceIdentifier.AugmentationIdentifier(qNameSet); @@ -259,7 +260,7 @@ public class InstanceIdentifierUtils { String name = attribute.getName(); Object value = parseAttribute(attribute); - map.put(QName.create(name), value); + map.put(QNameFactory.create(name), value); } return map; diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/common/actor/MeteredBoundedMailbox.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/common/actor/MeteredBoundedMailbox.java new file mode 100644 index 0000000000..646431522e --- /dev/null +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/common/actor/MeteredBoundedMailbox.java @@ -0,0 +1,93 @@ +/* + * 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.common.actor; + +import akka.actor.ActorPath; +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.dispatch.BoundedMailbox; +import akka.dispatch.MailboxType; +import akka.dispatch.MessageQueue; +import akka.dispatch.ProducesMessageQueue; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.MetricRegistry; +import com.google.common.base.Preconditions; +import com.typesafe.config.Config; +import org.opendaylight.controller.common.reporting.MetricsReporter; +import scala.concurrent.duration.FiniteDuration; + +import java.util.concurrent.TimeUnit; + +public class MeteredBoundedMailbox implements MailboxType, ProducesMessageQueue { + + private MeteredMessageQueue queue; + private Integer capacity; + private FiniteDuration pushTimeOut; + private ActorPath actorPath; + private MetricsReporter reporter; + + private final String QUEUE_SIZE = "queue-size"; + private final Long DEFAULT_TIMEOUT = 10L; + + public MeteredBoundedMailbox(ActorSystem.Settings settings, Config config) { + Preconditions.checkArgument( config.hasPath("mailbox-capacity"), "Missing configuration [mailbox-capacity]" ); + this.capacity = config.getInt("mailbox-capacity"); + Preconditions.checkArgument( this.capacity > 0, "mailbox-capacity must be > 0"); + + Long timeout = -1L; + if ( config.hasPath("mailbox-push-timeout-time") ){ + timeout = config.getDuration("mailbox-push-timeout-time", TimeUnit.NANOSECONDS); + } else { + timeout = DEFAULT_TIMEOUT; + } + Preconditions.checkArgument( timeout > 0, "mailbox-push-timeout-time must be > 0"); + this.pushTimeOut = new FiniteDuration(timeout, TimeUnit.NANOSECONDS); + + reporter = MetricsReporter.getInstance(); + } + + + @Override + public MessageQueue create(final scala.Option owner, scala.Option system) { + this.queue = new MeteredMessageQueue(this.capacity, this.pushTimeOut); + monitorQueueSize(owner, this.queue); + return this.queue; + } + + private void monitorQueueSize(scala.Option owner, final MeteredMessageQueue monitoredQueue) { + if (owner.isEmpty()) { + return; //there's no actor to monitor + } + actorPath = owner.get().path(); + MetricRegistry registry = reporter.getMetricsRegistry(); + + String actorName = registry.name(actorPath.toString(), QUEUE_SIZE); + + if (registry.getMetrics().containsKey(actorName)) + return; //already registered + + reporter.getMetricsRegistry().register(actorName, + new Gauge() { + @Override + public Integer getValue() { + return monitoredQueue.size(); + } + }); + } + + + public static class MeteredMessageQueue extends BoundedMailbox.MessageQueue { + + public MeteredMessageQueue(int capacity, FiniteDuration pushTimeOut) { + super(capacity, pushTimeOut); + } + } + +} + diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/common/reporting/MetricsReporter.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/common/reporting/MetricsReporter.java new file mode 100644 index 0000000000..5c3e11f8b8 --- /dev/null +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/common/reporting/MetricsReporter.java @@ -0,0 +1,46 @@ +/* + * 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.common.reporting; + +import com.codahale.metrics.JmxReporter; +import com.codahale.metrics.MetricRegistry; + +/** + * Maintains metrics registry that is provided to reporters. + * At the moment only one reporter exists {@code JmxReporter}. + * More reporters can be added. + *

+ * The consumers of this class will only be interested in {@code MetricsRegistry} + * where metrics for that consumer gets stored. + */ +public class MetricsReporter implements AutoCloseable{ + + private final MetricRegistry METRICS_REGISTRY = new MetricRegistry(); + private final String DOMAIN = "org.opendaylight.controller"; + + public final JmxReporter jmxReporter = JmxReporter.forRegistry(METRICS_REGISTRY).inDomain(DOMAIN).build(); + + private static MetricsReporter inst = new MetricsReporter(); + + private MetricsReporter(){ + jmxReporter.start(); + } + + public static MetricsReporter getInstance(){ + return inst; + } + + public MetricRegistry getMetricsRegistry(){ + return METRICS_REGISTRY; + } + + @Override + public void close() throws Exception { + jmxReporter.close(); + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/InstanceIdentifierForXmlCodec.java similarity index 99% rename from opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java rename to opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/InstanceIdentifierForXmlCodec.java index 92a7fbae79..a9a49142a9 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/InstanceIdentifierForXmlCodec.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/InstanceIdentifierForXmlCodec.java @@ -5,7 +5,7 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.remote.rpc.utils; +package org.opendaylight.controller.xml.codec; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/RandomPrefix.java similarity index 96% rename from opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java rename to opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/RandomPrefix.java index 55cc8192a8..1349e1ece3 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/RandomPrefix.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/RandomPrefix.java @@ -5,7 +5,7 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.remote.rpc.utils; +package org.opendaylight.controller.xml.codec; import org.opendaylight.yangtools.yang.common.QName; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlDocumentUtils.java similarity index 99% rename from opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java rename to opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlDocumentUtils.java index b4cca1ab48..8af6a3140b 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlDocumentUtils.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlDocumentUtils.java @@ -5,7 +5,7 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.remote.rpc.utils; +package org.opendaylight.controller.xml.codec; import com.google.common.base.Function; import com.google.common.base.Objects; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlStreamUtils.java similarity index 99% rename from opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java rename to opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlStreamUtils.java index e4576c445b..c9d5e89ae1 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlStreamUtils.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlStreamUtils.java @@ -5,7 +5,7 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.remote.rpc.utils; +package org.opendaylight.controller.xml.codec; import com.google.common.annotations.Beta; import com.google.common.base.Preconditions; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlUtils.java similarity index 99% rename from opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java rename to opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlUtils.java index e07401a3e0..5848561676 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/utils/XmlUtils.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/xml/codec/XmlUtils.java @@ -5,7 +5,7 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.remote.rpc.utils; +package org.opendaylight.controller.xml.codec; import com.google.common.base.Optional; import org.opendaylight.controller.netconf.util.xml.XmlUtil; diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/QNameFactoryTest.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/QNameFactoryTest.java new file mode 100644 index 0000000000..76d4ceb2f5 --- /dev/null +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/cluster/datastore/node/utils/QNameFactoryTest.java @@ -0,0 +1,29 @@ +package org.opendaylight.controller.cluster.datastore.node.utils; + +import org.junit.Test; +import org.opendaylight.controller.cluster.datastore.util.TestModel; +import org.opendaylight.yangtools.yang.common.QName; + +import static junit.framework.Assert.assertTrue; +import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertFalse; + +public class QNameFactoryTest { + + @Test + public void testBasic(){ + QName expected = TestModel.AUG_NAME_QNAME; + QName created = QNameFactory.create(expected.toString()); + + assertFalse( expected == created); + + assertEquals(expected, created); + + QName cached = QNameFactory.create(expected.toString()); + + assertEquals(expected, cached); + + assertTrue( cached == created ); + } + +} diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/common/actor/MeteredBoundedMailboxTest.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/common/actor/MeteredBoundedMailboxTest.java new file mode 100644 index 0000000000..1e60803f05 --- /dev/null +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/common/actor/MeteredBoundedMailboxTest.java @@ -0,0 +1,96 @@ +/* + * 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.common.actor; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.actor.DeadLetter; +import akka.actor.Props; +import akka.actor.UntypedActor; +import akka.japi.Creator; +import akka.testkit.JavaTestKit; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import scala.concurrent.duration.FiniteDuration; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + +public class MeteredBoundedMailboxTest { + + private static ActorSystem actorSystem; + private final ReentrantLock lock = new ReentrantLock(); + + @Before + public void setUp() throws Exception { + actorSystem = ActorSystem.create("testsystem"); + } + + @After + public void tearDown() throws Exception { + if (actorSystem != null) + actorSystem.shutdown(); + } + + @Test + public void test_WhenQueueIsFull_ShouldSendMsgToDeadLetter() throws InterruptedException { + final JavaTestKit mockReceiver = new JavaTestKit(actorSystem); + actorSystem.eventStream().subscribe(mockReceiver.getRef(), DeadLetter.class); + + + final FiniteDuration TEN_SEC = new FiniteDuration(10, TimeUnit.SECONDS); + String boundedMailBox = actorSystem.name() + ".bounded-mailbox"; + ActorRef pingPongActor = actorSystem.actorOf(PingPongActor.props(lock).withMailbox(boundedMailBox), + "pingpongactor"); + + actorSystem.mailboxes().settings(); + lock.lock(); + //queue capacity = 10 + //need to send 12 messages; 1 message is dequeued and actor waits on lock, + //2nd to 11th messages are put on the queue + //12th message is sent to dead letter. + for (int i=0;i<12;i++){ + pingPongActor.tell("ping", mockReceiver.getRef()); + } + + mockReceiver.expectMsgClass(TEN_SEC, DeadLetter.class); + + lock.unlock(); + + Object[] eleven = mockReceiver.receiveN(11, TEN_SEC); + } + + /** + * For testing + */ + public static class PingPongActor extends UntypedActor{ + + ReentrantLock lock; + + private PingPongActor(ReentrantLock lock){ + this.lock = lock; + } + + public static Props props(final ReentrantLock lock){ + return Props.create(new Creator(){ + @Override + public PingPongActor create() throws Exception { + return new PingPongActor(lock); + } + }); + } + + @Override + public void onReceive(Object message) throws Exception { + lock.lock(); + if ("ping".equals(message)) + getSender().tell("pong", getSelf()); + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/xml/codec/XmlUtilsTest.java similarity index 98% rename from opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java rename to opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/xml/codec/XmlUtilsTest.java index a408e1d55a..0688bfbc5c 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/utils/XmlUtilsTest.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/java/org/opendaylight/controller/xml/codec/XmlUtilsTest.java @@ -6,7 +6,7 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ -package org.opendaylight.controller.remote.rpc.utils; +package org.opendaylight.controller.xml.codec; import com.google.common.collect.ImmutableList; diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/resources/application.conf b/opendaylight/md-sal/sal-clustering-commons/src/test/resources/application.conf index e69de29bb2..0392dec3dd 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/test/resources/application.conf +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/resources/application.conf @@ -0,0 +1,8 @@ +testsystem { + + bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 10 + mailbox-push-timeout-time = 100ms + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang b/opendaylight/md-sal/sal-clustering-commons/src/test/resources/org/opendaylight/controller/xml/codec/rpcTest.yang similarity index 100% rename from opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/org/opendaylight/controller/remote/rpc/utils/rpcTest.yang rename to opendaylight/md-sal/sal-clustering-commons/src/test/resources/org/opendaylight/controller/xml/codec/rpcTest.yang diff --git a/opendaylight/md-sal/sal-clustering-commons/src/test/resources/reference.conf b/opendaylight/md-sal/sal-clustering-commons/src/test/resources/reference.conf new file mode 100644 index 0000000000..3481bae8ae --- /dev/null +++ b/opendaylight/md-sal/sal-clustering-commons/src/test/resources/reference.conf @@ -0,0 +1,8 @@ +testsystem { + + bounded-mailbox { + mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox" + mailbox-capacity = 1000 + mailbox-push-timeout-time = 10ms + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf b/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf index 5bf231dbe1..0535179aad 100644 --- a/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf +++ b/opendaylight/md-sal/sal-clustering-config/src/main/resources/initial/akka.conf @@ -25,7 +25,7 @@ odl-cluster-data { } cluster { - seed-nodes = ["akka.tcp://opendaylight-cluster-data@:2550"] + seed-nodes = ["akka.tcp://opendaylight-cluster-data@:2550"] auto-down-unreachable-after = 10s } @@ -47,7 +47,7 @@ odl-cluster-rpc { } cluster { - seed-nodes = ["akka.tcp://opendaylight-cluster-rpc@:2551"] + seed-nodes = ["akka.tcp://opendaylight-cluster-rpc@:2551"] auto-down-unreachable-after = 10s } diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java index 940559ef89..32e32f94eb 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java @@ -10,39 +10,114 @@ package org.opendaylight.controller.md.sal.common.api.data; import org.opendaylight.yangtools.concepts.Path; /** - * A chain of transactions. Transactions in a chain need to be committed in sequence and each - * transaction should see the effects of previous transactions as if they happened. A chain - * makes no guarantees of atomicity, in fact transactions are committed as soon as possible. + * A chain of transactions. Transactions in a chain need to be committed in + * sequence and each transaction should see the effects of previous committed transactions + * as they occurred. A chain makes no guarantees of atomicity across the chained transactions - + * the transactions are committed as soon as possible in the order that they were submitted. * + * This behaviour is different from the default AsyncDataBroker, where a + * transaction is always created from the current global state, not taking into + * account any transactions previously committed by the calling thread. Due to + * the asynchronous nature of transaction submission this can lead to surprising + * results. If a thread executes the following sequence sufficiently quickly: + * + * AsyncWriteTransaction t1 = broker.newWriteOnlyTransaction(); + * t1.put(id, data); + * t1.submit(); + * + * AsyncReadTransaction t2 = broker.newReadOnlyTransaction(); + * Optional maybeData = t2.read(id).get(); + * + * it may happen, that it sees maybeData.isPresent() == false, simply because + * t1 has not completed the processes of being applied and t2 is actually + * allocated from the previous state. This is obviously bad for users who create + * incremental state in the datastore and actually read what they write in + * subsequent transactions. + * + * Using a TransactionChain instead of a broker solves this particular problem, + * and leads to expected behavior: t2 will always see the data written in t1 + * present. */ -public interface TransactionChain

, D> extends AutoCloseable, AsyncDataTransactionFactory { +public interface TransactionChain

, D> extends AutoCloseable, + AsyncDataTransactionFactory { /** * Create a new read only transaction which will continue the chain. - * The previous read-write transaction has to be either COMMITED or CANCELLED. + * + *

+ * The previous write transaction has to be either SUBMITTED + * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED + * ({@link #close close} was invoked). + *

+ * The returned read-only transaction presents an isolated view of the data if the previous + * write transaction was successful - in other words, this read-only transaction will see the + * state changes made by the previous write transaction in the chain. However, state which + * was introduced by other transactions outside this transaction chain after creation of + * the previous transaction is not visible. * * @return New transaction in the chain. - * @throws IllegalStateException if the previous transaction was not COMMITED - * or CANCELLED. - * @throws TransactionChainClosedException if the chain has been closed. + * @throws IllegalStateException + * if the previous transaction was not SUBMITTED or CANCELLED. + * @throws TransactionChainClosedException + * if the chain has been closed. */ @Override public AsyncReadOnlyTransaction newReadOnlyTransaction(); - /** - * Create a new read write transaction which will continue the chain. - * The previous read-write transaction has to be either COMMITED or CANCELLED. + * Create a new read-write transaction which will continue the chain. + * + *

+ * The previous write transaction has to be either SUBMITTED + * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED + * ({@link #close close} was invoked). + *

+ * The returned read-write transaction presents an isolated view of the data if the previous + * write transaction was successful - in other words, this read-write transaction will see the + * state changes made by the previous write transaction in the chain. However, state which + * was introduced by other transactions outside this transaction chain after creation of + * the previous transaction is not visible. + *

+ * Committing this read-write transaction using {@link AsyncWriteTransaction#submit submit} + * will submit the state changes in this transaction to be visible to any subsequent + * transaction in this chain and also to any transaction outside this chain. * * @return New transaction in the chain. - * @throws IllegalStateException if the previous transaction was not COMMITTED - * or CANCELLED. - * @throws TransactionChainClosedException if the chain has been closed. + * @throws IllegalStateException + * if the previous transaction was not SUBMITTED or CANCELLED. + * @throws TransactionChainClosedException + * if the chain has been closed. */ @Override public AsyncReadWriteTransaction newReadWriteTransaction(); + /** + * Create a new write-only transaction which will continue the chain. + * + *

+ * The previous write transaction has to be either SUBMITTED + * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED + * ({@link #close close} was invoked). + *

+ * The returned write-only transaction presents an isolated view of the data if the previous + * write transaction was successful - in other words, this write-only transaction will see the + * state changes made by the previous write transaction in the chain. However, state which + * was introduced by other transactions outside this transaction chain after creation of + * the previous transaction is not visible. + *

+ * Committing this write-only transaction using {@link AsyncWriteTransaction#submit submit} + * will submit the state changes in this transaction to be visible to any subsequent + * transaction in this chain and also to any transaction outside this chain. + * + * @return New transaction in the chain. + * @throws IllegalStateException + * if the previous transaction was not SUBMITTED or CANCELLED. + * @throws TransactionChainClosedException + * if the chain has been closed. + */ + @Override + public AsyncWriteTransaction newWriteOnlyTransaction(); + @Override void close(); } - diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainFactory.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainFactory.java index 94d21f5fd6..470e611004 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainFactory.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainFactory.java @@ -13,6 +13,7 @@ import org.opendaylight.yangtools.concepts.Path; * Interface for creating transaction chains. */ public interface TransactionChainFactory

, D> { + /** * Create a new transaction chain. The chain will be initialized to read * from its backing datastore, with no outstanding transaction. Listener diff --git a/opendaylight/md-sal/sal-distributed-datastore/pom.xml b/opendaylight/md-sal/sal-distributed-datastore/pom.xml index 648e8d23d0..9c5129d6a4 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/pom.xml +++ b/opendaylight/md-sal/sal-distributed-datastore/pom.xml @@ -135,6 +135,11 @@ 1.1-SNAPSHOT + + com.codahale.metrics + metrics-core + 3.0.1 + junit @@ -168,10 +173,11 @@ ${project.groupId}.${project.artifactId} - !*snappy;!org.jboss.*;* + !*snappy;!org.jboss.*;!com.jcraft.*;* sal-clustering-commons; sal-akka-raft; + *metrics*; !sal*; !*config-api*; !*testkit*; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/AbstractUntypedActor.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/AbstractUntypedActor.java index ac01f42a7f..b258c4466a 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/AbstractUntypedActor.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/AbstractUntypedActor.java @@ -18,7 +18,7 @@ public abstract class AbstractUntypedActor extends UntypedActor { Logging.getLogger(getContext().system(), this); - public AbstractUntypedActor(){ + public AbstractUntypedActor() { LOG.debug("Actor created {}", getSelf()); getContext(). system(). @@ -29,16 +29,18 @@ public abstract class AbstractUntypedActor extends UntypedActor { @Override public void onReceive(Object message) throws Exception { LOG.debug("Received message {}", message.getClass().getSimpleName()); handleReceive(message); - LOG.debug("Done handling message {}", message.getClass().getSimpleName()); + LOG.debug("Done handling message {}", + message.getClass().getSimpleName()); } protected abstract void handleReceive(Object message) throws Exception; - protected void ignoreMessage(Object message){ + protected void ignoreMessage(Object message) { LOG.debug("Unhandled message {} ", message); } - protected void unknownMessage(Object message) throws Exception{ + protected void unknownMessage(Object message) throws Exception { + LOG.debug("Received unhandled message {}", message); unhandled(message); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java index 4fa26ffb20..404a4e0203 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DistributedDataStore.java @@ -10,9 +10,8 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorRef; import akka.actor.ActorSystem; + import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; import org.opendaylight.controller.cluster.datastore.identifiers.ShardManagerIdentifier; import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener; import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply; @@ -28,8 +27,6 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransactio import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain; import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.util.PropertyUtils; -import org.opendaylight.yangtools.util.concurrent.SpecialExecutors; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; @@ -42,37 +39,12 @@ import org.slf4j.LoggerFactory; */ public class DistributedDataStore implements DOMStore, SchemaContextListener, AutoCloseable { - private static final Logger - LOG = LoggerFactory.getLogger(DistributedDataStore.class); - - private static final String EXECUTOR_MAX_POOL_SIZE_PROP = - "mdsal.dist-datastore-executor-pool.size"; - private static final int DEFAULT_EXECUTOR_MAX_POOL_SIZE = 10; - - private static final String EXECUTOR_MAX_QUEUE_SIZE_PROP = - "mdsal.dist-datastore-executor-queue.size"; - private static final int DEFAULT_EXECUTOR_MAX_QUEUE_SIZE = 5000; + private static final Logger LOG = LoggerFactory.getLogger(DistributedDataStore.class); private final ActorContext actorContext; private SchemaContext schemaContext; - /** - * Executor used to run FutureTask's - * - * This is typically used when we need to make a request to an actor and - * wait for it's response and the consumer needs to be provided a Future. - */ - private final ListeningExecutorService executor = - MoreExecutors.listeningDecorator( - SpecialExecutors.newBlockingBoundedFastThreadPool( - PropertyUtils.getIntSystemProperty( - EXECUTOR_MAX_POOL_SIZE_PROP, - DEFAULT_EXECUTOR_MAX_POOL_SIZE), - PropertyUtils.getIntSystemProperty( - EXECUTOR_MAX_QUEUE_SIZE_PROP, - DEFAULT_EXECUTOR_MAX_QUEUE_SIZE), "DistDataStore")); - public DistributedDataStore(ActorSystem actorSystem, String type, ClusterWrapper cluster, Configuration configuration, InMemoryDOMDataStoreConfigProperties dataStoreProperties) { Preconditions.checkNotNull(actorSystem, "actorSystem should not be null"); @@ -95,15 +67,16 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au } + @SuppressWarnings("unchecked") @Override - public >> ListenerRegistration registerChangeListener( + public >> + ListenerRegistration registerChangeListener( YangInstanceIdentifier path, L listener, AsyncDataBroker.DataChangeScope scope) { Preconditions.checkNotNull(path, "path should not be null"); Preconditions.checkNotNull(listener, "listener should not be null"); - LOG.debug("Registering listener: {} for path: {} scope: {}", listener, path, scope); ActorRef dataChangeListenerActor = actorContext.getActorSystem().actorOf( @@ -112,10 +85,8 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au String shardName = ShardStrategyFactory.getStrategy(path).findShard(path); Object result = actorContext.executeLocalShardOperation(shardName, - new RegisterChangeListener(path, dataChangeListenerActor.path(), - scope), - ActorContext.ASK_DURATION - ); + new RegisterChangeListener(path, dataChangeListenerActor.path(), scope), + ActorContext.ASK_DURATION); if (result != null) { RegisterChangeListenerReply reply = (RegisterChangeListenerReply) result; @@ -127,34 +98,31 @@ public class DistributedDataStore implements DOMStore, SchemaContextListener, Au LOG.debug( "No local shard for shardName {} was found so returning a noop registration", shardName); + return new NoOpDataChangeListenerRegistration(listener); } - - - - @Override public DOMStoreTransactionChain createTransactionChain() { - return new TransactionChainProxy(actorContext, executor, schemaContext); + return new TransactionChainProxy(actorContext, schemaContext); } @Override public DOMStoreReadTransaction newReadOnlyTransaction() { return new TransactionProxy(actorContext, TransactionProxy.TransactionType.READ_ONLY, - executor, schemaContext); + schemaContext); } @Override public DOMStoreWriteTransaction newWriteOnlyTransaction() { return new TransactionProxy(actorContext, TransactionProxy.TransactionType.WRITE_ONLY, - executor, schemaContext); + schemaContext); } @Override public DOMStoreReadWriteTransaction newReadWriteTransaction() { return new TransactionProxy(actorContext, TransactionProxy.TransactionType.READ_WRITE, - executor, schemaContext); + schemaContext); } @Override public void onGlobalContextUpdated(SchemaContext schemaContext) { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java index c329a10c04..75f540ade0 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java @@ -17,6 +17,8 @@ import akka.japi.Creator; import akka.serialization.Serialization; import com.google.common.base.Optional; 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 org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; import org.opendaylight.controller.cluster.datastore.identifiers.ShardTransactionIdentifier; @@ -111,21 +113,27 @@ public class Shard extends RaftActor { } - private static Map mapPeerAddresses(Map peerAddresses){ - Map map = new HashMap<>(); + private static Map mapPeerAddresses( + Map peerAddresses) { + Map map = new HashMap<>(); - for(Map.Entry entry : peerAddresses.entrySet()){ + for (Map.Entry entry : peerAddresses + .entrySet()) { map.put(entry.getKey().toString(), entry.getValue()); } return map; } + + + public static Props props(final ShardIdentifier name, final Map peerAddresses, final InMemoryDOMDataStoreConfigProperties dataStoreProperties) { Preconditions.checkNotNull(name, "name should not be null"); - Preconditions.checkNotNull(peerAddresses, "peerAddresses should not be null"); + Preconditions + .checkNotNull(peerAddresses, "peerAddresses should not be null"); return Props.create(new Creator() { @@ -164,14 +172,16 @@ public class Shard extends RaftActor { } } else if (message instanceof PeerAddressResolved) { PeerAddressResolved resolved = (PeerAddressResolved) message; - setPeerAddress(resolved.getPeerId().toString(), resolved.getPeerAddress()); + setPeerAddress(resolved.getPeerId().toString(), + resolved.getPeerAddress()); } else { super.onReceiveCommand(message); } } private ActorRef createTypedTransactionActor( - CreateTransaction createTransaction, ShardTransactionIdentifier transactionId) { + CreateTransaction createTransaction, + ShardTransactionIdentifier transactionId) { if (createTransaction.getTransactionType() == TransactionProxy.TransactionType.READ_ONLY.ordinal()) { @@ -203,24 +213,26 @@ public class Shard extends RaftActor { .props(store.newWriteOnlyTransaction(), getSelf(), schemaContext), transactionId.toString()); } else { - // FIXME: This does not seem right throw new IllegalArgumentException( - "CreateTransaction message has unidentified transaction type=" + "Shard="+name + ":CreateTransaction message has unidentified transaction type=" + createTransaction.getTransactionType()); } } private void createTransaction(CreateTransaction createTransaction) { - ShardTransactionIdentifier transactionId = ShardTransactionIdentifier.builder().remoteTransactionId(createTransaction.getTransactionId()).build(); + ShardTransactionIdentifier transactionId = + ShardTransactionIdentifier.builder() + .remoteTransactionId(createTransaction.getTransactionId()) + .build(); LOG.debug("Creating transaction : {} ", transactionId); ActorRef transactionActor = createTypedTransactionActor(createTransaction, transactionId); getSender() .tell(new CreateTransactionReply( - Serialization.serializedActorPath(transactionActor), - createTransaction.getTransactionId()).toSerializable(), + Serialization.serializedActorPath(transactionActor), + createTransaction.getTransactionId()).toSerializable(), getSelf()); } @@ -255,22 +267,21 @@ public class Shard extends RaftActor { final ListenableFuture future = cohort.commit(); final ActorRef self = getSelf(); - future.addListener(new Runnable() { - @Override - public void run() { - try { - future.get(); - sender - .tell(new CommitTransactionReply().toSerializable(), - self); - shardMBean.incrementCommittedTransactionCount(); - shardMBean.setLastCommittedTransactionTime(new Date()); - } catch (InterruptedException | ExecutionException e) { - shardMBean.incrementFailedTransactionsCount(); - sender.tell(new akka.actor.Status.Failure(e),self); - } + + Futures.addCallback(future, new FutureCallback() { + public void onSuccess(Void v) { + sender.tell(new CommitTransactionReply().toSerializable(),self); + shardMBean.incrementCommittedTransactionCount(); + shardMBean.setLastCommittedTransactionTime(new Date()); } - }, getContext().dispatcher()); + + public void onFailure(Throwable t) { + LOG.error(t, "An exception happened during commit"); + shardMBean.incrementFailedTransactionsCount(); + sender.tell(new akka.actor.Status.Failure(t), self); + } + }); + } private void handleForwardedCommit(ForwardedCommitTransaction message) { @@ -329,7 +340,7 @@ public class Shard extends RaftActor { LOG.debug( "registerDataChangeListener sending reply, listenerRegistrationPath = {} " - , listenerRegistration.path().toString()); + , listenerRegistration.path().toString()); getSender() .tell(new RegisterChangeListenerReply(listenerRegistration.path()), @@ -370,7 +381,7 @@ public class Shard extends RaftActor { // Update stats ReplicatedLogEntry lastLogEntry = getLastLogEntry(); - if(lastLogEntry != null){ + if (lastLogEntry != null) { shardMBean.setLastLogIndex(lastLogEntry.getIndex()); shardMBean.setLastLogTerm(lastLogEntry.getTerm()); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java index 5fce64e248..2972772a48 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardManager.java @@ -290,10 +290,17 @@ public class ShardManager extends AbstractUntypedActor { @Override public SupervisorStrategy supervisorStrategy() { + return new OneForOneStrategy(10, Duration.create("1 minute"), new Function() { @Override public SupervisorStrategy.Directive apply(Throwable t) { + StringBuilder sb = new StringBuilder(); + for(StackTraceElement element : t.getStackTrace()) { + sb.append("\n\tat ") + .append(element.toString()); + } + LOG.warning("Supervisor Strategy of resume applied {}",sb.toString()); return SupervisorStrategy.resume(); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java index 97bb196f9f..49c7b7e78f 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardReadWriteTransaction.java @@ -53,7 +53,7 @@ public class ShardReadWriteTransaction extends ShardTransaction { } else if (MergeData.SERIALIZABLE_CLASS.equals(message.getClass())) { mergeData(transaction, MergeData.fromSerializable(message, schemaContext)); } else if (DeleteData.SERIALIZABLE_CLASS.equals(message.getClass())) { - deleteData(transaction,DeleteData.fromSerizalizable(message)); + deleteData(transaction,DeleteData.fromSerializable(message)); } else if (ReadyTransaction.SERIALIZABLE_CLASS.equals(message.getClass())) { readyTransaction(transaction,new ReadyTransaction()); } else if(DataExists.SERIALIZABLE_CLASS.equals(message.getClass())) { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java index 91e578b46d..b01fe7d4ac 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ShardWriteTransaction.java @@ -50,7 +50,7 @@ public class ShardWriteTransaction extends ShardTransaction { } else if (MergeData.SERIALIZABLE_CLASS.equals(message.getClass())) { mergeData(transaction, MergeData.fromSerializable(message, schemaContext)); } else if (DeleteData.SERIALIZABLE_CLASS.equals(message.getClass())) { - deleteData(transaction,DeleteData.fromSerizalizable(message)); + deleteData(transaction,DeleteData.fromSerializable(message)); } else if (ReadyTransaction.SERIALIZABLE_CLASS.equals(message.getClass())) { readyTransaction(transaction,new ReadyTransaction()); }else { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java index 500b73ce9d..34d3531283 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohort.java @@ -14,6 +14,8 @@ import akka.actor.Props; import akka.event.Logging; import akka.event.LoggingAdapter; import akka.japi.Creator; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction; import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply; @@ -26,8 +28,6 @@ import org.opendaylight.controller.cluster.datastore.messages.PreCommitTransacti import org.opendaylight.controller.cluster.datastore.modification.CompositeModification; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; -import java.util.concurrent.ExecutionException; - public class ThreePhaseCommitCohort extends AbstractUntypedActor { private final DOMStoreThreePhaseCommitCohort cohort; private final ActorRef shardActor; @@ -58,13 +58,17 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { @Override public void handleReceive(Object message) throws Exception { - if (message.getClass().equals(CanCommitTransaction.SERIALIZABLE_CLASS)) { + if (message.getClass() + .equals(CanCommitTransaction.SERIALIZABLE_CLASS)) { canCommit(new CanCommitTransaction()); - } else if (message.getClass().equals(PreCommitTransaction.SERIALIZABLE_CLASS)) { + } else if (message.getClass() + .equals(PreCommitTransaction.SERIALIZABLE_CLASS)) { preCommit(new PreCommitTransaction()); - } else if (message.getClass().equals(CommitTransaction.SERIALIZABLE_CLASS)) { + } else if (message.getClass() + .equals(CommitTransaction.SERIALIZABLE_CLASS)) { commit(new CommitTransaction()); - } else if (message.getClass().equals(AbortTransaction.SERIALIZABLE_CLASS)) { + } else if (message.getClass() + .equals(AbortTransaction.SERIALIZABLE_CLASS)) { abort(new AbortTransaction()); } else { unknownMessage(message); @@ -76,17 +80,19 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { final ActorRef sender = getSender(); final ActorRef self = getSelf(); - future.addListener(new Runnable() { - @Override - public void run() { - try { - future.get(); - sender.tell(new AbortTransactionReply().toSerializable(), self); - } catch (InterruptedException | ExecutionException e) { - log.error(e, "An exception happened when aborting"); - } + Futures.addCallback(future, new FutureCallback() { + public void onSuccess(Void v) { + sender + .tell(new AbortTransactionReply().toSerializable(), + self); + } + + public void onFailure(Throwable t) { + LOG.error(t, "An exception happened during abort"); + sender + .tell(new akka.actor.Status.Failure(t), self); } - }, getContext().dispatcher()); + }); } private void commit(CommitTransaction message) { @@ -103,18 +109,19 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { final ListenableFuture future = cohort.preCommit(); final ActorRef sender = getSender(); final ActorRef self = getSelf(); + Futures.addCallback(future, new FutureCallback() { + public void onSuccess(Void v) { + sender + .tell(new PreCommitTransactionReply().toSerializable(), + self); + } - future.addListener(new Runnable() { - @Override - public void run() { - try { - future.get(); - sender.tell(new PreCommitTransactionReply().toSerializable(), self); - } catch (InterruptedException | ExecutionException e) { - log.error(e, "An exception happened when preCommitting"); - } + public void onFailure(Throwable t) { + LOG.error(t, "An exception happened during pre-commit"); + sender + .tell(new akka.actor.Status.Failure(t), self); } - }, getContext().dispatcher()); + }); } @@ -122,18 +129,19 @@ public class ThreePhaseCommitCohort extends AbstractUntypedActor { final ListenableFuture future = cohort.canCommit(); final ActorRef sender = getSender(); final ActorRef self = getSelf(); + Futures.addCallback(future, new FutureCallback() { + public void onSuccess(Boolean canCommit) { + sender.tell(new CanCommitTransactionReply(canCommit) + .toSerializable(), self); + } - future.addListener(new Runnable() { - @Override - public void run() { - try { - Boolean canCommit = future.get(); - sender.tell(new CanCommitTransactionReply(canCommit).toSerializable(), self); - } catch (InterruptedException | ExecutionException e) { - log.error(e, "An exception happened when checking canCommit"); - } + public void onFailure(Throwable t) { + LOG.error(t, "An exception happened during canCommit"); + sender + .tell(new akka.actor.Status.Failure(t), self); } - }, getContext().dispatcher()); + }); + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java index 5b447943ea..fc455b193e 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxy.java @@ -10,11 +10,13 @@ package org.opendaylight.controller.cluster.datastore; import akka.actor.ActorPath; import akka.actor.ActorSelection; +import akka.dispatch.Futures; +import akka.dispatch.OnComplete; +import com.google.common.collect.Lists; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.SettableFuture; -import org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException; import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction; import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransaction; @@ -28,124 +30,156 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCoh import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import scala.concurrent.Future; + import java.util.Collections; import java.util.List; -import java.util.concurrent.Callable; /** * ThreePhaseCommitCohortProxy represents a set of remote cohort proxies */ -public class ThreePhaseCommitCohortProxy implements - DOMStoreThreePhaseCommitCohort{ +public class ThreePhaseCommitCohortProxy implements DOMStoreThreePhaseCommitCohort{ - private static final Logger - LOG = LoggerFactory.getLogger(DistributedDataStore.class); + private static final Logger LOG = LoggerFactory.getLogger(DistributedDataStore.class); private final ActorContext actorContext; private final List cohortPaths; - private final ListeningExecutorService executor; private final String transactionId; - - public ThreePhaseCommitCohortProxy(ActorContext actorContext, - List cohortPaths, - String transactionId, - ListeningExecutorService executor) { - + public ThreePhaseCommitCohortProxy(ActorContext actorContext, List cohortPaths, + String transactionId) { this.actorContext = actorContext; this.cohortPaths = cohortPaths; this.transactionId = transactionId; - this.executor = executor; } - @Override public ListenableFuture canCommit() { + @Override + public ListenableFuture canCommit() { LOG.debug("txn {} canCommit", transactionId); - Callable call = new Callable() { + Future> combinedFuture = + invokeCohorts(new CanCommitTransaction().toSerializable()); + + final SettableFuture returnFuture = SettableFuture.create(); + + combinedFuture.onComplete(new OnComplete>() { @Override - public Boolean call() throws Exception { - for(ActorPath actorPath : cohortPaths){ - - Object message = new CanCommitTransaction().toSerializable(); - LOG.debug("txn {} Sending {} to {}", transactionId, message, actorPath); - - ActorSelection cohort = actorContext.actorSelection(actorPath); - - try { - Object response = - actorContext.executeRemoteOperation(cohort, - message, - ActorContext.ASK_DURATION); - - if (response.getClass().equals(CanCommitTransactionReply.SERIALIZABLE_CLASS)) { - CanCommitTransactionReply reply = - CanCommitTransactionReply.fromSerializable(response); - if (!reply.getCanCommit()) { - return false; - } + public void onComplete(Throwable failure, Iterable responses) throws Throwable { + if(failure != null) { + returnFuture.setException(failure); + return; + } + + boolean result = true; + for(Object response: responses) { + if (response.getClass().equals(CanCommitTransactionReply.SERIALIZABLE_CLASS)) { + CanCommitTransactionReply reply = + CanCommitTransactionReply.fromSerializable(response); + if (!reply.getCanCommit()) { + result = false; + break; } - } catch(RuntimeException e){ - // FIXME : Need to properly handle this - LOG.error("Unexpected Exception", e); - return false; + } else { + LOG.error("Unexpected response type {}", response.getClass()); + returnFuture.setException(new IllegalArgumentException( + String.format("Unexpected response type {}", response.getClass()))); + return; } } - return true; + returnFuture.set(Boolean.valueOf(result)); } - }; + }, actorContext.getActorSystem().dispatcher()); + + return returnFuture; + } + + private Future> invokeCohorts(Object message) { + List> futureList = Lists.newArrayListWithCapacity(cohortPaths.size()); + for(ActorPath actorPath : cohortPaths) { + + LOG.debug("txn {} Sending {} to {}", transactionId, message, actorPath); - return executor.submit(call); + ActorSelection cohort = actorContext.actorSelection(actorPath); + + futureList.add(actorContext.executeRemoteOperationAsync(cohort, message, + ActorContext.ASK_DURATION)); + } + + return Futures.sequence(futureList, actorContext.getActorSystem().dispatcher()); } - @Override public ListenableFuture preCommit() { + @Override + public ListenableFuture preCommit() { LOG.debug("txn {} preCommit", transactionId); - return voidOperation(new PreCommitTransaction().toSerializable(), PreCommitTransactionReply.SERIALIZABLE_CLASS); + return voidOperation(new PreCommitTransaction().toSerializable(), + PreCommitTransactionReply.SERIALIZABLE_CLASS, true); } - @Override public ListenableFuture abort() { + @Override + public ListenableFuture abort() { LOG.debug("txn {} abort", transactionId); - return voidOperation(new AbortTransaction().toSerializable(), AbortTransactionReply.SERIALIZABLE_CLASS); + + // Note - we pass false for propagateException. In the front-end data broker, this method + // is called when one of the 3 phases fails with an exception. We'd rather have that + // original exception propagated to the client. If our abort fails and we propagate the + // exception then that exception will supersede and suppress the original exception. But + // it's the original exception that is the root cause and of more interest to the client. + + return voidOperation(new AbortTransaction().toSerializable(), + AbortTransactionReply.SERIALIZABLE_CLASS, false); } - @Override public ListenableFuture commit() { + @Override + public ListenableFuture commit() { LOG.debug("txn {} commit", transactionId); - return voidOperation(new CommitTransaction().toSerializable(), CommitTransactionReply.SERIALIZABLE_CLASS); + return voidOperation(new CommitTransaction().toSerializable(), + CommitTransactionReply.SERIALIZABLE_CLASS, true); } - private ListenableFuture voidOperation(final Object message, final Class expectedResponseClass){ - Callable call = new Callable() { - - @Override public Void call() throws Exception { - for(ActorPath actorPath : cohortPaths){ - ActorSelection cohort = actorContext.actorSelection(actorPath); - - LOG.debug("txn {} Sending {} to {}", transactionId, message, actorPath); - - try { - Object response = - actorContext.executeRemoteOperation(cohort, - message, - ActorContext.ASK_DURATION); - - if (response != null && !response.getClass() - .equals(expectedResponseClass)) { - throw new RuntimeException( - String.format( - "did not get the expected response \n\t\t expected : %s \n\t\t actual : %s", - expectedResponseClass.toString(), - response.getClass().toString()) - ); + private ListenableFuture voidOperation(final Object message, + final Class expectedResponseClass, final boolean propagateException) { + + Future> combinedFuture = invokeCohorts(message); + + final SettableFuture returnFuture = SettableFuture.create(); + + combinedFuture.onComplete(new OnComplete>() { + @Override + public void onComplete(Throwable failure, Iterable responses) throws Throwable { + + Throwable exceptionToPropagate = failure; + if(exceptionToPropagate == null) { + for(Object response: responses) { + if(!response.getClass().equals(expectedResponseClass)) { + exceptionToPropagate = new IllegalArgumentException( + String.format("Unexpected response type {}", + response.getClass())); + break; } - } catch(TimeoutException e){ - LOG.error(String.format("A timeout occurred when processing operation : %s", message)); } } - return null; + + if(exceptionToPropagate != null) { + if(propagateException) { + // We don't log the exception here to avoid redundant logging since we're + // propagating to the caller in MD-SAL core who will log it. + returnFuture.setException(exceptionToPropagate); + } else { + // Since the caller doesn't want us to propagate the exception we'll also + // not log it normally. But it's usually not good to totally silence + // exceptions so we'll log it to debug level. + LOG.debug(String.format("%s failed", message.getClass().getSimpleName()), + exceptionToPropagate); + returnFuture.set(null); + } + } else { + returnFuture.set(null); + } } - }; + }, actorContext.getActorSystem().dispatcher()); - return executor.submit(call); + return returnFuture; } public List getCohortPaths() { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxy.java index 5e9defa5b5..76bbef713c 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxy.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxy.java @@ -15,39 +15,34 @@ import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain; import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import com.google.common.util.concurrent.ListeningExecutorService; - /** * TransactionChainProxy acts as a proxy for a DOMStoreTransactionChain created on a remote shard */ public class TransactionChainProxy implements DOMStoreTransactionChain{ private final ActorContext actorContext; - private final ListeningExecutorService transactionExecutor; private final SchemaContext schemaContext; - public TransactionChainProxy(ActorContext actorContext, ListeningExecutorService transactionExecutor, - SchemaContext schemaContext) { + public TransactionChainProxy(ActorContext actorContext, SchemaContext schemaContext) { this.actorContext = actorContext; - this.transactionExecutor = transactionExecutor; this.schemaContext = schemaContext; } @Override public DOMStoreReadTransaction newReadOnlyTransaction() { return new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, schemaContext); + TransactionProxy.TransactionType.READ_ONLY, schemaContext); } @Override public DOMStoreReadWriteTransaction newReadWriteTransaction() { return new TransactionProxy(actorContext, - TransactionProxy.TransactionType.WRITE_ONLY, transactionExecutor, schemaContext); + TransactionProxy.TransactionType.READ_WRITE, schemaContext); } @Override public DOMStoreWriteTransaction newWriteOnlyTransaction() { return new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_WRITE, transactionExecutor, schemaContext); + TransactionProxy.TransactionType.WRITE_ONLY, schemaContext); } @Override diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java index 95862ae9d9..5b5b1296af 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/TransactionProxy.java @@ -12,13 +12,14 @@ import akka.actor.ActorPath; import akka.actor.ActorRef; import akka.actor.ActorSelection; import akka.actor.Props; +import akka.dispatch.OnComplete; + import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListeningExecutorService; -import org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundException; -import org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException; +import com.google.common.util.concurrent.SettableFuture; + import org.opendaylight.controller.cluster.datastore.identifiers.TransactionIdentifier; import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction; import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction; @@ -44,11 +45,12 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import scala.concurrent.Future; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicLong; /** @@ -80,25 +82,22 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { private final ActorContext actorContext; private final Map remoteTransactionPaths = new HashMap<>(); private final TransactionIdentifier identifier; - private final ListeningExecutorService executor; private final SchemaContext schemaContext; + private boolean inReadyState; - public TransactionProxy( - ActorContext actorContext, - TransactionType transactionType, - ListeningExecutorService executor, - SchemaContext schemaContext - ) { + public TransactionProxy(ActorContext actorContext, TransactionType transactionType, + SchemaContext schemaContext) { this.actorContext = Preconditions.checkNotNull(actorContext, "actorContext should not be null"); this.transactionType = Preconditions.checkNotNull(transactionType, "transactionType should not be null"); - this.executor = Preconditions.checkNotNull(executor, "executor should not be null"); this.schemaContext = Preconditions.checkNotNull(schemaContext, "schemaContext should not be null"); String memberName = actorContext.getCurrentMemberName(); if(memberName == null){ memberName = "UNKNOWN-MEMBER"; } - this.identifier = TransactionIdentifier.builder().memberName(memberName).counter(counter.getAndIncrement()).build(); + + this.identifier = TransactionIdentifier.builder().memberName(memberName).counter( + counter.getAndIncrement()).build(); LOG.debug("Created txn {}", identifier); @@ -108,6 +107,9 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { public CheckedFuture>, ReadFailedException> read( final YangInstanceIdentifier path) { + Preconditions.checkState(transactionType != TransactionType.WRITE_ONLY, + "Read operation on write-only transaction is not allowed"); + LOG.debug("txn {} read {}", identifier, path); createTransactionIfMissing(actorContext, path); @@ -115,8 +117,12 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { return transactionContext(path).readData(path); } - @Override public CheckedFuture exists( - YangInstanceIdentifier path) { + @Override + public CheckedFuture exists(YangInstanceIdentifier path) { + + Preconditions.checkState(transactionType != TransactionType.WRITE_ONLY, + "Exists operation on write-only transaction is not allowed"); + LOG.debug("txn {} exists {}", identifier, path); createTransactionIfMissing(actorContext, path); @@ -124,9 +130,18 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { return transactionContext(path).dataExists(path); } + private void checkModificationState() { + Preconditions.checkState(transactionType != TransactionType.READ_ONLY, + "Modification operation on read-only transaction is not allowed"); + Preconditions.checkState(!inReadyState, + "Transaction is sealed - further modifications are allowed"); + } + @Override public void write(YangInstanceIdentifier path, NormalizedNode data) { + checkModificationState(); + LOG.debug("txn {} write {}", identifier, path); createTransactionIfMissing(actorContext, path); @@ -137,6 +152,8 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { @Override public void merge(YangInstanceIdentifier path, NormalizedNode data) { + checkModificationState(); + LOG.debug("txn {} merge {}", identifier, path); createTransactionIfMissing(actorContext, path); @@ -147,6 +164,8 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { @Override public void delete(YangInstanceIdentifier path) { + checkModificationState(); + LOG.debug("txn {} delete {}", identifier, path); createTransactionIfMissing(actorContext, path); @@ -156,25 +175,36 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { @Override public DOMStoreThreePhaseCommitCohort ready() { + + checkModificationState(); + + inReadyState = true; + List cohortPaths = new ArrayList<>(); - LOG.debug("txn {} Trying to get {} transactions ready for commit", identifier, remoteTransactionPaths.size()); + LOG.debug("txn {} Trying to get {} transactions ready for commit", identifier, + remoteTransactionPaths.size()); for(TransactionContext transactionContext : remoteTransactionPaths.values()) { - LOG.debug("txn {} Readying transaction for shard {}", identifier, transactionContext.getShardName()); + LOG.debug("txn {} Readying transaction for shard {}", identifier, + transactionContext.getShardName()); Object result = transactionContext.readyTransaction(); if(result.getClass().equals(ReadyTransactionReply.SERIALIZABLE_CLASS)){ - ReadyTransactionReply reply = ReadyTransactionReply.fromSerializable(actorContext.getActorSystem(),result); - String resolvedCohortPath = transactionContext - .getResolvedCohortPath(reply.getCohortPath().toString()); + ReadyTransactionReply reply = ReadyTransactionReply.fromSerializable( + actorContext.getActorSystem(),result); + String resolvedCohortPath = transactionContext.getResolvedCohortPath( + reply.getCohortPath().toString()); cohortPaths.add(actorContext.actorFor(resolvedCohortPath)); + } else { + LOG.error("Was expecting {} but got {}", ReadyTransactionReply.SERIALIZABLE_CLASS, + result.getClass()); } } - return new ThreePhaseCommitCohortProxy(actorContext, cohortPaths, identifier.toString(), executor); + return new ThreePhaseCommitCohortProxy(actorContext, cohortPaths, identifier.toString()); } @Override @@ -213,8 +243,7 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { Object response = actorContext.executeShardOperation(shardName, new CreateTransaction(identifier.toString(),this.transactionType.ordinal() ).toSerializable(), ActorContext.ASK_DURATION); - if (response.getClass() - .equals(CreateTransactionReply.SERIALIZABLE_CLASS)) { + if (response.getClass().equals(CreateTransactionReply.SERIALIZABLE_CLASS)) { CreateTransactionReply reply = CreateTransactionReply.fromSerializable(response); @@ -229,11 +258,13 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { transactionActor); remoteTransactionPaths.put(shardName, transactionContext); + } else { + LOG.error("Was expecting {} but got {}", CreateTransactionReply.SERIALIZABLE_CLASS, + response.getClass()); } - } catch(TimeoutException | PrimaryNotFoundException e){ + } catch(Exception e){ LOG.error("txn {} Creating NoOpTransaction because of : {}", identifier, e.getMessage()); - remoteTransactionPaths.put(shardName, - new NoOpTransactionContext(shardName)); + remoteTransactionPaths.put(shardName, new NoOpTransactionContext(shardName, e)); } } @@ -272,7 +303,8 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { this.actor = actor; } - @Override public String getShardName() { + @Override + public String getShardName() { return shardName; } @@ -280,96 +312,105 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { return actor; } - @Override public String getResolvedCohortPath(String cohortPath) { + @Override + public String getResolvedCohortPath(String cohortPath) { return actorContext.resolvePath(actorPath, cohortPath); } - @Override public void closeTransaction() { - getActor().tell( - new CloseTransaction().toSerializable(), null); + @Override + public void closeTransaction() { + actorContext.sendRemoteOperationAsync(getActor(), new CloseTransaction().toSerializable()); } - @Override public Object readyTransaction() { + @Override + public Object readyTransaction() { return actorContext.executeRemoteOperation(getActor(), - new ReadyTransaction().toSerializable(), - ActorContext.ASK_DURATION - ); - + new ReadyTransaction().toSerializable(), ActorContext.ASK_DURATION); } - @Override public void deleteData(YangInstanceIdentifier path) { - getActor().tell(new DeleteData(path).toSerializable(), null); + @Override + public void deleteData(YangInstanceIdentifier path) { + actorContext.sendRemoteOperationAsync(getActor(), new DeleteData(path).toSerializable() ); } - @Override public void mergeData(YangInstanceIdentifier path, - NormalizedNode data) { - getActor() - .tell(new MergeData(path, data, schemaContext).toSerializable(), - null); + @Override + public void mergeData(YangInstanceIdentifier path, NormalizedNode data) { + actorContext.sendRemoteOperationAsync(getActor(), + new MergeData(path, data, schemaContext).toSerializable()); } @Override public CheckedFuture>, ReadFailedException> readData( final YangInstanceIdentifier path) { - Callable>> call = - new Callable>>() { - - @Override public Optional> call() - throws Exception { - Object response = actorContext - .executeRemoteOperation(getActor(), - new ReadData(path).toSerializable(), - ActorContext.ASK_DURATION); - if (response.getClass() - .equals(ReadDataReply.SERIALIZABLE_CLASS)) { - ReadDataReply reply = ReadDataReply - .fromSerializable(schemaContext, path, - response); + final SettableFuture>> returnFuture = SettableFuture.create(); + + OnComplete onComplete = new OnComplete() { + @Override + public void onComplete(Throwable failure, Object response) throws Throwable { + if(failure != null) { + returnFuture.setException(new ReadFailedException( + "Error reading data for path " + path, failure)); + } else { + if (response.getClass().equals(ReadDataReply.SERIALIZABLE_CLASS)) { + ReadDataReply reply = ReadDataReply.fromSerializable(schemaContext, + path, response); if (reply.getNormalizedNode() == null) { - return Optional.absent(); + returnFuture.set(Optional.>absent()); + } else { + returnFuture.set(Optional.>of( + reply.getNormalizedNode())); } - return Optional.>of( - reply.getNormalizedNode()); + } else { + returnFuture.setException(new ReadFailedException( + "Invalid response reading data for path " + path)); } - - throw new ReadFailedException("Read Failed " + path); } - }; + } + }; - return MappingCheckedFuture - .create(executor.submit(call), ReadFailedException.MAPPER); - } + Future future = actorContext.executeRemoteOperationAsync(getActor(), + new ReadData(path).toSerializable(), ActorContext.ASK_DURATION); + future.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); - @Override public void writeData(YangInstanceIdentifier path, - NormalizedNode data) { - getActor() - .tell(new WriteData(path, data, schemaContext).toSerializable(), - null); + return MappingCheckedFuture.create(returnFuture, ReadFailedException.MAPPER); } - @Override public CheckedFuture dataExists( - final YangInstanceIdentifier path) { - - Callable call = new Callable() { - - @Override public Boolean call() throws Exception { - Object o = actorContext.executeRemoteOperation(getActor(), - new DataExists(path).toSerializable(), - ActorContext.ASK_DURATION - ); - + @Override + public void writeData(YangInstanceIdentifier path, NormalizedNode data) { + actorContext.sendRemoteOperationAsync(getActor(), + new WriteData(path, data, schemaContext).toSerializable()); + } - if (DataExistsReply.SERIALIZABLE_CLASS - .equals(o.getClass())) { - return DataExistsReply.fromSerializable(o).exists(); + @Override + public CheckedFuture dataExists( + final YangInstanceIdentifier path) { + + final SettableFuture returnFuture = SettableFuture.create(); + + OnComplete onComplete = new OnComplete() { + @Override + public void onComplete(Throwable failure, Object response) throws Throwable { + if(failure != null) { + returnFuture.setException(new ReadFailedException( + "Error checking exists for path " + path, failure)); + } else { + if (response.getClass().equals(DataExistsReply.SERIALIZABLE_CLASS)) { + returnFuture.set(Boolean.valueOf(DataExistsReply. + fromSerializable(response).exists())); + } else { + returnFuture.setException(new ReadFailedException( + "Invalid response checking exists for path " + path)); + } } - - throw new ReadFailedException("Exists Failed " + path); } }; - return MappingCheckedFuture - .create(executor.submit(call), ReadFailedException.MAPPER); + + Future future = actorContext.executeRemoteOperationAsync(getActor(), + new DataExists(path).toSerializable(), ActorContext.ASK_DURATION); + future.onComplete(onComplete, actorContext.getActorSystem().dispatcher()); + + return MappingCheckedFuture.create(returnFuture, ReadFailedException.MAPPER); } } @@ -379,22 +420,28 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { LOG = LoggerFactory.getLogger(NoOpTransactionContext.class); private final String shardName; + private final Exception failure; private ActorRef cohort; - public NoOpTransactionContext(String shardName){ + public NoOpTransactionContext(String shardName, Exception failure){ this.shardName = shardName; + this.failure = failure; } - @Override public String getShardName() { + + @Override + public String getShardName() { return shardName; } - @Override public String getResolvedCohortPath(String cohortPath) { + @Override + public String getResolvedCohortPath(String cohortPath) { return cohort.path().toString(); } - @Override public void closeTransaction() { + @Override + public void closeTransaction() { LOG.warn("txn {} closeTransaction called", identifier); } @@ -404,11 +451,13 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { return new ReadyTransactionReply(cohort.path()).toSerializable(); } - @Override public void deleteData(YangInstanceIdentifier path) { + @Override + public void deleteData(YangInstanceIdentifier path) { LOG.warn("txt {} deleteData called path = {}", identifier, path); } - @Override public void mergeData(YangInstanceIdentifier path, + @Override + public void mergeData(YangInstanceIdentifier path, NormalizedNode data) { LOG.warn("txn {} mergeData called path = {}", identifier, path); } @@ -417,8 +466,8 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { public CheckedFuture>, ReadFailedException> readData( YangInstanceIdentifier path) { LOG.warn("txn {} readData called path = {}", identifier, path); - return Futures.immediateCheckedFuture( - Optional.>absent()); + return Futures.immediateFailedCheckedFuture(new ReadFailedException( + "Error reading data for path " + path, failure)); } @Override public void writeData(YangInstanceIdentifier path, @@ -429,10 +478,8 @@ public class TransactionProxy implements DOMStoreReadWriteTransaction { @Override public CheckedFuture dataExists( YangInstanceIdentifier path) { LOG.warn("txn {} dataExists called path = {}", identifier, path); - - // Returning false instead of an exception to keep this aligned with - // read - return Futures.immediateCheckedFuture(false); + return Futures.immediateFailedCheckedFuture(new ReadFailedException( + "Error checking exists for path " + path, failure)); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteData.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteData.java index 17861a5a68..9ae851e76c 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteData.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DeleteData.java @@ -31,7 +31,7 @@ public class DeleteData implements SerializableMessage { .setInstanceIdentifierPathArguments(InstanceIdentifierUtils.toSerializable(path)).build(); } - public static DeleteData fromSerizalizable(Object serializable){ + public static DeleteData fromSerializable(Object serializable){ ShardTransactionMessages.DeleteData o = (ShardTransactionMessages.DeleteData) serializable; return new DeleteData(InstanceIdentifierUtils.fromSerializable(o.getInstanceIdentifierPathArguments())); } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java index 4706c66e25..e12a9663d1 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/utils/ActorContext.java @@ -14,6 +14,7 @@ import akka.actor.ActorSelection; import akka.actor.ActorSystem; import akka.actor.PoisonPill; import akka.util.Timeout; + import org.opendaylight.controller.cluster.datastore.ClusterWrapper; import org.opendaylight.controller.cluster.datastore.Configuration; import org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundException; @@ -22,9 +23,9 @@ import org.opendaylight.controller.cluster.datastore.messages.FindLocalShard; import org.opendaylight.controller.cluster.datastore.messages.FindPrimary; import org.opendaylight.controller.cluster.datastore.messages.LocalShardFound; import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.duration.Duration; @@ -54,8 +55,6 @@ public class ActorContext { private final ClusterWrapper clusterWrapper; private final Configuration configuration; - private SchemaContext schemaContext = null; - public ActorContext(ActorSystem actorSystem, ActorRef shardManager, ClusterWrapper clusterWrapper, Configuration configuration) { @@ -174,6 +173,33 @@ public class ActorContext { } } + /** + * Execute an operation on a remote actor asynchronously. + * + * @param actor the ActorSelection + * @param message the message to send + * @param duration the maximum amount of time to send he message + * @return a Future containing the eventual result + */ + public Future executeRemoteOperationAsync(ActorSelection actor, Object message, + FiniteDuration duration) { + + LOG.debug("Sending remote message {} to {}", message.getClass().toString(), actor.toString()); + + return ask(actor, message, new Timeout(duration)); + } + + /** + * Sends an operation to be executed by a remote actor asynchronously without waiting for a + * reply (essentially set and forget). + * + * @param actor the ActorSelection + * @param message the message to send + */ + public void sendRemoteOperationAsync(ActorSelection actor, Object message) { + actor.tell(message, ActorRef.noSender()); + } + /** * Execute an operation on the primary for a given shard *

diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java index 592bc49d9e..ce31c3ad57 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedConfigDataStoreProviderModule.java @@ -27,9 +27,9 @@ public class DistributedConfigDataStoreProviderModule extends @Override public java.lang.AutoCloseable createInstance() { return DistributedDataStoreFactory.createInstance("config", getConfigSchemaServiceDependency(), - InMemoryDOMDataStoreConfigProperties.create(getMaxShardDataChangeExecutorPoolSize(), - getMaxShardDataChangeExecutorQueueSize(), - getMaxShardDataChangeListenerQueueSize())); + InMemoryDOMDataStoreConfigProperties.create(getConfigMaxShardDataChangeExecutorPoolSize(), + getConfigMaxShardDataChangeExecutorQueueSize(), + getConfigMaxShardDataChangeListenerQueueSize())); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java index 9eb72d64d0..4d5b07420f 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/config/yang/config/distributed_datastore_provider/DistributedOperationalDataStoreProviderModule.java @@ -28,9 +28,9 @@ public class DistributedOperationalDataStoreProviderModule extends public java.lang.AutoCloseable createInstance() { return DistributedDataStoreFactory.createInstance("operational", getOperationalSchemaServiceDependency(), - InMemoryDOMDataStoreConfigProperties.create(getMaxShardDataChangeExecutorPoolSize(), - getMaxShardDataChangeExecutorQueueSize(), - getMaxShardDataChangeListenerQueueSize())); + InMemoryDOMDataStoreConfigProperties.create(getOperationalMaxShardDataChangeExecutorPoolSize(), + getOperationalMaxShardDataChangeExecutorQueueSize(), + getOperationalMaxShardDataChangeListenerQueueSize())); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang b/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang index ecb823e624..6bca5ce25c 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/yang/distributed-datastore-provider.yang @@ -49,19 +49,19 @@ module distributed-datastore-provider { } } - leaf max-shard-data-change-executor-queue-size { + leaf config-max-shard-data-change-executor-queue-size { default 1000; type uint16; description "The maximum queue size for each shard's data store data change notification executor."; } - leaf max-shard-data-change-executor-pool-size { + leaf config-max-shard-data-change-executor-pool-size { default 20; type uint16; description "The maximum thread pool size for each shard's data store data change notification executor."; } - leaf max-shard-data-change-listener-queue-size { + leaf config-max-shard-data-change-listener-queue-size { default 1000; type uint16; description "The maximum queue size for each shard's data store data change listeners."; @@ -82,19 +82,19 @@ module distributed-datastore-provider { } } - leaf max-shard-data-change-executor-queue-size { + leaf operational-max-shard-data-change-executor-queue-size { default 1000; type uint16; description "The maximum queue size for each shard's data store data change notification executor."; } - leaf max-shard-data-change-executor-pool-size { + leaf operational-max-shard-data-change-executor-pool-size { default 20; type uint16; description "The maximum thread pool size for each shard's data store data change notification executor."; } - leaf max-shard-data-change-listener-queue-size { + leaf operational-max-shard-data-change-listener-queue-size { default 1000; type uint16; description "The maximum queue size for each shard's data store data change listeners."; diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionFailureTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionFailureTest.java index 2c23afca12..16b73040a5 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionFailureTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ShardTransactionFailureTest.java @@ -33,6 +33,7 @@ import static org.junit.Assert.assertTrue; /** * Covers negative test cases + * * @author Basheeruddin Ahmed */ public class ShardTransactionFailureTest extends AbstractActorTest { @@ -48,7 +49,7 @@ public class ShardTransactionFailureTest extends AbstractActorTest { private static final ShardIdentifier SHARD_IDENTIFIER = ShardIdentifier.builder().memberName("member-1") - .shardName("inventory").type("config").build(); + .shardName("inventory").type("operational").build(); static { store.onGlobalContextUpdated(testSchemaContext); @@ -95,7 +96,7 @@ public class ShardTransactionFailureTest extends AbstractActorTest { throws Throwable { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null)); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext()); @@ -129,7 +130,7 @@ public class ShardTransactionFailureTest extends AbstractActorTest { throws Throwable { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null)); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext()); @@ -164,7 +165,7 @@ public class ShardTransactionFailureTest extends AbstractActorTest { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null)); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null)); final Props props = ShardTransaction.props(store.newWriteOnlyTransaction(), shard, TestModel.createTestContext()); @@ -203,7 +204,7 @@ public class ShardTransactionFailureTest extends AbstractActorTest { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null)); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext()); @@ -241,7 +242,7 @@ public class ShardTransactionFailureTest extends AbstractActorTest { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null)); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext()); @@ -279,7 +280,7 @@ public class ShardTransactionFailureTest extends AbstractActorTest { final ActorRef shard = - getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null)); + getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null)); final Props props = ShardTransaction.props(store.newReadWriteTransaction(), shard, TestModel.createTestContext()); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortFailureTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortFailureTest.java new file mode 100644 index 0000000000..34697977a5 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortFailureTest.java @@ -0,0 +1,232 @@ +/* + * + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + * + */ + +package org.opendaylight.controller.cluster.datastore; + +import akka.actor.ActorRef; +import akka.actor.Props; +import akka.testkit.TestActorRef; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; +import org.opendaylight.controller.cluster.datastore.messages.ForwardedCommitTransaction; +import org.opendaylight.controller.cluster.datastore.modification.CompositeModification; +import org.opendaylight.controller.cluster.datastore.modification.Modification; +import org.opendaylight.controller.md.cluster.datastore.model.TestModel; +import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore; +import org.opendaylight.controller.protobuff.messages.cohort3pc.ThreePhaseCommitCohortMessages; +import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages; +import org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages; +import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import scala.concurrent.Await; +import scala.concurrent.Future; +import scala.concurrent.duration.Duration; +import scala.concurrent.duration.FiniteDuration; + +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + + +public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest { + + private static ListeningExecutorService storeExecutor = + MoreExecutors.listeningDecorator(MoreExecutors.sameThreadExecutor()); + + private static final InMemoryDOMDataStore store = + new InMemoryDOMDataStore("OPER", storeExecutor, + MoreExecutors.sameThreadExecutor()); + + private static final SchemaContext testSchemaContext = + TestModel.createTestContext(); + + private static final ShardIdentifier SHARD_IDENTIFIER = + ShardIdentifier.builder().memberName("member-1") + .shardName("inventory").type("config").build(); + + static { + store.onGlobalContextUpdated(testSchemaContext); + } + + private FiniteDuration ASK_RESULT_DURATION = Duration.create(5000, TimeUnit.MILLISECONDS); + + + @Test(expected = TestException.class) + public void testNegativeAbortResultsInException() throws Exception { + + final ActorRef shard = + getSystem() + .actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null)); + final DOMStoreThreePhaseCommitCohort mockCohort = Mockito + .mock(DOMStoreThreePhaseCommitCohort.class); + final CompositeModification mockComposite = + Mockito.mock(CompositeModification.class); + final Props props = + ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite); + + final TestActorRef subject = TestActorRef + .create(getSystem(), props, + "testNegativeAbortResultsInException"); + + when(mockCohort.abort()).thenReturn( + Futures.immediateFailedFuture(new TestException())); + + Future future = + akka.pattern.Patterns.ask(subject, + ThreePhaseCommitCohortMessages.AbortTransaction.newBuilder() + .build(), 3000); + assertTrue(future.isCompleted()); + + Await.result(future, ASK_RESULT_DURATION); + + + + } + + + @Test(expected = OptimisticLockFailedException.class) + public void testNegativeCanCommitResultsInException() throws Exception { + + final ActorRef shard = + getSystem() + .actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null)); + final DOMStoreThreePhaseCommitCohort mockCohort = Mockito + .mock(DOMStoreThreePhaseCommitCohort.class); + final CompositeModification mockComposite = + Mockito.mock(CompositeModification.class); + final Props props = + ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite); + + final TestActorRef subject = TestActorRef + .create(getSystem(), props, + "testNegativeCanCommitResultsInException"); + + when(mockCohort.canCommit()).thenReturn( + Futures + .immediateFailedFuture( + new OptimisticLockFailedException("some exception"))); + + Future future = + akka.pattern.Patterns.ask(subject, + ThreePhaseCommitCohortMessages.CanCommitTransaction.newBuilder() + .build(), 3000); + + + Await.result(future, ASK_RESULT_DURATION); + + } + + + @Test(expected = TestException.class) + public void testNegativePreCommitResultsInException() throws Exception { + + final ActorRef shard = + getSystem() + .actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null)); + final DOMStoreThreePhaseCommitCohort mockCohort = Mockito + .mock(DOMStoreThreePhaseCommitCohort.class); + final CompositeModification mockComposite = + Mockito.mock(CompositeModification.class); + final Props props = + ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite); + + final TestActorRef subject = TestActorRef + .create(getSystem(), props, + "testNegativePreCommitResultsInException"); + + when(mockCohort.preCommit()).thenReturn( + Futures + .immediateFailedFuture( + new TestException())); + + Future future = + akka.pattern.Patterns.ask(subject, + ThreePhaseCommitCohortMessages.PreCommitTransaction.newBuilder() + .build(), 3000); + + Await.result(future, ASK_RESULT_DURATION); + + } + + @Test(expected = TestException.class) + public void testNegativeCommitResultsInException() throws Exception { + + final TestActorRef subject = TestActorRef + .create(getSystem(), + Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null), + "testNegativeCommitResultsInException"); + + final ActorRef shardTransaction = + getSystem().actorOf( + ShardTransaction.props(store.newReadWriteTransaction(), subject, + TestModel.createTestContext())); + + ShardTransactionMessages.WriteData writeData = + ShardTransactionMessages.WriteData.newBuilder() + .setInstanceIdentifierPathArguments( + NormalizedNodeMessages.InstanceIdentifier.newBuilder() + .build()).setNormalizedNode( + NormalizedNodeMessages.Node.newBuilder().build() + + ).build(); + + //This is done so that Modification list is updated which is used during commit + Future future = + akka.pattern.Patterns.ask(shardTransaction, writeData, 3000); + + //ready transaction creates the cohort so that we get into the + //block where in commmit is done + ShardTransactionMessages.ReadyTransaction readyTransaction = + ShardTransactionMessages.ReadyTransaction.newBuilder().build(); + + future = + akka.pattern.Patterns.ask(shardTransaction, readyTransaction, 3000); + + //but when the message is sent it will have the MockCommit object + //so that we can simulate throwing of exception + ForwardedCommitTransaction mockForwardCommitTransaction = + Mockito.mock(ForwardedCommitTransaction.class); + DOMStoreThreePhaseCommitCohort mockThreePhaseCommitTransaction = + Mockito.mock(DOMStoreThreePhaseCommitCohort.class); + when(mockForwardCommitTransaction.getCohort()) + .thenReturn(mockThreePhaseCommitTransaction); + when(mockThreePhaseCommitTransaction.commit()).thenReturn(Futures + .immediateFailedFuture( + new TestException())); + Modification mockModification = Mockito.mock( + Modification.class); + when(mockForwardCommitTransaction.getModification()) + .thenReturn(mockModification); + + when(mockModification.toSerializable()).thenReturn( + PersistentMessages.CompositeModification.newBuilder().build()); + + future = + akka.pattern.Patterns.ask(subject, + mockForwardCommitTransaction + , 3000); + Await.result(future, ASK_RESULT_DURATION); + + + } + + private class TestException extends Exception { + } + + +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java index 4eca5671f6..87231f0884 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/ThreePhaseCommitCohortProxyTest.java @@ -1,96 +1,245 @@ package org.opendaylight.controller.cluster.datastore; -import akka.actor.ActorRef; +import akka.actor.ActorPath; +import akka.actor.ActorSelection; import akka.actor.Props; +import akka.dispatch.Futures; +import com.google.common.collect.Lists; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; - -import junit.framework.Assert; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; -import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Stubber; +import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction; import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply; +import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransaction; import org.opendaylight.controller.cluster.datastore.messages.CanCommitTransactionReply; +import org.opendaylight.controller.cluster.datastore.messages.CommitTransaction; import org.opendaylight.controller.cluster.datastore.messages.CommitTransactionReply; +import org.opendaylight.controller.cluster.datastore.messages.PreCommitTransaction; import org.opendaylight.controller.cluster.datastore.messages.PreCommitTransactionReply; +import org.opendaylight.controller.cluster.datastore.messages.SerializableMessage; +import org.opendaylight.controller.cluster.datastore.utils.ActorContext; import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor; -import org.opendaylight.controller.cluster.datastore.utils.MockActorContext; +import scala.concurrent.duration.FiniteDuration; -import java.util.Arrays; -import java.util.concurrent.Executors; - -import static org.junit.Assert.assertNotNull; +import java.util.List; +import java.util.concurrent.ExecutionException; public class ThreePhaseCommitCohortProxyTest extends AbstractActorTest { - private ThreePhaseCommitCohortProxy proxy; - private Props props; - private ActorRef actorRef; - private MockActorContext actorContext; - private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( - Executors.newSingleThreadExecutor()); + @Mock + private ActorContext actorContext; @Before - public void setUp(){ - props = Props.create(MessageCollectorActor.class); - actorRef = getSystem().actorOf(props); - actorContext = new MockActorContext(this.getSystem()); + public void setUp() { + MockitoAnnotations.initMocks(this); - proxy = - new ThreePhaseCommitCohortProxy(actorContext, - Arrays.asList(actorRef.path()), "txn-1", executor); + doReturn(getSystem()).when(actorContext).getActorSystem(); + } + private ThreePhaseCommitCohortProxy setupProxy(int nCohorts) { + List cohorts = Lists.newArrayList(); + for(int i = 1; i <= nCohorts; i++) { + ActorPath path = getSystem().actorOf(Props.create(MessageCollectorActor.class)).path(); + cohorts.add(path); + doReturn(mock(ActorSelection.class)).when(actorContext).actorSelection(path); + } + + return new ThreePhaseCommitCohortProxy(actorContext, cohorts, "txn-1"); } - @After - public void tearDown() { - executor.shutdownNow(); + private void setupMockActorContext(Class requestType, Object... responses) { + Stubber stubber = doReturn(responses[0] instanceof Throwable ? Futures + .failed((Throwable) responses[0]) : Futures + .successful(((SerializableMessage) responses[0]).toSerializable())); + + for(int i = 1; i < responses.length; i++) { + stubber = stubber.doReturn(responses[i] instanceof Throwable ? Futures + .failed((Throwable) responses[i]) : Futures + .successful(((SerializableMessage) responses[i]).toSerializable())); + } + + stubber.when(actorContext).executeRemoteOperationAsync(any(ActorSelection.class), + isA(requestType), any(FiniteDuration.class)); + } + + private void verifyCohortInvocations(int nCohorts, Class requestType) { + verify(actorContext, times(nCohorts)).executeRemoteOperationAsync( + any(ActorSelection.class), isA(requestType), any(FiniteDuration.class)); + } + + @Test + public void testCanCommitWithOneCohort() throws Exception { + + ThreePhaseCommitCohortProxy proxy = setupProxy(1); + + setupMockActorContext(CanCommitTransaction.SERIALIZABLE_CLASS, + new CanCommitTransactionReply(true)); + + ListenableFuture future = proxy.canCommit(); + + assertEquals("canCommit", true, future.get()); + + setupMockActorContext(CanCommitTransaction.SERIALIZABLE_CLASS, + new CanCommitTransactionReply(false)); + + future = proxy.canCommit(); + + assertEquals("canCommit", false, future.get()); + + verifyCohortInvocations(2, CanCommitTransaction.SERIALIZABLE_CLASS); } @Test - public void testCanCommit() throws Exception { - actorContext.setExecuteRemoteOperationResponse(new CanCommitTransactionReply(true).toSerializable()); + public void testCanCommitWithMultipleCohorts() throws Exception { + + ThreePhaseCommitCohortProxy proxy = setupProxy(2); + + setupMockActorContext(CanCommitTransaction.SERIALIZABLE_CLASS, + new CanCommitTransactionReply(true), new CanCommitTransactionReply(true)); ListenableFuture future = proxy.canCommit(); - Assert.assertTrue(future.get().booleanValue()); + assertEquals("canCommit", true, future.get()); + verifyCohortInvocations(2, CanCommitTransaction.SERIALIZABLE_CLASS); + } + + @Test + public void testCanCommitWithMultipleCohortsAndOneFailure() throws Exception { + + ThreePhaseCommitCohortProxy proxy = setupProxy(3); + + setupMockActorContext(CanCommitTransaction.SERIALIZABLE_CLASS, + new CanCommitTransactionReply(true), new CanCommitTransactionReply(false), + new CanCommitTransactionReply(true)); + + ListenableFuture future = proxy.canCommit(); + + assertEquals("canCommit", false, future.get()); + + verifyCohortInvocations(3, CanCommitTransaction.SERIALIZABLE_CLASS); + } + + @Test(expected = ExecutionException.class) + public void testCanCommitWithExceptionFailure() throws Exception { + + ThreePhaseCommitCohortProxy proxy = setupProxy(1); + + setupMockActorContext(CanCommitTransaction.SERIALIZABLE_CLASS, new RuntimeException("mock")); + + proxy.canCommit().get(); + } + + @Test(expected = ExecutionException.class) + public void testCanCommitWithInvalidResponseType() throws Exception { + + ThreePhaseCommitCohortProxy proxy = setupProxy(1); + + setupMockActorContext(CanCommitTransaction.SERIALIZABLE_CLASS, + new PreCommitTransactionReply()); + + proxy.canCommit().get(); } @Test public void testPreCommit() throws Exception { - actorContext.setExecuteRemoteOperationResponse(new PreCommitTransactionReply().toSerializable()); + ThreePhaseCommitCohortProxy proxy = setupProxy(1); - ListenableFuture future = proxy.preCommit(); + setupMockActorContext(PreCommitTransaction.SERIALIZABLE_CLASS, + new PreCommitTransactionReply()); - future.get(); + proxy.preCommit().get(); + verifyCohortInvocations(1, PreCommitTransaction.SERIALIZABLE_CLASS); + } + + @Test(expected = ExecutionException.class) + public void testPreCommitWithFailure() throws Exception { + ThreePhaseCommitCohortProxy proxy = setupProxy(2); + + setupMockActorContext(PreCommitTransaction.SERIALIZABLE_CLASS, + new PreCommitTransactionReply(), new RuntimeException("mock")); + + proxy.preCommit().get(); } @Test public void testAbort() throws Exception { - actorContext.setExecuteRemoteOperationResponse(new AbortTransactionReply().toSerializable()); + ThreePhaseCommitCohortProxy proxy = setupProxy(1); - ListenableFuture future = proxy.abort(); + setupMockActorContext(AbortTransaction.SERIALIZABLE_CLASS, new AbortTransactionReply()); - future.get(); + proxy.abort().get(); + verifyCohortInvocations(1, AbortTransaction.SERIALIZABLE_CLASS); + } + + @Test + public void testAbortWithFailure() throws Exception { + ThreePhaseCommitCohortProxy proxy = setupProxy(1); + + setupMockActorContext(AbortTransaction.SERIALIZABLE_CLASS, new RuntimeException("mock")); + + // The exception should not get propagated. + proxy.abort().get(); + + verifyCohortInvocations(1, AbortTransaction.SERIALIZABLE_CLASS); } @Test public void testCommit() throws Exception { - actorContext.setExecuteRemoteOperationResponse(new CommitTransactionReply().toSerializable()); - ListenableFuture future = proxy.commit(); + ThreePhaseCommitCohortProxy proxy = setupProxy(2); + + setupMockActorContext(CommitTransaction.SERIALIZABLE_CLASS, new CommitTransactionReply(), + new CommitTransactionReply()); + + proxy.commit().get(); + + verifyCohortInvocations(2, CommitTransaction.SERIALIZABLE_CLASS); + } + + @Test(expected = ExecutionException.class) + public void testCommitWithFailure() throws Exception { + + ThreePhaseCommitCohortProxy proxy = setupProxy(2); - future.get(); + setupMockActorContext(CommitTransaction.SERIALIZABLE_CLASS, new CommitTransactionReply(), + new RuntimeException("mock")); + + proxy.commit().get(); + } + + @Test(expected = ExecutionException.class) + public void teseCommitWithInvalidResponseType() throws Exception { + + ThreePhaseCommitCohortProxy proxy = setupProxy(1); + + setupMockActorContext(CommitTransaction.SERIALIZABLE_CLASS, new PreCommitTransactionReply()); + + proxy.commit().get(); } @Test - public void testGetCohortPaths() throws Exception { - assertNotNull(proxy.getCohortPaths()); + public void testGetCohortPaths() { + + ThreePhaseCommitCohortProxy proxy = setupProxy(2); + + List paths = proxy.getCohortPaths(); + assertNotNull("getCohortPaths returned null", paths); + assertEquals("getCohortPaths size", 2, paths.size()); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxyTest.java new file mode 100644 index 0000000000..9b7039764f --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionChainProxyTest.java @@ -0,0 +1,52 @@ +/* + * + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + * + */ + +package org.opendaylight.controller.cluster.datastore; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.cluster.datastore.utils.ActorContext; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction; +import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class TransactionChainProxyTest { + ActorContext actorContext = Mockito.mock(ActorContext.class); + SchemaContext schemaContext = Mockito.mock(SchemaContext.class); + @Test + public void testNewReadOnlyTransaction() throws Exception { + + DOMStoreTransaction dst = new TransactionChainProxy(actorContext, schemaContext).newReadOnlyTransaction(); + Assert.assertTrue(dst instanceof DOMStoreReadTransaction); + + } + + @Test + public void testNewReadWriteTransaction() throws Exception { + DOMStoreTransaction dst = new TransactionChainProxy(actorContext, schemaContext).newReadWriteTransaction(); + Assert.assertTrue(dst instanceof DOMStoreReadWriteTransaction); + + } + + @Test + public void testNewWriteOnlyTransaction() throws Exception { + DOMStoreTransaction dst = new TransactionChainProxy(actorContext, schemaContext).newWriteOnlyTransaction(); + Assert.assertTrue(dst instanceof DOMStoreWriteTransaction); + + } + + @Test(expected=UnsupportedOperationException.class) + public void testClose() throws Exception { + new TransactionChainProxy(actorContext, schemaContext).close(); + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java index 62052f38ab..14696f786e 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java @@ -1,32 +1,44 @@ package org.opendaylight.controller.cluster.datastore; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import akka.actor.ActorPath; import akka.actor.ActorRef; +import akka.actor.ActorSelection; import akka.actor.Props; +import akka.dispatch.Futures; import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import junit.framework.Assert; -import org.junit.After; + import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.opendaylight.controller.cluster.datastore.TransactionProxy.TransactionType.READ_ONLY; +import static org.opendaylight.controller.cluster.datastore.TransactionProxy.TransactionType.WRITE_ONLY; +import static org.opendaylight.controller.cluster.datastore.TransactionProxy.TransactionType.READ_WRITE; + +import org.opendaylight.controller.cluster.datastore.TransactionProxy.TransactionType; import org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundException; import org.opendaylight.controller.cluster.datastore.exceptions.TimeoutException; import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction; +import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction; +import org.opendaylight.controller.cluster.datastore.messages.DataExists; import org.opendaylight.controller.cluster.datastore.messages.DataExistsReply; import org.opendaylight.controller.cluster.datastore.messages.DeleteData; import org.opendaylight.controller.cluster.datastore.messages.MergeData; -import org.opendaylight.controller.cluster.datastore.messages.PrimaryFound; +import org.opendaylight.controller.cluster.datastore.messages.ReadData; import org.opendaylight.controller.cluster.datastore.messages.ReadDataReply; +import org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction; import org.opendaylight.controller.cluster.datastore.messages.ReadyTransactionReply; import org.opendaylight.controller.cluster.datastore.messages.WriteData; +import org.opendaylight.controller.cluster.datastore.shardstrategy.DefaultShardStrategy; import org.opendaylight.controller.cluster.datastore.shardstrategy.ShardStrategyFactory; import org.opendaylight.controller.cluster.datastore.utils.ActorContext; import org.opendaylight.controller.cluster.datastore.utils.DoNothingActor; -import org.opendaylight.controller.cluster.datastore.utils.MessageCollectorActor; -import org.opendaylight.controller.cluster.datastore.utils.MockActorContext; -import org.opendaylight.controller.cluster.datastore.utils.MockClusterWrapper; import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration; import org.opendaylight.controller.md.cluster.datastore.model.TestModel; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; @@ -34,377 +46,433 @@ import org.opendaylight.controller.protobuff.messages.transaction.ShardTransacti import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; 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; + +import scala.concurrent.Future; import scala.concurrent.duration.FiniteDuration; -import java.util.List; -import java.util.concurrent.Executors; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; -import static junit.framework.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.argThat; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.isA; + +@SuppressWarnings("resource") public class TransactionProxyTest extends AbstractActorTest { + @SuppressWarnings("serial") + static class TestException extends RuntimeException { + } + + static interface Invoker { + void invoke(TransactionProxy proxy) throws Exception; + } + private final Configuration configuration = new MockConfiguration(); - private final ActorContext testContext = - new ActorContext(getSystem(), getSystem().actorOf(Props.create(DoNothingActor.class)), new MockClusterWrapper(), configuration ); + @Mock + private ActorContext mockActorContext; - private final ListeningExecutorService transactionExecutor = - MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + private SchemaContext schemaContext; + + String memberName = "mock-member"; @Before public void setUp(){ - ShardStrategyFactory.setConfiguration(configuration); - } + MockitoAnnotations.initMocks(this); - @After - public void tearDown() { - transactionExecutor.shutdownNow(); - } + schemaContext = TestModel.createTestContext(); - @Test - public void testRead() throws Exception { - final Props props = Props.create(DoNothingActor.class); - final ActorRef actorRef = getSystem().actorOf(props); + doReturn(getSystem()).when(mockActorContext).getActorSystem(); - final MockActorContext actorContext = new MockActorContext(this.getSystem()); - actorContext.setExecuteLocalOperationResponse(createPrimaryFound(actorRef)); - actorContext.setExecuteShardOperationResponse(createTransactionReply(actorRef)); - actorContext.setExecuteRemoteOperationResponse("message"); + ShardStrategyFactory.setConfiguration(configuration); + } + private CreateTransaction eqCreateTransaction(final String memberName, + final TransactionType type) { + ArgumentMatcher matcher = new ArgumentMatcher() { + @Override + public boolean matches(Object argument) { + CreateTransaction obj = CreateTransaction.fromSerializable(argument); + return obj.getTransactionId().startsWith(memberName) && + obj.getTransactionType() == type.ordinal(); + } + }; + + return argThat(matcher); + } - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); + private DataExists eqDataExists() { + ArgumentMatcher matcher = new ArgumentMatcher() { + @Override + public boolean matches(Object argument) { + DataExists obj = DataExists.fromSerializable(argument); + return obj.getPath().equals(TestModel.TEST_PATH); + } + }; + return argThat(matcher); + } - actorContext.setExecuteRemoteOperationResponse( - new ReadDataReply(TestModel.createTestContext(), null) - .toSerializable()); + private ReadData eqReadData() { + ArgumentMatcher matcher = new ArgumentMatcher() { + @Override + public boolean matches(Object argument) { + ReadData obj = ReadData.fromSerializable(argument); + return obj.getPath().equals(TestModel.TEST_PATH); + } + }; - ListenableFuture>> read = - transactionProxy.read(TestModel.TEST_PATH); + return argThat(matcher); + } - Optional> normalizedNodeOptional = read.get(); + private WriteData eqWriteData(final NormalizedNode nodeToWrite) { + ArgumentMatcher matcher = new ArgumentMatcher() { + @Override + public boolean matches(Object argument) { + WriteData obj = WriteData.fromSerializable(argument, schemaContext); + return obj.getPath().equals(TestModel.TEST_PATH) && + obj.getData().equals(nodeToWrite); + } + }; + + return argThat(matcher); + } - Assert.assertFalse(normalizedNodeOptional.isPresent()); + private MergeData eqMergeData(final NormalizedNode nodeToWrite) { + ArgumentMatcher matcher = new ArgumentMatcher() { + @Override + public boolean matches(Object argument) { + MergeData obj = MergeData.fromSerializable(argument, schemaContext); + return obj.getPath().equals(TestModel.TEST_PATH) && + obj.getData().equals(nodeToWrite); + } + }; + + return argThat(matcher); + } - actorContext.setExecuteRemoteOperationResponse(new ReadDataReply( - TestModel.createTestContext(),ImmutableNodes.containerNode(TestModel.TEST_QNAME)).toSerializable()); + private DeleteData eqDeleteData() { + ArgumentMatcher matcher = new ArgumentMatcher() { + @Override + public boolean matches(Object argument) { + DeleteData obj = DeleteData.fromSerializable(argument); + return obj.getPath().equals(TestModel.TEST_PATH); + } + }; - read = transactionProxy.read(TestModel.TEST_PATH); + return argThat(matcher); + } - normalizedNodeOptional = read.get(); + private Object readyTxReply(ActorPath path) { + return new ReadyTransactionReply(path).toSerializable(); + } - Assert.assertTrue(normalizedNodeOptional.isPresent()); + private Future readDataReply(NormalizedNode data) { + return Futures.successful(new ReadDataReply(schemaContext, data) + .toSerializable()); } - @Test - public void testExists() throws Exception { - final Props props = Props.create(DoNothingActor.class); - final ActorRef actorRef = getSystem().actorOf(props); + private Future dataExistsReply(boolean exists) { + return Futures.successful(new DataExistsReply(exists).toSerializable()); + } - final MockActorContext actorContext = new MockActorContext(this.getSystem()); - actorContext.setExecuteLocalOperationResponse(createPrimaryFound(actorRef)); - actorContext.setExecuteShardOperationResponse(createTransactionReply(actorRef)); - actorContext.setExecuteRemoteOperationResponse("message"); + private ActorSelection actorSelection(ActorRef actorRef) { + return getSystem().actorSelection(actorRef.path()); + } + private FiniteDuration anyDuration() { + return any(FiniteDuration.class); + } - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); + private CreateTransactionReply createTransactionReply(ActorRef actorRef){ + return CreateTransactionReply.newBuilder() + .setTransactionActorPath(actorRef.path().toString()) + .setTransactionId("txn-1").build(); + } + private ActorRef setupActorContextWithInitialCreateTransaction(TransactionType type) { + ActorRef actorRef = getSystem().actorOf(Props.create(DoNothingActor.class)); + doReturn(getSystem().actorSelection(actorRef.path())). + when(mockActorContext).actorSelection(actorRef.path().toString()); + doReturn(memberName).when(mockActorContext).getCurrentMemberName(); + doReturn(createTransactionReply(actorRef)).when(mockActorContext). + executeShardOperation(eq(DefaultShardStrategy.DEFAULT_SHARD), + eqCreateTransaction(memberName, type), anyDuration()); + doReturn(actorRef.path().toString()).when(mockActorContext).resolvePath( + anyString(), eq(actorRef.path().toString())); + doReturn(actorRef.path()).when(mockActorContext).actorFor(actorRef.path().toString()); + + return actorRef; + } - actorContext.setExecuteRemoteOperationResponse(new DataExistsReply(false).toSerializable()); + @Test + public void testRead() throws Exception { + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_ONLY); - CheckedFuture exists = - transactionProxy.exists(TestModel.TEST_PATH); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + READ_ONLY, schemaContext); - Assert.assertFalse(exists.checkedGet()); + doReturn(readDataReply(null)).when(mockActorContext).executeRemoteOperationAsync( + eq(actorSelection(actorRef)), eqReadData(), anyDuration()); - actorContext.setExecuteRemoteOperationResponse(new DataExistsReply(true).toSerializable()); + Optional> readOptional = transactionProxy.read( + TestModel.TEST_PATH).get(5, TimeUnit.SECONDS); - exists = transactionProxy.exists(TestModel.TEST_PATH); + assertEquals("NormalizedNode isPresent", false, readOptional.isPresent()); - Assert.assertTrue(exists.checkedGet()); + NormalizedNode expectedNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - actorContext.setExecuteRemoteOperationResponse("bad message"); + doReturn(readDataReply(expectedNode)).when(mockActorContext).executeRemoteOperationAsync( + eq(actorSelection(actorRef)), eqReadData(), anyDuration()); - exists = transactionProxy.exists(TestModel.TEST_PATH); + readOptional = transactionProxy.read(TestModel.TEST_PATH).get(5, TimeUnit.SECONDS); - try { - exists.checkedGet(); - fail(); - } catch(ReadFailedException e){ - } + assertEquals("NormalizedNode isPresent", true, readOptional.isPresent()); + assertEquals("Response NormalizedNode", expectedNode, readOptional.get()); } @Test(expected = ReadFailedException.class) public void testReadWhenAnInvalidMessageIsSentInReply() throws Exception { - final Props props = Props.create(DoNothingActor.class); - final ActorRef actorRef = getSystem().actorOf(props); - - final MockActorContext actorContext = new MockActorContext(this.getSystem()); - actorContext.setExecuteLocalOperationResponse(createPrimaryFound(actorRef)); - actorContext.setExecuteShardOperationResponse(createTransactionReply(actorRef)); - actorContext.setExecuteRemoteOperationResponse("message"); - - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); + setupActorContextWithInitialCreateTransaction(READ_ONLY); + doReturn(Futures.successful(new Object())).when(mockActorContext). + executeRemoteOperationAsync(any(ActorSelection.class), any(), anyDuration()); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + READ_ONLY, schemaContext); - CheckedFuture>, ReadFailedException> - read = transactionProxy.read(TestModel.TEST_PATH); - - read.checkedGet(); + transactionProxy.read(TestModel.TEST_PATH).checkedGet(5, TimeUnit.SECONDS); } - @Test - public void testReadWhenAPrimaryNotFoundExceptionIsThrown() throws Exception { - final ActorContext actorContext = mock(ActorContext.class); - - when(actorContext.executeShardOperation(anyString(), any(), any( - FiniteDuration.class))).thenThrow(new PrimaryNotFoundException("test")); + @Test(expected = TestException.class) + public void testReadWithAsyncRemoteOperatonFailure() throws Throwable { + setupActorContextWithInitialCreateTransaction(READ_ONLY); - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); + doThrow(new TestException()).when(mockActorContext). + executeRemoteOperationAsync(any(ActorSelection.class), any(), anyDuration()); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + READ_ONLY, schemaContext); - ListenableFuture>> read = - transactionProxy.read(TestModel.TEST_PATH); - - Assert.assertFalse(read.get().isPresent()); - + try { + transactionProxy.read(TestModel.TEST_PATH).checkedGet(5, TimeUnit.SECONDS); + fail("Expected ReadFailedException"); + } catch(ReadFailedException e) { + // Expected - throw cause - expects TestException. + throw e.getCause(); + } } + private void testExceptionOnInitialCreateTransaction(Exception exToThrow, Invoker invoker) + throws Throwable { - @Test - public void testReadWhenATimeoutExceptionIsThrown() throws Exception { - final ActorContext actorContext = mock(ActorContext.class); + doThrow(exToThrow).when(mockActorContext).executeShardOperation( + anyString(), any(), anyDuration()); - when(actorContext.executeShardOperation(anyString(), any(), any( - FiniteDuration.class))).thenThrow(new TimeoutException("test", new Exception("reason"))); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + READ_ONLY, schemaContext); - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); + try { + invoker.invoke(transactionProxy); + fail("Expected ReadFailedException"); + } catch(ReadFailedException e) { + // Expected - throw cause - expects TestException. + throw e.getCause(); + } + } + private void testReadWithExceptionOnInitialCreateTransaction(Exception exToThrow) throws Throwable { + testExceptionOnInitialCreateTransaction(exToThrow, new Invoker() { + @Override + public void invoke(TransactionProxy proxy) throws Exception { + proxy.read(TestModel.TEST_PATH).checkedGet(5, TimeUnit.SECONDS); + } + }); + } - ListenableFuture>> read = - transactionProxy.read(TestModel.TEST_PATH); + @Test(expected = PrimaryNotFoundException.class) + public void testReadWhenAPrimaryNotFoundExceptionIsThrown() throws Throwable { + testReadWithExceptionOnInitialCreateTransaction(new PrimaryNotFoundException("test")); + } - Assert.assertFalse(read.get().isPresent()); + @Test(expected = TimeoutException.class) + public void testReadWhenATimeoutExceptionIsThrown() throws Throwable { + testReadWithExceptionOnInitialCreateTransaction(new TimeoutException("test", + new Exception("reason"))); + } + @Test(expected = TestException.class) + public void testReadWhenAnyOtherExceptionIsThrown() throws Throwable { + testReadWithExceptionOnInitialCreateTransaction(new TestException()); } @Test - public void testReadWhenAAnyOtherExceptionIsThrown() throws Exception { - final ActorContext actorContext = mock(ActorContext.class); - - when(actorContext.executeShardOperation(anyString(), any(), any( - FiniteDuration.class))).thenThrow(new NullPointerException()); - - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); + public void testExists() throws Exception { + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + READ_ONLY, schemaContext); - try { - ListenableFuture>> read = - transactionProxy.read(TestModel.TEST_PATH); - fail("A null pointer exception was expected"); - } catch(NullPointerException e){ + doReturn(dataExistsReply(false)).when(mockActorContext).executeRemoteOperationAsync( + eq(actorSelection(actorRef)), eqDataExists(), anyDuration()); - } - } + Boolean exists = transactionProxy.exists(TestModel.TEST_PATH).checkedGet(); + assertEquals("Exists response", false, exists); + doReturn(dataExistsReply(true)).when(mockActorContext).executeRemoteOperationAsync( + eq(actorSelection(actorRef)), eqDataExists(), anyDuration()); - @Test - public void testWrite() throws Exception { - final Props props = Props.create(MessageCollectorActor.class); - final ActorRef actorRef = getSystem().actorOf(props); + exists = transactionProxy.exists(TestModel.TEST_PATH).checkedGet(); - final MockActorContext actorContext = new MockActorContext(this.getSystem()); - actorContext.setExecuteLocalOperationResponse(createPrimaryFound(actorRef)); - actorContext.setExecuteShardOperationResponse(createTransactionReply(actorRef)); - actorContext.setExecuteRemoteOperationResponse("message"); + assertEquals("Exists response", true, exists); + } - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); + @Test(expected = PrimaryNotFoundException.class) + public void testExistsWhenAPrimaryNotFoundExceptionIsThrown() throws Throwable { + testExceptionOnInitialCreateTransaction(new PrimaryNotFoundException("test"), new Invoker() { + @Override + public void invoke(TransactionProxy proxy) throws Exception { + proxy.exists(TestModel.TEST_PATH).checkedGet(5, TimeUnit.SECONDS); + } + }); + } - transactionProxy.write(TestModel.TEST_PATH, - ImmutableNodes.containerNode(TestModel.NAME_QNAME)); + @Test(expected = ReadFailedException.class) + public void testExistsWhenAnInvalidMessageIsSentInReply() throws Exception { + setupActorContextWithInitialCreateTransaction(READ_ONLY); - Object messages = testContext - .executeLocalOperation(actorRef, "messages", - ActorContext.ASK_DURATION); + doReturn(Futures.successful(new Object())).when(mockActorContext). + executeRemoteOperationAsync(any(ActorSelection.class), any(), anyDuration()); - Assert.assertNotNull(messages); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + READ_ONLY, schemaContext); - Assert.assertTrue(messages instanceof List); + transactionProxy.exists(TestModel.TEST_PATH).checkedGet(5, TimeUnit.SECONDS); + } - List listMessages = (List) messages; + @Test(expected = TestException.class) + public void testExistsWithAsyncRemoteOperatonFailure() throws Throwable { + setupActorContextWithInitialCreateTransaction(READ_ONLY); - Assert.assertEquals(1, listMessages.size()); + doThrow(new TestException()).when(mockActorContext). + executeRemoteOperationAsync(any(ActorSelection.class), any(), anyDuration()); - Assert.assertEquals(WriteData.SERIALIZABLE_CLASS, listMessages.get(0).getClass()); - } + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + READ_ONLY, schemaContext); - private Object createPrimaryFound(ActorRef actorRef) { - return new PrimaryFound(actorRef.path().toString()).toSerializable(); + try { + transactionProxy.exists(TestModel.TEST_PATH).checkedGet(5, TimeUnit.SECONDS); + fail("Expected ReadFailedException"); + } catch(ReadFailedException e) { + // Expected - throw cause - expects TestException. + throw e.getCause(); + } } @Test - public void testMerge() throws Exception { - final Props props = Props.create(MessageCollectorActor.class); - final ActorRef actorRef = getSystem().actorOf(props); + public void testWrite() throws Exception { + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(WRITE_ONLY); - final MockActorContext actorContext = new MockActorContext(this.getSystem()); - actorContext.setExecuteLocalOperationResponse(createPrimaryFound(actorRef)); - actorContext.setExecuteShardOperationResponse(createTransactionReply(actorRef)); - actorContext.setExecuteRemoteOperationResponse("message"); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + WRITE_ONLY, schemaContext); - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); + NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - transactionProxy.merge(TestModel.TEST_PATH, - ImmutableNodes.containerNode(TestModel.NAME_QNAME)); + transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); - Object messages = testContext - .executeLocalOperation(actorRef, "messages", - ActorContext.ASK_DURATION); + verify(mockActorContext).sendRemoteOperationAsync( + eq(actorSelection(actorRef)), eqWriteData(nodeToWrite)); + } - Assert.assertNotNull(messages); + @Test + public void testMerge() throws Exception { + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(WRITE_ONLY); - Assert.assertTrue(messages instanceof List); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + WRITE_ONLY, schemaContext); - List listMessages = (List) messages; + NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); - Assert.assertEquals(1, listMessages.size()); + transactionProxy.merge(TestModel.TEST_PATH, nodeToWrite); - Assert.assertEquals(MergeData.SERIALIZABLE_CLASS, listMessages.get(0).getClass()); + verify(mockActorContext).sendRemoteOperationAsync( + eq(actorSelection(actorRef)), eqMergeData(nodeToWrite)); } @Test public void testDelete() throws Exception { - final Props props = Props.create(MessageCollectorActor.class); - final ActorRef actorRef = getSystem().actorOf(props); - - final MockActorContext actorContext = new MockActorContext(this.getSystem()); - actorContext.setExecuteLocalOperationResponse(createPrimaryFound(actorRef)); - actorContext.setExecuteShardOperationResponse(createTransactionReply(actorRef)); - actorContext.setExecuteRemoteOperationResponse("message"); + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(WRITE_ONLY); - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + WRITE_ONLY, schemaContext); transactionProxy.delete(TestModel.TEST_PATH); - Object messages = testContext - .executeLocalOperation(actorRef, "messages", - ActorContext.ASK_DURATION); - - Assert.assertNotNull(messages); - - Assert.assertTrue(messages instanceof List); - - List listMessages = (List) messages; - - Assert.assertEquals(1, listMessages.size()); - - Assert.assertEquals(DeleteData.SERIALIZABLE_CLASS, listMessages.get(0).getClass()); + verify(mockActorContext).sendRemoteOperationAsync( + eq(actorSelection(actorRef)), eqDeleteData()); } + @SuppressWarnings("unchecked") @Test public void testReady() throws Exception { - final Props props = Props.create(DoNothingActor.class); - final ActorRef doNothingActorRef = getSystem().actorOf(props); + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_WRITE); - final MockActorContext actorContext = new MockActorContext(this.getSystem()); - actorContext.setExecuteLocalOperationResponse(createPrimaryFound(doNothingActorRef)); - actorContext.setExecuteShardOperationResponse(createTransactionReply(doNothingActorRef)); - actorContext.setExecuteRemoteOperationResponse(new ReadyTransactionReply(doNothingActorRef.path()).toSerializable()); + doReturn(readDataReply(null)).when(mockActorContext).executeRemoteOperationAsync( + eq(actorSelection(actorRef)), eqReadData(), anyDuration()); - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); + doReturn(readyTxReply(actorRef.path())).when(mockActorContext).executeRemoteOperation( + eq(actorSelection(actorRef)), isA(ReadyTransaction.SERIALIZABLE_CLASS), anyDuration()); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + READ_WRITE, schemaContext); transactionProxy.read(TestModel.TEST_PATH); DOMStoreThreePhaseCommitCohort ready = transactionProxy.ready(); - Assert.assertTrue(ready instanceof ThreePhaseCommitCohortProxy); + assertTrue(ready instanceof ThreePhaseCommitCohortProxy); ThreePhaseCommitCohortProxy proxy = (ThreePhaseCommitCohortProxy) ready; - Assert.assertTrue("No cohort paths returned", proxy.getCohortPaths().size() > 0); - + assertEquals("getCohortPaths", Arrays.asList(actorRef.path()), proxy.getCohortPaths()); } @Test - public void testGetIdentifier(){ - final Props props = Props.create(DoNothingActor.class); - final ActorRef doNothingActorRef = getSystem().actorOf(props); - - final MockActorContext actorContext = new MockActorContext(this.getSystem()); - actorContext.setExecuteShardOperationResponse( createTransactionReply(doNothingActorRef) ); - - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); - - Assert.assertNotNull(transactionProxy.getIdentifier()); + public void testGetIdentifier() { + setupActorContextWithInitialCreateTransaction(READ_ONLY); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + TransactionProxy.TransactionType.READ_ONLY, schemaContext); + + Object id = transactionProxy.getIdentifier(); + assertNotNull("getIdentifier returned null", id); + assertTrue("Invalid identifier: " + id, id.toString().startsWith(memberName)); } + @SuppressWarnings("unchecked") @Test - public void testClose(){ - final Props props = Props.create(MessageCollectorActor.class); - final ActorRef actorRef = getSystem().actorOf(props); + public void testClose() throws Exception{ + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(READ_WRITE); - final MockActorContext actorContext = new MockActorContext(this.getSystem()); - actorContext.setExecuteLocalOperationResponse(createPrimaryFound(actorRef)); - actorContext.setExecuteShardOperationResponse(createTransactionReply(actorRef)); - actorContext.setExecuteRemoteOperationResponse("message"); + doReturn(readDataReply(null)).when(mockActorContext).executeRemoteOperationAsync( + eq(actorSelection(actorRef)), eqReadData(), anyDuration()); - TransactionProxy transactionProxy = - new TransactionProxy(actorContext, - TransactionProxy.TransactionType.READ_ONLY, transactionExecutor, TestModel.createTestContext()); + TransactionProxy transactionProxy = new TransactionProxy(mockActorContext, + READ_WRITE, schemaContext); transactionProxy.read(TestModel.TEST_PATH); transactionProxy.close(); - Object messages = testContext - .executeLocalOperation(actorRef, "messages", - ActorContext.ASK_DURATION); - - Assert.assertNotNull(messages); - - Assert.assertTrue(messages instanceof List); - - List listMessages = (List) messages; - - Assert.assertEquals(1, listMessages.size()); - - Assert.assertTrue(listMessages.get(0).getClass().equals(CloseTransaction.SERIALIZABLE_CLASS)); - } - - private CreateTransactionReply createTransactionReply(ActorRef actorRef){ - return CreateTransactionReply.newBuilder() - .setTransactionActorPath(actorRef.path().toString()) - .setTransactionId("txn-1") - .build(); + verify(mockActorContext).sendRemoteOperationAsync( + eq(actorSelection(actorRef)), isA(CloseTransaction.SERIALIZABLE_CLASS)); } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java index 5874eccda4..fda9ccdfdb 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/ActorContextTest.java @@ -1,11 +1,14 @@ package org.opendaylight.controller.cluster.datastore.utils; +import java.util.concurrent.TimeUnit; import akka.actor.ActorRef; +import akka.actor.ActorSelection; import akka.actor.ActorSystem; import akka.actor.Props; import akka.actor.UntypedActor; import akka.japi.Creator; import akka.testkit.JavaTestKit; + import org.junit.Test; import org.opendaylight.controller.cluster.datastore.AbstractActorTest; import org.opendaylight.controller.cluster.datastore.ClusterWrapper; @@ -14,6 +17,9 @@ import org.opendaylight.controller.cluster.datastore.messages.FindLocalShard; import org.opendaylight.controller.cluster.datastore.messages.LocalShardFound; import org.opendaylight.controller.cluster.datastore.messages.LocalShardNotFound; +import scala.concurrent.Await; +import scala.concurrent.Future; +import scala.concurrent.duration.Duration; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; @@ -74,14 +80,23 @@ public class ActorContextTest extends AbstractActorTest{ } private static Props props(final boolean found, final ActorRef actorRef){ - return Props.create(new Creator() { + return Props.create(new MockShardManagerCreator(found, actorRef) ); + } - @Override public MockShardManager create() - throws Exception { - return new MockShardManager(found, - actorRef); - } - }); + @SuppressWarnings("serial") + private static class MockShardManagerCreator implements Creator { + final boolean found; + final ActorRef actorRef; + + MockShardManagerCreator(boolean found, ActorRef actorRef) { + this.found = found; + this.actorRef = actorRef; + } + + @Override + public MockShardManager create() throws Exception { + return new MockShardManager(found, actorRef); + } } } @@ -90,6 +105,7 @@ public class ActorContextTest extends AbstractActorTest{ new JavaTestKit(getSystem()) {{ new Within(duration("1 seconds")) { + @Override protected void run() { ActorRef shardActorRef = getSystem().actorOf(Props.create(EchoActor.class)); @@ -118,6 +134,7 @@ public class ActorContextTest extends AbstractActorTest{ new JavaTestKit(getSystem()) {{ new Within(duration("1 seconds")) { + @Override protected void run() { ActorRef shardManagerActorRef = getSystem() @@ -145,6 +162,7 @@ public class ActorContextTest extends AbstractActorTest{ new JavaTestKit(getSystem()) {{ new Within(duration("1 seconds")) { + @Override protected void run() { ActorRef shardActorRef = getSystem().actorOf(Props.create(EchoActor.class)); @@ -173,6 +191,7 @@ public class ActorContextTest extends AbstractActorTest{ new JavaTestKit(getSystem()) {{ new Within(duration("1 seconds")) { + @Override protected void run() { ActorRef shardManagerActorRef = getSystem() @@ -193,4 +212,68 @@ public class ActorContextTest extends AbstractActorTest{ }}; } + + @Test + public void testExecuteRemoteOperation() { + new JavaTestKit(getSystem()) {{ + + new Within(duration("3 seconds")) { + @Override + protected void run() { + + ActorRef shardActorRef = getSystem().actorOf(Props.create(EchoActor.class)); + + ActorRef shardManagerActorRef = getSystem() + .actorOf(MockShardManager.props(true, shardActorRef)); + + ActorContext actorContext = + new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class), + mock(Configuration.class)); + + ActorSelection actor = actorContext.actorSelection(shardActorRef.path()); + + Object out = actorContext.executeRemoteOperation(actor, "hello", duration("3 seconds")); + + assertEquals("hello", out); + + expectNoMsg(); + } + }; + }}; + } + + @Test + public void testExecuteRemoteOperationAsync() { + new JavaTestKit(getSystem()) {{ + + new Within(duration("3 seconds")) { + @Override + protected void run() { + + ActorRef shardActorRef = getSystem().actorOf(Props.create(EchoActor.class)); + + ActorRef shardManagerActorRef = getSystem() + .actorOf(MockShardManager.props(true, shardActorRef)); + + ActorContext actorContext = + new ActorContext(getSystem(), shardManagerActorRef , mock(ClusterWrapper.class), + mock(Configuration.class)); + + ActorSelection actor = actorContext.actorSelection(shardActorRef.path()); + + Future future = actorContext.executeRemoteOperationAsync(actor, "hello", + Duration.create(3, TimeUnit.SECONDS)); + + try { + Object result = Await.result(future, Duration.create(3, TimeUnit.SECONDS)); + assertEquals("Result", "hello", result); + } catch(Exception e) { + throw new AssertionError(e); + } + + expectNoMsg(); + } + }; + }}; + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java index 5d3853f311..b19fd3a529 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/utils/MockActorContext.java @@ -8,7 +8,7 @@ package org.opendaylight.controller.cluster.datastore.utils; - +import static org.junit.Assert.assertNotNull; import akka.actor.ActorRef; import akka.actor.ActorSelection; import akka.actor.ActorSystem; @@ -16,10 +16,12 @@ import scala.concurrent.duration.FiniteDuration; public class MockActorContext extends ActorContext { - private Object executeShardOperationResponse; - private Object executeRemoteOperationResponse; - private Object executeLocalOperationResponse; - private Object executeLocalShardOperationResponse; + private volatile Object executeShardOperationResponse; + private volatile Object executeRemoteOperationResponse; + private volatile Object executeLocalOperationResponse; + private volatile Object executeLocalShardOperationResponse; + private volatile Exception executeRemoteOperationFailure; + private volatile Object inputMessage; public MockActorContext(ActorSystem actorSystem) { super(actorSystem, null, new MockClusterWrapper(), new MockConfiguration()); @@ -52,6 +54,10 @@ public class MockActorContext extends ActorContext { executeRemoteOperationResponse = response; } + public void setExecuteRemoteOperationFailure(Exception executeRemoteOperationFailure) { + this.executeRemoteOperationFailure = executeRemoteOperationFailure; + } + public void setExecuteLocalOperationResponse( Object executeLocalOperationResponse) { this.executeLocalOperationResponse = executeLocalOperationResponse; @@ -62,12 +68,20 @@ public class MockActorContext extends ActorContext { this.executeLocalShardOperationResponse = executeLocalShardOperationResponse; } - @Override public Object executeLocalOperation(ActorRef actor, + @SuppressWarnings("unchecked") + public T getInputMessage(Class expType) throws Exception { + assertNotNull("Input message was null", inputMessage); + return (T) expType.getMethod("fromSerializable", Object.class).invoke(null, inputMessage); + } + + @Override + public Object executeLocalOperation(ActorRef actor, Object message, FiniteDuration duration) { return this.executeLocalOperationResponse; } - @Override public Object executeLocalShardOperation(String shardName, + @Override + public Object executeLocalShardOperation(String shardName, Object message, FiniteDuration duration) { return this.executeLocalShardOperationResponse; } diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransactionChain.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransactionChain.java index 5876c50d51..b916fddca7 100644 --- a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransactionChain.java +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransactionChain.java @@ -12,8 +12,6 @@ package org.opendaylight.controller.sal.core.spi.data; * sequence and each transaction must see the effects of previous transactions * as if they happened. A chain makes no guarantees of atomicity, in fact * transactions are committed as soon as possible. - * - * */ public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, AutoCloseable { @@ -35,7 +33,7 @@ public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, Au * if the chain has been closed. */ @Override - public DOMStoreReadTransaction newReadOnlyTransaction(); + DOMStoreReadTransaction newReadOnlyTransaction(); /** * Create a new read write transaction which will continue the chain. The @@ -55,10 +53,10 @@ public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, Au * if the chain has been closed. */ @Override - public DOMStoreReadWriteTransaction newReadWriteTransaction(); + DOMStoreReadWriteTransaction newReadWriteTransaction(); /** - * Create a new read write transaction which will continue the chain. The + * Create a new write-only transaction which will continue the chain. The * previous read-write transaction has to be either READY or CANCELLED. * * @@ -68,8 +66,7 @@ public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, Au * if the chain has been closed. */ @Override - public DOMStoreWriteTransaction newWriteOnlyTransaction(); - + DOMStoreWriteTransaction newWriteOnlyTransaction(); /** * Closes Transaction Chain. @@ -80,6 +77,5 @@ public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, Au * @throws IllegalStateException If any of the outstanding created transactions was not canceled or ready. */ @Override - public void close(); - + void close(); } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java index ac1f2e32d5..536cfa0081 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChangeListenerNotifyTask.java @@ -7,6 +7,8 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; +import com.google.common.base.Preconditions; + import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; import org.opendaylight.yangtools.util.concurrent.NotificationManager; @@ -16,35 +18,37 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; class ChangeListenerNotifyTask implements Runnable { - private static final Logger LOG = LoggerFactory.getLogger(ChangeListenerNotifyTask.class); - private final Iterable> listeners; - private final AsyncDataChangeEvent> event; - @SuppressWarnings("rawtypes") - private final NotificationManager - notificationMgr; + private final NotificationManager notificationMgr; + private final AsyncDataChangeEvent> event; + private final DataChangeListenerRegistration listener; @SuppressWarnings("rawtypes") - public ChangeListenerNotifyTask(final Iterable> listeners, + public ChangeListenerNotifyTask(final DataChangeListenerRegistration listener, final AsyncDataChangeEvent> event, final NotificationManager notificationMgr) { - this.listeners = listeners; - this.event = event; - this.notificationMgr = notificationMgr; + this.notificationMgr = Preconditions.checkNotNull(notificationMgr); + this.listener = Preconditions.checkNotNull(listener); + this.event = Preconditions.checkNotNull(event); } @Override public void run() { - - for (DataChangeListenerRegistration listener : listeners) { - notificationMgr.submitNotification(listener.getInstance(), event); + final AsyncDataChangeListener> l = listener.getInstance(); + if (l == null) { + LOG.trace("Skipping event delivery to unregistered listener {}", l); + return; } + LOG.trace("Listener {} event {}", l, event); + + // FIXME: Yo dawg I heard you like queues, so this was queued to be queued + notificationMgr.submitNotification(l, event); } @Override public String toString() { - return "ChangeListenerNotifyTask [listeners=" + listeners + ", event=" + event + "]"; + return "ChangeListenerNotifyTask [listener=" + listener + ", event=" + event + "]"; } } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java index 5faebcef36..f457e3b9e9 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMImmutableDataChangeEvent.java @@ -7,6 +7,8 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; +import com.google.common.base.Preconditions; + import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -19,8 +21,6 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import com.google.common.base.Preconditions; - public final class DOMImmutableDataChangeEvent implements AsyncDataChangeEvent> { @@ -184,6 +184,10 @@ public final class DOMImmutableDataChangeEvent implements updated.put(path, after); return this; } + + public boolean isEmpty() { + return created.isEmpty() && removed.isEmpty() && updated.isEmpty(); + } } private static final class RemoveEventFactory implements SimpleEventFactory { diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java index d0d3fe9e6a..129013378e 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java @@ -49,7 +49,6 @@ import org.slf4j.LoggerFactory; import javax.annotation.concurrent.GuardedBy; -import java.util.Collections; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -176,7 +175,7 @@ public class InMemoryDOMDataStore implements DOMStore, Identifiable, Sch .addCreated(path, data) // .build(); - new ChangeListenerNotifyTask(Collections.singletonList(reg), event, + new ChangeListenerNotifyTask(reg, event, dataChangeListenerNotificationManager).run(); } } diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java index d8feaa71f6..a4e8c86aa8 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeEventsTask.java @@ -7,36 +7,24 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; -import static org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.builder; - import com.google.common.base.Optional; import com.google.common.base.Preconditions; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; import java.util.Map.Entry; -import java.util.Set; import java.util.concurrent.Callable; +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.AsyncDataChangeListener; -import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder; import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.SimpleEventFactory; import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree; -import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Node; import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Walker; import org.opendaylight.yangtools.util.concurrent.NotificationManager; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer; @@ -54,14 +42,13 @@ import org.slf4j.LoggerFactory; */ final class ResolveDataChangeEventsTask implements Callable> { private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeEventsTask.class); - private static final DOMImmutableDataChangeEvent NO_CHANGE = builder(DataChangeScope.BASE).build(); - private final Multimap events = HashMultimap.create(); + @SuppressWarnings("rawtypes") + private final NotificationManager notificationMgr; private final DataTreeCandidate candidate; private final ListenerTree listenerRoot; - @SuppressWarnings("rawtypes") - private final NotificationManager notificationMgr; + private Multimap, DOMImmutableDataChangeEvent> collectedEvents; @SuppressWarnings("rawtypes") public ResolveDataChangeEventsTask(final DataTreeCandidate candidate, final ListenerTree listenerTree, @@ -81,153 +68,42 @@ final class ResolveDataChangeEventsTask implements Callable call() { + public synchronized Iterable call() { try (final Walker w = listenerRoot.getWalker()) { - resolveAnyChangeEvent(candidate.getRootPath(), Collections.singleton(w.getRootNode()), candidate.getRootNode()); - return createNotificationTasks(); - } - } - - /** - * - * Walks map of listeners to data change events, creates notification - * delivery tasks. - * - * Walks map of registered and affected listeners and creates notification - * tasks from set of listeners and events to be delivered. - * - * If set of listeners has more then one event (applicable to wildcarded - * listeners), merges all data change events into one, final which contains - * all separate updates. - * - * Dispatch between merge variant and reuse variant of notification task is - * done in - * {@link #addNotificationTask(com.google.common.collect.ImmutableList.Builder, Node, java.util.Collection)} - * - * @return Collection of notification tasks. - */ - private Collection createNotificationTasks() { - ImmutableList.Builder taskListBuilder = ImmutableList.builder(); - for (Entry> entry : events.asMap().entrySet()) { - addNotificationTask(taskListBuilder, entry.getKey(), entry.getValue()); - } - return taskListBuilder.build(); - } - - /** - * Adds notification task to task list. - * - * If entry collection contains one event, this event is reused and added to - * notification tasks for listeners (see - * {@link #addNotificationTaskByScope(com.google.common.collect.ImmutableList.Builder, Node, DOMImmutableDataChangeEvent)} - * . Otherwise events are merged by scope and distributed between listeners - * to particular scope. See - * {@link #addNotificationTasksAndMergeEvents(com.google.common.collect.ImmutableList.Builder, Node, java.util.Collection)} - * . - * - * @param taskListBuilder - * @param listeners - * @param entries - */ - private void addNotificationTask(final ImmutableList.Builder taskListBuilder, - final ListenerTree.Node listeners, final Collection entries) { - - if (!entries.isEmpty()) { - if (entries.size() == 1) { - addNotificationTaskByScope(taskListBuilder, listeners, Iterables.getOnlyElement(entries)); - } else { - addNotificationTasksAndMergeEvents(taskListBuilder, listeners, entries); - } - } - } + // Defensive: reset internal state + collectedEvents = ArrayListMultimap.create(); - /** - * - * Add notification deliveries task to the listener. - * - * - * @param taskListBuilder - * @param listeners - * @param event - */ - private void addNotificationTaskByScope( - final ImmutableList.Builder taskListBuilder, final ListenerTree.Node listeners, - final DOMImmutableDataChangeEvent event) { - DataChangeScope eventScope = event.getScope(); - for (DataChangeListenerRegistration listenerReg : listeners.getListeners()) { - DataChangeScope listenerScope = listenerReg.getScope(); - List> listenerSet = Collections - .> singletonList(listenerReg); - if (eventScope == DataChangeScope.BASE) { - taskListBuilder.add(new ChangeListenerNotifyTask(listenerSet, event, notificationMgr)); - } else if (eventScope == DataChangeScope.ONE && listenerScope != DataChangeScope.BASE) { - taskListBuilder.add(new ChangeListenerNotifyTask(listenerSet, event, notificationMgr)); - } else if (eventScope == DataChangeScope.SUBTREE && listenerScope == DataChangeScope.SUBTREE) { - taskListBuilder.add(new ChangeListenerNotifyTask(listenerSet, event, notificationMgr)); - } - } - } + // Run through the tree + final ResolveDataChangeState s = ResolveDataChangeState.initial(candidate.getRootPath(), w.getRootNode()); + resolveAnyChangeEvent(s, candidate.getRootNode()); - /** - * - * Add notification tasks with merged event - * - * Separate Events by scope and creates merged notification tasks for each - * and every scope which is present. - * - * Adds merged events to task list based on scope requested by client. - * - * @param taskListBuilder - * @param listeners - * @param entries - */ - private void addNotificationTasksAndMergeEvents( - final ImmutableList.Builder taskListBuilder, final ListenerTree.Node listeners, - final Collection entries) { - - final Builder baseBuilder = builder(DataChangeScope.BASE); - final Builder oneBuilder = builder(DataChangeScope.ONE); - final Builder subtreeBuilder = builder(DataChangeScope.SUBTREE); - - boolean baseModified = false; - boolean oneModified = false; - boolean subtreeModified = false; - for (final DOMImmutableDataChangeEvent entry : entries) { - switch (entry.getScope()) { - // Absence of breaks is intentional here. Subtree contains base and - // one, one also contains base - case BASE: - baseBuilder.merge(entry); - baseModified = true; - case ONE: - oneBuilder.merge(entry); - oneModified = true; - case SUBTREE: - subtreeBuilder.merge(entry); - subtreeModified = true; + /* + * Convert to tasks, but be mindful of multiple values -- those indicate multiple + * wildcard matches, which need to be merged. + */ + final Collection ret = new ArrayList<>(); + for (Entry, Collection> e : collectedEvents.asMap().entrySet()) { + final Collection col = e.getValue(); + final DOMImmutableDataChangeEvent event; + + if (col.size() != 1) { + final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE); + for (DOMImmutableDataChangeEvent i : col) { + b.merge(i); + } + + event = b.build(); + LOG.trace("Merged events {} into event {}", col, event); + } else { + event = col.iterator().next(); + } + + ret.add(new ChangeListenerNotifyTask(e.getKey(), event, notificationMgr)); } - } - if (baseModified) { - addNotificationTaskExclusively(taskListBuilder, listeners, baseBuilder.build()); - } - if (oneModified) { - addNotificationTaskExclusively(taskListBuilder, listeners, oneBuilder.build()); - } - if (subtreeModified) { - addNotificationTaskExclusively(taskListBuilder, listeners, subtreeBuilder.build()); - } - } - - private void addNotificationTaskExclusively( - final ImmutableList.Builder taskListBuilder, final Node listeners, - final DOMImmutableDataChangeEvent event) { - for (DataChangeListenerRegistration listener : listeners.getListeners()) { - if (listener.getScope() == event.getScope()) { - Set> listenerSet = Collections - .> singleton(listener); - taskListBuilder.add(new ChangeListenerNotifyTask(listenerSet, event, notificationMgr)); - } + // FIXME: so now we have tasks to submit tasks... Inception-style! + LOG.debug("Created tasks {}", ret); + return ret; } } @@ -245,94 +121,90 @@ final class ResolveDataChangeEventsTask implements Callable listeners, final DataTreeCandidateNode node) { - + private boolean resolveAnyChangeEvent(final ResolveDataChangeState state, final DataTreeCandidateNode node) { if (node.getModificationType() != ModificationType.UNMODIFIED && !node.getDataAfter().isPresent() && !node.getDataBefore().isPresent()) { LOG.debug("Modification at {} has type {}, but no before- and after-data. Assuming unchanged.", - path, node.getModificationType()); - return NO_CHANGE; + state.getPath(), node.getModificationType()); + return false; } // no before and after state is present switch (node.getModificationType()) { case SUBTREE_MODIFIED: - return resolveSubtreeChangeEvent(path, listeners, node); + return resolveSubtreeChangeEvent(state, node); case MERGE: case WRITE: Preconditions.checkArgument(node.getDataAfter().isPresent(), - "Modification at {} has type {} but no after-data", path, node.getModificationType()); - if (node.getDataBefore().isPresent()) { - return resolveReplacedEvent(path, listeners, node.getDataBefore().get(), node.getDataAfter().get()); - } else { - return resolveCreateEvent(path, listeners, node.getDataAfter().get()); + "Modification at {} has type {} but no after-data", state.getPath(), node.getModificationType()); + if (!node.getDataBefore().isPresent()) { + resolveCreateEvent(state, node.getDataAfter().get()); + return true; } + + return resolveReplacedEvent(state, node.getDataBefore().get(), node.getDataAfter().get()); case DELETE: Preconditions.checkArgument(node.getDataBefore().isPresent(), - "Modification at {} has type {} but no before-data", path, node.getModificationType()); - return resolveDeleteEvent(path, listeners, node.getDataBefore().get()); + "Modification at {} has type {} but no before-data", state.getPath(), node.getModificationType()); + resolveDeleteEvent(state, node.getDataBefore().get()); + return true; case UNMODIFIED: - return NO_CHANGE; + return false; } - throw new IllegalStateException(String.format("Unhandled node state %s at %s", node.getModificationType(), path)); + throw new IllegalStateException(String.format("Unhandled node state %s at %s", node.getModificationType(), state.getPath())); } - private DOMImmutableDataChangeEvent resolveReplacedEvent(final YangInstanceIdentifier path, - final Collection listeners, final NormalizedNode beforeData, - final NormalizedNode afterData) { - - // FIXME: BUG-1493: check the listeners to prune unneeded changes: - // for subtrees, we have to do all - // for one, we need to expand children - // for base, we just report replacement + private boolean resolveReplacedEvent(final ResolveDataChangeState state, + final NormalizedNode beforeData, final NormalizedNode afterData) { if (beforeData instanceof NormalizedNodeContainer) { - // Node is container (contains child) and we have interested - // listeners registered for it, that means we need to do - // resolution of changes on children level and can not - // shortcut resolution. - LOG.trace("Resolving subtree replace event for {} before {}, after {}",path,beforeData,afterData); + /* + * Node is a container (contains a child) and we have interested + * listeners registered for it, that means we need to do + * resolution of changes on children level and can not + * shortcut resolution. + */ + LOG.trace("Resolving subtree replace event for {} before {}, after {}", state.getPath(), beforeData, afterData); @SuppressWarnings("unchecked") NormalizedNodeContainer> beforeCont = (NormalizedNodeContainer>) beforeData; @SuppressWarnings("unchecked") NormalizedNodeContainer> afterCont = (NormalizedNodeContainer>) afterData; - return resolveNodeContainerReplaced(path, listeners, beforeCont, afterCont); - } else if (!beforeData.equals(afterData)) { - // Node is Leaf type (does not contain child nodes) - // so normal equals method is sufficient for determining change. - LOG.trace("Resolving leaf replace event for {} , before {}, after {}",path,beforeData,afterData); - DOMImmutableDataChangeEvent event = builder(DataChangeScope.BASE).setBefore(beforeData).setAfter(afterData) - .addUpdated(path, beforeData, afterData).build(); - addPartialTask(listeners, event); - return event; - } else { - return NO_CHANGE; + return resolveNodeContainerReplaced(state, beforeCont, afterCont); } + + // Node is a Leaf type (does not contain child nodes) + // so normal equals method is sufficient for determining change. + if (beforeData.equals(afterData)) { + LOG.trace("Skipping equal leaf {}", state.getPath()); + return false; + } + + LOG.trace("Resolving leaf replace event for {} , before {}, after {}", state.getPath(), beforeData, afterData); + DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE).addUpdated(state.getPath(), beforeData, afterData).build(); + state.addEvent(event); + state.collectEvents(beforeData, afterData, collectedEvents); + return true; } - private DOMImmutableDataChangeEvent resolveNodeContainerReplaced(final YangInstanceIdentifier path, - final Collection listeners, + private boolean resolveNodeContainerReplaced(final ResolveDataChangeState state, final NormalizedNodeContainer> beforeCont, final NormalizedNodeContainer> afterCont) { - final List childChanges = new LinkedList<>(); + if (!state.needsProcessing()) { + LOG.trace("Not processing replaced container {}", state.getPath()); + return true; + } // We look at all children from before and compare it with after state. + boolean childChanged = false; for (NormalizedNode beforeChild : beforeCont.getValue()) { final PathArgument childId = beforeChild.getIdentifier(); - YangInstanceIdentifier childPath = path.node(childId); - Collection childListeners = getListenerChildrenWildcarded(listeners, childId); - Optional> afterChild = afterCont.getChild(childId); - DOMImmutableDataChangeEvent childChange = resolveNodeContainerChildUpdated(childPath, childListeners, - beforeChild, afterChild); - // If change is empty (equals to NO_CHANGE) - if (childChange != NO_CHANGE) { - childChanges.add(childChange); + if (resolveNodeContainerChildUpdated(state.child(childId), beforeChild, afterCont.getChild(childId))) { + childChanged = true; } } @@ -345,187 +217,120 @@ final class ResolveDataChangeEventsTask implements Callable childListeners = getListenerChildrenWildcarded(listeners, childId); - YangInstanceIdentifier childPath = path.node(childId); - childChanges.add(resolveSameEventRecursivelly(childPath , childListeners, afterChild, - DOMImmutableDataChangeEvent.getCreateEventFactory())); + resolveSameEventRecursivelly(state.child(childId), afterChild, DOMImmutableDataChangeEvent.getCreateEventFactory()); + childChanged = true; } } - if (childChanges.isEmpty()) { - return NO_CHANGE; - } - Builder eventBuilder = builder(DataChangeScope.BASE) // - .setBefore(beforeCont) // - .setAfter(afterCont) - .addUpdated(path, beforeCont, afterCont); - for (DOMImmutableDataChangeEvent childChange : childChanges) { - eventBuilder.merge(childChange); + if (childChanged) { + DOMImmutableDataChangeEvent event = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE) + .addUpdated(state.getPath(), beforeCont, afterCont).build(); + state.addEvent(event); } - DOMImmutableDataChangeEvent replaceEvent = eventBuilder.build(); - addPartialTask(listeners, replaceEvent); - return replaceEvent; + state.collectEvents(beforeCont, afterCont, collectedEvents); + return childChanged; } - private DOMImmutableDataChangeEvent resolveNodeContainerChildUpdated(final YangInstanceIdentifier path, - final Collection listeners, final NormalizedNode before, - final Optional> after) { - + private boolean resolveNodeContainerChildUpdated(final ResolveDataChangeState state, + final NormalizedNode before, final Optional> after) { if (after.isPresent()) { // REPLACE or SUBTREE Modified - return resolveReplacedEvent(path, listeners, before, after.get()); - - } else { - // AFTER state is not present - child was deleted. - return resolveSameEventRecursivelly(path, listeners, before, - DOMImmutableDataChangeEvent.getRemoveEventFactory()); + return resolveReplacedEvent(state, before, after.get()); } + + // AFTER state is not present - child was deleted. + resolveSameEventRecursivelly(state, before, DOMImmutableDataChangeEvent.getRemoveEventFactory()); + return true; } /** * Resolves create events deep down the interest listener tree. * - * * @param path * @param listeners * @param afterState * @return */ - private DOMImmutableDataChangeEvent resolveCreateEvent(final YangInstanceIdentifier path, - final Collection listeners, final NormalizedNode afterState) { + private void resolveCreateEvent(final ResolveDataChangeState state, final NormalizedNode afterState) { @SuppressWarnings({ "unchecked", "rawtypes" }) final NormalizedNode node = (NormalizedNode) afterState; - return resolveSameEventRecursivelly(path, listeners, node, DOMImmutableDataChangeEvent.getCreateEventFactory()); + resolveSameEventRecursivelly(state, node, DOMImmutableDataChangeEvent.getCreateEventFactory()); } - private DOMImmutableDataChangeEvent resolveDeleteEvent(final YangInstanceIdentifier path, - final Collection listeners, final NormalizedNode beforeState) { - + private void resolveDeleteEvent(final ResolveDataChangeState state, final NormalizedNode beforeState) { @SuppressWarnings({ "unchecked", "rawtypes" }) final NormalizedNode node = (NormalizedNode) beforeState; - return resolveSameEventRecursivelly(path, listeners, node, DOMImmutableDataChangeEvent.getRemoveEventFactory()); + resolveSameEventRecursivelly(state, node, DOMImmutableDataChangeEvent.getRemoveEventFactory()); } - private DOMImmutableDataChangeEvent resolveSameEventRecursivelly(final YangInstanceIdentifier path, - final Collection listeners, final NormalizedNode node, - final SimpleEventFactory eventFactory) { - final DOMImmutableDataChangeEvent event = eventFactory.create(path, node); - DOMImmutableDataChangeEvent propagateEvent = event; + private void resolveSameEventRecursivelly(final ResolveDataChangeState state, + final NormalizedNode node, final SimpleEventFactory eventFactory) { + if (!state.needsProcessing()) { + LOG.trace("Skipping child {}", state.getPath()); + return; + } + // We have listeners for this node or it's children, so we will try // to do additional processing if (node instanceof NormalizedNodeContainer) { - LOG.trace("Resolving subtree recursive event for {}, type {}", path, eventFactory); - - Builder eventBuilder = builder(DataChangeScope.BASE); - eventBuilder.merge(event); - eventBuilder.setBefore(event.getOriginalSubtree()); - eventBuilder.setAfter(event.getUpdatedSubtree()); + LOG.trace("Resolving subtree recursive event for {}, type {}", state.getPath(), eventFactory); // Node has children, so we will try to resolve it's children // changes. @SuppressWarnings("unchecked") NormalizedNodeContainer> container = (NormalizedNodeContainer>) node; for (NormalizedNode child : container.getValue()) { - PathArgument childId = child.getIdentifier(); + final PathArgument childId = child.getIdentifier(); + LOG.trace("Resolving event for child {}", childId); - Collection childListeners = getListenerChildrenWildcarded(listeners, childId); - eventBuilder.merge(resolveSameEventRecursivelly(path.node(childId), childListeners, child, eventFactory)); + resolveSameEventRecursivelly(state.child(childId), child, eventFactory); } - propagateEvent = eventBuilder.build(); } - if (!listeners.isEmpty()) { - addPartialTask(listeners, propagateEvent); - } - return propagateEvent; - } - private DOMImmutableDataChangeEvent resolveSubtreeChangeEvent(final YangInstanceIdentifier path, - final Collection listeners, final DataTreeCandidateNode modification) { + final DOMImmutableDataChangeEvent event = eventFactory.create(state.getPath(), node); + LOG.trace("Adding event {} at path {}", event, state.getPath()); + state.addEvent(event); + state.collectEvents(event.getOriginalSubtree(), event.getUpdatedSubtree(), collectedEvents); + } - Preconditions.checkArgument(modification.getDataBefore().isPresent(), "Subtree change with before-data not present at path %s", path); - Preconditions.checkArgument(modification.getDataAfter().isPresent(), "Subtree change with after-data not present at path %s", path); + private boolean resolveSubtreeChangeEvent(final ResolveDataChangeState state, final DataTreeCandidateNode modification) { + Preconditions.checkArgument(modification.getDataBefore().isPresent(), "Subtree change with before-data not present at path %s", state.getPath()); + Preconditions.checkArgument(modification.getDataAfter().isPresent(), "Subtree change with after-data not present at path %s", state.getPath()); - Builder one = builder(DataChangeScope.ONE). - setBefore(modification.getDataBefore().get()). - setAfter(modification.getDataAfter().get()); - Builder subtree = builder(DataChangeScope.SUBTREE). - setBefore(modification.getDataBefore().get()). - setAfter(modification.getDataAfter().get()); - boolean oneModified = false; + DataChangeScope scope = null; for (DataTreeCandidateNode childMod : modification.getChildNodes()) { - PathArgument childId = childMod.getIdentifier(); - YangInstanceIdentifier childPath = path.node(childId); - Collection childListeners = getListenerChildrenWildcarded(listeners, childId); - + final ResolveDataChangeState childState = state.child(childMod.getIdentifier()); switch (childMod.getModificationType()) { case WRITE: case MERGE: case DELETE: - one.merge(resolveAnyChangeEvent(childPath, childListeners, childMod)); - oneModified = true; + if (resolveAnyChangeEvent(childState, childMod)) { + scope = DataChangeScope.ONE; + } break; case SUBTREE_MODIFIED: - subtree.merge(resolveSubtreeChangeEvent(childPath, childListeners, childMod)); + if (resolveSubtreeChangeEvent(childState, childMod) && scope == null) { + scope = DataChangeScope.SUBTREE; + } break; case UNMODIFIED: // no-op break; } } - final DOMImmutableDataChangeEvent oneChangeEvent; - if(oneModified) { - one.addUpdated(path, modification.getDataBefore().get(), modification.getDataAfter().get()); - oneChangeEvent = one.build(); - subtree.merge(oneChangeEvent); - } else { - oneChangeEvent = null; - subtree.addUpdated(path, modification.getDataBefore().get(), modification.getDataAfter().get()); - } - DOMImmutableDataChangeEvent subtreeEvent = subtree.build(); - if (!listeners.isEmpty()) { - if(oneChangeEvent != null) { - addPartialTask(listeners, oneChangeEvent); - } - addPartialTask(listeners, subtreeEvent); - } - return subtreeEvent; - } - private DOMImmutableDataChangeEvent addPartialTask(final Collection listeners, - final DOMImmutableDataChangeEvent event) { - for (ListenerTree.Node listenerNode : listeners) { - if (!listenerNode.getListeners().isEmpty()) { - LOG.trace("Adding event {} for listeners {}",event,listenerNode); - events.put(listenerNode, event); - } - } - return event; - } + final NormalizedNode before = modification.getDataBefore().get(); + final NormalizedNode after = modification.getDataAfter().get(); - private static Collection getListenerChildrenWildcarded(final Collection parentNodes, - final PathArgument child) { - if (parentNodes.isEmpty()) { - return Collections.emptyList(); - } - com.google.common.collect.ImmutableList.Builder result = ImmutableList.builder(); - if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) { - NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType()); - addChildrenNodesToBuilder(result, parentNodes, wildcardedIdentifier); + if (scope != null) { + DOMImmutableDataChangeEvent one = DOMImmutableDataChangeEvent.builder(scope).addUpdated(state.getPath(), before, after).build(); + state.addEvent(one); } - addChildrenNodesToBuilder(result, parentNodes, child); - return result.build(); - } - private static void addChildrenNodesToBuilder(final ImmutableList.Builder result, - final Collection parentNodes, final PathArgument childIdentifier) { - for (ListenerTree.Node node : parentNodes) { - Optional child = node.getChild(childIdentifier); - if (child.isPresent()) { - result.add(child.get()); - } - } + state.collectEvents(before, after, collectedEvents); + return scope != null; } @SuppressWarnings("rawtypes") diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeState.java b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeState.java new file mode 100644 index 0000000000..dca2eff705 --- /dev/null +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeState.java @@ -0,0 +1,227 @@ +/* + * 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 com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; +import org.opendaylight.controller.md.sal.dom.store.impl.DOMImmutableDataChangeEvent.Builder; +import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree.Node; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Recursion state used in {@link ResolveDataChangeEventsTask}. Instances of this + * method track which listeners are affected by a particular change node. It takes + * care of properly inheriting SUB/ONE listeners and also provides a means to + * understand when actual processing need not occur. + */ +final class ResolveDataChangeState { + private static final Logger LOG = LoggerFactory.getLogger(ResolveDataChangeState.class); + /** + * Inherited from all parents + */ + private final Iterable inheritedSub; + /** + * Inherited from immediate parent + */ + private final Iterable inheritedOne; + private final YangInstanceIdentifier nodeId; + private final Collection nodes; + + private final Map, Builder> subBuilders = new HashMap<>(); + private final Map, Builder> oneBuilders = new HashMap<>(); + private final Map, Builder> baseBuilders = new HashMap<>(); + + private ResolveDataChangeState(final YangInstanceIdentifier nodeId, + final Iterable inheritedSub, final Iterable inheritedOne, + final Collection nodes) { + this.nodeId = Preconditions.checkNotNull(nodeId); + this.nodes = Preconditions.checkNotNull(nodes); + this.inheritedSub = Preconditions.checkNotNull(inheritedSub); + this.inheritedOne = Preconditions.checkNotNull(inheritedOne); + + /* + * Collect the nodes which need to be propagated from us to the child. + */ + for (Node n : nodes) { + for (DataChangeListenerRegistration l : n.getListeners()) { + final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE); + switch (l.getScope()) { + case BASE: + baseBuilders.put(l, b); + break; + case ONE: + oneBuilders.put(l, b); + break; + case SUBTREE: + subBuilders.put(l, b); + break; + } + } + } + } + + /** + * Create an initial state handle at a particular root node. + * + * @param rootId root instance identifier + * @param root root node + * @return + */ + public static ResolveDataChangeState initial(final YangInstanceIdentifier rootId, final Node root) { + return new ResolveDataChangeState(rootId, Collections.emptyList(), + Collections.emptyList(), Collections.singletonList(root)); + } + + /** + * Create a state handle for iterating over a particular child. + * + * @param childId ID of the child + * @return State handle + */ + public ResolveDataChangeState child(final PathArgument childId) { + return new ResolveDataChangeState(nodeId.node(childId), + Iterables.concat(inheritedSub, subBuilders.values()), + oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId)); + } + + /** + * Get the current path + * + * @return Current path. + */ + public YangInstanceIdentifier getPath() { + return nodeId; + } + + /** + * Check if this child needs processing. + * + * @return True if processing needs to occur, false otherwise. + */ + public boolean needsProcessing() { + // May have underlying listeners, so we need to process + if (!nodes.isEmpty()) { + return true; + } + // Have SUBTREE listeners + if (!Iterables.isEmpty(inheritedSub)) { + return true; + } + // Have ONE listeners + if (!Iterables.isEmpty(inheritedOne)) { + return true; + } + + // FIXME: do we need anything else? If not, flip this to 'false' + return true; + } + + /** + * Add an event to all current listeners. + * + * @param event + */ + public void addEvent(final DOMImmutableDataChangeEvent event) { + // Subtree builders get always notified + for (Builder b : subBuilders.values()) { + b.merge(event); + } + for (Builder b : inheritedSub) { + b.merge(event); + } + + if (event.getScope() == DataChangeScope.ONE || event.getScope() == DataChangeScope.BASE) { + for (Builder b : oneBuilders.values()) { + b.merge(event); + } + } + + if (event.getScope() == DataChangeScope.BASE) { + for (Builder b : inheritedOne) { + b.merge(event); + } + for (Builder b : baseBuilders.values()) { + b.merge(event); + } + } + } + + /** + * Gather all non-empty events into the provided map. + * + * @param before before-image + * @param after after-image + * @param map target map + */ + public void collectEvents(final NormalizedNode before, final NormalizedNode after, + final Multimap, DOMImmutableDataChangeEvent> map) { + for (Entry, Builder> e : baseBuilders.entrySet()) { + final Builder b = e.getValue(); + if (!b.isEmpty()) { + map.put(e.getKey(), b.setBefore(before).setAfter(after).build()); + } + } + for (Entry, Builder> e : oneBuilders.entrySet()) { + final Builder b = e.getValue(); + if (!b.isEmpty()) { + map.put(e.getKey(), b.setBefore(before).setAfter(after).build()); + } + } + for (Entry, Builder> e : subBuilders.entrySet()) { + final Builder b = e.getValue(); + if (!b.isEmpty()) { + map.put(e.getKey(), b.setBefore(before).setAfter(after).build()); + } + } + + LOG.trace("Collected events {}", map); + } + + private static Collection getListenerChildrenWildcarded(final Collection parentNodes, + final PathArgument child) { + if (parentNodes.isEmpty()) { + return Collections.emptyList(); + } + + final List result = new ArrayList<>(); + if (child instanceof NodeWithValue || child instanceof NodeIdentifierWithPredicates) { + NodeIdentifier wildcardedIdentifier = new NodeIdentifier(child.getNodeType()); + addChildNodes(result, parentNodes, wildcardedIdentifier); + } + addChildNodes(result, parentNodes, child); + return result; + } + + private static void addChildNodes(final List result, final Collection parentNodes, final PathArgument childIdentifier) { + for (Node node : parentNodes) { + Optional child = node.getChild(childIdentifier); + if (child.isPresent()) { + result.add(child.get()); + } + } + } +} diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDataChangeListenerTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDataChangeListenerTest.java index 76a9354d1a..0e064cd504 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDataChangeListenerTest.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/AbstractDataChangeListenerTest.java @@ -7,8 +7,11 @@ */ package org.opendaylight.controller.md.sal.dom.store.impl; +import com.google.common.util.concurrent.MoreExecutors; + import java.util.Collection; import java.util.Map; + import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -36,8 +39,6 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContaine import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import com.google.common.util.concurrent.MoreExecutors; - public abstract class AbstractDataChangeListenerTest { protected static final YangInstanceIdentifier TOP_LEVEL = YangInstanceIdentifier @@ -74,6 +75,13 @@ public abstract class AbstractDataChangeListenerTest { } } + /** + * Create a new test task. The task will operate on the backed database, + * and will use the proper background executor service. + * + * @return Test task initialized to clean up {@value #TOP_LEVEL} and its + * children. + */ public final DatastoreTestTask newTestTask() { return new DatastoreTestTask(datastore, dclExecutorService).cleanup(DatastoreTestTask .simpleDelete(TOP_LEVEL)); diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java index 84337de419..af58f63331 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/DefaultDataChangeListenerTestSuite.java @@ -13,10 +13,18 @@ import org.junit.Test; import org.opendaylight.controller.md.sal.dom.store.impl.DatastoreTestTask.WriteTransactionCustomizer; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; +/** + * Base template for a test suite for testing DataChangeListener functionality. + */ public abstract class DefaultDataChangeListenerTestSuite extends AbstractDataChangeListenerTest { protected static final String FOO_SIBLING = "foo-sibling"; + /** + * Callback invoked when the test suite can modify task parameters. + * + * @param task Update task configuration as needed + */ abstract protected void customizeTask(DatastoreTestTask task); @Test diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java index cdf465aace..ddbba76ae0 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeBaseTest.java @@ -34,8 +34,11 @@ public class WildcardedScopeBaseTest extends DefaultDataChangeListenerTestSuite assertNotNull(change); - assertNotContains(change.getCreatedData(), TOP_LEVEL); - assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR)); + /* + * Created data must not contain nested-list item, since that is two-level deep. + */ + assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR)); + assertContains(change.getCreatedData(), path(FOO) ); assertEmpty(change.getUpdatedData()); assertEmpty(change.getRemovedPaths()); @@ -48,11 +51,18 @@ public class WildcardedScopeBaseTest extends DefaultDataChangeListenerTestSuite AsyncDataChangeEvent> change = task.getChangeEvent(); assertNotNull(change); - - assertContains(change.getCreatedData(), path(FOO, BAZ)); + /* + * Created data must NOT contain nested-list item since scope is base, and change is two + * level deep. + */ + assertNotContains(change.getCreatedData(), path(FOO, BAZ)); assertContains(change.getUpdatedData(), path(FOO)); assertNotContains(change.getUpdatedData(), TOP_LEVEL); - assertContains(change.getRemovedPaths(), path(FOO, BAR)); + /* + * Removed data must NOT contain nested-list item since scope is base, and change is two + * level deep. + */ + assertNotContains(change.getRemovedPaths(), path(FOO, BAR)); } @@ -64,8 +74,9 @@ public class WildcardedScopeBaseTest extends DefaultDataChangeListenerTestSuite assertNotNull(change); assertFalse(change.getCreatedData().isEmpty()); - assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR), path(FOO, BAZ)); - assertNotContains(change.getCreatedData(), TOP_LEVEL); + // Base event should contain only changed item, no details about child. + assertContains(change.getCreatedData(), path(FOO)); + assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR), path(FOO, BAZ)); assertEmpty(change.getUpdatedData()); assertEmpty(change.getRemovedPaths()); @@ -95,7 +106,12 @@ public class WildcardedScopeBaseTest extends DefaultDataChangeListenerTestSuite assertEmpty(change.getUpdatedData()); assertNotContains(change.getUpdatedData(), TOP_LEVEL); - assertContains(change.getRemovedPaths(), path(FOO),path(FOO, BAZ),path(FOO,BAR)); + /* + * Scope base listener event should contain top-level-list item and nested list path + * and should not contain baz, bar which are two-level deep + */ + assertContains(change.getRemovedPaths(), path(FOO)); + assertNotContains(change.getRemovedPaths(),path(FOO, BAZ),path(FOO,BAR)); } @Override diff --git a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java index 3407e0ffa4..75f9fce612 100644 --- a/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java +++ b/opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/WildcardedScopeOneTest.java @@ -14,6 +14,7 @@ import java.util.concurrent.ExecutionException; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; 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.top.level.list.NestedList; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @@ -34,8 +35,8 @@ public class WildcardedScopeOneTest extends DefaultDataChangeListenerTestSuite { assertNotNull(change); - assertNotContains(change.getCreatedData(), TOP_LEVEL); - assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR)); + assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR)); + assertContains(change.getCreatedData(), path(FOO), path(FOO).node(NestedList.QNAME)); assertEmpty(change.getUpdatedData()); assertEmpty(change.getRemovedPaths()); @@ -48,11 +49,18 @@ public class WildcardedScopeOneTest extends DefaultDataChangeListenerTestSuite { AsyncDataChangeEvent> change = task.getChangeEvent(); assertNotNull(change); - - assertContains(change.getCreatedData(), path(FOO, BAZ)); - assertContains(change.getUpdatedData(), path(FOO)); + /* + * Created data must NOT contain nested-list item since scope is base, and change is two + * level deep. + */ + assertNotContains(change.getCreatedData(), path(FOO, BAZ)); + assertContains(change.getUpdatedData(), path(FOO),path(FOO).node(NestedList.QNAME)); assertNotContains(change.getUpdatedData(), TOP_LEVEL); - assertContains(change.getRemovedPaths(), path(FOO, BAR)); + /* + * Removed data must NOT contain nested-list item since scope is base, and change is two + * level deep. + */ + assertNotContains(change.getRemovedPaths(), path(FOO, BAR)); } @@ -64,8 +72,9 @@ public class WildcardedScopeOneTest extends DefaultDataChangeListenerTestSuite { assertNotNull(change); assertFalse(change.getCreatedData().isEmpty()); - assertContains(change.getCreatedData(), path(FOO), path(FOO, BAR), path(FOO, BAZ)); - assertNotContains(change.getCreatedData(), TOP_LEVEL); + // Base event should contain only changed item, and details about immediate child. + assertContains(change.getCreatedData(), path(FOO),path(FOO).node(NestedList.QNAME)); + assertNotContains(change.getCreatedData(), TOP_LEVEL,path(FOO, BAR), path(FOO, BAZ)); assertEmpty(change.getUpdatedData()); assertEmpty(change.getRemovedPaths()); @@ -96,7 +105,8 @@ public class WildcardedScopeOneTest extends DefaultDataChangeListenerTestSuite { assertEmpty(change.getUpdatedData()); assertNotContains(change.getUpdatedData(), TOP_LEVEL); - assertContains(change.getRemovedPaths(), path(FOO),path(FOO, BAZ),path(FOO,BAR)); + assertContains(change.getRemovedPaths(), path(FOO),path(FOO).node(NestedList.QNAME)); + assertNotContains(change.getRemovedPaths(), path(FOO, BAZ),path(FOO,BAR)); } @Override diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java index 77e342641e..d6bfc0c3b6 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemas.java @@ -115,18 +115,21 @@ public final class NetconfStateSchemas { final CompositeNode schemasNode = (CompositeNode) NetconfMessageTransformUtil.findNode(schemasNodeResult.getResult(), DATA_STATE_SCHEMAS_IDENTIFIER); - return create(schemasNode); + return create(id, schemasNode); } /** * Parse response of get(netconf-state/schemas) to find all schemas under netconf-state/schemas */ @VisibleForTesting - protected static NetconfStateSchemas create(final CompositeNode schemasNode) { + protected static NetconfStateSchemas create(final RemoteDeviceId id, final CompositeNode schemasNode) { final Set availableYangSchemas = Sets.newHashSet(); for (final CompositeNode schemaNode : schemasNode.getCompositesByName(Schema.QNAME.withoutRevision())) { - availableYangSchemas.add(RemoteYangSchema.createFromCompositeNode(schemaNode)); + final Optional fromCompositeNode = RemoteYangSchema.createFromCompositeNode(id, schemaNode); + if(fromCompositeNode.isPresent()) { + availableYangSchemas.add(fromCompositeNode.get()); + } } return new NetconfStateSchemas(availableYangSchemas); @@ -143,19 +146,23 @@ public final class NetconfStateSchemas { return qname; } - static RemoteYangSchema createFromCompositeNode(final CompositeNode schemaNode) { + static Optional createFromCompositeNode(final RemoteDeviceId id, final CompositeNode schemaNode) { Preconditions.checkArgument(schemaNode.getKey().equals(Schema.QNAME.withoutRevision()), "Wrong QName %s", schemaNode.getKey()); QName childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_FORMAT.withoutRevision(); final String formatAsString = getSingleChildNodeValue(schemaNode, childNode).get(); - Preconditions.checkArgument(formatAsString.equals(Yang.QNAME.getLocalName()), - "Expecting format to be only %s, not %s", Yang.QNAME.getLocalName(), formatAsString); + if(formatAsString.equals(Yang.QNAME.getLocalName()) == false) { + logger.debug("{}: Ignoring schema due to unsupported format: {}", id, formatAsString); + return Optional.absent(); + } childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_LOCATION.withoutRevision(); final Set locationsAsString = getAllChildNodeValues(schemaNode, childNode); - Preconditions.checkArgument(locationsAsString.contains(Schema.Location.Enumeration.NETCONF.toString()), - "Expecting location to be %s, not %s", Schema.Location.Enumeration.NETCONF.toString(), locationsAsString); + if(locationsAsString.contains(Schema.Location.Enumeration.NETCONF.toString()) == false) { + logger.debug("{}: Ignoring schema due to unsupported location: {}", id, locationsAsString); + return Optional.absent(); + } childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_NAMESPACE.withoutRevision(); final String namespaceAsString = getSingleChildNodeValue(schemaNode, childNode).get(); @@ -171,7 +178,7 @@ public final class NetconfStateSchemas { ? QName.create(namespaceAsString, revisionAsString.get(), moduleNameAsString) : QName.create(URI.create(namespaceAsString), null, moduleNameAsString).withoutRevision(); - return new RemoteYangSchema(moduleQName); + return Optional.of(new RemoteYangSchema(moduleQName)); } private static Set getAllChildNodeValues(final CompositeNode schemaNode, final QName childNodeQName) { diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java index 1a7d90e9c0..0999efff0f 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java @@ -7,8 +7,8 @@ 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 com.google.common.collect.Sets; + import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -158,7 +158,7 @@ public final class NetconfSessionCapabilities { } // FIXME: do we really want to continue here? - moduleBasedCaps.add(QName.create(namespace, revision, moduleName)); + moduleBasedCaps.add(QName.cachedReference(QName.create(namespace, revision, moduleName))); nonModuleCaps.remove(capability); } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemasTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemasTest.java index 16a915e730..3f9c8caa0e 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemasTest.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfStateSchemasTest.java @@ -7,6 +7,7 @@ import static org.junit.matchers.JUnitMatchers.hasItem; import java.util.Set; import org.junit.Test; import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils; @@ -18,7 +19,7 @@ public class NetconfStateSchemasTest { public void testCreate() throws Exception { final Document schemasXml = XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/netconf-state.schemas.payload.xml")); final CompositeNode compositeNodeSchemas = (CompositeNode) XmlDocumentUtils.toDomNode(schemasXml); - final NetconfStateSchemas schemas = NetconfStateSchemas.create(compositeNodeSchemas); + final NetconfStateSchemas schemas = NetconfStateSchemas.create(new RemoteDeviceId("device"), compositeNodeSchemas); final Set availableYangSchemasQNames = schemas.getAvailableYangSchemasQNames(); assertEquals(73, availableYangSchemasQNames.size()); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml index 38ec5f5ac2..4e007f4c5d 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/pom.xml +++ b/opendaylight/md-sal/sal-remoterpc-connector/pom.xml @@ -59,18 +59,23 @@ org.opendaylight.controller sal-core-api - - org.opendaylight.controller - netconf-util - - org.opendaylight.controller - sal-core-spi + org.opendaylight.controller + sal-core-spi + + + org.opendaylight.controller + sal-common-impl - org.opendaylight.controller - sal-common-impl + org.opendaylight.controller + netconf-util + + org.opendaylight.controller + sal-clustering-commons + + @@ -112,6 +117,11 @@ scala-library + + com.codahale.metrics + metrics-core + 3.0.1 + junit @@ -156,8 +166,11 @@ ${project.groupId}.${project.artifactId} - !org.jboss.*;!com.jcraft.*;* + !org.iq80.*;!*snappy;!org.jboss.*;!com.jcraft.*;!org.fusesource.*;* + sal-clustering-commons; + sal-akka-raft; + *metrics*; !sal*; !*config-api*; !*testkit*; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java index 02e2d12015..4496bd3263 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RemoteRpcImplementation.java @@ -7,7 +7,7 @@ import org.opendaylight.controller.remote.rpc.messages.ErrorResponse; import org.opendaylight.controller.remote.rpc.messages.InvokeRpc; import org.opendaylight.controller.remote.rpc.messages.RpcResponse; import org.opendaylight.controller.remote.rpc.utils.ActorUtil; -import org.opendaylight.controller.remote.rpc.utils.XmlUtils; +import org.opendaylight.controller.xml.codec.XmlUtils; import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation; import org.opendaylight.controller.sal.core.api.RpcImplementation; import org.opendaylight.yangtools.yang.common.QName; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java index 611618f1f6..4ec96c29cd 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/RpcBroker.java @@ -20,7 +20,7 @@ import org.opendaylight.controller.remote.rpc.utils.LatestEntryRoutingLogic; import org.opendaylight.controller.remote.rpc.registry.RpcRegistry; import org.opendaylight.controller.remote.rpc.utils.ActorUtil; import org.opendaylight.controller.remote.rpc.utils.RoutingLogic; -import org.opendaylight.controller.remote.rpc.utils.XmlUtils; +import org.opendaylight.controller.xml.codec.XmlUtils; import org.opendaylight.controller.sal.connector.api.RpcRouter; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.yangtools.yang.common.RpcResult; diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java index e2ebcb2b25..76f5930457 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistry.java @@ -60,6 +60,8 @@ public class RpcRegistry extends UntypedActor { public RpcRegistry() { bucketStore = getContext().actorOf(Props.create(BucketStore.class), "store"); + + log.info("Bucket store path = {}", bucketStore.path().toString()); } public RpcRegistry(ActorRef bucketStore) { diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java index 2f634ce1fa..3b078aa062 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStore.java @@ -9,12 +9,14 @@ package org.opendaylight.controller.remote.rpc.registry.gossip; import akka.actor.ActorRef; +import akka.actor.ActorRefProvider; import akka.actor.Address; import akka.actor.Props; import akka.actor.UntypedActor; -import akka.cluster.Cluster; +import akka.cluster.ClusterActorRefProvider; import akka.event.Logging; import akka.event.LoggingAdapter; +import org.opendaylight.controller.utils.ConditionalProbe; import java.util.HashMap; import java.util.Map; @@ -64,25 +66,17 @@ public class BucketStore extends UntypedActor { /** * Cluster address for this node */ - private final Address selfAddress = Cluster.get(getContext().system()).selfAddress(); + private Address selfAddress; - /** - * Our private gossiper - */ - private ActorRef gossiper; + private ConditionalProbe probe; - public BucketStore(){ - gossiper = getContext().actorOf(Props.create(Gossiper.class), "gossiper"); - } + @Override + public void preStart(){ + ActorRefProvider provider = getContext().provider(); + selfAddress = provider.getDefaultAddress(); - /** - * This constructor is useful for testing. - * TODO: Pass Props instead of ActorRef - * - * @param gossiper - */ - public BucketStore(ActorRef gossiper){ - this.gossiper = gossiper; + if ( provider instanceof ClusterActorRefProvider) + getContext().actorOf(Props.create(Gossiper.class), "gossiper"); } @Override @@ -90,25 +84,28 @@ public class BucketStore extends UntypedActor { log.debug("Received message: node[{}], message[{}]", selfAddress, message); - if (message instanceof UpdateBucket) - receiveUpdateBucket(((UpdateBucket) message).getBucket()); + if (probe != null) { + probe.tell(message, getSelf()); + } - else if (message instanceof GetAllBuckets) + if (message instanceof ConditionalProbe) { + log.info("Received probe {} {}", getSelf(), message); + probe = (ConditionalProbe) message; + } else if (message instanceof UpdateBucket) { + receiveUpdateBucket(((UpdateBucket) message).getBucket()); + } else if (message instanceof GetAllBuckets) { receiveGetAllBucket(); - - else if (message instanceof GetLocalBucket) + } else if (message instanceof GetLocalBucket) { receiveGetLocalBucket(); - - else if (message instanceof GetBucketsByMembers) - receiveGetBucketsByMembers(((GetBucketsByMembers) message).getMembers()); - - else if (message instanceof GetBucketVersions) + } else if (message instanceof GetBucketsByMembers) { + receiveGetBucketsByMembers( + ((GetBucketsByMembers) message).getMembers()); + } else if (message instanceof GetBucketVersions) { receiveGetBucketVersions(); - - else if (message instanceof UpdateRemoteBuckets) - receiveUpdateRemoteBuckets(((UpdateRemoteBuckets) message).getBuckets()); - - else { + } else if (message instanceof UpdateRemoteBuckets) { + receiveUpdateRemoteBuckets( + ((UpdateRemoteBuckets) message).getBuckets()); + } else { log.debug("Unhandled message [{}]", message); unhandled(message); } @@ -230,7 +227,7 @@ public class BucketStore extends UntypedActor { if (remoteVersion == null) remoteVersion = -1L; //update only if remote version is newer - if ( remoteVersion > localVersion ) { + if ( remoteVersion.longValue() > localVersion.longValue() ) { remoteBuckets.put(entry.getKey(), receivedBucket); versions.put(entry.getKey(), remoteVersion); } diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java index 2320789d59..a8bc25c40b 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/remote/rpc/registry/gossip/Gossiper.java @@ -8,11 +8,13 @@ package org.opendaylight.controller.remote.rpc.registry.gossip; import akka.actor.ActorRef; +import akka.actor.ActorRefProvider; import akka.actor.ActorSelection; import akka.actor.Address; import akka.actor.Cancellable; import akka.actor.UntypedActor; import akka.cluster.Cluster; +import akka.cluster.ClusterActorRefProvider; import akka.cluster.ClusterEvent; import akka.cluster.Member; import akka.dispatch.Mapper; @@ -60,12 +62,12 @@ public class Gossiper extends UntypedActor { final LoggingAdapter log = Logging.getLogger(getContext().system(), this); - Cluster cluster = Cluster.get(getContext().system()); + private Cluster cluster; /** * ActorSystem's address for the current cluster node. */ - private Address selfAddress = cluster.selfAddress(); + private Address selfAddress; /** * All known cluster members @@ -89,11 +91,16 @@ public class Gossiper extends UntypedActor { @Override public void preStart(){ - - cluster.subscribe(getSelf(), - ClusterEvent.initialStateAsEvents(), - ClusterEvent.MemberEvent.class, - ClusterEvent.UnreachableMember.class); + ActorRefProvider provider = getContext().provider(); + selfAddress = provider.getDefaultAddress(); + + if ( provider instanceof ClusterActorRefProvider ) { + cluster = Cluster.get(getContext().system()); + cluster.subscribe(getSelf(), + ClusterEvent.initialStateAsEvents(), + ClusterEvent.MemberEvent.class, + ClusterEvent.UnreachableMember.class); + } if (autoStartGossipTicks) { gossipTask = getContext().system().scheduler().schedule( diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/utils/ConditionalProbe.java b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/utils/ConditionalProbe.java new file mode 100644 index 0000000000..13cec54175 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/main/java/org/opendaylight/controller/utils/ConditionalProbe.java @@ -0,0 +1,32 @@ +/* + * 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.utils; + +import akka.actor.ActorRef; +import com.google.common.base.Predicate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConditionalProbe { + private final ActorRef actorRef; + private final Predicate predicate; + Logger log = LoggerFactory.getLogger(ConditionalProbe.class); + + public ConditionalProbe(ActorRef actorRef, Predicate predicate) { + this.actorRef = actorRef; + this.predicate = predicate; + } + + public void tell(Object message, ActorRef sender){ + if(predicate.apply(message)) { + log.info("sending message to probe {}", message); + actorRef.tell(message, sender); + } + } +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java index da3942a828..e6793741a3 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/RpcRegistryTest.java @@ -1,14 +1,16 @@ package org.opendaylight.controller.remote.rpc.registry; + import akka.actor.ActorPath; import akka.actor.ActorRef; import akka.actor.ActorSelection; import akka.actor.ActorSystem; import akka.actor.ChildActorPath; import akka.actor.Props; -import akka.japi.Pair; import akka.testkit.JavaTestKit; +import com.google.common.base.Predicate; import com.typesafe.config.ConfigFactory; + import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; @@ -16,267 +18,269 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl; +import org.opendaylight.controller.remote.rpc.registry.gossip.Messages; import org.opendaylight.controller.sal.connector.api.RpcRouter; +import org.opendaylight.controller.utils.ConditionalProbe; import org.opendaylight.yangtools.yang.common.QName; import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.duration.FiniteDuration; +import javax.annotation.Nullable; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.SetLocalRouter; import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.AddOrUpdateRoutes; import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.RemoveRoutes; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRouters; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.FindRoutersReply; -import static org.opendaylight.controller.remote.rpc.registry.RpcRegistry.Messages.SetLocalRouter; public class RpcRegistryTest { - private static ActorSystem node1; - private static ActorSystem node2; - private static ActorSystem node3; - - private ActorRef registry1; - private ActorRef registry2; - private ActorRef registry3; - - @BeforeClass - public static void setup() throws InterruptedException { - Thread.sleep(1000); //give some time for previous test to close netty ports - node1 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberA")); - node2 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberB")); - node3 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberC")); - } - - @AfterClass - public static void teardown(){ - JavaTestKit.shutdownActorSystem(node1); - JavaTestKit.shutdownActorSystem(node2); - JavaTestKit.shutdownActorSystem(node3); - if (node1 != null) - node1.shutdown(); - if (node2 != null) - node2.shutdown(); - if (node3 != null) - node3.shutdown(); - - } - - @Before - public void createRpcRegistry() throws InterruptedException { - registry1 = node1.actorOf(Props.create(RpcRegistry.class)); - registry2 = node2.actorOf(Props.create(RpcRegistry.class)); - registry3 = node3.actorOf(Props.create(RpcRegistry.class)); - } - - @After - public void stopRpcRegistry() throws InterruptedException { - if (registry1 != null) - node1.stop(registry1); - if (registry2 != null) - node2.stop(registry2); - if (registry3 != null) - node3.stop(registry3); - } + private static ActorSystem node1; + private static ActorSystem node2; + private static ActorSystem node3; + + private ActorRef registry1; + private ActorRef registry2; + private ActorRef registry3; + + @BeforeClass + public static void setup() throws InterruptedException { + node1 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberA")); + node2 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberB")); + node3 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberC")); + } + + @AfterClass + public static void teardown() { + JavaTestKit.shutdownActorSystem(node1); + JavaTestKit.shutdownActorSystem(node2); + JavaTestKit.shutdownActorSystem(node3); + if (node1 != null) + node1.shutdown(); + if (node2 != null) + node2.shutdown(); + if (node3 != null) + node3.shutdown(); + + } + + @Before + public void createRpcRegistry() throws InterruptedException { + registry1 = node1.actorOf(Props.create(RpcRegistry.class)); + registry2 = node2.actorOf(Props.create(RpcRegistry.class)); + registry3 = node3.actorOf(Props.create(RpcRegistry.class)); + } + + @After + public void stopRpcRegistry() throws InterruptedException { + if (registry1 != null) + node1.stop(registry1); + if (registry2 != null) + node2.stop(registry2); + if (registry3 != null) + node3.stop(registry3); + } + + /** + * One node cluster. + * 1. Register rpc, ensure router can be found + * 2. Then remove rpc, ensure its deleted + * + * @throws URISyntaxException + * @throws InterruptedException + */ + @Test + public void testAddRemoveRpcOnSameNode() throws URISyntaxException, InterruptedException { + validateSystemStartup(); + + final JavaTestKit mockBroker = new JavaTestKit(node1); + + final ActorPath bucketStorePath = new ChildActorPath(registry1.path(), "store"); + + //install probe + final JavaTestKit probe1 = createProbeForMessage( + node1, bucketStorePath, Messages.BucketStoreMessages.UpdateBucket.class); + + //Add rpc on node 1 + registry1.tell(new SetLocalRouter(mockBroker.getRef()), mockBroker.getRef()); + registry1.tell(getAddRouteMessage(), mockBroker.getRef()); + + //Bucket store should get an update bucket message. Updated bucket contains added rpc. + probe1.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateBucket.class); + + //Now remove rpc + registry1.tell(getRemoveRouteMessage(), mockBroker.getRef()); + + //Bucket store should get an update bucket message. Rpc is removed in the updated bucket + probe1.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateBucket.class); + + + } + + + /** + * Three node cluster. + * 1. Register rpc on 1 node, ensure 2nd node gets updated + * 2. Remove rpc on 1 node, ensure 2nd node gets updated + * + * @throws URISyntaxException + * @throws InterruptedException + */ + @Test + public void testRpcAddRemoveInCluster() throws URISyntaxException, InterruptedException { - /** - * One node cluster. - * 1. Register rpc, ensure router can be found - * 2. Then remove rpc, ensure its deleted - * - * @throws URISyntaxException - * @throws InterruptedException - */ - @Test - public void testAddRemoveRpcOnSameNode() throws URISyntaxException, InterruptedException { - - final JavaTestKit mockBroker = new JavaTestKit(node1); - - //Add rpc on node 1 - registry1.tell(new SetLocalRouter(mockBroker.getRef()), mockBroker.getRef()); - registry1.tell(getAddRouteMessage(), mockBroker.getRef()); - - Thread.sleep(1000);// - - //find the route on node 1's registry - registry1.tell(new FindRouters(createRouteId()), mockBroker.getRef()); - FindRoutersReply message = mockBroker.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); - List> pairs = message.getRouterWithUpdateTime(); - - validateRouterReceived(pairs, mockBroker.getRef()); - - //Now remove rpc - registry1.tell(getRemoveRouteMessage(), mockBroker.getRef()); - Thread.sleep(1000); - //find the route on node 1's registry - registry1.tell(new FindRouters(createRouteId()), mockBroker.getRef()); - message = mockBroker.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); - pairs = message.getRouterWithUpdateTime(); - - Assert.assertTrue(pairs.isEmpty()); - } + validateSystemStartup(); + + final JavaTestKit mockBroker1 = new JavaTestKit(node1); + + //install probe on node2's bucket store + final ActorPath bucketStorePath = new ChildActorPath(registry2.path(), "store"); + final JavaTestKit probe2 = createProbeForMessage( + node2, bucketStorePath, Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - /** - * Three node cluster. - * 1. Register rpc on 1 node, ensure its router can be found on other 2. - * 2. Remove rpc on 1 node, ensure its removed on other 2. - * - * @throws URISyntaxException - * @throws InterruptedException - */ - @Test - public void testRpcAddRemoveInCluster() throws URISyntaxException, InterruptedException { - validateSystemStartup(); + //Add rpc on node 1 + registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); + registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); - final JavaTestKit mockBroker1 = new JavaTestKit(node1); - final JavaTestKit mockBroker2 = new JavaTestKit(node2); - final JavaTestKit mockBroker3 = new JavaTestKit(node3); + //Bucket store on node2 should get a message to update its local copy of remote buckets + probe2.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - //Add rpc on node 1 - registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); - registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); + //Now remove + registry1.tell(getRemoveRouteMessage(), mockBroker1.getRef()); - Thread.sleep(1000);// give some time for bucket store data sync + //Bucket store on node2 should get a message to update its local copy of remote buckets + probe2.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - //find the route in node 2's registry - List> pairs = findRouters(registry2, mockBroker2); - validateRouterReceived(pairs, mockBroker1.getRef()); + } - //find the route in node 3's registry - pairs = findRouters(registry3, mockBroker3); - validateRouterReceived(pairs, mockBroker1.getRef()); + /** + * Three node cluster. + * Register rpc on 2 nodes. Ensure 3rd gets updated. + * + * @throws Exception + */ + @Test + public void testRpcAddedOnMultiNodes() throws Exception { - //Now remove - registry1.tell(getRemoveRouteMessage(), mockBroker1.getRef()); - Thread.sleep(1000);// give some time for bucket store data sync + validateSystemStartup(); - pairs = findRouters(registry2, mockBroker2); - Assert.assertTrue(pairs.isEmpty()); + final JavaTestKit mockBroker1 = new JavaTestKit(node1); + final JavaTestKit mockBroker2 = new JavaTestKit(node2); + final JavaTestKit mockBroker3 = new JavaTestKit(node3); - pairs = findRouters(registry3, mockBroker3); - Assert.assertTrue(pairs.isEmpty()); - } + registry3.tell(new SetLocalRouter(mockBroker3.getRef()), mockBroker3.getRef()); - /** - * Three node cluster. - * Register rpc on 2 nodes. Ensure 2 routers are found on 3rd. - * - * @throws Exception - */ - @Test - public void testAnRpcAddedOnMultiNodesShouldReturnMultiRouter() throws Exception { + //install probe on node 3 + final ActorPath bucketStorePath = new ChildActorPath(registry3.path(), "store"); + final JavaTestKit probe3 = createProbeForMessage( + node3, bucketStorePath, Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - validateSystemStartup(); - final JavaTestKit mockBroker1 = new JavaTestKit(node1); - final JavaTestKit mockBroker2 = new JavaTestKit(node2); - final JavaTestKit mockBroker3 = new JavaTestKit(node3); + //Add rpc on node 1 + registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); + registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); - //Thread.sleep(5000);//let system come up + probe3.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateRemoteBuckets.class); - //Add rpc on node 1 - registry1.tell(new SetLocalRouter(mockBroker1.getRef()), mockBroker1.getRef()); - registry1.tell(getAddRouteMessage(), mockBroker1.getRef()); - //Add same rpc on node 2 - registry2.tell(new SetLocalRouter(mockBroker2.getRef()), mockBroker2.getRef()); - registry2.tell(getAddRouteMessage(), mockBroker2.getRef()); + //Add same rpc on node 2 + registry2.tell(new SetLocalRouter(mockBroker2.getRef()), mockBroker2.getRef()); + registry2.tell(getAddRouteMessage(), mockBroker2.getRef()); - registry3.tell(new SetLocalRouter(mockBroker3.getRef()), mockBroker3.getRef()); - Thread.sleep(1000);// give some time for bucket store data sync + probe3.expectMsgClass( + FiniteDuration.apply(10, TimeUnit.SECONDS), + Messages.BucketStoreMessages.UpdateRemoteBuckets.class); + } - //find the route in node 3's registry - registry3.tell(new FindRouters(createRouteId()), mockBroker3.getRef()); - FindRoutersReply message = mockBroker3.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); - List> pairs = message.getRouterWithUpdateTime(); + private JavaTestKit createProbeForMessage(ActorSystem node, ActorPath subjectPath, final Class clazz) { + final JavaTestKit probe = new JavaTestKit(node); - validateMultiRouterReceived(pairs, mockBroker1.getRef(), mockBroker2.getRef()); + ConditionalProbe conditionalProbe = + new ConditionalProbe(probe.getRef(), new Predicate() { + @Override + public boolean apply(@Nullable Object input) { + return clazz.equals(input.getClass()); + } + }); - } + ActorSelection subject = node.actorSelection(subjectPath); + subject.tell(conditionalProbe, ActorRef.noSender()); - private List> findRouters(ActorRef registry, JavaTestKit receivingActor) throws URISyntaxException { - registry.tell(new FindRouters(createRouteId()), receivingActor.getRef()); - FindRoutersReply message = receivingActor.expectMsgClass(JavaTestKit.duration("10 second"), FindRoutersReply.class); - return message.getRouterWithUpdateTime(); - } + return probe; - private void validateMultiRouterReceived(List> actual, ActorRef... expected) { - Assert.assertTrue(actual != null); - Assert.assertTrue(actual.size() == expected.length); - } + } - private void validateRouterReceived(List> actual, ActorRef expected){ - Assert.assertTrue(actual != null); - Assert.assertTrue(actual.size() == 1); + private void validateSystemStartup() throws InterruptedException { - for (Pair pair : actual){ - Assert.assertTrue(expected.path().uid() == pair.first().path().uid()); - } - } + ActorPath gossiper1Path = new ChildActorPath(new ChildActorPath(registry1.path(), "store"), "gossiper"); + ActorPath gossiper2Path = new ChildActorPath(new ChildActorPath(registry2.path(), "store"), "gossiper"); + ActorPath gossiper3Path = new ChildActorPath(new ChildActorPath(registry3.path(), "store"), "gossiper"); - private void validateSystemStartup() throws InterruptedException { + ActorSelection gossiper1 = node1.actorSelection(gossiper1Path); + ActorSelection gossiper2 = node2.actorSelection(gossiper2Path); + ActorSelection gossiper3 = node3.actorSelection(gossiper3Path); - Thread.sleep(5000); - ActorPath gossiper1Path = new ChildActorPath(new ChildActorPath(registry1.path(), "store"), "gossiper"); - ActorPath gossiper2Path = new ChildActorPath(new ChildActorPath(registry2.path(), "store"), "gossiper"); - ActorPath gossiper3Path = new ChildActorPath(new ChildActorPath(registry3.path(), "store"), "gossiper"); - ActorSelection gossiper1 = node1.actorSelection(gossiper1Path); - ActorSelection gossiper2 = node2.actorSelection(gossiper2Path); - ActorSelection gossiper3 = node3.actorSelection(gossiper3Path); + if (!resolveReference(gossiper1, gossiper2, gossiper3)) + Assert.fail("Could not find gossipers"); + } + private Boolean resolveReference(ActorSelection... gossipers) { - if (!resolveReference(gossiper1, gossiper2, gossiper3)) - Assert.fail("Could not find gossipers"); - } + Boolean resolved = true; + for (int i = 0; i < 5; i++) { - private Boolean resolveReference(ActorSelection... gossipers) throws InterruptedException { + resolved = true; + System.out.println(System.currentTimeMillis() + " Resolving gossipers; trial #" + i); - Boolean resolved = true; + for (ActorSelection gossiper : gossipers) { + ActorRef ref = null; - for (int i=0; i< 5; i++) { - Thread.sleep(1000); - for (ActorSelection gossiper : gossipers) { - Future future = gossiper.resolveOne(new FiniteDuration(5000, TimeUnit.MILLISECONDS)); + try { + Future future = gossiper.resolveOne(new FiniteDuration(15000, TimeUnit.MILLISECONDS)); + ref = Await.result(future, new FiniteDuration(10000, TimeUnit.MILLISECONDS)); + } catch (Exception e) { + System.out.println("Could not find gossiper in attempt#" + i + ". Got exception " + e.getMessage()); + } - ActorRef ref = null; - try { - ref = Await.result(future, new FiniteDuration(10000, TimeUnit.MILLISECONDS)); - } catch (Exception e) { - e.printStackTrace(); - } + if (ref == null) + resolved = false; + } - if (ref == null) - resolved = false; - } + if (resolved) break; - if (resolved) break; - } - return resolved; } + return resolved; + } - private AddOrUpdateRoutes getAddRouteMessage() throws URISyntaxException { - return new AddOrUpdateRoutes(createRouteIds()); - } + private AddOrUpdateRoutes getAddRouteMessage() throws URISyntaxException { + return new AddOrUpdateRoutes(createRouteIds()); + } - private RemoveRoutes getRemoveRouteMessage() throws URISyntaxException { - return new RemoveRoutes(createRouteIds()); - } + private RemoveRoutes getRemoveRouteMessage() throws URISyntaxException { + return new RemoveRoutes(createRouteIds()); + } - private List> createRouteIds() throws URISyntaxException { - QName type = new QName(new URI("/mockrpc"), "mockrpc"); - List> routeIds = new ArrayList<>(); - routeIds.add(new RouteIdentifierImpl(null, type, null)); - return routeIds; - } + private List> createRouteIds() throws URISyntaxException { + QName type = new QName(new URI("/mockrpc"), "mockrpc"); + List> routeIds = new ArrayList<>(); + routeIds.add(new RouteIdentifierImpl(null, type, null)); + return routeIds; + } - private RpcRouter.RouteIdentifier createRouteId() throws URISyntaxException { - QName type = new QName(new URI("/mockrpc"), "mockrpc"); - return new RouteIdentifierImpl(null, type, null); - } -} \ No newline at end of file +} diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStoreTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStoreTest.java index fd6664af9e..78fcbd3a14 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStoreTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/BucketStoreTest.java @@ -7,34 +7,29 @@ */ package org.opendaylight.controller.remote.rpc.registry.gossip; -import akka.actor.ActorRef; import akka.actor.ActorSystem; +import akka.actor.Address; import akka.actor.Props; import akka.testkit.TestActorRef; -import akka.testkit.TestProbe; import com.typesafe.config.ConfigFactory; -import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; -import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.remote.rpc.TerminationMonitor; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; +import java.util.HashMap; +import java.util.Map; public class BucketStoreTest { private static ActorSystem system; private static BucketStore store; - private BucketStore mockStore; - @BeforeClass public static void setup() { - system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster")); + system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("unit-test")); system.actorOf(Props.create(TerminationMonitor.class), "termination-monitor"); store = createStore(); @@ -45,25 +40,108 @@ public class BucketStoreTest { system.shutdown(); } - @Before - public void createMocks(){ - mockStore = spy(store); - } - - @After - public void resetMocks(){ - reset(mockStore); - } - + /** + * Given a new local bucket + * Should replace + */ @Test - public void testReceiveUpdateBucket_WhenInputBucketShouldUpdateVersion(){ + public void testReceiveUpdateBucket(){ Bucket bucket = new BucketImpl(); Long expectedVersion = bucket.getVersion(); - mockStore.receiveUpdateBucket(bucket); + store.receiveUpdateBucket(bucket); + + Assert.assertEquals(bucket, store.getLocalBucket()); + Assert.assertEquals(expectedVersion, store.getLocalBucket().getVersion()); + } + + /** + * Given remote buckets + * Should merge with local copy of remote buckets + */ + @Test + public void testReceiveUpdateRemoteBuckets(){ + + Address localAddress = system.provider().getDefaultAddress(); + Bucket localBucket = new BucketImpl(); + + Address a1 = new Address("tcp", "system1"); + Address a2 = new Address("tcp", "system2"); + Address a3 = new Address("tcp", "system3"); + + Bucket b1 = new BucketImpl(); + Bucket b2 = new BucketImpl(); + Bucket b3 = new BucketImpl(); + + Map remoteBuckets = new HashMap<>(3); + remoteBuckets.put(a1, b1); + remoteBuckets.put(a2, b2); + remoteBuckets.put(a3, b3); + remoteBuckets.put(localAddress, localBucket); + + //Given remote buckets + store.receiveUpdateRemoteBuckets(remoteBuckets); + + //Should NOT contain local bucket + //Should contain ONLY 3 entries i.e a1, a2, a3 + Map remoteBucketsInStore = store.getRemoteBuckets(); + Assert.assertFalse("remote buckets contains local bucket", remoteBucketsInStore.containsKey(localAddress)); + Assert.assertTrue(remoteBucketsInStore.size() == 3); + + //Add a new remote bucket + Address a4 = new Address("tcp", "system4"); + Bucket b4 = new BucketImpl(); + remoteBuckets.clear(); + remoteBuckets.put(a4, b4); + store.receiveUpdateRemoteBuckets(remoteBuckets); + + //Should contain a4 + //Should contain 4 entries now i.e a1, a2, a3, a4 + remoteBucketsInStore = store.getRemoteBuckets(); + Assert.assertTrue("Does not contain a4", remoteBucketsInStore.containsKey(a4)); + Assert.assertTrue(remoteBucketsInStore.size() == 4); + + //Update a bucket + Bucket b3_new = new BucketImpl(); + remoteBuckets.clear(); + remoteBuckets.put(a3, b3_new); + remoteBuckets.put(a1, null); + remoteBuckets.put(a2, null); + store.receiveUpdateRemoteBuckets(remoteBuckets); + + //Should only update a3 + remoteBucketsInStore = store.getRemoteBuckets(); + Bucket b3_inStore = remoteBucketsInStore.get(a3); + Assert.assertEquals(b3_new.getVersion(), b3_inStore.getVersion()); + + //Should NOT update a1 and a2 + Bucket b1_inStore = remoteBucketsInStore.get(a1); + Bucket b2_inStore = remoteBucketsInStore.get(a2); + Assert.assertEquals(b1.getVersion(), b1_inStore.getVersion()); + Assert.assertEquals(b2.getVersion(), b2_inStore.getVersion()); + Assert.assertTrue(remoteBucketsInStore.size() == 4); + + //Should update versions map + //versions map contains versions for all remote buckets (4) + local bucket + //so it should have total 5. + Map versionsInStore = store.getVersions(); + Assert.assertTrue(String.format("Expected:%s, Actual:%s", 5, versionsInStore.size()), + versionsInStore.size() == 5); + Assert.assertEquals(b1.getVersion(), versionsInStore.get(a1)); + Assert.assertEquals(b2.getVersion(), versionsInStore.get(a2)); + Assert.assertEquals(b3_new.getVersion(), versionsInStore.get(a3)); + Assert.assertEquals(b4.getVersion(), versionsInStore.get(a4)); + + //Send older version of bucket + remoteBuckets.clear(); + remoteBuckets.put(a3, b3); + store.receiveUpdateRemoteBuckets(remoteBuckets); + + //Should NOT update a3 + remoteBucketsInStore = store.getRemoteBuckets(); + b3_inStore = remoteBucketsInStore.get(a3); + Assert.assertTrue(b3_inStore.getVersion().longValue() == b3_new.getVersion().longValue()); - Assert.assertEquals(bucket, mockStore.getLocalBucket()); - Assert.assertEquals(expectedVersion, mockStore.getLocalBucket().getVersion()); } /** @@ -72,11 +150,8 @@ public class BucketStoreTest { * @return instance of BucketStore class */ private static BucketStore createStore(){ - TestProbe mockActor = new TestProbe(system); - ActorRef mockGossiper = mockActor.ref(); - final Props props = Props.create(BucketStore.class, mockGossiper); + final Props props = Props.create(BucketStore.class); final TestActorRef testRef = TestActorRef.create(system, props, "testStore"); - return testRef.underlyingActor(); } } \ No newline at end of file diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/GossiperTest.java b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/GossiperTest.java index bb60ed6eec..e61b54f067 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/GossiperTest.java +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/java/org/opendaylight/controller/remote/rpc/registry/gossip/GossiperTest.java @@ -45,7 +45,7 @@ public class GossiperTest { @BeforeClass public static void setup() throws InterruptedException { - system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster")); + system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("unit-test")); system.actorOf(Props.create(TerminationMonitor.class), "termination-monitor"); gossiper = createGossiper(); diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf index 61fab7e0fe..8100ed35ab 100644 --- a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/application.conf @@ -1,6 +1,6 @@ odl-cluster{ akka { - loglevel = "INFO" + loglevel = "DEBUG" #log-config-on-start = on actor { @@ -32,10 +32,7 @@ odl-cluster{ unit-test{ akka { loglevel = "INFO" - loggers = ["akka.event.slf4j.Slf4jLogger"] - actor { - provider = "akka.cluster.ClusterActorRefProvider" - } + #loggers = ["akka.event.slf4j.Slf4jLogger"] } } @@ -45,6 +42,9 @@ memberA{ loggers = ["akka.event.slf4j.Slf4jLogger"] actor { provider = "akka.cluster.ClusterActorRefProvider" + debug { + #lifecycle = on + } } remote { log-received-messages = off diff --git a/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/logback.xml b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/logback.xml new file mode 100644 index 0000000000..5246f01d05 --- /dev/null +++ b/opendaylight/md-sal/sal-remoterpc-connector/src/test/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfDocumentedException.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfDocumentedException.java index 2ceef3203c..bfa987ab8d 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfDocumentedException.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfDocumentedException.java @@ -87,7 +87,9 @@ public class RestconfDocumentedException extends WebApplicationException { * Constructs an instance with the given errors. */ public RestconfDocumentedException(String message, Throwable cause, List errors) { - super(message, cause); + // FIXME: We override getMessage so supplied message is lost for any public access + // this was lost also in original code. + super(cause); if(!errors.isEmpty()) { this.errors = ImmutableList.copyOf(errors); } else { diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/BaseYangSwaggerGenerator.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/BaseYangSwaggerGenerator.java index 1b27182514..5d0f3612e4 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/BaseYangSwaggerGenerator.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/BaseYangSwaggerGenerator.java @@ -57,6 +57,8 @@ public class BaseYangSwaggerGenerator { protected static final String API_VERSION = "1.0.0"; protected static final String SWAGGER_VERSION = "1.2"; protected static final String RESTCONF_CONTEXT_ROOT = "restconf"; + + static final String MODULE_NAME_SUFFIX = "_module"; protected final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); private final ModelGenerator jsonConverter = new ModelGenerator(); @@ -164,6 +166,7 @@ public class BaseYangSwaggerGenerator { List pathParams = new ArrayList(); String resourcePath = getDataStorePath("/config/", context); + addRootPostLink(m, (DataNodeContainer) node, pathParams, resourcePath, apis); addApis(node, apis, resourcePath, pathParams, schemaContext, true); pathParams = new ArrayList(); @@ -199,6 +202,16 @@ public class BaseYangSwaggerGenerator { return null; } + private void addRootPostLink(final Module m, final DataNodeContainer node, final List pathParams, + final String resourcePath, final List apis) { + if (containsListOrContainer(m.getChildNodes())) { + final Api apiForRootPostUri = new Api(); + apiForRootPostUri.setPath(resourcePath); + apiForRootPostUri.setOperations(operationPost(m.getName()+MODULE_NAME_SUFFIX, m.getDescription(), m, pathParams, true)); + apis.add(apiForRootPostUri); + } + } + protected ApiDeclaration createApiDeclaration(String basePath) { ApiDeclaration doc = new ApiDeclaration(); doc.setApiVersion(API_VERSION); @@ -229,45 +242,72 @@ public class BaseYangSwaggerGenerator { String resourcePath = parentPath + createPath(node, pathParams, schemaContext) + "/"; _logger.debug("Adding path: [{}]", resourcePath); api.setPath(resourcePath); - api.setOperations(operations(node, pathParams, addConfigApi)); - apis.add(api); + + Iterable childSchemaNodes = Collections. emptySet(); if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) { - DataNodeContainer schemaNode = (DataNodeContainer) node; - - for (DataSchemaNode childNode : schemaNode.getChildNodes()) { - // We don't support going to leaf nodes today. Only lists and - // containers. - if (childNode instanceof ListSchemaNode || childNode instanceof ContainerSchemaNode) { - // keep config and operation attributes separate. - if (childNode.isConfiguration() == addConfigApi) { - addApis(childNode, apis, resourcePath, pathParams, schemaContext, addConfigApi); - } + DataNodeContainer dataNodeContainer = (DataNodeContainer) node; + childSchemaNodes = dataNodeContainer.getChildNodes(); + } + api.setOperations(operation(node, pathParams, addConfigApi, childSchemaNodes)); + apis.add(api); + + for (DataSchemaNode childNode : childSchemaNodes) { + if (childNode instanceof ListSchemaNode || childNode instanceof ContainerSchemaNode) { + // keep config and operation attributes separate. + if (childNode.isConfiguration() == addConfigApi) { + addApis(childNode, apis, resourcePath, pathParams, schemaContext, addConfigApi); } } } } + private boolean containsListOrContainer(final Iterable nodes) { + for (DataSchemaNode child : nodes) { + if (child instanceof ListSchemaNode || child instanceof ContainerSchemaNode) { + return true; + } + } + return false; + } + /** * @param node * @param pathParams * @return */ - private List operations(DataSchemaNode node, List pathParams, boolean isConfig) { + private List operation(DataSchemaNode node, List pathParams, boolean isConfig, Iterable childSchemaNodes) { List operations = new ArrayList<>(); OperationBuilder.Get getBuilder = new OperationBuilder.Get(node, isConfig); operations.add(getBuilder.pathParams(pathParams).build()); if (isConfig) { - OperationBuilder.Post postBuilder = new OperationBuilder.Post(node); - operations.add(postBuilder.pathParams(pathParams).build()); - - OperationBuilder.Put putBuilder = new OperationBuilder.Put(node); + OperationBuilder.Put putBuilder = new OperationBuilder.Put(node.getQName().getLocalName(), + node.getDescription()); operations.add(putBuilder.pathParams(pathParams).build()); OperationBuilder.Delete deleteBuilder = new OperationBuilder.Delete(node); operations.add(deleteBuilder.pathParams(pathParams).build()); + + if (containsListOrContainer(childSchemaNodes)) { + operations.addAll(operationPost(node.getQName().getLocalName(), node.getDescription(), (DataNodeContainer) node, + pathParams, isConfig)); + } + } + return operations; + } + + /** + * @param node + * @param pathParams + * @return + */ + private List operationPost(final String name, final String description, final DataNodeContainer dataNodeContainer, List pathParams, boolean isConfig) { + List operations = new ArrayList<>(); + if (isConfig) { + OperationBuilder.Post postBuilder = new OperationBuilder.Post(name, description, dataNodeContainer); + operations.add(postBuilder.pathParams(pathParams).build()); } return operations; } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ModelGenerator.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ModelGenerator.java index 819892f647..f4274870c9 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ModelGenerator.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ModelGenerator.java @@ -7,8 +7,11 @@ */ package org.opendaylight.controller.sal.rest.doc.impl; +import static org.opendaylight.controller.sal.rest.doc.impl.BaseYangSwaggerGenerator.MODULE_NAME_SUFFIX; +import static org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder.Post.METHOD_NAME; import static org.opendaylight.controller.sal.rest.doc.util.RestDocgenUtil.resolveNodesName; +import com.google.common.base.Preconditions; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -27,6 +30,7 @@ import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ChoiceNode; import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; @@ -35,6 +39,7 @@ import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; 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.SchemaNode; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition; @@ -123,42 +128,26 @@ public class ModelGenerator { public JSONObject convertToJsonSchema(Module module, SchemaContext schemaContext) throws IOException, JSONException { JSONObject models = new JSONObject(); topLevelModule = module; - processContainers(module, models, schemaContext); + processModules(module, models); + processContainersAndLists(module, models, schemaContext); processRPCs(module, models, schemaContext); processIdentities(module, models); return models; } - private void processContainers(Module module, JSONObject models, SchemaContext schemaContext) throws IOException, - JSONException { + private void processModules(Module module, JSONObject models) throws JSONException { + createConcreteModelForPost(models, module.getName()+MODULE_NAME_SUFFIX, createPropertiesForPost(module)); + } + + private void processContainersAndLists(Module module, JSONObject models, SchemaContext schemaContext) + throws IOException, JSONException { String moduleName = module.getName(); for (DataSchemaNode childNode : module.getChildNodes()) { - JSONObject configModuleJSON = null; - JSONObject operationalModuleJSON = null; - - String childNodeName = childNode.getQName().getLocalName(); - /* - * For every container in the module - */ - if (childNode instanceof ContainerSchemaNode) { - configModuleJSON = processContainer((ContainerSchemaNode) childNode, moduleName, true, models, true, - schemaContext); - operationalModuleJSON = processContainer((ContainerSchemaNode) childNode, moduleName, true, models, - false, schemaContext); - } - - if (configModuleJSON != null) { - _logger.debug("Adding model for [{}]", OperationBuilder.CONFIG + childNodeName); - configModuleJSON.put("id", OperationBuilder.CONFIG + childNodeName); - models.put(OperationBuilder.CONFIG + childNodeName, configModuleJSON); - } - if (operationalModuleJSON != null) { - _logger.debug("Adding model for [{}]", OperationBuilder.OPERATIONAL + childNodeName); - operationalModuleJSON.put("id", OperationBuilder.OPERATIONAL + childNodeName); - models.put(OperationBuilder.OPERATIONAL + childNodeName, operationalModuleJSON); - } + // For every container and list in the module + processDataNodeContainer((DataNodeContainer) childNode, moduleName, models, true, schemaContext); + processDataNodeContainer((DataNodeContainer) childNode, moduleName, models, false, schemaContext); } } @@ -180,7 +169,7 @@ public class ModelGenerator { ContainerSchemaNode input = rpc.getInput(); if (input != null) { - JSONObject inputJSON = processContainer(input, moduleName, true, models, schemaContext); + JSONObject inputJSON = processDataNodeContainer(input, moduleName, models, schemaContext); String filename = "(" + rpc.getQName().getLocalName() + ")input"; inputJSON.put("id", filename); // writeToFile(filename, inputJSON.toString(2), moduleName); @@ -189,7 +178,7 @@ public class ModelGenerator { ContainerSchemaNode output = rpc.getOutput(); if (output != null) { - JSONObject outputJSON = processContainer(output, moduleName, true, models, schemaContext); + JSONObject outputJSON = processDataNodeContainer(output, moduleName, models, schemaContext); String filename = "(" + rpc.getQName().getLocalName() + ")output"; outputJSON.put("id", filename); models.put(filename, outputJSON); @@ -251,7 +240,7 @@ public class ModelGenerator { } /** - * Processes the container node and populates the moduleJSON + * Processes the container and list nodes and populates the moduleJSON * * @param container * @param moduleName @@ -259,28 +248,67 @@ public class ModelGenerator { * @throws JSONException * @throws IOException */ - private JSONObject processContainer(ContainerSchemaNode container, String moduleName, boolean addSchemaStmt, - JSONObject models, SchemaContext schemaContext) throws JSONException, IOException { - return processContainer(container, moduleName, addSchemaStmt, models, (Boolean) null, schemaContext); + private JSONObject processDataNodeContainer(DataNodeContainer dataNode, String moduleName, JSONObject models, + SchemaContext schemaContext) throws JSONException, IOException { + return processDataNodeContainer(dataNode, moduleName, models, (Boolean) null, schemaContext); } - private JSONObject processContainer(ContainerSchemaNode container, String moduleName, boolean addSchemaStmt, - JSONObject models, Boolean isConfig, SchemaContext schemaContext) throws JSONException, IOException { - JSONObject moduleJSON = getSchemaTemplate(); - if (addSchemaStmt) { - moduleJSON = getSchemaTemplate(); - } else { - moduleJSON = new JSONObject(); + private JSONObject processDataNodeContainer(DataNodeContainer dataNode, String moduleName, JSONObject models, + Boolean isConfig, SchemaContext schemaContext) throws JSONException, IOException { + if (dataNode instanceof ListSchemaNode || dataNode instanceof ContainerSchemaNode) { + Preconditions.checkArgument(dataNode instanceof SchemaNode, "Data node should be also schema node"); + Iterable containerChildren = dataNode.getChildNodes(); + JSONObject properties = processChildren(containerChildren, ((SchemaNode) dataNode).getQName(), moduleName, + models, isConfig, schemaContext); + + String nodeName = (BooleanUtils.isNotFalse(isConfig) ? OperationBuilder.CONFIG + : OperationBuilder.OPERATIONAL) + ((SchemaNode) dataNode).getQName().getLocalName(); + + JSONObject childSchema = getSchemaTemplate(); + childSchema.put(TYPE_KEY, OBJECT_TYPE); + childSchema.put(PROPERTIES_KEY, properties); + childSchema.put("id", nodeName); + models.put(nodeName, childSchema); + + if (BooleanUtils.isNotFalse(isConfig)) { + createConcreteModelForPost(models, ((SchemaNode) dataNode).getQName().getLocalName(), + createPropertiesForPost(dataNode)); + } + + JSONObject items = new JSONObject(); + items.put(REF_KEY, nodeName); + JSONObject dataNodeProperties = new JSONObject(); + dataNodeProperties.put(TYPE_KEY, dataNode instanceof ListSchemaNode ? ARRAY_TYPE : OBJECT_TYPE); + dataNodeProperties.put(ITEMS_KEY, items); + + return dataNodeProperties; } - moduleJSON.put(TYPE_KEY, OBJECT_TYPE); + return null; + } - String containerDescription = container.getDescription(); - moduleJSON.put(DESCRIPTION_KEY, containerDescription); + private void createConcreteModelForPost(final JSONObject models, final String localName, final JSONObject properties) + throws JSONException { + String nodePostName = OperationBuilder.CONFIG + localName + METHOD_NAME; + JSONObject postSchema = getSchemaTemplate(); + postSchema.put(TYPE_KEY, OBJECT_TYPE); + postSchema.put("id", nodePostName); + postSchema.put(PROPERTIES_KEY, properties); + models.put(nodePostName, postSchema); + } - JSONObject properties = processChildren(container.getChildNodes(), container.getQName(), moduleName, models, - isConfig, schemaContext); - moduleJSON.put(PROPERTIES_KEY, properties); - return moduleJSON; + private JSONObject createPropertiesForPost(final DataNodeContainer dataNodeContainer) throws JSONException { + JSONObject properties = new JSONObject(); + for (DataSchemaNode childNode : dataNodeContainer.getChildNodes()) { + if (childNode instanceof ListSchemaNode || childNode instanceof ContainerSchemaNode) { + JSONObject items = new JSONObject(); + items.put(REF_KEY, "(config)" + childNode.getQName().getLocalName()); + JSONObject property = new JSONObject(); + property.put(TYPE_KEY, childNode instanceof ListSchemaNode ? ARRAY_TYPE : OBJECT_TYPE); + property.put(ITEMS_KEY, items); + properties.put(childNode.getQName().getLocalName(), property); + } + } + return properties; } private JSONObject processChildren(Iterable nodes, QName parentQName, String moduleName, @@ -312,7 +340,8 @@ public class ModelGenerator { if (node instanceof LeafSchemaNode) { property = processLeafNode((LeafSchemaNode) node); } else if (node instanceof ListSchemaNode) { - property = processListSchemaNode((ListSchemaNode) node, moduleName, models, isConfig, schemaContext); + property = processDataNodeContainer((ListSchemaNode) node, moduleName, models, isConfig, + schemaContext); } else if (node instanceof LeafListSchemaNode) { property = processLeafListNode((LeafListSchemaNode) node); @@ -324,7 +353,7 @@ public class ModelGenerator { property = processAnyXMLNode((AnyXmlSchemaNode) node); } else if (node instanceof ContainerSchemaNode) { - property = processContainer((ContainerSchemaNode) node, moduleName, false, models, isConfig, + property = processDataNodeContainer((ContainerSchemaNode) node, moduleName, models, isConfig, schemaContext); } else { @@ -407,50 +436,6 @@ public class ModelGenerator { } } - /** - * Parses a ListSchema node. - * - * Due to a limitation of the RAML--->JAX-RS tool, sub-properties must be in a separate JSON schema file. Hence, we - * have to write some properties to a new file, while continuing to process the rest. - * - * @param listNode - * @param moduleName - * @param isConfig - * @return - * @throws JSONException - * @throws IOException - */ - private JSONObject processListSchemaNode(ListSchemaNode listNode, String moduleName, JSONObject models, - Boolean isConfig, SchemaContext schemaContext) throws JSONException, IOException { - - String fileName = (BooleanUtils.isNotFalse(isConfig) ? OperationBuilder.CONFIG : OperationBuilder.OPERATIONAL) - + listNode.getQName().getLocalName(); - - JSONObject childSchemaProperties = processChildren(listNode.getChildNodes(), listNode.getQName(), moduleName, - models, schemaContext); - JSONObject childSchema = getSchemaTemplate(); - childSchema.put(TYPE_KEY, OBJECT_TYPE); - childSchema.put(PROPERTIES_KEY, childSchemaProperties); - - /* - * Due to a limitation of the RAML--->JAX-RS tool, sub-properties must be in a separate JSON schema file. Hence, - * we have to write some properties to a new file, while continuing to process the rest. - */ - // writeToFile(fileName, childSchema.toString(2), moduleName); - childSchema.put("id", fileName); - models.put(fileName, childSchema); - - JSONObject listNodeProperties = new JSONObject(); - listNodeProperties.put(TYPE_KEY, ARRAY_TYPE); - - JSONObject items = new JSONObject(); - items.put(REF_KEY, fileName); - listNodeProperties.put(ITEMS_KEY, items); - - return listNodeProperties; - - } - /** * * @param leafNode diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/model/builder/OperationBuilder.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/model/builder/OperationBuilder.java index 9a33ee31b3..7e27b50541 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/model/builder/OperationBuilder.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/model/builder/OperationBuilder.java @@ -9,10 +9,12 @@ package org.opendaylight.controller.sal.rest.doc.model.builder; import java.util.ArrayList; import java.util.List; - import org.opendaylight.controller.sal.rest.doc.swagger.Operation; import org.opendaylight.controller.sal.rest.doc.swagger.Parameter; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; /** * @@ -56,21 +58,21 @@ public final class OperationBuilder { */ public static class Put { protected Operation spec; - protected DataSchemaNode schemaNode; + protected String nodeName; private final String METHOD_NAME = "PUT"; - public Put(DataSchemaNode node) { - this.schemaNode = node; + public Put(String nodeName, final String description) { + this.nodeName = nodeName; spec = new Operation(); - spec.setType(CONFIG + node.getQName().getLocalName()); - spec.setNotes(node.getDescription()); + spec.setType(CONFIG + nodeName); + spec.setNotes(description); } public Put pathParams(List params) { List parameters = new ArrayList<>(params); Parameter payload = new Parameter(); payload.setParamType("body"); - payload.setType(CONFIG + schemaNode.getQName().getLocalName()); + payload.setType(CONFIG + nodeName); parameters.add(payload); spec.setParameters(parameters); return this; @@ -78,7 +80,7 @@ public final class OperationBuilder { public Operation build() { spec.setMethod(METHOD_NAME); - spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName()); + spec.setNickname(METHOD_NAME + "-" + nodeName); return spec; } } @@ -88,18 +90,43 @@ public final class OperationBuilder { */ public static final class Post extends Put { - private final String METHOD_NAME = "POST"; + public static final String METHOD_NAME = "POST"; + private final DataNodeContainer dataNodeContainer; - public Post(DataSchemaNode node) { - super(node); + public Post(final String nodeName, final String description, final DataNodeContainer dataNodeContainer) { + super(nodeName, description); + this.dataNodeContainer = dataNodeContainer; + spec.setType(CONFIG + nodeName + METHOD_NAME); } @Override public Operation build() { spec.setMethod(METHOD_NAME); - spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName()); + spec.setNickname(METHOD_NAME + "-" + nodeName); return spec; } + + @Override + public Put pathParams(List params) { + List parameters = new ArrayList<>(params); + for (DataSchemaNode node : dataNodeContainer.getChildNodes()) { + if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) { + Parameter payload = new Parameter(); + payload.setParamType("body"); + payload.setType(CONFIG + node.getQName().getLocalName()); + payload.setName("**"+CONFIG + node.getQName().getLocalName()); + parameters.add(payload); + } + } + spec.setParameters(parameters); + return this; + + } + + public Post summary(final String summary) { + spec.setSummary(summary); + return this; + } } /** diff --git a/opendaylight/md-sal/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocGeneratorTest.java b/opendaylight/md-sal/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocGeneratorTest.java index 19f82b5386..9165281f9d 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocGeneratorTest.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocGeneratorTest.java @@ -9,6 +9,7 @@ import com.google.common.base.Preconditions; import java.io.File; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; @@ -23,6 +24,7 @@ import org.opendaylight.controller.sal.core.api.model.SchemaService; import org.opendaylight.controller.sal.rest.doc.swagger.Api; import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration; import org.opendaylight.controller.sal.rest.doc.swagger.Operation; +import org.opendaylight.controller.sal.rest.doc.swagger.Parameter; import org.opendaylight.controller.sal.rest.doc.swagger.Resource; import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList; import org.opendaylight.yangtools.yang.model.api.Module; @@ -52,8 +54,7 @@ public class ApiDocGeneratorTest { } /** - * Method: getApiDeclaration(String module, String revision, UriInfo - * uriInfo) + * Method: getApiDeclaration(String module, String revision, UriInfo uriInfo) */ @Test public void testGetModuleDoc() throws Exception { @@ -61,13 +62,139 @@ public class ApiDocGeneratorTest { for (Entry m : helper.getModules().entrySet()) { if (m.getKey().getAbsolutePath().endsWith("toaster_short.yang")) { - ApiDeclaration doc = generator.getSwaggerDocSpec(m.getValue(), - "http://localhost:8080/restconf", "",schemaContext); + ApiDeclaration doc = generator.getSwaggerDocSpec(m.getValue(), "http://localhost:8080/restconf", "", + schemaContext); validateToaster(doc); validateTosterDocContainsModulePrefixes(doc); - Assert.assertNotNull(doc); + validateSwaggerModules(doc); + validateSwaggerApisForPost(doc); + } + } + } + + /** + * Validate whether ApiDelcaration contains Apis with concrete path and whether this Apis contain specified POST + * operations. + */ + private void validateSwaggerApisForPost(final ApiDeclaration doc) { + // two POST URI with concrete schema name in summary + Api lstApi = findApi("/config/toaster2:lst/", doc); + assertNotNull("Api /config/toaster2:lst/ wasn't found", lstApi); + assertTrue("POST for cont1 in lst is missing", + findOperation(lstApi.getOperations(), "POST", "(config)lstPOST", "(config)lst1", "(config)cont1")); + + Api cont1Api = findApi("/config/toaster2:lst/cont1/", doc); + assertNotNull("Api /config/toaster2:lst/cont1/ wasn't found", cont1Api); + assertTrue("POST for cont11 in cont1 is missing", + findOperation(cont1Api.getOperations(), "POST", "(config)cont1POST", "(config)cont11", "(config)lst11")); + + // no POST URI + Api cont11Api = findApi("/config/toaster2:lst/cont1/cont11/", doc); + assertNotNull("Api /config/toaster2:lst/cont1/cont11/ wasn't found", cont11Api); + assertTrue("POST operation shouldn't be present.", findOperations(cont11Api.getOperations(), "POST").isEmpty()); + + } + + /** + * Tries to find operation with name {@code operationName} and with summary {@code summary} + */ + private boolean findOperation(List operations, String operationName, String type, + String... searchedParameters) { + Set filteredOperations = findOperations(operations, operationName); + for (Operation operation : filteredOperations) { + if (operation.getType().equals(type)) { + List parameters = operation.getParameters(); + return containAllParameters(parameters, searchedParameters); } } + return false; + } + + private Set findOperations(final List operations, final String operationName) { + final Set filteredOperations = new HashSet<>(); + for (Operation operation : operations) { + if (operation.getMethod().equals(operationName)) { + filteredOperations.add(operation); + } + } + return filteredOperations; + } + + private boolean containAllParameters(final List searchedIns, String[] searchedWhats) { + for (String searchedWhat : searchedWhats) { + boolean parameterFound = false; + for (Parameter searchedIn : searchedIns) { + if (searchedIn.getType().equals(searchedWhat)) { + parameterFound = true; + } + } + if (!parameterFound) { + return false; + } + } + return true; + } + + /** + * Tries to find {@code Api} with path {@code path} + */ + private Api findApi(final String path, final ApiDeclaration doc) { + for (Api api : doc.getApis()) { + if (api.getPath().equals(path)) { + return api; + } + } + return null; + } + + /** + * Validates whether doc {@code doc} contains concrete specified models. + */ + private void validateSwaggerModules(ApiDeclaration doc) { + JSONObject models = doc.getModels(); + assertNotNull(models); + try { + JSONObject configLst = models.getJSONObject("(config)lst"); + assertNotNull(configLst); + + containsReferences(configLst, "lst1"); + containsReferences(configLst, "cont1"); + + JSONObject configLst1 = models.getJSONObject("(config)lst1"); + assertNotNull(configLst1); + + JSONObject configCont1 = models.getJSONObject("(config)cont1"); + assertNotNull(configCont1); + + containsReferences(configCont1, "cont11"); + containsReferences(configCont1, "lst11"); + + JSONObject configCont11 = models.getJSONObject("(config)cont11"); + assertNotNull(configCont11); + + JSONObject configLst11 = models.getJSONObject("(config)lst11"); + assertNotNull(configLst11); + } catch (JSONException e) { + fail("JSONException wasn't expected"); + } + + } + + /** + * Checks whether object {@code mainObject} contains in properties/items key $ref with concrete value. + */ + private void containsReferences(final JSONObject mainObject, final String childObject) throws JSONException { + JSONObject properties = mainObject.getJSONObject("properties"); + assertNotNull(properties); + + JSONObject nodeInProperties = properties.getJSONObject(childObject); + assertNotNull(nodeInProperties); + + JSONObject itemsInNodeInProperties = nodeInProperties.getJSONObject("items"); + assertNotNull(itemsInNodeInProperties); + + String itemRef = itemsInNodeInProperties.getString("$ref"); + assertEquals("(config)" + childObject, itemRef); } @Test @@ -76,14 +203,13 @@ public class ApiDocGeneratorTest { for (Entry m : helper.getModules().entrySet()) { if (m.getKey().getAbsolutePath().endsWith("toaster.yang")) { - ApiDeclaration doc = generator.getSwaggerDocSpec(m.getValue(), - "http://localhost:8080/restconf", "",schemaContext); + ApiDeclaration doc = generator.getSwaggerDocSpec(m.getValue(), "http://localhost:8080/restconf", "", + schemaContext); Assert.assertNotNull(doc); - //testing bugs.opendaylight.org bug 1290. UnionType model type. + // testing bugs.opendaylight.org bug 1290. UnionType model type. String jsonString = doc.getModels().toString(); - assertTrue( - jsonString.contains( "testUnion\":{\"type\":\"integer or string\",\"required\":false}" ) ); + assertTrue(jsonString.contains("testUnion\":{\"type\":\"integer or string\",\"required\":false}")); } } } @@ -98,10 +224,9 @@ public class ApiDocGeneratorTest { * @throws Exception */ private void validateToaster(ApiDeclaration doc) throws Exception { - Set expectedUrls = new TreeSet<>(Arrays.asList(new String[] { - "/config/toaster2:toaster/", "/operational/toaster2:toaster/", - "/operations/toaster2:cancel-toast", "/operations/toaster2:make-toast", - "/operations/toaster2:restock-toaster", + Set expectedUrls = new TreeSet<>(Arrays.asList(new String[] { "/config/toaster2:toaster/", + "/operational/toaster2:toaster/", "/operations/toaster2:cancel-toast", + "/operations/toaster2:make-toast", "/operations/toaster2:restock-toaster", "/config/toaster2:toaster/toasterSlot/{slotId}/toaster-augmented:slotInfo/" })); Set actualUrls = new TreeSet<>(); @@ -120,8 +245,7 @@ public class ApiDocGeneratorTest { fail("Missing expected urls: " + expectedUrls); } - Set expectedConfigMethods = new TreeSet<>(Arrays.asList(new String[] { "GET", - "PUT", "DELETE" })); + Set expectedConfigMethods = new TreeSet<>(Arrays.asList(new String[] { "GET", "PUT", "DELETE" })); Set actualConfigMethods = new TreeSet<>(); for (Operation oper : configApi.getOperations()) { actualConfigMethods.add(oper.getMethod()); @@ -136,8 +260,7 @@ public class ApiDocGeneratorTest { // TODO: we should really do some more validation of the // documentation... /** - * Missing validation: Explicit validation of URLs, and their methods - * Input / output models. + * Missing validation: Explicit validation of URLs, and their methods Input / output models. */ } @@ -173,25 +296,25 @@ public class ApiDocGeneratorTest { try { JSONObject configToaster = topLevelJson.getJSONObject("(config)toaster"); assertNotNull("(config)toaster JSON object missing", configToaster); - //without module prefix + // without module prefix containsProperties(configToaster, "toasterSlot"); JSONObject toasterSlot = topLevelJson.getJSONObject("(config)toasterSlot"); assertNotNull("(config)toasterSlot JSON object missing", toasterSlot); - //with module prefix + // with module prefix containsProperties(toasterSlot, "toaster-augmented:slotInfo"); } catch (JSONException e) { - fail("Json exception while reading JSON object. Original message "+e.getMessage()); + fail("Json exception while reading JSON object. Original message " + e.getMessage()); } } - private void containsProperties(final JSONObject jsonObject,final String...properties) throws JSONException { + private void containsProperties(final JSONObject jsonObject, final String... properties) throws JSONException { for (String property : properties) { JSONObject propertiesObject = jsonObject.getJSONObject("properties"); assertNotNull("Properties object missing in ", propertiesObject); JSONObject concretePropertyObject = propertiesObject.getJSONObject(property); - assertNotNull(property + " is missing",concretePropertyObject); + assertNotNull(property + " is missing", concretePropertyObject); } } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/test/resources/yang/toaster.yang b/opendaylight/md-sal/sal-rest-docgen/src/test/resources/yang/toaster.yang index d33bc46225..20bbd78622 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/test/resources/yang/toaster.yang +++ b/opendaylight/md-sal/sal-rest-docgen/src/test/resources/yang/toaster.yang @@ -164,7 +164,7 @@ module toaster { "This variable indicates the current state of the toaster."; } - } + } rpc make-toast { description diff --git a/opendaylight/md-sal/sal-rest-docgen/src/test/resources/yang/toaster_short.yang b/opendaylight/md-sal/sal-rest-docgen/src/test/resources/yang/toaster_short.yang index 6884076d5d..1a4d94d2d9 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/test/resources/yang/toaster_short.yang +++ b/opendaylight/md-sal/sal-rest-docgen/src/test/resources/yang/toaster_short.yang @@ -129,6 +129,39 @@ "The darkness factor. Basically, the number of ms to multiple the doneness value by."; } } // container toaster + + list lst { + container cont1 { + container cont11 { + leaf lf111 { + type uint32; + } + leaf lf112 { + type string; + } + } + list lst11 { + leaf lf111 { + type string; + } + } + } + list lst1 { + key "key1 key2"; + leaf key1 { + type int32; + } + leaf key2 { + type int8; + } + leaf lf11 { + type int16; + } + } + leaf lf1 { + type string; + } + } rpc make-toast { description diff --git a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowComparator.java b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowComparator.java index cd9738c894..2b67edfd9d 100644 --- a/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowComparator.java +++ b/opendaylight/md-sal/statistics-manager/src/main/java/org/opendaylight/controller/md/statistics/manager/FlowComparator.java @@ -35,9 +35,6 @@ final class FlowComparator { if (statsFlow == null || storedFlow == null) { return false; } - if (statsFlow.getClass() != storedFlow.getClass()) { - return false; - } if (statsFlow.getContainerName()== null) { if (storedFlow.getContainerName()!= null) { return false; @@ -99,9 +96,6 @@ final class FlowComparator { } if (storedFlow == null && statsFlow != null) return false; if (statsFlow == null && storedFlow != null) return false; - if (storedFlow.getClass() != statsFlow.getClass()) { - return false; - } if (storedFlow.getEthernetMatch() == null) { if (statsFlow.getEthernetMatch() != null) { return false; diff --git a/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyExporter.java b/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyExporter.java index d7ce9485c6..451cad4816 100644 --- a/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyExporter.java +++ b/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyExporter.java @@ -91,7 +91,7 @@ class FlowCapableTopologyExporter implements FlowTopologyDiscoveryListener, Open public void applyOperation(final ReadWriteTransaction transaction) { final Node node = toTopologyNode(toTopologyNodeId(notification.getId()), notification.getNodeRef()); final InstanceIdentifier path = getNodePath(toTopologyNodeId(notification.getId())); - transaction.put(LogicalDatastoreType.OPERATIONAL, path, node); + transaction.merge(LogicalDatastoreType.OPERATIONAL, path, node, true); } }); } @@ -130,7 +130,7 @@ class FlowCapableTopologyExporter implements FlowTopologyDiscoveryListener, Open TerminationPoint point = toTerminationPoint(toTerminationPointId(notification.getId()), notification.getNodeConnectorRef()); final InstanceIdentifier path = tpPath(nodeId, point.getKey().getTpId()); - transaction.put(LogicalDatastoreType.OPERATIONAL, path, point); + transaction.merge(LogicalDatastoreType.OPERATIONAL, path, point, true); if ((fcncu.getState() != null && fcncu.getState().isLinkDown()) || (fcncu.getConfiguration() != null && fcncu.getConfiguration().isPORTDOWN())) { removeAffectedLinks(point.getTpId()); diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java index 004a22f694..bd91af5bcc 100644 --- a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java @@ -9,7 +9,9 @@ package org.opendaylight.controller.netconf.client; import io.netty.channel.Channel; + import java.util.Collection; + import org.opendaylight.controller.netconf.nettyutil.AbstractNetconfSession; import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXICodec; import org.opendaylight.controller.netconf.nettyutil.handler.NetconfEXIToMessageDecoder; @@ -24,8 +26,16 @@ public class NetconfClientSession extends AbstractNetconfSession capabilities; - public NetconfClientSession(NetconfClientSessionListener sessionListener, Channel channel, long sessionId, - Collection capabilities) { + /** + * Construct a new session. + * + * @param sessionListener + * @param channel + * @param sessionId + * @param capabilities set of advertised capabilities. Expected to be immutable. + */ + public NetconfClientSession(final NetconfClientSessionListener sessionListener, final Channel channel, final long sessionId, + final Collection capabilities) { super(sessionListener, channel, sessionId); this.capabilities = capabilities; logger.debug("Client Session {} created", toString()); @@ -41,7 +51,7 @@ public class NetconfClientSession extends AbstractNetconfSession promise, - Channel channel, - Timer timer, - NetconfClientSessionListener sessionListener, - long connectionTimeoutMillis) { + protected NetconfClientSessionNegotiator(final NetconfClientSessionPreferences sessionPreferences, + final Promise promise, + final Channel channel, + final Timer timer, + final NetconfClientSessionListener sessionListener, + final long connectionTimeoutMillis) { super(sessionPreferences, promise, channel, timer, sessionListener, connectionTimeoutMillis); } @Override - protected void handleMessage(NetconfHelloMessage netconfMessage) throws NetconfDocumentedException { + protected void handleMessage(final NetconfHelloMessage netconfMessage) throws NetconfDocumentedException { final NetconfClientSession session = getSessionForHelloMessage(netconfMessage); replaceHelloMessageInboundHandler(session); @@ -98,7 +100,7 @@ public class NetconfClientSessionNegotiator extends }); } - private boolean shouldUseExi(NetconfHelloMessage helloMsg) { + private boolean shouldUseExi(final NetconfHelloMessage helloMsg) { return containsExi10Capability(helloMsg.getDocument()) && containsExi10Capability(sessionPreferences.getHelloMessage().getDocument()); } @@ -113,7 +115,7 @@ public class NetconfClientSessionNegotiator extends return false; } - private long extractSessionId(Document doc) { + private long extractSessionId(final Document doc) { final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE); String textContent = sessionIdNode.getTextContent(); if (textContent == null || textContent.equals("")) { @@ -124,10 +126,14 @@ public class NetconfClientSessionNegotiator extends } @Override - protected NetconfClientSession getSession(NetconfClientSessionListener sessionListener, Channel channel, - NetconfHelloMessage message) throws NetconfDocumentedException { + protected NetconfClientSession getSession(final NetconfClientSessionListener sessionListener, final Channel channel, + final NetconfHelloMessage message) throws NetconfDocumentedException { long sessionId = extractSessionId(message.getDocument()); - Collection capabilities = NetconfMessageUtil.extractCapabilitiesFromHello(message.getDocument()); + + // Copy here is important: it disconnects the strings from the document + Collection capabilities = ImmutableList.copyOf(NetconfMessageUtil.extractCapabilitiesFromHello(message.getDocument())); + + // FIXME: scalability: we could instantiate a cache to share the same collections return new NetconfClientSession(sessionListener, channel, sessionId, capabilities); } @@ -138,15 +144,15 @@ public class NetconfClientSessionNegotiator extends private static final String EXI_CONFIRMED_HANDLER = "exiConfirmedHandler"; private final NetconfClientSession session; - private NetconfStartExiMessage startExiMessage; + private final NetconfStartExiMessage startExiMessage; - ExiConfirmationInboundHandler(NetconfClientSession session, final NetconfStartExiMessage startExiMessage) { + ExiConfirmationInboundHandler(final NetconfClientSession session, final NetconfStartExiMessage startExiMessage) { this.session = session; this.startExiMessage = startExiMessage; } @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { ctx.pipeline().remove(ExiConfirmationInboundHandler.EXI_CONFIRMED_HANDLER); NetconfMessage netconfMessage = (NetconfMessage) msg; diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java index 1a2eb3f1ab..4ca7bdf958 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshClientAdapter.java @@ -41,7 +41,7 @@ class SshClientAdapter implements Runnable { private OutputStream stdIn; - private Queue postponed = new LinkedList<>(); + private final Queue postponed = new LinkedList<>(); private ChannelHandlerContext ctx; private ChannelPromise disconnectPromise; @@ -50,42 +50,36 @@ class SshClientAdapter implements Runnable { private final Object lock = new Object(); - public SshClientAdapter(SshClient sshClient, Invoker invoker) { + public SshClientAdapter(final SshClient sshClient, final Invoker invoker) { this.sshClient = sshClient; this.invoker = invoker; } - // TODO: refactor + // TODO ganymed spawns a Thread that receives the data from remote inside TransportManager + // Get rid of this thread and reuse Ganymed internal thread (not sure if its possible without modifications in ganymed) public void run() { try { - SshSession session = sshClient.openSession(); + final SshSession session = sshClient.openSession(); invoker.invoke(session); - InputStream stdOut = session.getStdout(); - session.getStderr(); + final InputStream stdOut = session.getStdout(); synchronized (lock) { - stdIn = session.getStdin(); - ByteBuf message; - while ((message = postponed.poll()) != null) { - writeImpl(message); + while (postponed.peek() != null) { + writeImpl(postponed.poll()); } } while (!stopRequested.get()) { - byte[] readBuff = new byte[BUFFER_SIZE]; - int c = stdOut.read(readBuff); + final byte[] readBuff = new byte[BUFFER_SIZE]; + final int c = stdOut.read(readBuff); if (c == -1) { continue; } - byte[] tranBuff = new byte[c]; - System.arraycopy(readBuff, 0, tranBuff, 0, c); - ByteBuf byteBuf = Unpooled.buffer(c); - byteBuf.writeBytes(tranBuff); - ctx.fireChannelRead(byteBuf); + ctx.fireChannelRead(Unpooled.copiedBuffer(readBuff, 0, c)); } - } catch (Exception e) { + } catch (final Exception e) { logger.error("Unexpected exception", e); } finally { sshClient.close(); @@ -99,7 +93,7 @@ class SshClientAdapter implements Runnable { } // TODO: needs rework to match netconf framer API. - public void write(ByteBuf message) throws IOException { + public void write(final ByteBuf message) throws IOException { synchronized (lock) { if (stdIn == null) { postponed.add(message); @@ -109,28 +103,28 @@ class SshClientAdapter implements Runnable { } } - private void writeImpl(ByteBuf message) throws IOException { + private void writeImpl(final ByteBuf message) throws IOException { message.getBytes(0, stdIn, message.readableBytes()); message.release(); stdIn.flush(); } - public void stop(ChannelPromise promise) { + public void stop(final ChannelPromise promise) { synchronized (lock) { stopRequested.set(true); disconnectPromise = promise; } } - public Thread start(ChannelHandlerContext ctx, ChannelFuture channelFuture) { + public Thread start(final ChannelHandlerContext ctx, final ChannelFuture channelFuture) { checkArgument(channelFuture.isSuccess()); checkNotNull(ctx.channel().remoteAddress()); synchronized (this) { checkState(this.ctx == null); this.ctx = ctx; } - String threadName = toString(); - Thread thread = new Thread(this, threadName); + final String threadName = toString(); + final Thread thread = new Thread(this, threadName); thread.start(); return thread; } diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java index 44893b8794..9cdc5926f0 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ssh/client/SshSession.java @@ -9,8 +9,6 @@ package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client; import ch.ethz.ssh2.Session; -import ch.ethz.ssh2.StreamGobbler; - import ch.ethz.ssh2.channel.Channel; import java.io.Closeable; import java.io.IOException; @@ -23,19 +21,20 @@ import java.io.OutputStream; class SshSession implements Closeable { private final Session session; - public SshSession(Session session) { + public SshSession(final Session session) { this.session = session; } - - public void startSubSystem(String name) throws IOException { + public void startSubSystem(final String name) throws IOException { session.startSubSystem(name); } public InputStream getStdout() { - return new StreamGobbler(session.getStdout()); + return session.getStdout(); } + // FIXME according to http://www.ganymed.ethz.ch/ssh2/FAQ.html#blocking you should read data from both stdout and stderr to prevent window filling up (since stdout and stderr share a window) + // FIXME stdErr is not used anywhere public InputStream getStderr() { return session.getStderr(); } diff --git a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java index 6300c56e72..3fffbb2d2c 100644 --- a/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java +++ b/opendaylight/netconf/netconf-ssh/src/main/java/org/opendaylight/controller/netconf/ssh/threads/Handshaker.java @@ -243,6 +243,12 @@ class ServerConnectionCallbackImpl implements ServerConnectionCallback { ChannelFuture clientChannelFuture = initializeNettyConnection(localAddress, bossGroup, sshClientHandler); // get channel final Channel channel = clientChannelFuture.awaitUninterruptibly().channel(); + + // write additional header before polling thread is started + // polling thread could process and forward data before additional header is written + // This will result into unexpected state: hello message without additional header and the next message with additional header + channel.writeAndFlush(Unpooled.copiedBuffer(additionalHeader.getBytes())); + new ClientInputStreamPoolingThread(session, ss.getStdout(), channel, new AutoCloseable() { @Override public void close() throws Exception { @@ -259,9 +265,6 @@ class ServerConnectionCallbackImpl implements ServerConnectionCallback { } } }, sshClientHandler.getChannelHandlerContext()).start(); - - // write additional header - channel.writeAndFlush(Unpooled.copiedBuffer(additionalHeader.getBytes())); } else { logger.debug("{} Wrong subsystem requested:'{}', closing ssh session", serverSession, subsystem); String reason = "Only netconf subsystem is supported, requested:" + subsystem; diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java index 59e9f4c980..7a4c10e23b 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/Main.java @@ -19,6 +19,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.List; +import java.util.concurrent.TimeUnit; import net.sourceforge.argparse4j.ArgumentParsers; import net.sourceforge.argparse4j.annotation.Arg; import net.sourceforge.argparse4j.inf.ArgumentParser; @@ -49,6 +50,9 @@ public final class Main { @Arg(dest = "starting-port") public int startingPort; + @Arg(dest = "generate-config-connection-timeout") + public int generateConfigsTimeout; + @Arg(dest = "generate-configs-dir") public File generateConfigsDir; @@ -58,6 +62,9 @@ public final class Main { @Arg(dest = "ssh") public boolean ssh; + @Arg(dest = "exi") + public boolean exi; + static ArgumentParser getParser() { final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf testool"); parser.addArgument("--devices-count") @@ -79,6 +86,12 @@ public final class Main { .help("First port for simulated device. Each other device will have previous+1 port number") .dest("starting-port"); + parser.addArgument("--generate-config-connection-timeout") + .type(Integer.class) + .setDefault((int)TimeUnit.MINUTES.toMillis(5)) + .help("Timeout to be generated in initial config files") + .dest("generate-config-connection-timeout"); + parser.addArgument("--generate-configs-batch-size") .type(Integer.class) .setDefault(100) @@ -96,6 +109,12 @@ public final class Main { .help("Whether to use ssh for transport or just pure tcp") .dest("ssh"); + parser.addArgument("--exi") + .type(Boolean.class) + .setDefault(false) + .help("Whether to use exi to transport xml content") + .dest("exi"); + return parser; } @@ -110,6 +129,8 @@ public final class Main { } public static void main(final String[] args) { + ch.ethz.ssh2.log.Logger.enabled = true; + final Params params = parseArgs(args, Params.getParser()); params.validate(); @@ -117,7 +138,7 @@ public final class Main { try { final List openDevices = netconfDeviceSimulator.start(params); if(params.generateConfigsDir != null) { - new ConfigGenerator(params.generateConfigsDir, openDevices).generate(params.ssh, params.generateConfigBatchSize); + new ConfigGenerator(params.generateConfigsDir, openDevices).generate(params.ssh, params.generateConfigBatchSize, params.generateConfigsTimeout); } } catch (final Exception e) { LOG.error("Unhandled exception", e); @@ -164,7 +185,7 @@ public final class Main { this.openDevices = openDevices; } - public void generate(final boolean useSsh, final int batchSize) { + public void generate(final boolean useSsh, final int batchSize, final int generateConfigsTimeout) { if(directory.exists() == false) { checkState(directory.mkdirs(), "Unable to create folder %s" + directory); } @@ -182,7 +203,7 @@ public final class Main { configBlueprint = configBlueprint.replace(NETCONF_USE_SSH, "%s"); final String before = configBlueprint.substring(0, configBlueprint.indexOf("")); - final String middleBlueprint = configBlueprint.substring(configBlueprint.indexOf(""), configBlueprint.indexOf("") + "".length()); + final String middleBlueprint = configBlueprint.substring(configBlueprint.indexOf(""), configBlueprint.indexOf("")); final String after = configBlueprint.substring(configBlueprint.indexOf("") + "".length()); int connectorCount = 0; @@ -196,7 +217,9 @@ public final class Main { } final String name = String.valueOf(openDevice) + SIM_DEVICE_SUFFIX; - final String configContent = String.format(middleBlueprint, name, String.valueOf(openDevice), String.valueOf(!useSsh)); + String configContent = String.format(middleBlueprint, name, String.valueOf(openDevice), String.valueOf(!useSsh)); + configContent = String.format("%s%s%d%s\n%s\n", configContent, "", generateConfigsTimeout, "", ""); + b.append(configContent); connectorCount++; if(connectorCount == batchSize) { diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java index b21c02ac35..3a52f0a85e 100644 --- a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java +++ b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/NetconfDeviceSimulator.java @@ -24,7 +24,6 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.util.HashedWheelTimer; import java.io.Closeable; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.lang.management.ManagementFactory; import java.net.Inet4Address; @@ -42,6 +41,7 @@ import java.util.concurrent.ExecutionException; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTreeWalker; import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory; @@ -91,7 +91,7 @@ public class NetconfDeviceSimulator implements Closeable { this.hashedWheelTimer = hashedWheelTimer; } - private NetconfServerDispatcher createDispatcher(final Map moduleBuilders) { + private NetconfServerDispatcher createDispatcher(final Map moduleBuilders, final boolean exi) { final Set capabilities = Sets.newHashSet(Collections2.transform(moduleBuilders.keySet(), new Function() { @Override @@ -108,8 +108,12 @@ public class NetconfDeviceSimulator implements Closeable { final DefaultCommitNotificationProducer commitNotifier = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); + final Set serverCapabilities = exi + ? NetconfServerSessionNegotiatorFactory.DEFAULT_BASE_CAPABILITIES + : Sets.newHashSet(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0, XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1); + final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( - hashedWheelTimer, simulatedOperationProvider, idProvider, CONNECTION_TIMEOUT_MILLIS, commitNotifier, new LoggingMonitoringService()); + hashedWheelTimer, simulatedOperationProvider, idProvider, CONNECTION_TIMEOUT_MILLIS, commitNotifier, new LoggingMonitoringService(), serverCapabilities); final NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer( serverNegotiatorFactory); @@ -147,7 +151,7 @@ public class NetconfDeviceSimulator implements Closeable { public List start(final Main.Params params) { final Map moduleBuilders = parseSchemasToModuleBuilders(params); - final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders); + final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders, params.exi); int currentPort = params.startingPort; diff --git a/pom.xml b/pom.xml index 0bfc64f892..22c03ac7ac 100644 --- a/pom.xml +++ b/pom.xml @@ -28,6 +28,7 @@ opendaylight/forwardingrulesmanager/implementation opendaylight/hosttracker/api opendaylight/hosttracker/implementation + opendaylight/hosttracker/shell opendaylight/hosttracker_new/api opendaylight/hosttracker_new/implementation opendaylight/containermanager/api @@ -39,11 +40,13 @@ opendaylight/statisticsmanager/api opendaylight/statisticsmanager/implementation opendaylight/topologymanager/implementation + opendaylight/topologymanager/shell opendaylight/usermanager/api opendaylight/usermanager/implementation opendaylight/connectionmanager/api opendaylight/connectionmanager/implementation opendaylight/security + opendaylight/karaf-tomcat-security