<relativePath>../../opendaylight/commons/opendaylight</relativePath>
</parent>
<artifactId>base-features</artifactId>
- <packaging>kar</packaging>
+ <packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>Base Features POM</description>
<properties>
</resource>
</resources>
<plugins>
- <plugin>
- <groupId>org.apache.karaf.tooling</groupId>
- <artifactId>karaf-maven-plugin</artifactId>
- <version>${karaf.version}</version>
- <extensions>true</extensions>
- <executions>
- <execution>
- <id>features-create-kar</id>
- <goals>
- <goal>features-create-kar</goal>
- </goals>
- <configuration>
- <featuresFile>${project.build.directory}/classes/${features.file}</featuresFile>
- </configuration>
- </execution>
- </executions>
- <!-- There is no useful configuration for the kar mojo. The features-generate-descriptor mojo configuration may be useful -->
- </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<bundle start="true" start-level="35">mvn:org.javassist/javassist/${javassist.version}</bundle>
<bundle start="true" start-level="35">mvn:commons-io/commons-io/${commons.io.version}</bundle>
<bundle start="true" start-level="35">mvn:commons-codec/commons-codec/${commons.codec.version}</bundle>
- <bundle start="true" start-level="35">mvn:org.apache.commons/commons-lang3/${commons.lang.version}</bundle>
+ <bundle start="true" start-level="35">mvn:org.apache.commons/commons-lang3/${commons.lang3.version}</bundle>
<bundle start="true" start-level="35">mvn:commons-net/commons-net/${commons.net.version}</bundle>
</feature>
<feature name="base-eclipselink-persistence" description="EclipseLink Persistence API" version="2.0.4.v201112161009">
<bundle start="true" start-level="35">mvn:orbit/org.apache.juli.extras/7.0.32.v201211081135</bundle>
<bundle start="true" start-level="35">mvn:orbit/org.apache.tomcat.api/7.0.32.v201211081135</bundle>
<bundle start="true" start-level="35">mvn:orbit/org.apache.tomcat.util/7.0.32.v201211201952</bundle>
- <bundle start="true" start-level="35">wrap:mvn:virgomirror/org.eclipse.jdt.core.compiler.batch/3.8.0.I20120518-2145</bundle>
</feature>
<feature name="base-spring" description="Opendaylight Spring Support" version="${spring.version}">
<bundle>mvn:org.ow2.asm/asm-all/${asm.version}</bundle>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../../opendaylight/commons/opendaylight</relativePath>
+ </parent>
+ <artifactId>controller-features</artifactId>
+ <packaging>pom</packaging>
+ <name>${project.artifactId}</name>
+ <description>Features POM</description>
+ <properties>
+ <features.file>features.xml</features.file>
+ </properties>
+ <build>
+ <resources>
+ <resource>
+ <filtering>true</filtering>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>filter</id>
+ <goals>
+ <goal>resources</goal>
+ </goals>
+ <phase>generate-resources</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/${features.file}</file>
+ <type>xml</type>
+ <classifier>features</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<features name="controller-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+
+ <feature name="odl-hosttracker" description="Controller Service: Host Tracker">
+ <feature>odl-clustering</feature>
+ <feature>odl-managers</feature>
+ <feature>odl-sal</feature>
+ <bundle>mvn:org.opendaylight.controller/hosttracker/${hosttracker.api.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/hosttracker.implementation/${hosttracker.implementation.version}</bundle>
+ </feature>
+ <feature name="odl-sal" description="Service Abstraction Layer"
+ version="${sal.version}">
+ <feature>base-felix-dm</feature>
+ <bundle start="true" start-level="35">mvn:org.apache.commons/commons-lang3/${commons.lang.version}</bundle>
+ <bundle>mvn:org.osgi/org.osgi.compendium/${osgi.compendium.version}</bundle>
+ <bundle>mvn:org.apache.felix/org.apache.felix.dependencymanager/${felix.dependencymanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal/${sal.version}</bundle>
+ <!-- The SAL Implementation doesn't follow API versioning, should be revisited in the bundle -->
+ <bundle>mvn:org.opendaylight.controller/sal.implementation/${sal.implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal.networkconfiguration/${sal.networkconfiguration.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal.networkconfiguration.implementation/${sal.networkconfiguration.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal.connection/${sal.connection.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/sal.connection.implementation/${sal.connection.version}</bundle>
+ </feature>
+ <feature name="odl-clustering" description="Controller Service: Clustering">
+ <feature>transaction</feature>
+ <feature>base-felix-dm</feature>
+ <feature>base-eclipselink-persistence</feature>
+ <feature>odl-sal</feature>
+ <bundle>mvn:org.opendaylight.controller/clustering.services/${clustering.services.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/clustering.services-implementation/${clustering.services_implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/clustering.stub/${clustering.stub.version}</bundle>
+ </feature>
+ <feature name="odl-legacy-configuration">
+ <feature>odl-sal</feature>
+ <bundle>mvn:org.opendaylight.controller/configuration/${configuration.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/configuration.implementation/${configuration.implementation.version}</bundle>
+ </feature>
+ <feature name="odl-configuration" description="Controller Service: Configuration">
+ <!-- org.opendaylight.controller.config.yangjmxgenerator is missing -->
+ <bundle>mvn:org.opendaylight.controller/config-api/${config.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-manager/${config.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-netconf-connector/${netconf.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-persister-api/${config.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-persister-directory-xml-adapter/${config.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-persister-file-xml-adapter/${config.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/config-persister-impl/${netconf.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/yang-jmx-generator/${yang-jmx-generator.version}</bundle>
+ </feature>
+ <feature name="odl-managers" description="Big boss">
+ <feature>odl-legacy-configuration</feature>
+ <feature>base-spring-security</feature>
+ <feature>base-felix-dm</feature>
+ <feature>odl-clustering</feature>
+ <bundle>mvn:org.opendaylight.controller.thirdparty/net.sf.jung2/2.0.1</bundle>
+ <bundle>mvn:org.opendaylight.controller/appauth/${appauth.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/hosttracker/${hosttracker.api.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/hosttracker.implementation/${hosttracker.implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/switchmanager/${switchmanager.api.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/switchmanager.implementation/${switchmanager.implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/statisticsmanager/${statisticsmanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/statisticsmanager.implementation/${statisticsmanager.implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/forwardingrulesmanager/${forwardingrulesmanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/forwardingrulesmanager.implementation/${forwardingrulesmanager.implementation.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/usermanager/${usermanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/usermanager.implementation/${usermanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/containermanager/${containermanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/containermanager.implementation/${containermanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/topologymanager/${topologymanager.version}</bundle>
+ <bundle>mvn:org.opendaylight.controller/forwarding.staticrouting</bundle>
+ <bundle>mvn:org.opendaylight.controller/routing.dijkstra_implementation</bundle>
+ <bundle>mvn:org.opendaylight.controller/connectionmanager</bundle>
+ <bundle>mvn:org.opendaylight.controller/connectionmanager.implementation</bundle>
+ </feature>
+</features>
</prerequisites>
<properties>
+ <akka.version>2.3.2</akka.version>
<aopalliance.version>1.0.0</aopalliance.version>
<appauth.version>0.4.2-SNAPSHOT</appauth.version>
+ <archetype-app-northbound>0.0.1-SNAPSHOT</archetype-app-northbound>
<aries.util.version>1.1.0</aries.util.version>
- <!-- Controller Modules Versions -->
<arphandler.version>0.5.2-SNAPSHOT</arphandler.version>
+ <!-- Controller Modules Versions -->
<asm.version>4.1</asm.version>
<!-- Plugin Versions -->
<bouncycastle.version>1.50</bouncycastle.version>
<bundle.plugin.version>2.4.0</bundle.plugin.version>
+ <bundlescanner.api.version>0.4.2-SNAPSHOT</bundlescanner.api.version>
+ <bundlescanner.implementation.version>0.4.2-SNAPSHOT</bundlescanner.implementation.version>
<bundlescanner.version>0.4.2-SNAPSHOT</bundlescanner.version>
- <checkstyle.version>2.10</checkstyle.version>
+ <checkstyle.version>2.12</checkstyle.version>
<clustering.services.version>0.5.1-SNAPSHOT</clustering.services.version>
<clustering.services_implementation.version>0.4.3-SNAPSHOT</clustering.services_implementation.version>
<clustering.stub.version>0.4.2-SNAPSHOT</clustering.stub.version>
<clustering.test.version>0.4.2-SNAPSHOT</clustering.test.version>
<commmons.northbound.version>0.4.2-SNAPSHOT</commmons.northbound.version>
<!-- Third Party Versions -->
+ <commons.catalina>7.0.32.v201211201336</commons.catalina>
+ <commons.catalina.ha>7.0.32.v201211201952</commons.catalina.ha>
+ <commons.catalina.tribes>7.0.32.v201211201952</commons.catalina.tribes>
+ <commons.checkstyle.version>0.0.3-SNAPSHOT</commons.checkstyle.version>
<commons.codec.version>1.7</commons.codec.version>
+ <commons.coyote>7.0.32.v201211201952</commons.coyote>
+ <commons.el>7.0.32.v201211081135</commons.el>
<commons.fileupload.version>1.2.2</commons.fileupload.version>
<commons.httpclient.version>0.1.2-SNAPSHOT</commons.httpclient.version>
<commons.io.version>2.4</commons.io.version>
- <commons.lang.version>3.1</commons.lang.version>
+ <commons.jasper>7.0.32.v201211201952</commons.jasper>
+ <commons.juli.version>7.0.32.v201211081135</commons.juli.version>
+ <commons.lang.version>2.6</commons.lang.version>
+ <commons.lang3.version>3.1</commons.lang3.version>
+ <commons.logback_settings.version>0.0.2-SNAPSHOT</commons.logback_settings.version>
<commons.net.version>3.0.1</commons.net.version>
+ <commons.opendaylight.commons.httpclient>0.1.2-SNAPSHOT</commons.opendaylight.commons.httpclient>
+ <commons.opendaylight.concepts.version>0.5.2-SNAPSHOT</commons.opendaylight.concepts.version>
+ <commons.opendaylight.version>1.4.2-SNAPSHOT</commons.opendaylight.version>
+ <commons.parent.version>1.0.2-SNAPSHOT</commons.parent.version>
+ <commons.tomcat.api>7.0.32.v201211081135</commons.tomcat.api>
+ <commons.tomcat.util>7.0.32.v201211201952</commons.tomcat.util>
<compiler.version>2.3.2</compiler.version>
<concepts.version>0.5.2-SNAPSHOT</concepts.version>
<config.version>0.2.5-SNAPSHOT</config.version>
<connectionmanager.version>0.1.2-SNAPSHOT</connectionmanager.version>
<containermanager.it.version>0.5.2-SNAPSHOT</containermanager.it.version>
<containermanager.northbound.version>0.4.2-SNAPSHOT</containermanager.northbound.version>
+ <containermanager.shell.version>0.5.2-SNAPSHOT</containermanager.shell.version>
<containermanager.version>0.5.2-SNAPSHOT</containermanager.version>
<controllermanager.northbound.version>0.0.2-SNAPSHOT</controllermanager.northbound.version>
<corsfilter.version>7.0.42</corsfilter.version>
<ctrie.version>0.2.0</ctrie.version>
<devices.web.version>0.4.2-SNAPSHOT</devices.web.version>
+ <dummy-console.version>1.1.0-SNAPSHOT</dummy-console.version>
<eclipse.persistence.version>2.5.0</eclipse.persistence.version>
<!-- enforcer version -->
<enforcer.version>1.3.1</enforcer.version>
<felix.fileinstall.version>3.1.6</felix.fileinstall.version>
<felix.webconsole.version>4.2.0</felix.webconsole.version>
<filtervalve.version>1.4.2-SNAPSHOT</filtervalve.version>
+ <findbugs.maven.plugin.version>2.4.0</findbugs.maven.plugin.version>
<flowprogrammer.northbound.version>0.4.2-SNAPSHOT</flowprogrammer.northbound.version>
<flows.web.version>0.4.2-SNAPSHOT</flows.web.version>
<forwarding.staticrouting>0.5.2-SNAPSHOT</forwarding.staticrouting>
<hosttracker.implementation.version>0.5.2-SNAPSHOT</hosttracker.implementation.version>
<hosttracker.northbound.version>0.4.2-SNAPSHOT</hosttracker.northbound.version>
<hosttracker_new.api.version>0.4.2-SNAPSHOT</hosttracker_new.api.version>
+ <hosttracker_new.implementation.version>0.4.2-SNAPSHOT</hosttracker_new.implementation.version>
+ <httpservice-bridge.northbound.version>0.0.2-SNAPSHOT</httpservice-bridge.northbound.version>
<ietf-inet-types.version>2010.09.24.4-SNAPSHOT</ietf-inet-types.version>
<ietf-restconf.version>2013.10.19.1-SNAPSHOT</ietf-restconf.version>
<ietf-topology.version>2013.10.21.2-SNAPSHOT</ietf-topology.version>
<java.version.target>1.7</java.version.target>
<javassist.version>3.17.1-GA</javassist.version>
<javax.annotation.version>1.2</javax.annotation.version>
+ <jdepend.maven.plugin.version>2.0-beta-2</jdepend.maven.plugin.version>
<!-- Third party version -->
<jersey-servlet.version>1.17</jersey-servlet.version>
<jersey.version>1.17</jersey.version>
<jersey2.version>2.8</jersey2.version>
<jettison.version>1.3.3</jettison.version>
<jmxGeneratorPath>src/main/yang-gen-config</jmxGeneratorPath>
+ <jolokia-bridge.version>0.0.2-SNAPSHOT</jolokia-bridge.version>
<jolokia.version>1.1.4</jolokia.version>
<jsr305.api.version>2.0.1</jsr305.api.version>
<jsr311.api.version>1.1.1</jsr311.api.version>
<jsr311.v2.api.version>2.0</jsr311.v2.api.version>
<junit.version>4.8.1</junit.version>
<karaf.branding.version>1.0.0-SNAPSHOT</karaf.branding.version>
+ <karaf.shell.version>3.0.0</karaf.shell.version>
<karaf.version>3.0.1</karaf.version>
+ <lifecycle.mapping.version>1.0.0</lifecycle.mapping.version>
<logback.version>1.0.9</logback.version>
<logging.bridge.version>0.4.2-SNAPSHOT</logging.bridge.version>
<maven.compile.plugin.version>2.5.1</maven.compile.plugin.version>
<!-- Java Versions -->
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
+ <maven.plugin.api.version>3.0.5</maven.plugin.api.version>
<mdsal.version>1.1-SNAPSHOT</mdsal.version>
<mockito.version>1.9.5</mockito.version>
<netconf.version>0.2.5-SNAPSHOT</netconf.version>
<networkconfig.neutron.version>0.4.2-SNAPSHOT</networkconfig.neutron.version>
<!-- ODL repository / plugin repository -->
<nexusproxy>http://nexus.opendaylight.org/content</nexusproxy>
+ <northbound.commons.version>0.4.2-SNAPSHOT</northbound.commons.version>
+ <northbound.hosttracker.version>1.4.2-SNAPSHOT</northbound.hosttracker.version>
+ <northbound.jolokia.version>1.4.2-SNAPSHOT</northbound.jolokia.version>
<opendaylight-l2-types.version>2013.08.27.4-SNAPSHOT</opendaylight-l2-types.version>
<org.json.version>20080701</org.json.version>
<osgi-brandfragment.web.version>0.0.2-SNAPSHOT</osgi-brandfragment.web.version>
<samples.loadbalancer.northbound.version>0.4.2-SNAPSHOT</samples.loadbalancer.northbound.version>
<samples.simpleforwarding.version>0.4.2-SNAPSHOT</samples.simpleforwarding.version>
<sanitytest.version>0.4.2-SNAPSHOT</sanitytest.version>
+ <scala.version>2.11</scala.version>
<security.version>0.4.2-SNAPSHOT</security.version>
<sitedeploy>dav:http://nexus.opendaylight.org/content/sites/site</sitedeploy>
<siteplugin>3.2</siteplugin>
<xtend.dstdir>src/main/xtend-gen</xtend.dstdir>
<xtend.version>2.4.3</xtend.version>
<yang-ext.version>2013.09.07.4-SNAPSHOT</yang-ext.version>
+ <yang-jmx-generator.version>1.0.0-SNAPSHOT</yang-jmx-generator.version>
<yangtools.version>0.6.2-SNAPSHOT</yangtools.version>
</properties>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
+ <!-- 3rd party dependencies needed by config-->
+ <dependency>
+ <groupId>com.jcabi</groupId>
+ <artifactId>jcabi-maven-slf4j</artifactId>
+ <version>0.8</version>
+ </dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>${jersey.version}</version>
</dependency>
+
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>${jersey.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-actor_${scala.version}</artifactId>
+ <version>${akka.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-cluster_${scala.version}</artifactId>
+ <version>${akka.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-persistence-experimental_${scala.version}</artifactId>
+ <version>${akka.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-remote_${scala.version}</artifactId>
+ <version>${akka.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-testkit_${scala.version}</artifactId>
+ <version>${akka.version}</version>
+ </dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>${commons.lang.version}</version>
+ </dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<artifactId>jsr311-api</artifactId>
<version>${jsr311.api.version}</version>
</dependency>
+ <dependency>
+ <groupId>net.sourceforge.pmd</groupId>
+ <artifactId>pmd</artifactId>
+ <version>5.1.0</version>
+ </dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>javax.activation</artifactId>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.catalina</artifactId>
- <version>7.0.32.v201211201336</version>
+ <version>${commons.catalina}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.catalina.ha</artifactId>
- <version>7.0.32.v201211201952</version>
+ <version>${commons.catalina.ha}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.catalina.tribes</artifactId>
- <version>7.0.32.v201211201952</version>
+ <version>${commons.catalina.tribes}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.coyote</artifactId>
- <version>7.0.32.v201211201952</version>
+ <version>${commons.coyote}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.el</artifactId>
- <version>7.0.32.v201211081135</version>
+ <version>${commons.el}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.jasper</artifactId>
- <version>7.0.32.v201211201952</version>
+ <version>${commons.jasper}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.juli.extras</artifactId>
- <version>7.0.32.v201211081135</version>
+ <version>${commons.juli.version}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.tomcat.api</artifactId>
- <version>7.0.32.v201211081135</version>
+ <version>${commons.tomcat.api}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.tomcat.util</artifactId>
- <version>7.0.32.v201211201952</version>
+ <version>${commons.tomcat.util}</version>
</dependency>
<dependency>
<groupId>org.aopalliance</groupId>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
- <version>${commons.lang.version}</version>
+ <version>${commons.lang3.version}</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>enunciate-core-annotations</artifactId>
<version>${enunciate.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.codehaus.gmaven.runtime</groupId>
+ <artifactId>gmaven-runtime-2.0</artifactId>
+ <version>1.5</version>
+ </dependency>
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>${jettison.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.eclipse</groupId>
+ <artifactId>jdt</artifactId>
+ <version>3.3.0-v20070607-1300</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.equinox</groupId>
+ <artifactId>app</artifactId>
+ <version>1.0.0-v20070606</version>
+ </dependency>
<!-- equinox http service bridge -->
<dependency>
<artifactId>servlet</artifactId>
<version>1.0.0-v20070606</version>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.jdt</groupId>
+ <artifactId>core</artifactId>
+ <version>3.3.0-v_771</version>
+ </dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.antlr</artifactId>
<artifactId>org.eclipse.xtend.lib</artifactId>
<version>${xtend.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+ <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
+ <version>2.4</version>
+ </dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>${javassist.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>jaxrs-api</artifactId>
+ <version>3.0.4.Final</version>
+ </dependency>
<dependency>
<groupId>org.jboss.spec.javax.transaction</groupId>
<artifactId>jboss-transaction-api_1.1_spec</artifactId>
<artifactId>containermanager.northbound</artifactId>
<version>${containermanager.northbound.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager.shell</artifactId>
+ <version>${containermanager.shell.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>controllermanager.northbound</artifactId>
<artifactId>filter-valve</artifactId>
<version>${filtervalve.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>flow-management-compatibility</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>flowprogrammer.northbound</artifactId>
<artifactId>netconf-client</artifactId>
<version>${netconf.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-client</artifactId>
+ <version>${netconf.version}</version>
+ <type>test-jar</type>
+ </dependency>
<!--Netconf config-->
<dependency>
<artifactId>protocol_plugins.stub</artifactId>
<version>${protocol_plugins.stub.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>remoterpc-routingtable.implementation</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>routing.dijkstra_implementation</artifactId>
<artifactId>model-inventory</artifactId>
<version>${mdsal.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-topology</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster-consumer</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller.samples</groupId>
+ <artifactId>sample-toaster-provider</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.tests</groupId>
+ <artifactId>sal-remoterpc-connector-test-consumer</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller.tests</groupId>
+ <artifactId>sal-remoterpc-connector-test-provider</artifactId>
+ <version>${mdsal.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.controller.thirdparty</groupId>
<artifactId>com.sun.jersey.jersey-servlet</artifactId>
<artifactId>yang-ext</artifactId>
<version>${yang-ext.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+ <artifactId>antlr4-runtime-osgi-nohead</artifactId>
+ <version>4.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools.thirdparty</groupId>
+ <artifactId>xtend-lib-osgi</artifactId>
+ <version>${xtend.version}</version>
+ </dependency>
<dependency>
<groupId>org.openexi</groupId>
<artifactId>nagasena</artifactId>
<artifactId>chameleon-mbeans</artifactId>
<version>1.0.0</version>
</dependency>
+ <dependency>
+ <groupId>org.reflections</groupId>
+ <artifactId>reflections</artifactId>
+ <version>0.9.9-RC1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.scala-lang</groupId>
+ <artifactId>scala-library</artifactId>
+ <version>${scala.version}.1</version>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.aop</artifactId>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
- <version>1.0.0</version>
+ <version>${lifecycle.mapping.version}</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
<sourceDirectory>${project.basedir}</sourceDirectory>
<includes>**\/*.java,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat</includes>
- <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/</excludes>
+ <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/,**\/xtend-gen\/</excludes>
</configuration>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
- <!--
- <dependency>
- <groupId>com.googlecode.json-simple</groupId>
- <artifactId>json-simple</artifactId>
- <version>1.1</version>
- </dependency>
- -->
+
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-client</artifactId>
- <version>${netconf.version}</version>
</dependency>
</dependencies>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
- <version>1.3</version>
+ <version>${maven.antrun.plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>2.4</version>
<executions>
<execution>
<goals>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
- <version>2.14.1</version>
<configuration>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
<parallel>classes</parallel>
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
- <version>1.0</version>
+ <version>${gmaven.plugin.version}</version>
</plugin>
<plugin>
<groupId>org.opendaylight.yangtools</groupId>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
- <version>2.12</version>
<configuration>
<failsOnError>false</failsOnError>
<failOnViolation>false</failOnViolation>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>threadpool-config-api</artifactId>
- <version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-maven-slf4j</artifactId>
- <version>0.8</version>
</dependency>
<dependency>
<dependency>
<groupId>org.codehaus.gmaven.runtime</groupId>
<artifactId>gmaven-runtime-2.0</artifactId>
- <version>1.5</version>
<exclusions>
<exclusion>
<groupId>org.sonatype.gossip</groupId>
<dependency>
<groupId>net.sourceforge.pmd</groupId>
<artifactId>pmd</artifactId>
- <version>5.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>jdt</artifactId>
- <version>3.3.0-v20070607-1300</version>
<scope>test</scope>
<exclusions>
<dependency>
<groupId>org.eclipse.equinox</groupId>
<artifactId>app</artifactId>
- <version>1.0.0-v20070606</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>core</artifactId>
- <version>3.3.0-v_771</version>
<scope>test</scope>
<exclusions>
OpenType<?> innerCompositeType;
if(isDerivedType(innerTypeBaseType, innerType)) {
- innerCompositeType = getCompositeType(innerTypeBaseType, baseInnerTypeDefinition);
+ innerCompositeType = baseInnerTypeDefinition instanceof UnionTypeDefinition ?
+ getCompositeTypeForUnion(baseInnerTypeDefinition) :
+ getCompositeType(innerTypeBaseType, baseInnerTypeDefinition);
} else {
innerCompositeType = SimpleTypeResolver.getSimpleType(innerType);
}
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
- <version>3.0.5</version>
+ <version>${maven.plugin.api.version}</version>
</dependency>
</dependencies>
</project>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
- <version>1.0.0</version>
+ <version>${lifecycle.mapping.version}</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
Map<ConnectionConstants, String> params) {
if (connectionService == null)
return null;
- Node node = connectionService.connect(connectionIdentifier, params);
+ Node node = connectionService.connect(type, connectionIdentifier, params);
AbstractScheme scheme = schemes.get(activeScheme);
if (scheme != null && node != null)
scheme.addNode(node);
--- /dev/null
+package org.opendaylight.controller.containermanager;
+
+import java.util.List;
+
+public interface IContainerManagerShell {
+ public List<String> psc();
+ public List<String> pfc();
+ public List<String> psd();
+ public List<String> psp();
+ public List<String> psm();
+ public List<String> addContainer(String arg1, String arg2);
+ public List<String> createContainer(String arg1, String arg2);
+ public List<String> removeContainerShell(String arg1);
+ public List<String> addContainerEntry(String arg1, String arg2, String arg3);
+ public List<String> removeContainerEntry(String arg1, String arg2, String arg3);
+ public List<String> addContainerFlow(String arg1, String arg2, String arg3);
+ public List<String> removeContainerFlow(String arg1, String arg2);
+ public List<String> containermgrGetRoles();
+ public List<String> containermgrGetAuthorizedGroups(String arg1);
+ public List<String> containermgrGetAuthorizedResources(String arg1);
+ public List<String> containermgrGetResourcesForGroup(String arg1);
+ public List<String> containermgrGetUserLevel(String arg1);
+ public List<String> containermgrGetUserResources(String arg1);
+ public List<String> saveConfig();
+}
\ No newline at end of file
package org.opendaylight.controller.containermanager.internal;
import org.eclipse.osgi.framework.console.CommandProvider;
+
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Set;
import java.util.Hashtable;
+
import org.opendaylight.controller.containermanager.IContainerManager;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
import org.apache.felix.dm.Component;
import org.opendaylight.controller.clustering.services.ICacheUpdateAware;
import org.opendaylight.controller.clustering.services.IClusterGlobalServices;
CommandProvider.class.getName(),
IContainerInternal.class.getName(),
IContainerAuthorization.class.getName(),
- ICacheUpdateAware.class.getName()}, props);
+ ICacheUpdateAware.class.getName(),
+ IContainerManagerShell.class.getName()}, props);
c.add(createServiceDependency()
.setService(IClusterGlobalServices.class)
import org.opendaylight.controller.containermanager.ContainerFlowConfig;
import org.opendaylight.controller.containermanager.IContainerAuthorization;
import org.opendaylight.controller.containermanager.IContainerManager;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
import org.opendaylight.controller.containermanager.NodeConnectorsChangeEvent;
import org.opendaylight.controller.sal.authorization.AppRoleLevel;
import org.opendaylight.controller.sal.authorization.Privilege;
public class ContainerManager extends Authorization<String> implements IContainerManager, IObjectReader,
CommandProvider, ICacheUpdateAware<String, Object>, IContainerInternal, IContainerAuthorization,
- IConfigurationAware {
+ IConfigurationAware, IContainerManagerShell {
private static final Logger logger = LoggerFactory.getLogger(ContainerManager.class);
private static String CONTAINERS_FILE_NAME = "containers.conf";
private static final String allContainersGroup = "allContainers";
public boolean inContainerMode() {
return this.containerConfigs.size() > 0;
}
+
+ public List<String> psc() {
+ List<String> result = new ArrayList<String>();
+ for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+ ContainerConfig sc = entry.getValue();
+ result.add(String.format("%s: %s", sc.getContainerName(), sc.toString()));
+ }
+ result.add("Total number of containers: " + containerConfigs.entrySet().size());
+ return result;
+ }
+
+ public List<String> pfc() {
+ List<String> result = new ArrayList<String>();
+ for (Map.Entry<String, ContainerConfig> entry : containerConfigs.entrySet()) {
+ ContainerConfig sc = entry.getValue();
+ result.add(String.format("%s: %s", sc.getContainerName(), sc.getContainerFlowConfigs()));
+ }
+ return result;
+ }
+
+ public List<String> psd() {
+ List<String> result = new ArrayList<String>();
+ for (String containerName : containerData.keySet()) {
+ ContainerData sd = containerData.get(containerName);
+ for (Node sid : sd.getSwPorts().keySet()) {
+ Set<NodeConnector> s = sd.getSwPorts().get(sid);
+ result.add("\t" + sid + " : " + s);
+ }
+
+ for (ContainerFlow s : sd.getContainerFlowSpecs()) {
+ result.add("\t" + s.toString());
+ }
+ }
+ return result;
+ }
+
+ public List<String> psp() {
+ List<String> result = new ArrayList<String>();
+ for (NodeConnector sp : nodeConnectorToContainers.keySet()) {
+ result.add(nodeConnectorToContainers.get(sp).toString());
+ }
+ return result;
+ }
+
+ public List<String> psm() {
+ List<String> result = new ArrayList<String>();
+ for (Node sp : nodeToContainers.keySet()) {
+ result.add(nodeToContainers.get(sp).toString());
+ }
+ return result;
+ }
+
+ public List<String> addContainer(String arg1, String arg2) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String staticVlan = arg2;
+ ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, null, null);
+ result.add((this.addRemoveContainer(containerConfig, false)).toString());
+ return result;
+ }
+
+ public List<String> createContainer(String arg1, String arg2) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String staticVlan = arg2;
+ if (staticVlan == null) {
+ result.add("Static Vlan not specified");
+ return result;
+ }
+ List<String> ports = new ArrayList<String>();
+ for (long l = 1L; l < 10L; l++) {
+ ports.add(NodeConnectorCreator.createOFNodeConnector((short) 1, NodeCreator.createOFNode(l)).toString());
+ }
+ List<ContainerFlowConfig> cFlowList = new ArrayList<ContainerFlowConfig>();
+ cFlowList.add(this.createSampleContainerFlowConfig("tcp", true));
+ ContainerConfig containerConfig = new ContainerConfig(containerName, staticVlan, ports, cFlowList);
+ result.add((this.addRemoveContainer(containerConfig, false)).toString());
+ return result;
+ }
+
+ public List<String> removeContainerShell(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ ContainerConfig containerConfig = new ContainerConfig(containerName, "", null, null);
+ result.add((this.addRemoveContainer(containerConfig, true)).toString());
+ return result;
+ }
+
+ public List<String> addContainerEntry(String arg1, String arg2, String arg3) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String nodeId = arg2;
+ if (nodeId == null) {
+ result.add("Node Id not specified");
+ return result;
+ }
+ String portId = arg3;
+ if (portId == null) {
+ result.add("Port not specified");
+ return result;
+ }
+ Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
+ Short port = Short.valueOf(portId);
+ NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
+ List<String> portList = new ArrayList<String>(1);
+ portList.add(nc.toString());
+ result.add((this.addRemoveContainerEntries(containerName, portList, false)).toString());
+ return result;
+ }
+
+ public List<String> removeContainerEntry(String arg1, String arg2, String arg3) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String nodeId = arg2;
+ if (nodeId == null) {
+ result.add("Node Id not specified");
+ return result;
+ }
+ String portId = arg3;
+ if (portId == null) {
+ result.add("Port not specified");
+ return result;
+ }
+ Node node = NodeCreator.createOFNode(Long.valueOf(nodeId));
+ Short port = Short.valueOf(portId);
+ NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(port, node);
+ List<String> portList = new ArrayList<String>(1);
+ portList.add(nc.toString());
+ result.add((this.addRemoveContainerEntries(containerName, portList, true)).toString());
+ return result;
+ }
+ public List<String> addContainerFlow(String arg1, String arg2, String arg3) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String cflowName = arg2;
+ if (cflowName == null) {
+ result.add("cflowName not specified");
+ return result;
+ }
+ String unidirectional = arg3;
+ boolean boolUnidirectional = Boolean.parseBoolean(unidirectional);
+ List<ContainerFlowConfig> list = new ArrayList<ContainerFlowConfig>();
+ list.add(createSampleContainerFlowConfig(cflowName, boolUnidirectional));
+ result.add((this.addRemoveContainerFlow(containerName, list, false)).toString());
+ return result;
+ }
+
+ public List<String> removeContainerFlow(String arg1, String arg2) {
+ List<String> result = new ArrayList<String>();
+ String containerName = arg1;
+ if (containerName == null) {
+ result.add("Container Name not specified");
+ return result;
+ }
+ String cflowName = arg2;
+ if (cflowName == null) {
+ result.add("cflowName not specified");
+ return result;
+ }
+ Set<String> set = new HashSet<String>(1);
+ set.add(cflowName);
+ result.add((this.removeContainerFlows(containerName, set)).toString());
+ return result;
+ }
+
+ public List<String> containermgrGetRoles() {
+ List<String> result = new ArrayList<String>();
+ result.add("Configured roles for Container Mgr:");
+ List<String> list = this.getRoles();
+ for (String role : list) {
+ result.add(role + "\t" + roles.get(role));
+ }
+ return result;
+ }
+
+ public List<String> containermgrGetAuthorizedGroups(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String roleName = arg1;
+ if (roleName == null || roleName.trim().isEmpty()) {
+ result.add("Invalid argument");
+ result.add("mmGetAuthorizedGroups <role_name>");
+ return result;
+ }
+ result.add("Resource Groups associated to role " + roleName + ":");
+ List<ResourceGroup> list = this.getAuthorizedGroups(roleName);
+ for (ResourceGroup group : list) {
+ result.add(group.toString());
+ }
+ return result;
+ }
+ public List<String> containermgrGetAuthorizedResources(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String roleName = arg1;
+ if (roleName == null || roleName.trim().isEmpty()) {
+ result.add("Invalid argument");
+ result.add("mmGetAuthorizedResources <role_name>");
+ return result;
+ }
+ result.add("Resource associated to role " + roleName + ":");
+ List<Resource> list = this.getAuthorizedResources(roleName);
+ for (Resource resource : list) {
+ result.add(resource.toString());
+ }
+ return result;
+ }
+ public List<String> containermgrGetResourcesForGroup(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String groupName = arg1;
+ if (groupName == null || groupName.trim().isEmpty()) {
+ result.add("Invalid argument");
+ result.add("containermgrResourcesForGroup <group_name>");
+ return result;
+ }
+ result.add("Group " + groupName + " contains the following resources:");
+ List<Object> resources = this.getResources(groupName);
+ for (Object resource : resources) {
+ result.add(resource.toString());
+ }
+ return result;
+ }
+ public List<String> containermgrGetUserLevel(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String userName = arg1;
+ if (userName == null || userName.trim().isEmpty()) {
+ result.add("Invalid argument");
+ result.add("containermgrGetUserLevel <user_name>");
+ return result;
+ }
+ result.add("User " + userName + " has level: " + this.getUserLevel(userName));
+ return result;
+ }
+ public List<String> containermgrGetUserResources(String arg1) {
+ List<String> result = new ArrayList<String>();
+ String userName = arg1;
+ if (userName == null || userName.trim().isEmpty()) {
+ result.add("Invalid argument");
+ result.add("containermgrGetUserResources <user_name>");
+ return result;
+ }
+ result.add("User " + userName + " owns the following resources: ");
+ Set<Resource> resources = this.getAllResourcesforUser(userName);
+ for (Resource resource : resources) {
+ result.add(resource.toString());
+ }
+ return result;
+ }
+ public List<String> saveConfig() {
+ List<String> result = new ArrayList<String>();
+ Status status = new Status(StatusCode.NOSERVICE, "Configuration service not reachable");
+
+ IConfigurationService configService = (IConfigurationService) ServiceHelper.getGlobalInstance(
+ IConfigurationService.class, this);
+ if (configService != null) {
+ status = configService.saveConfigurations();
+ }
+ result.add(status.toString());
+ return result;
+ }
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+ <artifactId>containermanager.shell</artifactId>
+ <version>${containermanager.shell.version}</version>
+ <packaging>bundle</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ <version>${karaf.shell.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>containermanager</artifactId>
+ <version>${containermanager.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${bundle.plugin.version}</version>
+ <configuration>
+ <instructions>
+ <Import-Package>org.apache.felix.service.command,
+ org.apache.karaf.shell.commands,
+ org.apache.karaf.shell.console,
+ *</Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "addcontainer", description="Add Container")
+public class AddContainer extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="staticVlan", description="staticVlan", required=true, multiValued=false)
+ String staticVlan = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.addContainer(containerName, staticVlan)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "AddContainerEntry", description="add container entry")
+public class AddContainerEntry extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="nodeId", description="node ID", required=true, multiValued=false)
+ String nodeId = null;
+
+ @Argument(index=2, name="portId", description="portId", required=true, multiValued=false)
+ String portId = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.addContainerEntry(containerName, nodeId, portId)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "addContainerFlow", description="adds container flow")
+public class AddContainerFlow extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="cflowName", description="c Flow name", required=true, multiValued=false)
+ String cflowName = null;
+
+ @Argument(index=2, name="unidirectional", description="unidirectional", required=true, multiValued=false)
+ String unidirectional = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.addContainerFlow(containerName, cflowName, unidirectional)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetAuthorizedGroups", description="Get authorized groups")
+public class ContainermgrGetAuthorizedGroups extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="roleName", description="role name", required=true, multiValued=false)
+ String roleName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetAuthorizedGroups(roleName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetAuthorizedResources", description="Get authorized resources")
+public class ContainermgrGetAuthorizedResources extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="roleName", description="role name", required=true, multiValued=false)
+ String roleName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetAuthorizedResources(roleName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetResourcesForGroup", description="Get resources for group")
+public class ContainermgrGetResourcesForGroup extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="groupName", description="group name", required=true, multiValued=false)
+ String groupName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetResourcesForGroup(groupName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetRoles", description="Get container mgr roles")
+public class ContainermgrGetRoles extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetRoles()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetUserLevel", description="Get user level")
+public class ContainermgrGetUserLevel extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="userName", description="user name", required=true, multiValued=false)
+ String userName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetUserLevel(userName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "containermgrGetUserResources", description="Get user resources")
+public class ContainermgrGetUserResources extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="userName", description="user name", required=true, multiValued=false)
+ String userName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.containermgrGetUserResources(userName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "createcontainer", description="create container")
+public class CreateContainer extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="staticVlan", description="staticVlan", required=true, multiValued=false)
+ String staticVlan = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.createContainer(containerName, staticVlan)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "pfc", description="Display pfc")
+public class Pfc extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.pfc()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "psc", description="Display ")
+public class Psc extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.psc()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "psd", description="Display psd")
+public class Psd extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.psd()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "psm", description="Display psm")
+public class Psm extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.psm()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "psp", description="Display psp")
+public class Psp extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.psp()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "removecontainer", description="remove container")
+public class RemoveContainer extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.removeContainerShell(containerName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "removeContainerEntry", description="remove container entry")
+public class RemoveContainerEntry extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="nodeId", description="node ID", required=true, multiValued=false)
+ String nodeId = null;
+
+ @Argument(index=2, name="portId", description="portId", required=true, multiValued=false)
+ String portId = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.removeContainerEntry(containerName, nodeId, portId)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "removeContainerFlow", description="removes container flow")
+public class RemoveContainerFlow extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Argument(index=0, name="containerName", description="container name", required=true, multiValued=false)
+ String containerName = null;
+
+ @Argument(index=1, name="cflowName", description="c Flow name", required=true, multiValued=false)
+ String cflowName = null;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.removeContainerFlow(containerName, cflowName)) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import org.apache.felix.gogo.commands.Command;
+import org.apache.karaf.shell.console.OsgiCommandSupport;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+@Command(scope = "containermanager", name = "saveConfig", description="Save config")
+public class SaveConfig extends OsgiCommandSupport{
+ private IContainerManagerShell containerManager;
+
+ @Override
+ protected Object doExecute() throws Exception {
+ for(String p : containerManager.saveConfig()) {
+ System.out.println(p);
+ }
+ return null;
+ }
+
+ public void setContainerManager(IContainerManagerShell containerManager){
+ this.containerManager = containerManager;
+ }
+}
\ No newline at end of file
--- /dev/null
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <reference id="containerManagerRef" interface="org.opendaylight.controller.containermanager.IContainerManagerShell"/>
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.AddContainer">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.AddContainerEntry">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.AddContainerFlow">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetAuthorizedGroups">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetAuthorizedResources">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetResourcesForGroup">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetRoles">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetUserLevel">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.ContainermgrGetUserResources">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.CreateContainer">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.Pfc">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.Psc">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.Psd">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.Psm">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.Psp">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.RemoveContainer">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.RemoveContainerEntry">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.RemoveContainerFlow">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ <command>
+ <action class="org.opendaylight.controller.containermanager.shell.SaveConfig">
+ <property name="containerManager" ref="containerManagerRef"/>
+ </action>
+ </command>
+
+ </command-bundle>
+
+
+</blueprint>
--- /dev/null
+package org.opendaylight.controller.containermanager.shell;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.lang.reflect.Field;
+
+import org.junit.Assert;
+import org.junit.Test;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import org.opendaylight.controller.containermanager.IContainerManagerShell;
+
+
+public class ContainerManagerShellTest {
+ private IContainerManagerShell containerManager;
+
+ @Test
+ public void testAddContainer() throws Exception {
+ String containerName = "test", staticVlan = "1234";
+ AddContainer addConTest = new AddContainer();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ List<String> result2 = new ArrayList<String>(Arrays.asList("Container Name not specified"));
+ when(containerManager.addContainer(containerName, staticVlan)).thenReturn(result);
+ when(containerManager.addContainer(null, null)).thenReturn(result2);
+
+ Field cNField = addConTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ Field sVField = addConTest.getClass().getDeclaredField("staticVlan");
+ sVField.setAccessible(true);
+
+ cNField.set(addConTest, "test");
+ sVField.set(addConTest, "1234");
+
+ addConTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ addConTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ baos.reset();
+
+ cNField.set(addConTest, null);
+ sVField.set(addConTest, null);
+ addConTest.doExecute();
+ Assert.assertEquals("Container Name not specified\n", baos.toString());
+ }
+
+ @Test
+ public void testAddContainerEntry() throws Exception {
+ String containerName = "test", nodeId = "1234", portId = "5678";
+ AddContainerEntry addConEntTest = new AddContainerEntry();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.addContainerEntry(containerName, nodeId, portId)).thenReturn(result);
+
+ Field cNField = addConEntTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ Field nIField = addConEntTest.getClass().getDeclaredField("nodeId");
+ nIField.setAccessible(true);
+ Field pIField = addConEntTest.getClass().getDeclaredField("portId");
+ pIField.setAccessible(true);
+
+ cNField.set(addConEntTest, "test");
+ nIField.set(addConEntTest, "1234");
+ pIField.set(addConEntTest, "5678");
+
+ addConEntTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ addConEntTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testAddContainerFlow() throws Exception {
+ String containerName = "test", cflowName = "1234", unidirectional = "5678";
+ AddContainerFlow addConFlowTest = new AddContainerFlow();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.addContainerFlow(containerName, cflowName, unidirectional)).thenReturn(result);
+
+ Field cNField = addConFlowTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ Field cfField = addConFlowTest.getClass().getDeclaredField("cflowName");
+ cfField.setAccessible(true);
+ Field unField = addConFlowTest.getClass().getDeclaredField("unidirectional");
+ unField.setAccessible(true);
+
+ cNField.set(addConFlowTest, "test");
+ cfField.set(addConFlowTest, "1234");
+ unField.set(addConFlowTest, "5678");
+
+ addConFlowTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ addConFlowTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetAuthorizedGroups() throws Exception {
+ String roleName = "test";
+ ContainermgrGetAuthorizedGroups contmgrGTest = new ContainermgrGetAuthorizedGroups();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetAuthorizedGroups(roleName)).thenReturn(result);
+
+ Field rNField = contmgrGTest.getClass().getDeclaredField("roleName");
+ rNField.setAccessible(true);
+
+ rNField.set(contmgrGTest, "test");
+
+ contmgrGTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrGTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetAuthorizedResources() throws Exception {
+ String roleName = "test";
+ ContainermgrGetAuthorizedResources contmgrRTest = new ContainermgrGetAuthorizedResources();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetAuthorizedResources(roleName)).thenReturn(result);
+
+ Field rNField = contmgrRTest.getClass().getDeclaredField("roleName");
+ rNField.setAccessible(true);
+
+ rNField.set(contmgrRTest, "test");
+
+ contmgrRTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrRTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetResourcesForGroup() throws Exception {
+ String groupName = "test";
+ ContainermgrGetResourcesForGroup contmgrRTest = new ContainermgrGetResourcesForGroup();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetResourcesForGroup(groupName)).thenReturn(result);
+
+ Field gNField = contmgrRTest.getClass().getDeclaredField("groupName");
+ gNField.setAccessible(true);
+
+ gNField.set(contmgrRTest, groupName);
+
+ contmgrRTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrRTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetRoles() throws Exception {
+ ContainermgrGetRoles contmgrRTest = new ContainermgrGetRoles();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetRoles()).thenReturn(result);
+
+ contmgrRTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrRTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetUserLevel() throws Exception {
+ String userName = "test";
+ ContainermgrGetUserLevel contmgrUTest = new ContainermgrGetUserLevel();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetUserLevel(userName)).thenReturn(result);
+
+ Field gNField = contmgrUTest.getClass().getDeclaredField("userName");
+ gNField.setAccessible(true);
+
+ gNField.set(contmgrUTest, userName);
+
+ contmgrUTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrUTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testContainermgrGetUserResources() throws Exception {
+ String userName = "test";
+ ContainermgrGetUserResources contmgrUTest = new ContainermgrGetUserResources();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.containermgrGetUserResources(userName)).thenReturn(result);
+
+ Field gNField = contmgrUTest.getClass().getDeclaredField("userName");
+ gNField.setAccessible(true);
+
+ gNField.set(contmgrUTest, userName);
+
+ contmgrUTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ contmgrUTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testPfc() throws Exception {
+ Pfc pfc = new Pfc();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.pfc()).thenReturn(result);
+
+ pfc.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ pfc.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testPsc() throws Exception {
+ Psc psc = new Psc();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.psc()).thenReturn(result);
+
+ psc.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ psc.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testPsd() throws Exception {
+ Psd psd = new Psd();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.psd()).thenReturn(result);
+
+ psd.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ psd.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testPsm() throws Exception {
+ Psm psm = new Psm();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.psm()).thenReturn(result);
+
+ psm.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ psm.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testPsp() throws Exception {
+ Psp psp = new Psp();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.psp()).thenReturn(result);
+
+ psp.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ psp.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testRemoveContainer() throws Exception {
+ String containerName = "test";
+ RemoveContainer remConTest = new RemoveContainer();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.removeContainerShell(containerName)).thenReturn(result);
+
+ Field cNField = remConTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ cNField.set(remConTest, "test");
+
+ remConTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ remConTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testRemoveContainerEntry() throws Exception {
+ String containerName = "test", nodeId = "1234", portId = "5678";
+ RemoveContainerEntry remConEntTest = new RemoveContainerEntry();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.removeContainerEntry(containerName, nodeId, portId)).thenReturn(result);
+
+ Field cNField = remConEntTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ Field nIField = remConEntTest.getClass().getDeclaredField("nodeId");
+ nIField.setAccessible(true);
+ Field pIField = remConEntTest.getClass().getDeclaredField("portId");
+ pIField.setAccessible(true);
+
+ cNField.set(remConEntTest, "test");
+ nIField.set(remConEntTest, "1234");
+ pIField.set(remConEntTest, "5678");
+
+ remConEntTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ remConEntTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testRemoveContainerFlow() throws Exception {
+ String containerName = "test", cflowName = "1234";
+ RemoveContainerFlow remConFlowTest = new RemoveContainerFlow();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.removeContainerFlow(containerName, cflowName)).thenReturn(result);
+
+ Field cNField = remConFlowTest.getClass().getDeclaredField("containerName");
+ cNField.setAccessible(true);
+ Field cfField = remConFlowTest.getClass().getDeclaredField("cflowName");
+ cfField.setAccessible(true);
+
+ cNField.set(remConFlowTest, "test");
+ cfField.set(remConFlowTest, "1234");
+
+ remConFlowTest.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ remConFlowTest.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+
+ @Test
+ public void testSaveConfig() throws Exception {
+ SaveConfig saveConfig = new SaveConfig();
+ containerManager = mock(IContainerManagerShell.class);
+ List<String> result = new ArrayList<String>(Arrays.asList("status"));
+ when(containerManager.saveConfig()).thenReturn(result);
+
+ saveConfig.setContainerManager(containerManager);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ System.setOut(new PrintStream(baos));
+ saveConfig.doExecute();
+ Assert.assertEquals("status\n", baos.toString());
+ }
+}
\ No newline at end of file
<type>xml</type>
<scope>runtime</scope>
</dependency>
+ <!-- scope is compile so all features (there is only one) are installed
+ into startup.properties and the feature repo itself is not installed -->
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>base-features</artifactId>
<version>${project.version}</version>
- <type>kar</type>
+ <type>pom</type>
<scope>runtime</scope>
</dependency>
<dependency>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
- <artifactId>features-file</artifactId>
+ <artifactId>features-yangtools</artifactId>
<version>${yangtools.version}</version>
<classifier>features</classifier>
<type>xml</type>
# Extra packages to import from the boot class loader
org.osgi.framework.system.packages.extra=org.apache.karaf.branding,sun.reflect,sun.reflect.misc,sun.misc,sun.nio.ch
-# https://bugs.eclipse.org/bugs/show_bug.cgi?id=325578
-# Extend the framework to avoid the resources to be presented with
-# a URL of type bundleresource: but to be presented as file:
-osgi.hook.configurators.include=org.eclipse.virgo.kernel.equinox.extensions.hooks.ExtensionsHookConfigurator
# Embedded Tomcat configuration File
org.eclipse.gemini.web.tomcat.config.path=configuration/tomcat-server.xml
org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true
-# Use Equinox as default OSGi Framework Implementation
-karaf.framework=equinox
# Netconf startup configuration
netconf.tcp.address=127.0.0.1
mvn\:org.ops4j.pax.url/pax-url-maven-commons/1.6.0 = 5
mvn\:org.ops4j.pax.url/pax-url-aether/1.6.0 = 5
mvn\:org.ops4j.pax.url/pax-url-wrap/1.6.0 = 5
-mvn\:javax.annotation/javax.annotation-api/1.2 = 5
+#mvn\:javax.annotation/javax.annotation-api/1.2 = 5
mvn\:org.ops4j.pax.logging/pax-logging-api/1.7.2 = 8
mvn\:org.ops4j.pax.logging/pax-logging-service/1.7.2 = 8
mvn\:org.apache.karaf.service/org.apache.karaf.service.guard/3.0.1 = 10
--- /dev/null
+#
+# The properties defined in this file will be made available through system
+# properties at the very beginning of the Karaf's boot process.
+#
+
+# Use Equinox as default OSGi Framework Implementation
+karaf.framework=equinox
+
+# https://bugs.eclipse.org/bugs/show_bug.cgi?id=325578
+# Extend the framework to avoid the resources to be presented with
+# a URL of type bundleresource: but to be presented as file:
+osgi.hook.configurators.include=org.eclipse.virgo.kernel.equinox.extensions.hooks.ExtensionsHookConfigurator
+
+
+# Log level when the pax-logging service is not available
+# This level will only be used while the pax-logging service bundle
+# is not fully available.
+# To change log levels, please refer to the org.ops4j.pax.logging.cfg file
+# instead.
+org.ops4j.pax.logging.DefaultServiceLog.level = ERROR
+
+#
+# Name of this Karaf instance.
+#
+karaf.name = root
+
+#
+# Default repository where bundles will be loaded from before using
+# other Maven repositories. For the full Maven configuration, see
+# the org.ops4j.pax.url.mvn.cfg file.
+#
+karaf.default.repository = system
+
+#
+# Location of a shell script that will be run when starting a shell
+# session. This script can be used to create aliases and define
+# additional commands.
+#
+karaf.shell.init.script = ${karaf.etc}/shell.init.script
+
+#
+# Sets the maximum size of the shell command history. If not set,
+# defaults to 500 entries. Setting to 0 will disable history.
+#
+# karaf.shell.history.maxSize = 0
+
+#
+# Deletes the entire karaf.data directory at every start
+#
+karaf.clean.all = false
+
+#
+# Deletes the karaf.data/cache directory at every start
+#
+karaf.clean.cache = false
+
+#
+# Roles to use when logging into a local Karaf console.
+#
+# The syntax is the following:
+# [classname:]principal
+# where classname is the class name of the principal object
+# (defaults to org.apache.karaf.jaas.modules.RolePrincipal)
+# and principal is the name of the principal of that class
+# (defaults to instance).
+#
+karaf.local.roles = admin,manager,viewer
+
+#
+# Set this empty property to avoid errors when validating xml documents.
+#
+xml.catalog.files =
+
+#
+# Suppress the bell in the console when hitting backspace too many times
+# for example
+#
+jline.nobell = true
+
+#
+# ServiceMix specs options
+#
+org.apache.servicemix.specs.debug = false
+org.apache.servicemix.specs.timeout = 0
+
+#
+# Settings for the OSGi 4.3 Weaving
+# By default, we will not weave any classes. Change this setting to include classes
+# that you application needs to have woven.
+#
+org.apache.aries.proxy.weaving.enabled = none
+# Classes not to weave - Aries default + Xerces which is known to have issues.
+org.apache.aries.proxy.weaving.disabled = org.objectweb.asm.*,org.slf4j.*,org.apache.log4j.*,javax.*,org.apache.xerces.*
+
+#
+# By default, only Karaf shell commands are secured, but additional services can be
+# secured by expanding this filter
+#
+karaf.secured.services = (&(osgi.command.scope=*)(osgi.command.function=*))
+
+#
+# Security properties
+#
+# To enable OSGi security, uncomment the properties below,
+# install the framework-security feature and restart.
+#
+#java.security.policy=${karaf.etc}/all.policy
+#org.osgi.framework.security=osgi
+#org.osgi.framework.trust.repositories=${karaf.etc}/trustStore.ks
</plugins>
</build>
</profile>
+ <profile>
+ <id>docs</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>swagger-ui</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+ </profile>
</profiles>
</project>
<name>runtime-mapping-singleton</name>
</binding-mapping-service>
</module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-forwarded-data-broker</type>
+ <name>binding-async-data-broker</name>
+ <binding-forwarded-data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <dom-async-broker>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>dom-broker</name>
+ </dom-async-broker>
+ <binding-mapping-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </binding-mapping-service>
+ </binding-forwarded-data-broker>
+ </module>
</modules>
<services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
<service>
</instance>
</service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <instance>
+ <name>binding-data-broker</name>
+ <provider>/modules/module[type='binding-forwarded-data-broker'][name='binding-async-data-broker']</provider>
+ </instance>
+ </service>
+
<service>
<type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
<instance>
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
<artifactId>model-topology</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
<artifactId>model-topology</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
</dependencies>
@Override
public void onFlowRemoved(final FlowRemoved notification) {
+ // notified upon remove flow rpc successfully invoked
if (notification == null) {
return;
}
@Override
public void onSwitchFlowRemoved(final SwitchFlowRemoved notification) {
- // FIXME: unfinished?
+ // notified upon remove flow message from device arrives
+ if (notification == null) {
+ return;
+ }
+
+ final NodeRef node = notification.getNode();
+ if (node == null) {
+ LOG.debug("Notification {} has not node, ignoring it", notification);
+ return;
+ }
+
+ Node adNode;
+ try {
+ adNode = NodeMapping.toADNode(notification.getNode());
+ } catch (ConstructionException e) {
+ LOG.warn("Failed to construct AD node for {}, ignoring notification", node, e);
+ return;
+ }
+ flowProgrammerPublisher.flowRemoved(adNode, ToSalConversionsUtils.toFlow(notification, adNode));
}
@Override
VlanIdBuilder vlanIDBuilder = new VlanIdBuilder();
vlanIDBuilder.setVlanId(new VlanId((NetUtils
.getUnsignedShort((short) vlan.getValue()))));
+ vlanIDBuilder.setVlanIdPresent(true);
vlanMatchBuild.setVlanId(vlanIDBuilder.build());
}
return nodeConnectorId.getValue();
}
+ public static NodeId toAdNodeId(final NodeConnectorId nodeConnectorId) {
+ NodeId nodeId = null;
+ if (nodeConnectorId != null) {
+ nodeId = new NodeId(nodeConnectorId.getValue().replaceFirst(":[0-9]+$", ""));
+ }
+ return nodeId;
+ }
+
public static NodeConnectorId toControllerNodeConnectorId(final NodeId node) {
return new NodeConnectorId(node.getValue() + ":" + 4294967293L);
}
import org.opendaylight.controller.sal.core.NodeConnector;
import org.opendaylight.controller.sal.flowprogrammer.Flow;
import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchType;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Dscp;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.Address;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv4;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv6;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.GenericFlowAttributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanPcp;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.MacAddressFilter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatch;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatch;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.net.InetAddresses;
public class ToSalConversionsUtils {
+ private static final Logger LOG = LoggerFactory.getLogger(ToSalConversionsUtils.class);
+
private ToSalConversionsUtils() {
}
public static Flow toFlow(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow source, Node node) {
final Flow target = new Flow();
+ genericFlowToAdFlow(source, target);
+
+ target.setMatch(toMatch(source.getMatch()));
+
+ List<Action> actions = getAction(source);
+ if (actions != null) {
+ target.setActions(actionFrom(actions, node));
+ }
+
+ return target;
+ }
+
+ /**
+ * @param source notification, missing instructions
+ * @param node corresponding node where the flow change occured
+ * @return ad-sal node, build from given data
+ */
+ public static Flow toFlow(SwitchFlowRemoved source, Node node) {
+ final Flow target = new Flow();
+ genericFlowToAdFlow(source, target);
+
+ target.setMatch(toMatch(source.getMatch()));
+
+ return target;
+ }
+ /**
+ * @param source
+ * @param target
+ */
+ private static void genericFlowToAdFlow(GenericFlowAttributes source,
+ final Flow target) {
Integer hardTimeout = source.getHardTimeout();
if (hardTimeout != null) {
target.setHardTimeout(hardTimeout.shortValue());
if (priority != null) {
target.setPriority(priority.shortValue());
}
-
- target.setMatch(toMatch(source.getMatch()));
-
- List<Action> actions = getAction(source);
- if (actions != null) {
- target.setActions(actionFrom(actions, node));
- }
-
target.setId(source.getCookie().getValue().longValue());
- return target;
}
public static List<Action> getAction(
return nodeConnector;
}
- public static Match toMatch(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match source) {
+ public static Match toMatch(org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match source) {
Match target = new Match();
if (source != null) {
fillFrom(target, source.getVlanMatch());
fillFrom(target, source.getLayer3Match());
fillFrom(target, source.getLayer4Match());
fillFrom(target, source.getIpMatch());
+ fillFrom(target, source.getInPort());
}
return target;
}
+ /**
+ * @param target
+ * @param inPort
+ */
+ private static void fillFrom(Match target, NodeConnectorId inPort) {
+ if (inPort != null) {
+ String inPortValue = inPort.getValue();
+ if (inPortValue != null) {
+ try {
+ target.setField(MatchType.IN_PORT, NodeMapping.toADNodeConnector(inPort,
+ NodeMapping.toAdNodeId(inPort)));
+ } catch (ConstructionException e) {
+ LOG.warn("nodeConnector construction failed", e);
+ }
+ }
+ }
+ }
+
private static void fillFrom(Match target, VlanMatch vlanMatch) {
if (vlanMatch != null) {
VlanId vlanId = vlanMatch.getVlanId();
import org.junit.Test;
import org.opendaylight.controller.sal.compatibility.NodeMapping;
import org.opendaylight.controller.sal.core.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
/**
}
}
+ /**
+ * Test method for
+ * {@link org.opendaylight.controller.sal.compatibility.NodeMapping#toAdNodeId(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId)}
+ * .
+ */
+ @Test
+ public void testToAdNodeId() {
+ NodeId observed;
+ observed = NodeMapping.toAdNodeId(null);
+ Assert.assertNull(observed);
+
+ observed = NodeMapping.toAdNodeId(new NodeConnectorId("MD_SAL|openflow:5:2"));
+ Assert.assertEquals("MD_SAL|openflow:5", observed.getValue());
+ }
+
}
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>flow-management-compatibility</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
*/
package org.opendaylight.controller.md.inventory.manager;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.LinkedBlockingDeque;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.yangtools.concepts.Registration;
-import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class FlowCapableInventoryProvider implements AutoCloseable {
+import com.google.common.base.Preconditions;
+
+class FlowCapableInventoryProvider implements AutoCloseable, Runnable {
+ private static final Logger LOG = LoggerFactory.getLogger(FlowCapableInventoryProvider.class);
+ private static final int QUEUE_DEPTH = 500;
+ private static final int MAX_BATCH = 100;
+
+ private final BlockingQueue<InventoryOperation> queue = new LinkedBlockingDeque<>(QUEUE_DEPTH);
+ private final NotificationProviderService notificationService;
+ private final DataProviderService dataService;
+ private Registration<?> listenerRegistration;
+ private Thread thread;
- private final static Logger LOG = LoggerFactory.getLogger(FlowCapableInventoryProvider.class);
+ FlowCapableInventoryProvider(final DataProviderService dataService, final NotificationProviderService notificationService) {
+ this.dataService = Preconditions.checkNotNull(dataService);
+ this.notificationService = Preconditions.checkNotNull(notificationService);
+ }
+
+ void start() {
+ final NodeChangeCommiter changeCommiter = new NodeChangeCommiter(FlowCapableInventoryProvider.this);
+ this.listenerRegistration = this.notificationService.registerNotificationListener(changeCommiter);
- private DataProviderService dataService;
- private NotificationProviderService notificationService;
- private Registration<NotificationListener> listenerRegistration;
- private final NodeChangeCommiter changeCommiter = new NodeChangeCommiter(FlowCapableInventoryProvider.this);
+ thread = new Thread(this);
+ thread.setDaemon(true);
+ thread.setName("FlowCapableInventoryProvider");
+ thread.start();
- public void start() {
- this.listenerRegistration = this.notificationService.registerNotificationListener(this.changeCommiter);
LOG.info("Flow Capable Inventory Provider started.");
}
- protected DataModificationTransaction startChange() {
- DataProviderService _dataService = this.dataService;
- return _dataService.beginTransaction();
+ void enqueue(final InventoryOperation op) {
+ try {
+ queue.put(op);
+ } catch (InterruptedException e) {
+ LOG.warn("Failed to enqueue operation {}", op, e);
+ }
}
@Override
- public void close() {
- try {
- LOG.info("Flow Capable Inventory Provider stopped.");
- if (this.listenerRegistration != null) {
+ public void close() throws InterruptedException {
+ LOG.info("Flow Capable Inventory Provider stopped.");
+ if (this.listenerRegistration != null) {
+ try {
this.listenerRegistration.close();
+ } catch (Exception e) {
+ LOG.error("Failed to stop inventory provider", e);
}
- } catch (Exception e) {
- String errMsg = "Error by stop Flow Capable Inventory Provider.";
- LOG.error(errMsg, e);
- throw new RuntimeException(errMsg, e);
+ listenerRegistration = null;
}
- }
- public DataProviderService getDataService() {
- return this.dataService;
- }
+ if (thread != null) {
+ thread.interrupt();
+ thread.join();
+ thread = null;
+ }
- public void setDataService(final DataProviderService dataService) {
- this.dataService = dataService;
- }
- public NotificationProviderService getNotificationService() {
- return this.notificationService;
}
- public void setNotificationService(
- final NotificationProviderService notificationService) {
- this.notificationService = notificationService;
+ @Override
+ public void run() {
+ try {
+ for (;;) {
+ InventoryOperation op = queue.take();
+
+ final DataModificationTransaction tx = dataService.beginTransaction();
+ LOG.debug("New operations available, starting transaction {}", tx.getIdentifier());
+
+ int ops = 0;
+ do {
+ op.applyOperation(tx);
+
+ ops++;
+ if (ops < MAX_BATCH) {
+ op = queue.poll();
+ } else {
+ op = null;
+ }
+ } while (op != null);
+
+ LOG.debug("Processed {} operations, submitting transaction {}", ops, tx.getIdentifier());
+
+ try {
+ final RpcResult<TransactionStatus> result = tx.commit().get();
+ if(!result.isSuccessful()) {
+ LOG.error("Transaction {} failed", tx.getIdentifier());
+ }
+ } catch (ExecutionException e) {
+ LOG.warn("Failed to commit inventory change", e.getCause());
+ }
+ }
+ } catch (InterruptedException e) {
+ LOG.info("Processing interrupted, terminating", e);
+ }
+
+ // Drain all events, making sure any blocked threads are unblocked
+ while (!queue.isEmpty()) {
+ queue.poll();
+ }
}
}
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class InventoryActivator extends AbstractBindingAwareProvider {
-
- private static FlowCapableInventoryProvider provider = new FlowCapableInventoryProvider();
+ private static final Logger LOG = LoggerFactory.getLogger(InventoryActivator.class);
+ private FlowCapableInventoryProvider provider;
@Override
public void onSessionInitiated(final ProviderContext session) {
- DataProviderService salDataService = session.<DataProviderService> getSALService(DataProviderService.class);
+ DataProviderService salDataService = session.getSALService(DataProviderService.class);
NotificationProviderService salNotifiService =
- session.<NotificationProviderService> getSALService(NotificationProviderService.class);
- InventoryActivator.provider.setDataService(salDataService);
- InventoryActivator.provider.setNotificationService(salNotifiService);
- InventoryActivator.provider.start();
+ session.getSALService(NotificationProviderService.class);
+
+ provider = new FlowCapableInventoryProvider(salDataService, salNotifiService);
+ provider.start();
}
@Override
protected void stopImpl(final BundleContext context) {
- InventoryActivator.provider.close();
+ if (provider != null) {
+ try {
+ provider.close();
+ } catch (InterruptedException e) {
+ LOG.warn("Interrupted while waiting for shutdown", e);
+ }
+ provider = null;
+ }
}
}
--- /dev/null
+/**
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.inventory.manager;
+
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+
+interface InventoryOperation {
+
+ void applyOperation(DataModificationTransaction tx);
+
+}
*/
package org.opendaylight.controller.md.inventory.manager;
-import java.util.concurrent.Future;
-
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdated;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Objects;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.JdkFutureAdapters;
-
-public class NodeChangeCommiter implements OpendaylightInventoryListener {
+import com.google.common.base.Preconditions;
- protected final static Logger LOG = LoggerFactory.getLogger(NodeChangeCommiter.class);
+class NodeChangeCommiter implements OpendaylightInventoryListener {
+ private static final Logger LOG = LoggerFactory.getLogger(NodeChangeCommiter.class);
private final FlowCapableInventoryProvider manager;
public NodeChangeCommiter(final FlowCapableInventoryProvider manager) {
- this.manager = manager;
- }
-
- public FlowCapableInventoryProvider getManager() {
- return this.manager;
+ this.manager = Preconditions.checkNotNull(manager);
}
@Override
public synchronized void onNodeConnectorRemoved(final NodeConnectorRemoved connector) {
-
- final NodeConnectorRef ref = connector.getNodeConnectorRef();
- final DataModificationTransaction it = this.getManager().startChange();
- LOG.debug("removing node connector {} ", ref.getValue());
- it.removeOperationalData(ref.getValue());
- Future<RpcResult<TransactionStatus>> commitResult = it.commit();
- listenOnTransactionState(it.getIdentifier(), commitResult, "nodeConnector removal", ref.getValue());
+ manager.enqueue(new InventoryOperation() {
+ @Override
+ public void applyOperation(final DataModificationTransaction tx) {
+ final NodeConnectorRef ref = connector.getNodeConnectorRef();
+ LOG.debug("removing node connector {} ", ref.getValue());
+ tx.removeOperationalData(ref.getValue());
+ }
+ });
}
@Override
public synchronized void onNodeConnectorUpdated(final NodeConnectorUpdated connector) {
-
- final NodeConnectorRef ref = connector.getNodeConnectorRef();
- final FlowCapableNodeConnectorUpdated flowConnector = connector
- .getAugmentation(FlowCapableNodeConnectorUpdated.class);
- final DataModificationTransaction it = this.manager.startChange();
- final NodeConnectorBuilder data = new NodeConnectorBuilder(connector);
- NodeConnectorId id = connector.getId();
- NodeConnectorKey nodeConnectorKey = new NodeConnectorKey(id);
- data.setKey(nodeConnectorKey);
- boolean notEquals = (!Objects.equal(flowConnector, null));
- if (notEquals) {
- final FlowCapableNodeConnector augment = InventoryMapping.toInventoryAugment(flowConnector);
- data.addAugmentation(FlowCapableNodeConnector.class, augment);
- }
- InstanceIdentifier<? extends Object> value = ref.getValue();
- LOG.debug("updating node connector : {}.", value);
- NodeConnector build = data.build();
- it.putOperationalData((value), build);
- Future<RpcResult<TransactionStatus>> commitResult = it.commit();
- listenOnTransactionState(it.getIdentifier(), commitResult, "nodeConnector update", ref.getValue());
+ manager.enqueue(new InventoryOperation() {
+ @Override
+ public void applyOperation(final DataModificationTransaction tx) {
+ final NodeConnectorRef ref = connector.getNodeConnectorRef();
+ final NodeConnectorBuilder data = new NodeConnectorBuilder(connector);
+ data.setKey(new NodeConnectorKey(connector.getId()));
+
+ final FlowCapableNodeConnectorUpdated flowConnector = connector
+ .getAugmentation(FlowCapableNodeConnectorUpdated.class);
+ if (flowConnector != null) {
+ final FlowCapableNodeConnector augment = InventoryMapping.toInventoryAugment(flowConnector);
+ data.addAugmentation(FlowCapableNodeConnector.class, augment);
+ }
+ InstanceIdentifier<? extends Object> value = ref.getValue();
+ LOG.debug("updating node connector : {}.", value);
+ NodeConnector build = data.build();
+ tx.putOperationalData(value, build);
+ }
+ });
}
@Override
public synchronized void onNodeRemoved(final NodeRemoved node) {
-
- final NodeRef ref = node.getNodeRef();
- final DataModificationTransaction it = this.manager.startChange();
- LOG.debug("removing node : {}", ref.getValue());
- it.removeOperationalData((ref.getValue()));
- Future<RpcResult<TransactionStatus>> commitResult = it.commit();
- listenOnTransactionState(it.getIdentifier(), commitResult, "node removal", ref.getValue());
+ manager.enqueue(new InventoryOperation() {
+ @Override
+ public void applyOperation(final DataModificationTransaction tx) {
+ final NodeRef ref = node.getNodeRef();
+ LOG.debug("removing node : {}", ref.getValue());
+ tx.removeOperationalData((ref.getValue()));
+ }
+ });
}
@Override
public synchronized void onNodeUpdated(final NodeUpdated node) {
-
- final NodeRef ref = node.getNodeRef();
- final FlowCapableNodeUpdated flowNode = node
- .<FlowCapableNodeUpdated> getAugmentation(FlowCapableNodeUpdated.class);
- final DataModificationTransaction it = this.manager.startChange();
- final NodeBuilder nodeBuilder = new NodeBuilder(node);
- nodeBuilder.setKey(new NodeKey(node.getId()));
- boolean equals = Objects.equal(flowNode, null);
- if (equals) {
+ final FlowCapableNodeUpdated flowNode = node.getAugmentation(FlowCapableNodeUpdated.class);
+ if (flowNode == null) {
return;
}
- final FlowCapableNode augment = InventoryMapping.toInventoryAugment(flowNode);
- nodeBuilder.addAugmentation(FlowCapableNode.class, augment);
- InstanceIdentifier<? extends Object> value = ref.getValue();
- InstanceIdentifierBuilder<Node> builder = ((InstanceIdentifier<Node>) value).builder();
- InstanceIdentifierBuilder<FlowCapableNode> augmentation = builder
- .<FlowCapableNode> augmentation(FlowCapableNode.class);
- final InstanceIdentifier<FlowCapableNode> path = augmentation.build();
- LOG.debug("updating node :{} ", path);
- it.putOperationalData(path, augment);
-
- Future<RpcResult<TransactionStatus>> commitResult = it.commit();
- listenOnTransactionState(it.getIdentifier(), commitResult, "node update", ref.getValue());
- }
-
- /**
- * @param txId transaction identificator
- * @param future transaction result
- * @param action performed by transaction
- * @param nodeConnectorPath target value
- */
- private static void listenOnTransactionState(final Object txId, Future<RpcResult<TransactionStatus>> future,
- final String action, final InstanceIdentifier<?> nodeConnectorPath) {
- Futures.addCallback(JdkFutureAdapters.listenInPoolThread(future),new FutureCallback<RpcResult<TransactionStatus>>() {
-
- @Override
- public void onFailure(Throwable t) {
- LOG.error("Action {} [{}] failed for Tx:{}", action, nodeConnectorPath, txId, t);
-
- }
+ manager.enqueue(new InventoryOperation() {
@Override
- public void onSuccess(RpcResult<TransactionStatus> result) {
- if(!result.isSuccessful()) {
- LOG.error("Action {} [{}] failed for Tx:{}", action, nodeConnectorPath, txId);
- }
+ public void applyOperation(final DataModificationTransaction tx) {
+ final NodeRef ref = node.getNodeRef();
+ final NodeBuilder nodeBuilder = new NodeBuilder(node);
+ nodeBuilder.setKey(new NodeKey(node.getId()));
+
+ final FlowCapableNode augment = InventoryMapping.toInventoryAugment(flowNode);
+ nodeBuilder.addAugmentation(FlowCapableNode.class, augment);
+
+ @SuppressWarnings("unchecked")
+ InstanceIdentifierBuilder<Node> builder = ((InstanceIdentifier<Node>) ref.getValue()).builder();
+ InstanceIdentifierBuilder<FlowCapableNode> augmentation = builder.augmentation(FlowCapableNode.class);
+ final InstanceIdentifier<FlowCapableNode> path = augmentation.build();
+ LOG.debug("updating node :{} ", path);
+ tx.putOperationalData(path, augment);
}
});
}
}
}
+ container action-types {
+ uses action-list;
+ }
+
grouping action-list {
list action {
key "order";
+++ /dev/null
-module opendaylight-inventory-config {
- namespace "urn:opendaylight:inventory:config";
- prefix inv-config;
-
- import yang-ext {prefix ext; revision-date "2013-07-09";}
- import opendaylight-inventory {prefix inv; revision-date "2013-08-19";}
-
-
- revision "2013-08-19" {
- description "Initial revision of Inventory model";
- }
- /** Base structure **/
- container nodes {
- list node {
- key "id";
- ext:context-instance "node-context";
-
- uses inv:node;
- }
- }
-}
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
- <version>1.0.0</version>
+ <version>${lifecycle.mapping.version}</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
- <plugin>
- <!-- FIXME: BUG-272: remove this configuration override -->
- <!-- replaced with new configuration -->
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-checkstyle-plugin</artifactId>
- <version>2.12</version>
- <configuration>
- <failsOnError>false</failsOnError>
- <failOnViolation>false</failOnViolation>
- <configLocation>checkstyle-logging.xml</configLocation>
- <consoleOutput>true</consoleOutput>
- <includeTestSourceDirectory>true</includeTestSourceDirectory>
- <sourceDirectory>${project.basedir}</sourceDirectory>
- <includes>**\/*.java,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat,**\/*.yang</includes>
- <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/${jmxGeneratorPath}\/,**\/${salGeneratorPath}\/</excludes>
- </configuration>
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>checkstyle-logging</artifactId>
- <version>${yangtools.version}</version>
- </dependency>
- </dependencies>
- <executions>
- <execution>
- <goals>
- <goal>check</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
- <version>2.4.0</version>
+ <version>${findbugs.maven.plugin.version}</version>
<configuration>
<effort>Max</effort>
<threshold>Low</threshold>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jdepend-maven-plugin</artifactId>
- <version>2.0-beta-2</version>
+ <version>${jdepend.maven.plugin.version}</version>
</plugin>
</plugins>
</reporting>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>remoterpc-routingtable.implementation</artifactId>
- <version>0.4.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>clustering.services</artifactId>
- <version>0.4.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal</artifactId>
- <version>0.5.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal.implementation</artifactId>
- <version>0.4.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager.it.implementation</artifactId>
- <version>0.5.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>clustering.stub</artifactId>
- <version>0.4.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>pax-exam-container-native</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-junit4</artifactId>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-link-mvn</artifactId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal</artifactId>
- <version>0.5.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-connector-api</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common-util</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common-api</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-util</artifactId>
- <version>1.0-SNAPSHOT</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-binding-broker-impl</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-broker-impl</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-manager</artifactId>
- <version>0.2.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>equinoxSDK381</groupId>
<artifactId>org.eclipse.osgi</artifactId>
- <version>3.8.1.v20120830-144521</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
- <version>1.7.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
- <version>1.0.9</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
- <version>1.0.9</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools.thirdparty</groupId>
<artifactId>antlr4-runtime-osgi-nohead</artifactId>
- <version>4.0</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools.thirdparty</groupId>
<artifactId>xtend-lib-osgi</artifactId>
- <version>2.4.3</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>clustering.services</artifactId>
- <version>0.4.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<dependency>
<groupId>equinoxSDK381</groupId>
<artifactId>org.eclipse.osgi</artifactId>
- <version>3.8.1.v20120830-144521</version>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam-junit4</artifactId>
- <version>3.0.0</version>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-junit4</artifactId>
- <version>3.0.0</version>
+ <version>${exam.version}</version>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>pax-exam</artifactId>
- <version>3.0.0</version>
+ <version>${exam.version}</version>
</dependency>
</dependencies>
<properties>
<parent>
<artifactId>sal-remoterpc-connector-test-parent</artifactId>
<groupId>org.opendaylight.controller.tests</groupId>
- <version>1.0-SNAPSHOT</version>
+ <version>1.1-SNAPSHOT</version>
</parent>
<artifactId>remoterpc-routingtable-nb-it</artifactId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.5.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>commons.northbound</artifactId>
- <version>0.4.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal</artifactId>
- <version>0.5.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
- <version>5.0.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>remoterpc-routingtable.implementation</artifactId>
- <version>0.4.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-public interface BindingDataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, BindingDataChangeListener>{
+public interface BindingDataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, BindingDataChangeListener>, BindingService {
@Override
BindingDataReadTransaction newReadOnlyTransaction();
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+
+/**
+ *
+ * Marker interface for MD-SAL services which are available for users of MD-SAL.
+ *
+ * BindingService is marker interface for infrastructure services provided by
+ * the SAL. These services may be session-specific, and wrapped by custom
+ * delegator patterns in order to introduce additional semantics / checks
+ * to the system.
+ *
+ * This interface extends {@link BindingAwareService}, order to be make
+ * new services available via
+ * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext}
+ * and via
+ * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext}
+ *
+ */
+public interface BindingService extends BindingAwareService {
+
+}
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
- <version>0.9.9-RC1</version>
<scope>compile</scope>
</dependency>
<dependency>
org.opendaylight.controller.md.sal.binding.impl,
<!--org.opendaylight.controller.sal.binding.dom.*,-->
org.opendaylight.controller.sal.binding.osgi.*,
- org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.binding.impl.rev131028
+ org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.binding.impl.rev131028.*
</Private-Package>
</instructions>
</configuration>
--- /dev/null
+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.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;
+import org.osgi.framework.BundleContext;
+
+public class BindingAsyncDataBrokerImplModule extends
+ org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingAsyncDataBrokerImplModule implements
+ Provider {
+ private BundleContext bundleContext;
+
+ public BindingAsyncDataBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public BindingAsyncDataBrokerImplModule(
+ final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ final org.opendaylight.controller.config.yang.md.sal.binding.impl.BindingAsyncDataBrokerImplModule oldModule,
+ final java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ Broker domBroker = getDomAsyncBrokerDependency();
+ BindingIndependentMappingService mappingService = getBindingMappingServiceDependency();
+
+ // FIXME: Switch this to DOM Broker registration which would not require
+ // BundleContext when API are updated.
+ ProviderSession session = domBroker.registerProvider(this, getBundleContext());
+ DOMDataBroker domDataBroker = session.getService(DOMDataBroker.class);
+ SchemaService schemaService = session.getService(SchemaService.class);
+ return new ForwardedBindingDataBroker(domDataBroker, mappingService, schemaService);
+ }
+
+ // FIXME: Remove this when DOM Broker registration would not require
+ // BundleContext
+ @Deprecated
+ private BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ // FIXME: Remove this when DOM Broker registration would not require
+ // BundleContext
+ @Deprecated
+ void setBundleContext(final BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ @Override
+ public Collection<ProviderFunctionality> getProviderFunctionality() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public void onSessionInitiated(final ProviderSession arg0) {
+ // intentional NOOP
+ }
+
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: opendaylight-sal-binding-broker-impl yang module local name: binding-forwarded-data-broker
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Fri May 16 17:18:18 CEST 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.md.sal.binding.impl;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.osgi.framework.BundleContext;
+
+public class BindingAsyncDataBrokerImplModuleFactory extends org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingAsyncDataBrokerImplModuleFactory {
+
+
+
+
+ @Override
+ public BindingAsyncDataBrokerImplModule instantiateModule(final String instanceName,
+ final DependencyResolver dependencyResolver, final BindingAsyncDataBrokerImplModule oldModule,
+ final AutoCloseable oldInstance, final BundleContext bundleContext) {
+ BindingAsyncDataBrokerImplModule module = super.instantiateModule(instanceName, dependencyResolver, oldModule, oldInstance, bundleContext);
+ module.setBundleContext(bundleContext);
+ return module;
+ }
+
+ @Override
+ public BindingAsyncDataBrokerImplModule instantiateModule(final String instanceName,
+ final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
+ // TODO Auto-generated method stub
+ BindingAsyncDataBrokerImplModule module = super.instantiateModule(instanceName, dependencyResolver, bundleContext);
+ module.setBundleContext(bundleContext);
+ return module;
+ }
+}
\r
private BundleContext bundleContext;\r
\r
- public BindingBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
- org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {\r
+ public BindingBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {\r
super(identifier, dependencyResolver);\r
}\r
\r
- public BindingBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
- org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,\r
- BindingBrokerImplModule oldModule, java.lang.AutoCloseable oldInstance) {\r
+ public BindingBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,\r
+ final BindingBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) {\r
super(identifier, dependencyResolver, oldModule, oldInstance);\r
}\r
\r
private RootBindingAwareBroker createStandaloneBroker() {\r
RootBindingAwareBroker broker = new RootBindingAwareBroker(getIdentifier().getInstanceName());\r
\r
- broker.setDataBroker(getDataBrokerDependency());\r
+ broker.setLegacyDataBroker(getDataBrokerDependency());\r
broker.setNotificationBroker(getNotificationServiceDependency());\r
broker.setRpcBroker(new RpcProviderRegistryImpl(broker.getIdentifier()));\r
+ // FIXME: Also set Async Data Broker\r
return broker;\r
}\r
\r
private RootBindingAwareBroker createForwardedBroker() {\r
DomForwardedBindingBrokerImpl broker = new DomForwardedBindingBrokerImpl(getIdentifier().getInstanceName());\r
\r
- broker.setDataBroker(getDataBrokerDependency());\r
+ broker.setLegacyDataBroker(getDataBrokerDependency());\r
broker.setNotificationBroker(getNotificationServiceDependency());\r
broker.setRpcBroker(new RpcProviderRegistryImpl(broker.getIdentifier()));\r
\r
broker.getMountManager().setDataCommitExecutor(SingletonHolder.getDefaultCommitExecutor());\r
broker.getMountManager().setNotificationExecutor(SingletonHolder.getDefaultNotificationExecutor());\r
\r
-\r
+ // FIXME: Also set Async Data Broker\r
DomForwardingUtils.reuseForwardingFrom(broker, broker.getDataBroker());\r
broker.startForwarding();\r
return broker;\r
return bundleContext;\r
}\r
\r
- public void setBundleContext(BundleContext bundleContext) {\r
+ public void setBundleContext(final BundleContext bundleContext) {\r
this.bundleContext = bundleContext;\r
}\r
}\r
Broker domBroker = getDomAsyncBrokerDependency();
ProviderSession session = domBroker.registerProvider(this, getBundleContext());
DOMDataBroker domDataBroker = session.getService(DOMDataBroker.class);
+ SchemaService schemaService = session.getService(SchemaService.class);
ForwardedBackwardsCompatibleDataBroker dataBroker = new ForwardedBackwardsCompatibleDataBroker(domDataBroker,
- mappingService, listeningExecutor);
-
- session.getService(SchemaService.class).registerSchemaServiceListener(dataBroker);
+ mappingService, schemaService,listeningExecutor);
dataBroker.setConnector(BindingDomConnectorDeployer.createConnector(getBindingMappingServiceDependency()));
dataBroker.setDomProviderContext(session);
import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector;
import org.opendaylight.controller.sal.binding.impl.forward.DomForwardedBroker;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
import org.opendaylight.yangtools.concepts.Delegator;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBroker>, DomForwardedBroker,
- SchemaContextListener {
+ SchemaContextListener, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedDataBroker.class);
// The Broker to whom we do all forwarding
private final BindingToNormalizedNodeCodec codec;
private BindingIndependentConnector connector;
private ProviderSession context;
+ private final ListenerRegistration<SchemaServiceListener> schemaListenerRegistration;
protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker,
- final BindingIndependentMappingService mappingService) {
+ final BindingIndependentMappingService mappingService,final SchemaService schemaService) {
this.domDataBroker = domDataBroker;
this.mappingService = mappingService;
this.codec = new BindingToNormalizedNodeCodec(mappingService);
+ this.schemaListenerRegistration = schemaService.registerSchemaServiceListener(this);
}
protected BindingToNormalizedNodeCodec getCodec() {
// NOOP
}
+ @Override
+ public void close() throws Exception {
+ this.schemaListenerRegistration.close();
+ }
+
}
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.Delegator;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
private static final Logger LOG = LoggerFactory.getLogger(ForwardedBackwardsCompatibleDataBroker.class);
private final ConcurrentHashMap<InstanceIdentifier<?>, CommitHandlerRegistrationImpl> commitHandlers = new ConcurrentHashMap<>();
- private final ListenerRegistry<DataChangeListener> fakeRegistry = ListenerRegistry.create();
private final ListeningExecutorService executorService;
public ForwardedBackwardsCompatibleDataBroker(final DOMDataBroker domDataBroker,
- final BindingIndependentMappingService mappingService, final ListeningExecutorService executor) {
- super(domDataBroker, mappingService);
+ final BindingIndependentMappingService mappingService, final SchemaService schemaService,final ListeningExecutorService executor) {
+ super(domDataBroker, mappingService,schemaService);
executorService = executor;
LOG.info("ForwardedBackwardsCompatibleBroker started.");
}
throw new UnsupportedOperationException("Data reader contract is not supported.");
}
- @Override
- public void close() throws Exception {
- // TODO Auto-generated method stub
-
- }
-
public ListenableFuture<RpcResult<TransactionStatus>> commit(final ForwardedBackwardsCompatibleTransacion tx) {
final List<DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject>> subTrans = new ArrayList<>();
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
*/
public class ForwardedBindingDataBroker extends AbstractForwardedDataBroker implements BindingDataBroker {
- public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingIndependentMappingService mappingService) {
- super(domDataBroker, mappingService);
+ public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingIndependentMappingService mappingService, final SchemaService schemaService) {
+ super(domDataBroker, mappingService,schemaService);
}
@Override
import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils
import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
+import org.opendaylight.yangtools.yang.binding.RpcService
class RuntimeCodeGenerator extends AbstractRuntimeCodeGenerator {
val proxyName = iface.directProxyName;
val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName)
if(potentialClass != null) {
- return potentialClass.newInstance;
+ return potentialClass.newInstance as RpcService;
}
val supertype = iface.asCtClass
val createdCls = createClass(iface.directProxyName, supertype) [
'''
]
]
- return createdCls.toClass(iface.classLoader).newInstance
+ return createdCls.toClass(iface.classLoader).newInstance as RpcService
]
}
val routerName = iface.routerName;
val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName)
if(potentialClass != null) {
- return potentialClass.newInstance;
+ return potentialClass.newInstance as RpcService;
}
val targetCls = createClass(iface.routerName, supertype) [
'''
]
]
- return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance
+ return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as RpcService
];
}
*/
package org.opendaylight.controller.sal.binding.impl;
-import com.google.common.collect.ImmutableClassToInstanceMap;
+import static com.google.common.base.Preconditions.checkState;
+
+import org.opendaylight.controller.md.sal.binding.api.BindingDataBroker;
import org.opendaylight.controller.md.sal.binding.util.AbstractBindingSalProviderInstance;
import org.opendaylight.controller.md.sal.binding.util.BindingContextUtils;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableClassToInstanceMap;
public class RootBindingAwareBroker implements //
Mutable, //
Identifiable<String>, //
- BindingAwareBroker, AutoCloseable,
- RpcProviderRegistry {
+ BindingAwareBroker, AutoCloseable, RpcProviderRegistry {
private final static Logger LOG = LoggerFactory.getLogger(RootBindingAwareBroker.class);
private NotificationProviderService notificationBroker;
- private DataProviderService dataBroker;
+ private DataProviderService legacyDataBroker;
+
+ private BindingDataBroker dataBroker;
private MountPointManagerImpl mountManager;
return mountManager;
}
- public void setMountManager(MountPointManagerImpl mountManager) {
+ public void setMountManager(final MountPointManagerImpl mountManager) {
this.mountManager = mountManager;
}
private ImmutableClassToInstanceMap<BindingAwareService> supportedProviderServices;
- public RootBindingAwareBroker(String instanceName) {
+ public RootBindingAwareBroker(final String instanceName) {
this.identifier = instanceName;
mountManager = new MountPointManagerImpl();
}
+ @Override
public String getIdentifier() {
return identifier;
}
}
public DataProviderService getDataBroker() {
- return this.dataBroker;
+ return this.legacyDataBroker;
}
public NotificationProviderService getNotificationBroker() {
return rpcBroker;
}
- public void setRpcBroker(RpcProviderRegistry rpcBroker) {
+ public void setRpcBroker(final RpcProviderRegistry rpcBroker) {
this.rpcBroker = rpcBroker;
}
- public void setNotificationBroker(NotificationProviderService notificationBroker) {
+ public void setNotificationBroker(final NotificationProviderService notificationBroker) {
this.notificationBroker = notificationBroker;
}
- public void setDataBroker(DataProviderService dataBroker) {
- this.dataBroker = dataBroker;
+ public void setLegacyDataBroker(final DataProviderService dataBroker) {
+ this.legacyDataBroker = dataBroker;
}
public void start() {
controllerRoot = new RootSalInstance(getRpcProviderRegistry(), getNotificationBroker(), getDataBroker());
+ ImmutableClassToInstanceMap.Builder<BindingAwareService> consBuilder = ImmutableClassToInstanceMap.builder();
- supportedConsumerServices = ImmutableClassToInstanceMap.<BindingAwareService> builder()
- .put(NotificationService.class, getRoot()) //
- .put(DataBrokerService.class, getRoot()) //
- .put(RpcConsumerRegistry.class, getRoot()) //
- .put(MountService.class, mountManager).build();
-
+ consBuilder.put(NotificationService.class, getRoot());
+ consBuilder.put(DataBrokerService.class, getRoot());
+ consBuilder.put(RpcConsumerRegistry.class, getRoot());
+ if(dataBroker != null) {
+ consBuilder.put(BindingDataBroker.class, dataBroker);
+ }
+ consBuilder.put(MountService.class, mountManager).build();
+ supportedConsumerServices = consBuilder.build();
supportedProviderServices = ImmutableClassToInstanceMap.<BindingAwareService> builder()
- .putAll(supportedConsumerServices)
- .put(NotificationProviderService.class, getRoot()) //
- .put(DataProviderService.class, getRoot()) //
- .put(RpcProviderRegistry.class, getRoot()) //
+ .putAll(supportedConsumerServices).put(NotificationProviderService.class, getRoot())
+ .put(DataProviderService.class, getRoot()).put(RpcProviderRegistry.class, getRoot())
.put(MountProviderService.class, mountManager).build();
}
@Override
- public ConsumerContext registerConsumer(BindingAwareConsumer consumer, BundleContext ctx) {
+ public ConsumerContext registerConsumer(final BindingAwareConsumer consumer, final BundleContext ctx) {
checkState(supportedConsumerServices != null, "Broker is not initialized.");
return BindingContextUtils.createConsumerContextAndInitialize(consumer, supportedConsumerServices);
}
@Override
- public ProviderContext registerProvider(BindingAwareProvider provider, BundleContext ctx) {
+ public ProviderContext registerProvider(final BindingAwareProvider provider, final BundleContext ctx) {
checkState(supportedProviderServices != null, "Broker is not initialized.");
return BindingContextUtils.createProviderContextAndInitialize(provider, supportedProviderServices);
}
}
@Override
- public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> type, T implementation)
- throws IllegalStateException {
+ public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(final Class<T> type,
+ final T implementation) throws IllegalStateException {
return getRoot().addRoutedRpcImplementation(type, implementation);
}
@Override
- public <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> type, T implementation)
+ public <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> type, final T implementation)
throws IllegalStateException {
return getRoot().addRpcImplementation(type, implementation);
}
@Override
- public <T extends RpcService> T getRpcService(Class<T> module) {
+ public <T extends RpcService> T getRpcService(final Class<T> module) {
return getRoot().getRpcService(module);
}
+
@Override
public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
- L arg0) {
+ final L arg0) {
return getRoot().registerRouteChangeListener(arg0);
}
-
public class RootSalInstance extends
AbstractBindingSalProviderInstance<DataProviderService, NotificationProviderService, RpcProviderRegistry> {
- public RootSalInstance(RpcProviderRegistry rpcRegistry, NotificationProviderService notificationBroker,
- DataProviderService dataBroker) {
+ public RootSalInstance(final RpcProviderRegistry rpcRegistry,
+ final NotificationProviderService notificationBroker, final DataProviderService dataBroker) {
super(rpcRegistry, notificationBroker, dataBroker);
}
}
+
+ public void setDataBroker(final BindingDataBroker asyncDataBroker) {
+ dataBroker = asyncDataBroker;
+ }
}
config:provided-service sal:binding-data-consumer-broker;
config:java-name-prefix ForwardedCompatibleDataBrokerImpl;
}
+
+ identity binding-forwarded-data-broker {
+ base config:module-type;
+ config:provided-service sal:binding-async-data-broker;
+ config:java-name-prefix BindingAsyncDataBrokerImpl;
+ }
identity binding-rpc-broker {
base config:module-type;
config:java-name-prefix RuntimeMapping;
}
+ grouping dom-forwarding-component {
+ container dom-async-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:dom-broker-osgi-registry;
+ }
+ }
+ }
+
+ container binding-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-broker-impl {
when "/config:modules/config:module/config:type = 'binding-broker-impl'";
case binding-data-compatible-broker {
when "/config:modules/config:module/config:type = 'binding-data-compatible-broker'";
- container dom-async-broker {
- uses config:service-ref {
- refine type {
- mandatory true;
- config:required-identity dom:dom-broker-osgi-registry;
- }
- }
- }
-
- container binding-mapping-service {
- uses config:service-ref {
- refine type {
- mandatory true;
- config:required-identity binding-dom-mapping-service;
- }
- }
+ uses dom-forwarding-component;
+ }
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case binding-forwarded-data-broker {
+ when "/config:modules/config:module/config:type = 'binding-forwarded-data-broker'";
+ container binding-forwarded-data-broker {
+ uses dom-forwarding-component;
}
}
}
import org.opendaylight.controller.sal.dom.broker.impl.HashMapDataStore;
import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareDataStoreAdapter;
import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareRpcBroker;
-import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ListeningExecutorService;
-public class BindingTestContext implements AutoCloseable, SchemaContextProvider {
+public class BindingTestContext implements AutoCloseable {
public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier TREE_ROOT = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
.builder().toInstance();
private MountPointManagerImpl biMountImpl;
- private SchemaContext schemaContext;
+
private ImmutableMap<LogicalDatastoreType, DOMStore> newDatastores;
private BackwardsCompatibleDataBroker biCompatibleBroker;
- private final List<SchemaContextListener> schemaListeners = new ArrayList<>();
-
private DataProviderService baData;
private DOMDataBroker newDOMDataBroker;
- @Override
- public SchemaContext getSchemaContext() {
- return schemaContext;
- }
+ private final MockSchemaService mockSchemaService = new MockSchemaService();
+
+
public DOMDataBroker getDomAsyncDataBroker() {
return newDOMDataBroker;
this.startWithSchema = startWithSchema;
}
+ @Deprecated
public void startDomDataStore() {
checkState(dataStore == null, "DataStore already started.");
checkState(biDataImpl != null, "Dom Data Broker not present");
} else {
dataStore = schemaAwareDataStore;
}
-
+ mockSchemaService.registerSchemaServiceListener(schemaAwareDataStore);
biDataImpl.registerConfigurationReader(TREE_ROOT, dataStore);
biDataImpl.registerOperationalReader(TREE_ROOT, dataStore);
biDataImpl.registerCommitHandler(TREE_ROOT, dataStore);
biCompatibleBroker = new BackwardsCompatibleDataBroker(newDOMDataBroker);
- schemaListeners.add(configStore);
- schemaListeners.add(operStore);
- schemaListeners.add(biCompatibleBroker);
+ mockSchemaService.registerSchemaServiceListener(configStore);
+ mockSchemaService.registerSchemaServiceListener(operStore);
+ mockSchemaService.registerSchemaServiceListener(biCompatibleBroker);
biDataLegacyBroker = biCompatibleBroker;
}
baBrokerImpl.getMountManager().setDataCommitExecutor(executor);
baBrokerImpl.getMountManager().setNotificationExecutor(executor);
baBrokerImpl.setRpcBroker(new RpcProviderRegistryImpl("test"));
- baBrokerImpl.setDataBroker(baData);
+ baBrokerImpl.setLegacyDataBroker(baData);
baBrokerImpl.setNotificationBroker(baNotifyImpl);
baBrokerImpl.start();
}
public void startBindingToDomMappingService() {
checkState(classPool != null, "ClassPool needs to be present");
mappingServiceImpl = new RuntimeGeneratedMappingServiceImpl(classPool);
+ mockSchemaService.registerSchemaServiceListener(mappingServiceImpl);
}
public void updateYangSchema(final String[] files) {
- schemaContext = getContext(files);
-
- if (schemaAwareDataStore != null) {
- schemaAwareDataStore.onGlobalContextUpdated(schemaContext);
- }
- if (mappingServiceImpl != null) {
- mappingServiceImpl.onGlobalContextUpdated(schemaContext);
- }
- for(SchemaContextListener listener : schemaListeners) {
- listener.onGlobalContextUpdated(schemaContext);
- }
+ mockSchemaService.changeSchema(getContext(files));
}
public static String[] getAllYangFilesOnClasspath() {
}
public void startNewBindingDataBroker() {
- ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl, executor);
- schemaListeners.add(forwarded);
+ ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl,mockSchemaService, executor);
baData = forwarded;
}
private void startDomBroker() {
checkState(executor != null);
biBrokerImpl = new BrokerImpl();
- biBrokerImpl.setRouter(new SchemaAwareRpcBroker("/", this));
+ biBrokerImpl.setRouter(new SchemaAwareRpcBroker("/", mockSchemaService));
}
public MountProvisionService getDomMountProviderService() {
return biMountImpl;
}
+
+
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.binding.test.util;
+
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+
+@SuppressWarnings("deprecation")
+public final class MockSchemaService implements SchemaService, SchemaContextProvider {
+
+ private SchemaContext schemaContext;
+
+ ListenerRegistry<SchemaServiceListener> listeners = ListenerRegistry.create();
+
+ @Override
+ public void addModule(final Module module) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized SchemaContext getGlobalContext() {
+ return schemaContext;
+ }
+
+ @Override
+ public synchronized SchemaContext getSessionContext() {
+ return schemaContext;
+ }
+
+ @Override
+ public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(
+ final SchemaServiceListener listener) {
+ return listeners.register(listener);
+ }
+
+ @Override
+ public void removeModule(final Module module) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized SchemaContext getSchemaContext() {
+ return schemaContext;
+ }
+
+ public synchronized void changeSchema(final SchemaContext newContext) {
+ schemaContext = newContext;
+ for (ListenerRegistration<SchemaServiceListener> listener : listeners) {
+ listener.getInstance().onGlobalContextUpdated(schemaContext);
+ }
+ }
+}
\ No newline at end of file
base "config:service-type";
config:java-class "org.opendaylight.controller.sal.binding.api.data.DataProviderService";
}
+
+ identity binding-async-data-broker {
+ base "config:service-type";
+ config:java-class "org.opendaylight.controller.md.sal.binding.api.BindingDataBroker";
+ }
identity binding-data-consumer-broker {
base "config:service-type";
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
- <version>1.7.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<artifactId>sal-binding-it</artifactId>
<properties>
- <exam.version>3.0.0</exam.version>
<sonar.jacoco.itReportPath>../sal-binding-broker/target/jacoco-it.exec</sonar.jacoco.itReportPath>
<!-- Sonar jacoco plugin to get integration test coverage info -->
<sonar.jacoco.reportPath>../sal-binding-broker/target/jacoco.exec</sonar.jacoco.reportPath>
- <url.version>1.5.0</url.version>
</properties>
<dependencies>
<dependency>
<groupId>org.opendaylight.yangtools.thirdparty</groupId>
<artifactId>antlr4-runtime-osgi-nohead</artifactId>
- <version>4.0</version>
</dependency>
<!--Compile scopes for all testing dependencies are intentional-->
<!--This way, all testing dependencies can be transitively used by other integration test modules-->
<dependency>
<groupId>org.opendaylight.yangtools.thirdparty</groupId>
<artifactId>xtend-lib-osgi</artifactId>
- <version>2.4.3</version>
</dependency>
<dependency>
<groupId>org.openexi</groupId>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
- <version>1.0.0</version>
+ <version>${lifecycle.mapping.version}</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
- <version>2.14.1</version>
<configuration>
<reuseForks>false</reuseForks>
</configuration>
<plugin>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>maven-paxexam-plugin</artifactId>
- <version>1.2.4</version>
<executions>
<execution>
<id>generate-config</id>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * Three phase Commit Cohort for subtree, which is
+ * uniquely associated with user submitted transcation.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncConfigurationCommitCohort<P extends Path<P>, D> {
+
+ /**
+ * Initiates a pre-commit of associated request
+ *
+ * Implementation MUST NOT do any blocking calls during this callback, all
+ * pre-commit preparation SHOULD happen asynchronously and MUST result in
+ * completing returned future object.
+ *
+ * @param rebasedTransaction
+ * Read-only view of transaction as if happened on top of actual
+ * data store
+ * @return Future which is completed once pre-commit phase for this request
+ * is finished.
+ */
+ ListenableFuture<Void> preCommit(AsyncReadTransaction<P, D> rebasedTransaction);
+
+ /**
+ *
+ * Initiates a commit phase of associated request
+ *
+ * Implementation MUST NOT do any blocking calls during this callback, all
+ * commit finalization SHOULD happen asynchronously and MUST result in
+ * completing returned future object.
+ *
+ * @return Future which is completed once commit phase for associated
+ * request is finished.
+ */
+ ListenableFuture<Void> commit();
+
+ /**
+ *
+ * Initiates abort phase of associated request
+ *
+ * Implementation MUST NOT do any blocking calls during this callback, all
+ * commit finalization SHOULD happen asynchronously and MUST result in
+ * completing returned future object.
+ *
+ * @return Future which is completed once commit phase for associated
+ * request is finished.
+ */
+ ListenableFuture<Void> abort();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.concepts.Path;
+
+/**
+ * Three Phase Commit Coordinator with support of user-supplied commit cohorts
+ * which participates in three-phase commit protocols
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncConfigurationCommitCoordinator<P extends Path<P>, D> {
+
+ /**
+ * Register configuration commit handler for particular subtree
+ *
+ * Configuration commit handler is invoked for all write transactions
+ * which modifies <code>subtree</code>
+ *
+ * @param subtree Subtree which configuration commit handler is interested it
+ * @param commitHandler Instance of user-provided commit handler
+ * @return Registration object representing this registration. Invoking {@link ObjectRegistration#close()}
+ * will unregister configuration commit handler.
+ */
+ <C extends AsyncConfigurationCommitCohort<P, D>> ObjectRegistration<C> registerConfigurationCommitHandler(
+ P subtree, C commitHandler);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+import com.google.common.util.concurrent.CheckedFuture;
+
+/**
+ * User-supplied participant in three-phase commit of transaction for configuration data tree
+ *
+ * Client-supplied implementation of commit handler for subtree, which
+ * is responsible for processing CAN-COMMIT phase of three-phase commit protocol
+ * and return CommitCohort, which provides access to additional transitions
+ * such as PRE-COMMIT, COMMIT and ABORT.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncConfigurationCommitHandler<P extends Path<P>, D> {
+
+ /**
+ *
+ * Requests a can commit phase
+ *
+ * Implementations SHOULD NOT do any blocking operation during
+ * processing this callback.
+ *
+ * <b>Implementation Notes</b>
+ * <ul>
+ * <li>Implementation are REQUIRED to use <code>request</code> object for any data related access</li>
+ * <li>Implementations SHOULD NOT use any other state stored outside configuration subtree for validation</li>
+ * <li>Validation should happen asynchronously, outside callback call by updating returned {@link CheckedFuture}
+ * object.</li>
+ * <li>If validation (CAN_COMMIT) phase:
+ * <ul>
+ * <li><b>is successful</b> - invocation of {@link CheckedFuture#checkedGet()} on returned future MUST
+ * return {@link AsyncConfigurationCommitCohort} associated with request.</li>
+ * <li><b>is unsuccessful</b> - invocation of {@link CheckedFuture#checkedGet()} must throw instance of {@link DataValidationFailedException}
+ * with human readable explanaition of error condition.
+ * </li>
+ * </ul>
+ * </li>
+ * @param request
+ * Commit Request submitted by client, which contains
+ * information about modifications and read-only view as
+ * if transaction happened.
+ * @return CheckedFuture which contains client-supplied implementation of {@link AsyncConfigurationCommitCohort}
+ * associated with submitted request, if can commit phase is
+ * successful, if can commit was unsuccessful, future must fail with
+ * {@link TransactionCommitFailedException} exception.
+ */
+ CheckedFuture<AsyncConfigurationCommitCohort<P, D>, DataValidationFailedException> canCommit(
+ ConfigurationCommitRequest<P, D> request);
+
+ /**
+ *
+ * Commit Request as was submitted by client code
+ *
+ * Commit Request contains list view of created / updated / removed
+ * path and read-only view of proposed client transaction,
+ * which may be used to retrieve modified or referenced data.
+ *
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location
+ * in tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+ static interface ConfigurationCommitRequest<P extends Path<P>, D> {
+
+ /**
+ *
+ * Read-only transaction which provides access only to configuration
+ * data tree as if submitted transaction successfully happened and
+ * no other concurrent modifications happened between allocation
+ * of client transactions and write of client transactions.
+ *
+ * Implementations of Commit Handlers are REQUIRED to use this
+ * read-only view to access any data from configuration data tree,
+ * in order to capture them as preconditions for this transaction.
+ *
+ * @return Read-only transaction which provides access only to configuration
+ * data tree as if submitted transaction successfully happened
+ */
+ AsyncReadTransaction<P, D> getReadOnlyView();
+
+ /**
+ *
+ * Returns iteration of paths, to data which was introduced by this transaction.
+ *
+ * @return Iteration of paths, which was introduced by this transaction.
+ */
+ Iterable<P> getCreatedPaths();
+ /**
+ *
+ * Returns iteration of paths, to data which was updated by this transaction.
+ *
+ * @return Iteration of paths, which was updated by this transaction.
+ */
+ Iterable<P> getUpdatedPaths();
+
+ /**
+ *
+ * Returns iteration of paths, to data which was removed by this transaction.
+ *
+ * @return Iteration of paths, which was removed by this transaction.
+ */
+ Iterable<P> getRemovedPaths();
+ }
+
+}
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.concepts.Path;
+/**
+ *
+ * Provides access to a conceptual data tree store.
+ *
+ * <p>
+ * Also provides the ability to subscribe for changes to data under a given
+ * branch of the tree.
+ *
+ * <p>
+ * All operations on data tree are performed via one of the transactions:
+ * <ul>
+ * <li>Read-Only - allocated using {@link #newReadOnlyTransaction()}
+ * <li>Write-Only - allocated using {@link #newWriteOnlyTransaction()}
+ * <li>Read-Write - allocated using {@link #newReadWriteTransaction()}
+ * </ul>
+ *
+ * <p>
+ * These transactions provide a stable isolated view of data tree, which is
+ * guaranteed to be not affected by other concurrent transactions, until
+ * transaction is committed.
+ *
+ * <p>
+ * For a detailed explanation of how transaction are isolated and how transaction-local
+ * changes are committed to global data tree, see
+ * {@link AsyncReadTransaction}, {@link AsyncWriteTransaction},
+ * {@link AsyncReadWriteTransaction} and {@link AsyncWriteTransaction#commit()}.
+ *
+ *
+ * <p>
+ * It is strongly recommended to use the type of transaction, which
+ * provides only the minimal capabilities you need. This allows for
+ * optimizations at the data broker / data store level. For example,
+ * implementations may optimize the transaction for reading if they know ahead
+ * of time that you only need to read data - such as not keeping additional meta-data,
+ * which may be required for write transactions.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
public interface AsyncDataBroker<P extends Path<P>, D, L extends AsyncDataChangeListener<P, D>> extends //
AsyncDataTransactionFactory<P, D> {
*
* Scope of Data Change
*
+ * <p>
* Represents scope of data change (addition, replacement, deletion).
*
- * The terminology for types is reused from LDAP
+ * The terminology for scope types is reused from LDAP.
+ *
+ * <h2>Examples</h2>
+ *
+ * Following is an example model with comments describing what notifications
+ * you would receive based on the scope you specify, when you are
+ * registering for changes on container a.
*
- * @see http://www.idevelopment.info/data/LDAP/LDAP_Resources/SEARCH_Setting_the_SCOPE_Parameter.shtml
+ * <pre>
+ * container a // scope BASE, ONE, SUBTREE
+ * leaf "foo" // scope ONE, SUBTREE
+ * container // scope ONE, SUBTREE
+ * leaf "bar" // scope SUBTREE
+ * list list // scope ONE, SUBTREE
+ * list [a] // scope SUBTREE
+ * id "a" // scope SUBTREE
+ * list [b] // scope SUBTREE
+ * id "b" // scope SUBTREE
+ * </pre>
+ *
+ * Following is an example model with comments describing what notifications
+ * you would receive based on the scope you specify, when you are
+ * registering for changes on list list (without specifying concrete item in
+ * the list).
+ *
+ * <pre>
+ * list list // scope BASE, ONE, SUBTREE
+ * list [a] // scope ONE, SUBTREE
+ * id "a" // scope SUBTREE
+ * list [b] // scope ONE, SUBTREE
+ * id "b" // scope SUBTREE
+ * </pre>
+ *
+ *
+ * @see http://www.idevelopment.info/data/LDAP/LDAP_Resources/
+ * SEARCH_Setting_the_SCOPE_Parameter.shtml
*/
public enum DataChangeScope {
- /**
- * Represents only a direct change of the node, such as replacement of node,
- * addition or deletion.
- *
- */
- BASE,
- /**
- * Represent a change (addition,replacement,deletion)
- * of the node or one of it's direct childs.
- *
- */
- ONE,
- /**
- * Represents a change of the node or any of it's child nodes.
- *
- */
- SUBTREE
+ /**
+ * Represents only a direct change of the node, such as replacement of a
+ * node, addition or deletion.
+ *
+ */
+ BASE,
+ /**
+ * Represent a change (addition,replacement,deletion) of the node or one
+ * of its direct children.
+ *
+ * This scope is superset of {@link #BASE}.
+ *
+ */
+ ONE,
+ /**
+ * Represents a change of the node or any of or any of its child nodes,
+ * direct and nested.
+ *
+ * This scope is superset of {@link #ONE} and {@link #BASE}.
+ *
+ */
+ SUBTREE
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public AsyncReadTransaction<P, D> newReadOnlyTransaction();
+ /**
+ * {@inheritDoc}
+ */
@Override
- public AsyncReadWriteTransaction<P,D> newReadWriteTransaction();
+ public AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
+ /**
+ * {@inheritDoc}
+ */
@Override
public AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
/**
- * Registers {@link DataChangeListener} for Data Change callbacks
- * which will be triggered on which will be triggered on the store
+ * Registers a {@link AsyncDataChangeListener} to receive
+ * notifications when data changes under a given path in the conceptual data
+ * tree.
+ * <p>
+ * You are able to register for notifications for any node or subtree
+ * which can be reached via the supplied path.
+ * <p>
+ * If path type <code>P</code> allows it, you may specify paths up to the leaf nodes
+ * then it is possible to listen on leaf nodes.
+ * <p>
+ * You are able to register for data change notifications for a subtree even
+ * if it does not exist. You will receive notification once that node is
+ * created.
+ * <p>
+ * If there is any preexisting data in data tree on path for which you are
+ * registering, you will receive initial data change event, which will
+ * contain all preexisting data, marked as created.
+ *
+ * <p>
+ * You are also able to specify the scope of the changes you want to be
+ * notified.
+ * <p>
+ * Supported scopes are:
+ * <ul>
+ * <li>{@link DataChangeScope#BASE} - notification events will only be
+ * triggered when a node referenced by path is created, removed or replaced.
+ * <li>{@link DataChangeScope#ONE} - notifications events will only be
+ * triggered when a node referenced by path is created, removed or replaced,
+ * or any or any of its immediate children are created, updated or removed.
+ * <li>{@link DataChangeScope#SUBTREE} - notification events will be
+ * triggered when a node referenced by the path is created, removed
+ * or replaced or any of the children in its subtree are created, removed
+ * or replaced.
+ * </ul>
+ * See {@link DataChangeScope} for examples.
+ * <p>
+ * This method returns a {@link ListenerRegistration} object. To
+ * "unregister" your listener for changes call the "close" method on this
+ * returned object.
+ * <p>
+ * You MUST call close when you no longer need to receive notifications
+ * (such as during shutdown or for example if your bundle is shutting down).
*
- * @param store Logical store in which listener is registered.
- * @param path Path (subtree identifier) on which client listener will be invoked.
- * @param listener Instance of listener which should be invoked on
- * @param triggeringScope Scope of change which triggers callback.
- * @return Listener registration of the listener, call {@link ListenerRegistration#close()}
- * to stop delivery of change events.
+ * @param store
+ * Logical Data Store - Logical Datastore you want to listen for
+ * changes in. For example
+ * {@link LogicalDatastoreType#OPERATIONAL} or
+ * {@link LogicalDatastoreType#CONFIGURATION}
+ * @param path
+ * Path (subtree identifier) on which client listener will be
+ * invoked.
+ * @param listener
+ * Instance of listener which should be invoked on
+ * @param triggeringScope
+ * Scope of change which triggers callback.
+ * @return Listener registration object, which may be used to unregister
+ * your listener using {@link ListenerRegistration#close()} to stop
+ * delivery of change events.
*/
- ListenerRegistration<L> registerDataChangeListener(LogicalDatastoreType store, P path, L listener, DataChangeScope triggeringScope);
+ ListenerRegistration<L> registerDataChangeListener(LogicalDatastoreType store, P path, L listener,
+ DataChangeScope triggeringScope);
}
import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.concepts.Path;
-public interface AsyncDataChangeEvent<P extends Path<P>,D> extends Immutable {
+/**
+ *
+ * An event which contains a capture of changes in a data subtree
+ *
+ * <p>
+ * Represents a notification indicating that some data at or under a particular
+ * path has changed. The notification contains a capture of the changes in the data
+ * subtree. This event is triggered by successful application of modifications
+ * from a transaction on the global data tree. Use the
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * method to register a listener for data change events.
+ *
+ * <p>
+ * A listener will only receive notifications for changes to data under the path
+ * they register for. See
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * to learn more about registration scopes.
+ *
+ * <p>
+ * The entire subtree under the path will be provided via instance methods of Data
+ * Change Event even if just a leaf node changes.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncDataChangeEvent<P extends Path<P>, D> extends Immutable {
/**
- * Returns a immutable map of paths and newly created objects
+ * Returns a map of paths and newly created objects, which were introduced by
+ * this change into conceptual data tree, if no new objects were introduced
+ * this map will be empty.
+ *<p>
+ * This map contains all data tree nodes (and paths to them) which were created
+ * and are in the scope of listener registration. The data tree nodes
+ * contain their whole subtree with their current state.
*
* @return map of paths and newly created objects
*/
Map<P, D> getCreatedData();
/**
- * Returns a immutable map of paths and respective updated objects after update.
- *
- * Original state of the object is in
- * {@link #getOriginalData()}
+ * Returns a map of paths and objects which were updated by this change in the
+ * conceptual data tree if no existing objects were updated
+ * this map will be empty.
+ *<p>
+ * This map contains all data tree nodes (and paths to them) which were updated
+ * and are in the scope of listener registration. The data tree nodes
+ * contain their whole subtree with their current state.
+ *<p>
+ * A Node is considered updated if it contents were replaced or one of its
+ * children was created, removed or updated.
+ *<p>
+ * Original state of the updated data tree nodes is in
+ * {@link #getOriginalData()} stored with same path.
*
* @return map of paths and newly created objects
*/
Map<P, D> getUpdatedData();
/**
- * Returns a immutable set of removed paths.
- *
- * Original state of the object is in
- * {@link #getOriginalData()}
+ * Returns an immutable set of removed paths.
+ *<p>
+ * This set contains the paths to the data tree nodes which are in the scope
+ * of the listener registration that have been removed.
+ *<p>
+ * Original state of the removed data tree nodes is in
+ * {@link #getOriginalData()} stored with same path.
*
* @return set of removed paths
*/
Set<P> getRemovedPaths();
/**
- * Return a immutable map of paths and original state of updated and removed objects.
+ * Returns an immutable map of updated or removed paths and their original
+ * states prior to this change.
*
- * This map is populated if at changed path was previous object, and captures
- * state of previous object.
+ *<p>
+ * This map contains the original version of the data tree nodes (and paths
+ * to them), which are in the scope of the listener registration.
*
* @return map of paths and original state of updated and removed objects.
*/
Map<P, ? extends D> getOriginalData();
/**
- * Returns a immutable stable view of data state, which
- * captures state of data store before the reported change.
+ * Returns an immutable stable view of data state, which captures the state of
+ * data store before the reported change.
*
+ *<p>
+ * The view is rooted at the point where the listener, to which the event is
+ * being delivered, was registered.
+ *<p>
+ * If listener used a wildcarded path (if supported by path type) during
+ * registration for change listeners this method returns null, and original
+ * state can be accessed only via {@link #getOriginalData()}
*
- * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
- *
- * @return Stable view of data before the change happened, rooted at the listener registration path.
+ * @return Stable view of data before the change happened, rooted at the
+ * listener registration path.
*
*/
D getOriginalSubtree();
/**
- * Returns a immutable stable view of data, which captures state of data store
- * after the reported change.
- *
- * The view is rooted at the point where the listener, to which the event is being delivered, was registered.
+ * Returns an immutable stable view of data, which captures the state of data
+ * store after the reported change.
+ *<p>
+ * The view is rooted at the point where the listener, to which the event is
+ * being delivered, was registered.
+ *<p>
+ * If listener used a wildcarded path (if supported by path type) during
+ * registration for change listeners this method returns null, and state
+ * can be accessed only via {@link #getCreatedData()},
+ * {@link #getUpdatedData()}, {@link #getRemovedPaths()}
*
- * @return Stable view of data after the change happened, rooted at the listener registration path.
+ * @return Stable view of data after the change happened, rooted at the
+ * listener registration path.
*/
D getUpdatedSubtree();
}
import org.opendaylight.yangtools.concepts.Path;
+/**
+ * Listener of data change events on particular subtree.
+ *
+ * <p>
+ * User-supplied implementations of this listener interface MUST register via
+ * {@link AsyncDataBroker#registerDataChangeListener(LogicalDatastoreType, Path, AsyncDataChangeListener, AsyncDataBroker.DataChangeScope)}
+ * in order to start receiving data change events, which capture state changes
+ * in a subtree.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is intended to be implemented
+ * by users of MD-SAL.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
public interface AsyncDataChangeListener<P extends Path<P>, D> extends EventListener {
/**
- * Note that this method may be invoked from a shared thread pool, so
+ *
+ * Invoked when there is data change for the particular path, which was used to
+ * register this listener.
+ * <p>
+ * This method may be also invoked during registration of the listener if
+ * there is any preexisting data in the conceptual data tree for supplied path.
+ * This initial event will contain all preexisting data as created.
+ *
+ * <p>
+ * <b>Note</b> that this method may be invoked from a shared thread pool, so
* implementations SHOULD NOT perform CPU-intensive operations and they
* definitely MUST NOT invoke any potentially blocking operations.
*
- * @param change Data Change Event being delivered.
+ * @param change
+ * Data Change Event being delivered.
*/
void onDataChanged(AsyncDataChangeEvent<P, D> change);
}
import org.opendaylight.yangtools.concepts.Path;
+/**
+ * A factory which allocates new transactions to operate on the data
+ * tree.
+ *
+ * <p>
+ * <b>Note:</b> This interface is not intended to be used directly, but rather
+ * via subinterfaces which introduces additional semantics to allocated
+ * transactions.
+ * <ul>
+ * <li> {@link AsyncDataBroker}
+ * <li> {@link TransactionChain}
+ * </ul>
+ *
+ * <p>
+ * All operations on the data tree are performed via one of the transactions:
+ * <ul>
+ * <li>Read-Only - allocated using {@link #newReadOnlyTransaction()}
+ * <li>Write-Only - allocated using {@link #newWriteOnlyTransaction()}
+ * <li>Read-Write - allocated using {@link #newReadWriteTransaction()}
+ * </ul>
+ *
+ * <p>
+ * These transactions provides a stable isolated view of the data tree, which is
+ * guaranteed to be not affected by other concurrent transactions, until
+ * transaction is committed.
+ *
+ * <p>
+ * For a detailed explanation of how transaction are isolated and how transaction-local
+ * changes are committed to global data tree, see
+ * {@link AsyncReadTransaction}, {@link AsyncWriteTransaction},
+ * {@link AsyncReadWriteTransaction} and {@link AsyncWriteTransaction#commit()}.
+ *
+ * <p>
+ * It is strongly recommended to use the type of transaction, which
+ * provides only the minimal capabilities you need. This allows for
+ * optimizations at the data broker / data store level. For example,
+ * implementations may optimize the transaction for reading if they know ahead
+ * of time that you only need to read data - such as not keeping additional meta-data,
+ * which may be required for write transactions.
+ *<p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @see AsyncDataBroker
+ * @see TransactionChain
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
public interface AsyncDataTransactionFactory<P extends Path<P>, D> {
+ /**
+ * Allocates a new read-only transaction which provides an immutable snapshot of
+ * the data tree.
+ *<p>
+ * The view of data tree is an immutable snapshot of current data tree state when
+ * transaction was allocated.
+ *
+ * @return new read-only transaction
+ */
AsyncReadTransaction<P, D> newReadOnlyTransaction();
+ /**
+ * Allocates new read-write transaction which provides a mutable view of the data
+ * tree.
+ *
+ * <p>
+ * Preconditions for mutation of data tree are captured from the snapshot of
+ * data tree state, when the transaction is allocated. If data was
+ * changed during transaction in an incompatible way then the commit of this transaction
+ * will fail. See {@link AsyncWriteTransaction#commit()} for more
+ * details about conflicting and not-conflicting changes and
+ * failure scenarios.
+ *
+ * @return new read-write transaction
+ */
AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
- AsyncWriteTransaction<P,D> newWriteOnlyTransaction();
+ /**
+ * Allocates new write-only transaction based on latest state of data
+ * tree.
+ *
+ * <p>
+ * Preconditions for mutation of data tree are captured from the snapshot of
+ * data tree state, when the transaction is allocated. If data was
+ * changed during transaction in an incompatible way then the commit of this transaction
+ * will fail. See {@link AsyncWriteTransaction#commit()} for more
+ * details about conflicting and not-conflicting changes and
+ * failure scenarios.
+ *
+ * <p>
+ * Since this transaction does not provide a view of the data it SHOULD BE
+ * used only by callers which are exclusive writers (exporters of data)
+ * to the subtree they modify. This prevents optimistic
+ * lock failures as described in {@link AsyncWriteTransaction#commit()}.
+ * <p>
+ * Exclusivity of writers to particular subtree SHOULD BE enforced by
+ * external locking mechanism.
+ *
+ * @return new write-only transaction
+ */
+ AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
}
import com.google.common.base.Optional;
import com.google.common.util.concurrent.ListenableFuture;
+/**
+ *
+ * Provides a stateful read-only view of the data tree.
+ *
+ * <p>
+ * View of the data tree is a stable point-in-time snapshot of the current data tree state when
+ * the transaction was created. It's state and underlying data tree
+ * is not affected by other concurrently running transactions.
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * <h2>Transaction isolation example</h2> Lest assume initial state of data tree
+ * for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * txRead = broker.newReadOnlyTransaction(); // read Transaction is snapshot of data
+ * txWrite = broker.newReadWriteTransactoin(); // concurrent write transaction
+ *
+ * txRead.read(OPERATIONAL,PATH).get(); // will return Optional containing A
+ * txWrite = broker.put(OPERATIONAL,PATH,B); // writes B to PATH
+ *
+ * txRead.read(OPERATIONAL,PATH).get(); // still returns Optional containing A
+ *
+ * txWrite.commit().get(); // data tree is updated, PATH contains B
+ * txRead.read(OPERATIONAL,PATH).get(); // still returns Optional containing A
+ *
+ * txAfterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * txAfterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B;
+ * </pre>
+ *
+ * <p>
+ * <b>Note:</b> example contains blocking calls on future only to illustrate
+ * that action happened after other asynchronous action. Use of blocking call
+ * {@link ListenableFuture#get()} is discouraged for most uses and you should
+ * use
+ * {@link com.google.common.util.concurrent.Futures#addCallback(ListenableFuture, com.google.common.util.concurrent.FutureCallback)}
+ * or other functions from {@link com.google.common.util.concurrent.Futures} to
+ * register more specific listeners.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
public interface AsyncReadTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
/**
*
- * Reads data from provided logical data store located at provided path
- *
+ * Reads data from provided logical data store located at the provided path.
+ *<p>
+ * If the target is a subtree, then the whole subtree is read (and will be
+ * accessible from the returned data object).
*
* @param store
* Logical data store from which read should occur.
* read
* @return Listenable Future which contains read result
* <ul>
- * <li>If data at supplied path exists the {@link Future#get()}
- * returns Optional object containing data
+ * <li>If data at supplied path exists the
+ * {@link ListeblaFuture#get()} returns Optional object containing
+ * data once read is done.
* <li>If data at supplied path does not exists the
- * {@link Future#get()} returns {@link Optional#absent()}.
+ * {@link ListenbleFuture#get()} returns {@link Optional#absent()}.
* </ul>
*/
ListenableFuture<Optional<D>> read(LogicalDatastoreType store, P path);
import org.opendaylight.yangtools.concepts.Path;
/**
- * Transaction enabling client to have combined transaction,
- * which provides read and write capabilities.
+ * Transaction enabling a client to have a combined read/write capabilities.
*
+ * <p>
+ * The initial state of the write transaction is stable snapshot of current data tree
+ * state captured when transaction was created and it's state and underlying
+ * data tree are not affected by other concurrently running transactions.
*
- * @param <P> Path Type
- * @param <D> Data Type
+ * <p>
+ * Write transactions are isolated from other concurrent write transactions. All
+ * writes are local to the transaction and represents only a proposal of state
+ * change for data tree and it is not visible to any other concurrently running
+ * transactions.
+ *
+ * <p>
+ * Applications publish the changes proposed in the transaction by calling {@link #commit}
+ * on the transaction. This seals the transaction
+ * (preventing any further writes using this transaction) and submits it to be
+ * processed and applied to global conceptual data tree.
+ *
+ * <p>
+ * The transaction commit may fail due to a concurrent transaction modifying and committing data in
+ * an incompatible way. See {@link #commit()} for more concrete commit failure examples.
+ *
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * <h2>Examples</h2>
+ *
+ * <h3>Transaction local state</h3>
+ *
+ * Let assume initial state of data tree for <code>PATH</code> is <code>A</code>
+ * .
+ *
+ * <pre>
+ * txWrite = broker.newReadWriteTransaction(); // concurrent write transaction
+ *
+ * txWrite.read(OPERATIONAL,PATH).get() // will return Optional containing A
+ * txWrite.put(OPERATIONAL,PATH,B); // writes B to PATH
+ * txWrite.read(OPERATIONAL,PATH).get() // will return Optional Containing B
+ *
+ * txWrite.commit().get(); // data tree is updated, PATH contains B
+ *
+ * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
+ * </pre>
+ *
+ * As you could see read-write transaction provides capabilities as
+ * {@link AsyncWriteTransaction} but also allows for reading proposed changes as
+ * if they already happened.
+ *
+ * <h3>Transaction isolation (read transaction, read-write transaction)</h3> Let
+ * assume initial state of data tree for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * txRead = broker.newReadOnlyTransaction(); // read Transaction is snapshot of data
+ * txWrite = broker.newReadWriteTransaction(); // concurrent write transaction
+ *
+ * txRead.read(OPERATIONAL,PATH).get(); // will return Optional containing A
+ * txWrite.read(OPERATIONAL,PATH).get() // will return Optional containing A
+ *
+ * txWrite.put(OPERATIONAL,PATH,B); // writes B to PATH
+ * txWrite.read(OPERATIONAL,PATH).get() // will return Optional Containing B
+ *
+ * txRead.read(OPERATIONAL,PATH).get(); // concurrent read transaction still returns
+ * // Optional containing A
+ *
+ * txWrite.commit().get(); // data tree is updated, PATH contains B
+ * txRead.read(OPERATIONAL,PATH).get(); // still returns Optional containing A
+ *
+ * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
+ * </pre>
+ *
+ * <h3>Transaction isolation (2 concurrent read-write transactions)</h3> Let
+ * assume initial state of data tree for <code>PATH</code> is <code>A</code>.
+ *
+ * <pre>
+ * tx1 = broker.newReadWriteTransaction(); // read Transaction is snapshot of data
+ * tx2 = broker.newReadWriteTransaction(); // concurrent write transaction
+ *
+ * tx1.read(OPERATIONAL,PATH).get(); // will return Optional containing A
+ * tx2.read(OPERATIONAL,PATH).get() // will return Optional containing A
+ *
+ * tx2.put(OPERATIONAL,PATH,B); // writes B to PATH
+ * tx2.read(OPERATIONAL,PATH).get() // will return Optional Containing B
+ *
+ * tx1.read(OPERATIONAL,PATH).get(); // tx1 read-write transaction still sees Optional
+ * // containing A since is isolated from tx2
+ * tx1.put(OPERATIONAL,PATH,C); // writes C to PATH
+ * tx1.read(OPERATIONAL,PATH).get() // will return Optional Containing C
+ *
+ * tx2.read(OPERATIONAL,PATH).get() // tx2 read-write transaction still sees Optional
+ * // containing B since is isolated from tx1
+ *
+ * tx2.commit().get(); // data tree is updated, PATH contains B
+ * tx1.read(OPERATIONAL,PATH).get(); // still returns Optional containing C since is isolated from tx2
+ *
+ * tx1afterCommit = broker.newReadOnlyTransaction(); // read Transaction is snapshot of new state
+ * tx1afterCommit.read(OPERATIONAL,PATH).get(); // returns Optional containing B
+ *
+ * tx1.commit() // Will fail with OptimisticLockFailedException
+ * // which means concurrent transaction changed the same PATH
+ *
+ * </pre>
+ *
+ * <p>
+ * <b>Note:</b> examples contains blocking calls on future only to illustrate
+ * that action happened after other asynchronous action. Use of blocking call
+ * {@link com.google.common.util.concurrent.ListenableFuture#get()} is discouraged for most uses and you should
+ * use
+ * {@link com.google.common.util.concurrent.Futures#addCallback(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.FutureCallback)}
+ * or other functions from {@link com.google.common.util.concurrent.Futures} to
+ * register more specific listeners.
+ *
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
*/
public interface AsyncReadWriteTransaction<P extends Path<P>, D> extends AsyncReadTransaction<P, D>,
AsyncWriteTransaction<P, D> {
/**
*
- * @author
+ * A common parent for all transactions which operate on a conceptual data tree.
+ *
+ * See derived transaction types for more concrete behavior:
+ * <ul>
+ * <li>{@link AsyncReadTransaction} - Read capabilities, user is able to read data from data tree</li>
+ * <li>{@link AsyncWriteTransaction} - Write capabilities, user is able to propose changes to data tree</li>
+ * <li>{@link AsyncReadWriteTransaction} - Read and Write capabilities, user is able to read state and to propose changes of state.</li>
+ * </ul>
+ *
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL.
*
* @param <P> Type of path (subtree identifier), which represents location in tree
* @param <D> Type of data (payload), which represents data payload
import com.google.common.util.concurrent.ListenableFuture;
-public interface AsyncWriteTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
+/**
+ * Write transaction provides mutation capabilities for a data tree.
+ *
+ * <p>
+ * Initial state of write transaction is a stable snapshot of the current data tree.
+ * The state is captured when the transaction is created and its state and underlying
+ * data tree are not affected by other concurrently running transactions.
+ * <p>
+ * Write transactions are isolated from other concurrent write transactions. All
+ * writes are local to the transaction and represent only a proposal of state
+ * change for the data tree and it is not visible to any other concurrently running
+ * transaction.
+ * <p>
+ * Applications publish the changes proposed in the transaction by calling {@link #commit}
+ * on the transaction. This seals the transaction
+ * (preventing any further writes using this transaction) and submits it to be
+ * processed and applied to global conceptual data tree.
+ * <p>
+ * The transaction commit may fail due to a concurrent transaction modifying and committing data in
+ * an incompatible way. See {@link #commit()} for more concrete commit failure examples.
+ *
+ *
+ * <p>
+ * <b>Implementation Note:</b> This interface is not intended to be implemented
+ * by users of MD-SAL, but only to be consumed by them.
+ *
+ * @param <P>
+ * Type of path (subtree identifier), which represents location in
+ * tree
+ * @param <D>
+ * Type of data (payload), which represents data payload
+ */
+public interface AsyncWriteTransaction<P extends Path<P>, D> extends AsyncTransaction<P, D> {
/**
- * Cancels transaction.
+ * Cancels the transaction.
*
- * Transaction could be only cancelled if it's status
- * is {@link TransactionStatus#NEW} or {@link TransactionStatus#SUBMITED}
+ * Transactions can only be cancelled if it's status is
+ * {@link TransactionStatus#NEW} or {@link TransactionStatus#SUBMITED}
*
- * Invoking cancel() on {@link TransactionStatus#FAILED} or {@link TransactionStatus#CANCELED}
- * will have no effect.
+ * Invoking cancel() on {@link TransactionStatus#FAILED} or
+ * {@link TransactionStatus#CANCELED} will have no effect.
*
- * @throws IllegalStateException If transaction status is {@link TransactionStatus#COMMITED}
+ * @throws IllegalStateException
+ * If transaction status is {@link TransactionStatus#COMMITED}
*
*/
public void cancel();
/**
- * Store a piece of data at specified path. This acts as a add / replace operation,
- * which is to say that whole subtree will be replaced by specified path.
+ * Store a piece of data at specified path. This acts as an add / replace
+ * operation, which is to say that whole subtree will be replaced by
+ * specified path. Performing the following put operations:
+ *
+ * <pre>
+ * 1) container { list [ a ] }
+ * 2) container { list [ b ] }
+ * </pre>
+ *
+ * will result in the following data being present:
+ *
+ * <pre>
+ * container { list [ b ] }
+ * </pre>
*
- * If you need add or merge of current object with specified use {@link #merge(LogicalDatastoreType, Path, Object)}
*
- * @param store Logical data store which should be modified
- * @param path Data object path
- * @param data Data object to be written to specified path
- * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+ * If you need to make sure that a parent object exists, but you do not want modify
+ * its preexisting state by using put, consider using
+ * {@link #merge(LogicalDatastoreType, Path, Object)}
+ *
+ * @param store
+ * Logical data store which should be modified
+ * @param path
+ * Data object path
+ * @param data
+ * Data object to be written to specified path
+ * @throws IllegalStateException
+ * if the transaction is no longer {@link TransactionStatus#NEW}
*/
public void put(LogicalDatastoreType store, P path, D data);
/**
- * Store a piece of data at specified path. This acts as a merge operation,
+ * Store a piece of data at the specified path. This acts as a merge operation,
* which is to say that any pre-existing data which is not explicitly
* overwritten will be preserved. This means that if you store a container,
- * its child lists will be merged. Performing the following put operations:
+ * its child lists will be merged. Performing the following merge
+ * operations:
*
+ * <pre>
* 1) container { list [ a ] }
* 2) container { list [ b ] }
+ * </pre>
*
* will result in the following data being present:
*
+ * <pre>
* container { list [ a, b ] }
+ * </pre>
*
- * This also means that storing the container will preserve any augmentations
- * which have been attached to it.
- *
- * If you require an explicit replace operation, use {@link #put(LogicalDatastoreType, Path, Object)} instead.
+ * This also means that storing the container will preserve any
+ * augmentations which have been attached to it.
+ *<p>
+ * If you require an explicit replace operation, use
+ * {@link #put(LogicalDatastoreType, Path, Object)} instead.
*
- * @param store Logical data store which should be modified
- * @param path Data object path
- * @param data Data object to be written to specified path
- * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+ * @param store
+ * Logical data store which should be modified
+ * @param path
+ * Data object path
+ * @param data
+ * Data object to be written to specified path
+ * @throws IllegalStateException
+ * if the transaction is no longer {@link TransactionStatus#NEW}
*/
public void merge(LogicalDatastoreType store, P path, D data);
* Remove a piece of data from specified path. This operation does not fail
* if the specified path does not exist.
*
- * @param store Logical data store which should be modified
- * @param path Data object path
- * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW}
+ * @param store
+ * Logical data store which should be modified
+ * @param path
+ * Data object path
+ * @throws IllegalStateException
+ * if the transaction is no longer {@link TransactionStatus#NEW}
*/
public void delete(LogicalDatastoreType store, P path);
*
* Closes transaction and resources allocated to the transaction.
*
- * This call does not change Transaction status. Client SHOULD
- * explicitly {@link #commit()} or {@link #cancel()} transaction.
+ * This call does not change Transaction status. Client SHOULD explicitly
+ * {@link #commit()} or {@link #cancel()} transaction.
*
- * @throws IllegalStateException if the transaction has not been
- * updated by invoking {@link #commit()} or {@link #cancel()}.
+ * @throws IllegalStateException
+ * if the transaction has not been updated by invoking
+ * {@link #commit()} or {@link #cancel()}.
*/
@Override
public void close();
/**
- * Initiates a commit of modification. This call logically seals the
- * transaction, preventing any the client from interacting with the
- * data stores. The transaction is marked as {@link TransactionStatus#SUBMITED}
- * and enqueued into the data store backed for processing.
+ * Submits transaction to be applied to update logical data tree.
+ * <p>
+ * This call logically seals the transaction, which prevents the client from
+ * further changing data tree using this transaction. Any subsequent calls to
+ * {@link #put(LogicalDatastoreType, Path, Object)},
+ * {@link #merge(LogicalDatastoreType, Path, Object)} or
+ * {@link #delete(LogicalDatastoreType, Path)} will fail with
+ * {@link IllegalStateException}.
+ *
+ * The transaction is marked as {@link TransactionStatus#SUBMITED} and
+ * enqueued into the data store backed for processing.
*
* <p>
- * The successful commit changes the state of the system and may affect
- * several components.
+ * Whether or not the commit is successful is determined by versioning
+ * of data tree and validation of registered commit participants
+ * {@link AsyncConfigurationCommitHandler}
+ * if transaction changes {@link LogicalDatastoreType#CONFIGURATION} data tree.
+ *<p>
+ * The effects of successful commit of data depends on
+ * other data change listeners {@link AsyncDataChangeListener} and
+ * {@link AsyncConfigurationCommitHandler}, which was registered to the
+ * same {@link AsyncDataBroker}, to which this transaction belongs.
*
+ * <h2>Failure scenarios</h2>
* <p>
- * The effects of successful commit of data are described in the
- * specifications and YANG models describing the Provider components of
- * controller. It is assumed that Consumer has an understanding of this
- * changes.
- *
- * @see DataCommitHandler for further information how two-phase commit is
- * processed.
- * @param store Identifier of the store, where commit should occur.
+ * Transaction may fail because of multiple reasons, such as
+ * <ul>
+ * <li>Another transaction finished earlier and modified the same node in
+ * non-compatible way (see below). In this case the returned future will fail with
+ * {@link OptimisticLockFailedException}. It is the responsibility of the
+ * caller to create a new transaction and submit the same modification again in
+ * order to update data tree.</li>
+ * <li>Data change introduced by this transaction did not pass validation by
+ * commit handlers or data was incorrectly structured. Returned future will
+ * fail with {@link DataValidationFailedException}. User should not retry to
+ * create new transaction with same data, since it probably will fail again.
+ * </li>
+ * </ul>
+ *
+ * <h3>Change compatibility</h3>
+ *
+ * There are several sets of changes which could be considered incompatible
+ * between two transactions which are derived from same initial state.
+ * Rules for conflict detection applies recursively for each subtree
+ * level.
+ *
+ * <h4>Change compatibility of leafs, leaf-list items</h4>
+ *
+ * Following table shows state changes and failures between two concurrent transactions,
+ * which are based on same initial state, Tx 1 completes successfully
+ * before Tx 2 is submitted.
+ *
+ * <table>
+ * <tr><th>Initial state</th><th>Tx 1</th><th>Tx 2</th><th>Result</th></tr>
+ * <tr><td>Empty</td><td>put(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, state is A=1</td></tr>
+ * <tr><td>Empty</td><td>put(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+ *
+ * <tr><td>Empty</td><td>merge(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, state is A=1</td></tr>
+ * <tr><td>Empty</td><td>merge(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+ *
+ *
+ * <tr><td>A=0</td><td>put(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, A=1</td></tr>
+ * <tr><td>A=0</td><td>put(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+ * <tr><td>A=0</td><td>merge(A,1)</td><td>put(A,2)</td><td>Tx 2 will fail, A=1</td></tr>
+ * <tr><td>A=0</td><td>merge(A,1)</td><td>merge(A,2)</td><td>A=2</td></tr>
+ *
+ * <tr><td>A=0</td><td>delete(A)</td><td>put(A,2)</td><td>Tx 2 will fail, A does not exists</td></tr>
+ * <tr><td>A=0</td><td>delete(A)</td><td>merge(A,2)</td><td>A=2</td></tr>
+ * </table>
+ *
+ * <h4>Change compatibility of subtrees</h4>
+ *
+ * Following table shows state changes and failures between two concurrent transactions,
+ * which are based on same initial state, Tx 1 completes successfully
+ * before Tx 2 is submitted.
+ *
+ * <table>
+ * <tr><th>Initial state</th><th>Tx 1</th><th>Tx 2</th><th>Result</th></tr>
+ *
+ * <tr><td>Empty</td><td>put(TOP,[])</td><td>put(TOP,[])</td><td>Tx 2 will fail, state is TOP=[]</td></tr>
+ * <tr><td>Empty</td><td>put(TOP,[])</td><td>merge(TOP,[])</td><td>TOP=[]</td></tr>
+ *
+ * <tr><td>Empty</td><td>put(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+ * <tr><td>Empty</td><td>put(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>TOP=[FOO=1,BAR=1]</td></tr>
+ *
+ * <tr><td>Empty</td><td>merge(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+ * <tr><td>Empty</td><td>merge(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>TOP=[FOO=1,BAR=1]</td></tr>
+ *
+ * <tr><td>TOP=[]</td><td>put(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>put(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>merge(TOP,[FOO=1])</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is TOP=[FOO=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>merge(TOP,[FOO=1])</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>put(TOP,[BAR=1])</td><td>Tx 2 will fail, state is empty store</td></tr>
+ * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>merge(TOP,[BAR=1])</td><td>state is TOP=[BAR=1]</td></tr>
+ *
+ * <tr><td>TOP=[]</td><td>put(TOP/FOO,1)</td><td>put(TOP/BAR,1])</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>put(TOP/FOO,1)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>merge(TOP/FOO,1)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>merge(TOP/FOO,1)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=1,BAR=1]</td></tr>
+ * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>put(TOP/BAR,1)</td><td>Tx 2 will fail, state is empty store</td></tr>
+ * <tr><td>TOP=[]</td><td>delete(TOP)</td><td>merge(TOP/BAR,1]</td><td>Tx 2 will fail, state is empty store</td></tr>
+ *
+ * <tr><td>TOP=[FOO=1]</td><td>put(TOP/FOO,2)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+ * <tr><td>TOP=[FOO=1]</td><td>put(TOP/FOO,2)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+ * <tr><td>TOP=[FOO=1]</td><td>merge(TOP/FOO,2)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+ * <tr><td>TOP=[FOO=1]</td><td>merge(TOP/FOO,2)</td><td>merge(TOP/BAR,1)</td><td>state is TOP=[FOO=2,BAR=1]</td></tr>
+ * <tr><td>TOP=[FOO=1]</td><td>delete(TOP/FOO)</td><td>put(TOP/BAR,1)</td><td>state is TOP=[BAR=1]</td></tr>
+ * <tr><td>TOP=[FOO=1]</td><td>delete(TOP/FOO)</td><td>merge(TOP/BAR,1]</td><td>state is TOP=[BAR=1]</td></tr>
+ * </table>
+ *
+ *
+ * <h3>Examples of failure scenarios</h3>
+ *
+ * <h4>Conflict of two transactions</h4>
+ *
+ * This example illustrates two concurrent transactions, which derived from
+ * same initial state of data tree and proposes conflicting modifications.
+ *
+ * <pre>
+ * txA = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+ * txB = broker.newWriteTransaction(); // allocates new transaction, data tree is empty
+ *
+ * txA.put(CONFIGURATION, PATH, A); // writes to PATH value A
+ * txB.put(CONFIGURATION, PATH, B) // writes to PATH value B
+ *
+ * ListenableFuture futureA = txA.commit(); // transaction A is sealed and committed
+ * ListenebleFuture futureB = txB.commit(); // transaction B is sealed and committed
+ * </pre>
+ *
+ * Commit of transaction A will be processed asynchronously and data tree
+ * will be updated to contain value <code>A</code> for <code>PATH</code>.
+ * Returned {@link ListenableFuture} will successfully complete once
+ * state is applied to data tree.
+ *
+ * Commit of Transaction B will fail, because previous transaction also
+ * modified path in a concurrent way. The state introduced by transaction B
+ * will not be applied. Returned {@link ListenableFuture} object will fail
+ * with {@link OptimisticLockFailedException} exception, which indicates to
+ * client that concurrent transaction prevented the submitted transaction from being
+ * applied.
+ *
* @return Result of the Commit, containing success information or list of
* encountered errors, if commit was not successful. The Future
* blocks until {@link TransactionStatus#COMMITED} is reached.
- * Future will fail with {@link TransactionCommitFailedException}
- * if Commit of this transaction failed.
+ * Future will fail with {@link TransactionCommitFailedException} if
+ * Commit of this transaction failed. TODO: Usability: Consider
+ * change from ListenableFuture to
+ * {@link com.google.common.util.concurrent.CheckedFuture} which
+ * will throw {@link TransactionCommitFailedException}.
*
- * @throws IllegalStateException if the transaction is not {@link TransactionStatus#NEW}
+ * @throws IllegalStateException
+ * if the transaction is not {@link TransactionStatus#NEW}
*/
public ListenableFuture<RpcResult<TransactionStatus>> commit();
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.common.api.data;
+
+import org.opendaylight.yangtools.concepts.Path;
+
+import com.google.common.base.Preconditions;
+
+/**
+ *
+ * Failure of asynchronous transaction commit caused by invalid data.
+ *
+ * This exception is raised and returned when transaction commit
+ * failed, because other data submitted via transactions
+ *
+ * Clients usually are not able recover from this error condition by
+ * retrieving same transaction, since data introduced by this transaction
+ * are invalid.
+ *
+ */
+public class DataValidationFailedException extends TransactionCommitFailedException {
+
+ private static final long serialVersionUID = 1L;
+
+ private Path<?> path;
+
+ private Class<? extends Path<?>> pathType;
+
+ public <P extends Path<P>> DataValidationFailedException(final Class<P> pathType,final P path, final String message, final Throwable cause) {
+ super(message, cause);
+ this.pathType = Preconditions.checkNotNull(pathType, "path type must not be null");
+ this.path = Preconditions.checkNotNull(path,"path must not be null.");
+ }
+
+ public <P extends Path<P>> DataValidationFailedException(final Class<P> pathType,final P path,final String message) {
+ this(pathType,path,message,null);
+ }
+
+ public final Path<?> getPath() {
+ return path;
+ }
+
+ public final Class<? extends Path<?>> getPathType() {
+ return pathType;
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.md.sal.common.api.data;
+
+/**
+*
+* Failure of asynchronous transaction commit caused by failure
+* of optimistic locking.
+*
+* This exception is raised and returned when transaction commit
+* failed, because other transaction finished successfully
+* and modified same data as failed transaction.
+*
+* Clients may recover from this error condition by
+* retrieving current state and submitting new updated
+* transaction.
+*
+*/
+public class OptimisticLockFailedException extends TransactionCommitFailedException {
+
+ private static final long serialVersionUID = 1L;
+
+ protected OptimisticLockFailedException(final String message, final Throwable cause, final boolean enableSuppression,
+ final boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public OptimisticLockFailedException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ public OptimisticLockFailedException(final String message) {
+ super(message);
+ }
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-parent</artifactId>
+ <version>1.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>sal-distributed-datastore</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-actor_${scala.version}</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-cluster_${scala.version}</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-persistence-experimental_${scala.version}</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-remote_${scala.version}</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe.akka</groupId>
+ <artifactId>akka-testkit_${scala.version}</artifactId>
+ </dependency>
+
+ <!-- SAL Dependencies -->
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ </dependency>
+
+ <!--
+ Adding a temporary dependency on the sal-broker-impl so that we can use InMemoryDOMDataStore
+
+ InMemoryDOMDataStore needs to be moved into its own module and be wired up using config subsystem before
+ this bundle can use it
+ -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-broker-impl</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-spi</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-binding</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.scala-lang</groupId>
+ <artifactId>scala-library</artifactId>
+ </dependency>
+
+ <!-- Test Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>${slf4j.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Export-package></Export-package>
+ <Private-Package></Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <configuration>
+ <includes>
+ <include>org.opendaylight.controller.*</include>
+ </includes>
+ <check>false</check>
+ </configuration>
+ <executions>
+ <execution>
+ <id>pre-test</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>post-test</id>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ <phase>test</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Architecture:Clustering</url>
+ </scm>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ *
+ */
+public class DistributedDataStore implements DOMStore {
+
+ @Override
+ public <L extends AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(InstanceIdentifier path, L listener, AsyncDataBroker.DataChangeScope scope) {
+ return new ListenerRegistrationProxy();
+ }
+
+ @Override
+ public DOMStoreTransactionChain createTransactionChain() {
+ return new TransactionChainProxy();
+ }
+
+ @Override
+ public DOMStoreReadTransaction newReadOnlyTransaction() {
+ return new TransactionProxy();
+ }
+
+ @Override
+ public DOMStoreWriteTransaction newWriteOnlyTransaction() {
+ return new TransactionProxy();
+ }
+
+ @Override
+ public DOMStoreReadWriteTransaction newReadWriteTransaction() {
+ return new TransactionProxy();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import akka.japi.Creator;
+import org.opendaylight.controller.cluster.datastore.messages.CloseListenerRegistration;
+import org.opendaylight.controller.cluster.datastore.messages.CloseListenerRegistrationReply;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class ListenerRegistration extends UntypedActor{
+
+ private final org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> registration;
+
+ public ListenerRegistration(org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> registration) {
+ this.registration = registration;
+ }
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ if(message instanceof CloseListenerRegistration){
+ closeListenerRegistration((CloseListenerRegistration) message);
+ }
+ }
+
+ public static Props props(final org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> registration){
+ return Props.create(new Creator<ListenerRegistration>(){
+
+ @Override
+ public ListenerRegistration create() throws Exception {
+ return new ListenerRegistration(registration);
+ }
+ });
+ }
+
+ private void closeListenerRegistration(CloseListenerRegistration message){
+ registration.close();
+ getSender().tell(new CloseListenerRegistrationReply(), getSelf());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+
+/**
+ * ListenerRegistrationProxy acts as a proxy for a ListenerRegistration that was done on a remote shard
+ *
+ * Registering a DataChangeListener on the Data Store creates a new instance of the ListenerRegistrationProxy
+ * The ListenerRegistrationProxy talks to a remote ListenerRegistration actor.
+ */
+public class ListenerRegistrationProxy implements ListenerRegistration {
+ @Override
+ public Object getInstance() {
+ throw new UnsupportedOperationException("getInstance");
+ }
+
+ @Override
+ public void close() {
+ throw new UnsupportedOperationException("close");
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.event.Logging;
+import akka.event.LoggingAdapter;
+import akka.persistence.UntypedProcessor;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChain;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChainReply;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
+import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import java.util.concurrent.Executors;
+
+/**
+ * A Shard represents a portion of the logical data tree
+ * <p/>
+ * Our Shard uses InMemoryDataStore as it's internal representation and delegates all requests it
+ *
+ */
+public class Shard extends UntypedProcessor {
+
+ ListeningExecutorService storeExecutor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(2));
+
+ private final InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", storeExecutor);
+
+ LoggingAdapter log = Logging.getLogger(getContext().system(), this);
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ if (message instanceof CreateTransactionChain) {
+ createTransactionChain();
+ } else if(message instanceof RegisterChangeListener){
+ registerChangeListener((RegisterChangeListener) message);
+ } else if(message instanceof UpdateSchemaContext){
+ updateSchemaContext((UpdateSchemaContext) message);
+ }
+ }
+
+ private void updateSchemaContext(UpdateSchemaContext message) {
+ store.onGlobalContextUpdated(message.getSchemaContext());
+ }
+
+ private void registerChangeListener(RegisterChangeListener registerChangeListener) {
+ org.opendaylight.yangtools.concepts.ListenerRegistration<AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>> registration =
+ store.registerChangeListener(registerChangeListener.getPath(), registerChangeListener.getListener(), registerChangeListener.getScope());
+ ActorRef listenerRegistration = getContext().actorOf(ListenerRegistration.props(registration));
+ getSender().tell(new RegisterChangeListenerReply(listenerRegistration.path()), getSelf());
+ }
+
+ private void createTransactionChain() {
+ DOMStoreTransactionChain chain = store.createTransactionChain();
+ ActorRef transactionChain = getContext().actorOf(ShardTransactionChain.props(chain));
+ getSender().tell(new CreateTransactionChainReply(transactionChain.path()), getSelf());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.Address;
+import akka.actor.UntypedActor;
+import akka.event.Logging;
+import akka.event.LoggingAdapter;
+import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
+import org.opendaylight.controller.cluster.datastore.messages.PrimaryNotFound;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The ShardManager has the following jobs,
+ *
+ * - Create all the local shard replicas that belong on this cluster member
+ * - Find the primary replica for any given shard
+ * - Engage in shard replica elections which decide which replica should be the primary
+ *
+ * Creation of Shard replicas
+ * ==========================
+ * When the ShardManager is constructed it reads the cluster configuration to find out which shard replicas
+ * belong on this member. It finds out the name of the current cluster member from the Akka Clustering Service.
+ *
+ * Replica Elections
+ * =================
+ * The Shard Manager uses multiple cues to initiate election.
+ * - When a member of the cluster dies
+ * - When a local shard replica dies
+ * - When a local shard replica comes alive
+ */
+public class ShardManager extends UntypedActor {
+
+ // Stores a mapping between a shard name and the address of the current primary
+ private final Map<String, Address> shardNameToPrimaryAddress = new HashMap<>();
+
+ // Stores a mapping between a member name and the address of the member
+ private final Map<String, Address> memberNameToAddress = new HashMap<>();
+
+ // Stores a mapping between the shard name and all the members on which a replica of that shard are available
+ private final Map<String, List<String>> shardNameToMembers = new HashMap<>();
+
+ LoggingAdapter log = Logging.getLogger(getContext().system(), this);
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ if(message instanceof FindPrimary ){
+ FindPrimary msg = ((FindPrimary) message);
+ getSender().tell(new PrimaryNotFound(msg.getShardName()), getSelf());
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import akka.event.Logging;
+import akka.event.LoggingAdapter;
+import akka.japi.Creator;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.DeleteData;
+import org.opendaylight.controller.cluster.datastore.messages.DeleteDataReply;
+import org.opendaylight.controller.cluster.datastore.messages.MergeData;
+import org.opendaylight.controller.cluster.datastore.messages.MergeDataReply;
+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.messages.WriteDataReply;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * The ShardTransaction Actor represents a remote transaction
+ *
+ * The ShardTransaction Actor delegates all actions to DOMDataReadWriteTransaction
+ *
+ * Even though the DOMStore and the DOMStoreTransactionChain implement multiple types of transactions
+ * the ShardTransaction Actor only works with read-write transactions. This is just to keep the logic simple. At this
+ * time there are no known advantages for creating a read-only or write-only transaction which may change over time
+ * at which point we can optimize things in the distributed store as well.
+ *
+ * Handles Messages
+ * ----------------
+ * {@link org.opendaylight.controller.cluster.datastore.messages.ReadData}
+ * {@link org.opendaylight.controller.cluster.datastore.messages.WriteData}
+ * {@link org.opendaylight.controller.cluster.datastore.messages.MergeData}
+ * {@link org.opendaylight.controller.cluster.datastore.messages.DeleteData}
+ * {@link org.opendaylight.controller.cluster.datastore.messages.ReadyTransaction}
+ * {@link org.opendaylight.controller.cluster.datastore.messages.CloseTransaction}
+ */
+public class ShardTransaction extends UntypedActor {
+
+ private final DOMStoreReadWriteTransaction transaction;
+
+ private final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
+
+ public ShardTransaction(DOMStoreReadWriteTransaction transaction) {
+ this.transaction = transaction;
+ }
+
+
+ public static Props props(final DOMStoreReadWriteTransaction transaction){
+ return Props.create(new Creator<ShardTransaction>(){
+
+ @Override
+ public ShardTransaction create() throws Exception {
+ return new ShardTransaction(transaction);
+ }
+ });
+ }
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ if(message instanceof ReadData){
+ readData((ReadData) message);
+ } else if(message instanceof WriteData){
+ writeData((WriteData) message);
+ } else if(message instanceof MergeData){
+ mergeData((MergeData) message);
+ } else if(message instanceof DeleteData){
+ deleteData((DeleteData) message);
+ } else if(message instanceof ReadyTransaction){
+ readyTransaction((ReadyTransaction) message);
+ } else if(message instanceof CloseTransaction){
+ closeTransaction((CloseTransaction) message);
+ }
+ }
+
+ private void readData(ReadData message) {
+ final ActorRef sender = getSender();
+ final ActorRef self = getSelf();
+ final InstanceIdentifier path = message.getPath();
+ final ListenableFuture<Optional<NormalizedNode<?, ?>>> future = transaction.read(path);
+
+ future.addListener(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Optional<NormalizedNode<?, ?>> optional = future.get();
+ if(optional.isPresent()){
+ sender.tell(new ReadDataReply(optional.get()), self);
+ } else {
+ //TODO : Need to decide what to do here
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ log.error(e, "An exception happened when reading data from path : " + path.toString());
+ }
+
+ }
+ }, getContext().dispatcher());
+ }
+
+
+ private void writeData(WriteData message){
+ transaction.write(message.getPath(), message.getData());
+ getSender().tell(new WriteDataReply(), getSelf());
+ }
+
+ private void mergeData(MergeData message){
+ transaction.merge(message.getPath(), message.getData());
+ getSender().tell(new MergeDataReply(), getSelf());
+ }
+
+ private void deleteData(DeleteData message){
+ transaction.delete(message.getPath());
+ getSender().tell(new DeleteDataReply(), getSelf());
+ }
+
+ private void readyTransaction(ReadyTransaction message){
+ DOMStoreThreePhaseCommitCohort cohort = transaction.ready();
+ ActorRef cohortActor = getContext().actorOf(ThreePhaseCommitCohort.props(cohort));
+ getSender().tell(new ReadyTransactionReply(cohortActor.path()), getSelf());
+
+ }
+
+ private void closeTransaction(CloseTransaction message){
+ transaction.close();
+ getSender().tell(new CloseTransactionReply(), getSelf());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import akka.japi.Creator;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionChain;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionChainReply;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+
+/**
+ * The ShardTransactionChain Actor represents a remote TransactionChain
+ */
+public class ShardTransactionChain extends UntypedActor{
+
+ private final DOMStoreTransactionChain chain;
+
+ public ShardTransactionChain(DOMStoreTransactionChain chain) {
+ this.chain = chain;
+ }
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ if(message instanceof CreateTransaction){
+ DOMStoreReadWriteTransaction transaction = chain.newReadWriteTransaction();
+ ActorRef transactionActor = getContext().actorOf(ShardTransaction.props(transaction));
+ getSender().tell(new CreateTransactionReply(transactionActor.path()), getSelf());
+ } else if (message instanceof CloseTransactionChain){
+ chain.close();
+ getSender().tell(new CloseTransactionChainReply(), getSelf());
+ }
+ }
+
+ public static Props props(final DOMStoreTransactionChain chain){
+ return Props.create(new Creator<ShardTransactionChain>(){
+
+ @Override
+ public ShardTransactionChain create() throws Exception {
+ return new ShardTransactionChain(chain);
+ }
+ });
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import akka.japi.Creator;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+
+public class ThreePhaseCommitCohort extends UntypedActor{
+ private final DOMStoreThreePhaseCommitCohort cohort;
+
+ public ThreePhaseCommitCohort(DOMStoreThreePhaseCommitCohort cohort) {
+
+ this.cohort = cohort;
+ }
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ throw new UnsupportedOperationException("onReceive");
+ }
+
+ public static Props props(final DOMStoreThreePhaseCommitCohort cohort) {
+ return Props.create(new Creator<ThreePhaseCommitCohort>(){
+ @Override
+ public ThreePhaseCommitCohort create() throws Exception {
+ return new ThreePhaseCommitCohort(cohort);
+ }
+ });
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+
+/**
+ * TransactionChainProxy acts as a proxy for a DOMStoreTransactionChain created on a remote shard
+ */
+public class TransactionChainProxy implements DOMStoreTransactionChain{
+ @Override
+ public DOMStoreReadTransaction newReadOnlyTransaction() {
+ throw new UnsupportedOperationException("newReadOnlyTransaction");
+ }
+
+ @Override
+ public DOMStoreReadWriteTransaction newReadWriteTransaction() {
+ throw new UnsupportedOperationException("newReadWriteTransaction");
+ }
+
+ @Override
+ public DOMStoreWriteTransaction newWriteOnlyTransaction() {
+ throw new UnsupportedOperationException("newWriteOnlyTransaction");
+ }
+
+ @Override
+ public void close() {
+ throw new UnsupportedOperationException("close");
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * TransactionProxy acts as a proxy for one or more transactions that were created on a remote shard
+ *
+ * Creating a transaction on the consumer side will create one instance of a transaction proxy. If during
+ * the transaction reads and writes are done on data that belongs to different shards then a separate transaction will
+ * be created on each of those shards by the TransactionProxy
+ *
+ * The TransactionProxy does not make any guarantees about atomicity or order in which the transactions on the various
+ * shards will be executed.
+ *
+ */
+public class TransactionProxy implements DOMStoreReadWriteTransaction {
+ @Override
+ public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(InstanceIdentifier path) {
+ throw new UnsupportedOperationException("read");
+ }
+
+ @Override
+ public void write(InstanceIdentifier path, NormalizedNode<?, ?> data) {
+ throw new UnsupportedOperationException("write");
+ }
+
+ @Override
+ public void merge(InstanceIdentifier path, NormalizedNode<?, ?> data) {
+ throw new UnsupportedOperationException("merge");
+ }
+
+ @Override
+ public void delete(InstanceIdentifier path) {
+ throw new UnsupportedOperationException("delete");
+ }
+
+ @Override
+ public DOMStoreThreePhaseCommitCohort ready() {
+ throw new UnsupportedOperationException("ready");
+ }
+
+ @Override
+ public Object getIdentifier() {
+ throw new UnsupportedOperationException("getIdentifier");
+ }
+
+ @Override
+ public void close() {
+ throw new UnsupportedOperationException("close");
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CloseListenerRegistration {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CloseListenerRegistrationReply {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CloseTransaction {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CloseTransactionChain {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CloseTransactionChainReply {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CloseTransactionReply {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CreateTransaction {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class CreateTransactionChain {
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import akka.actor.ActorPath;
+
+public class CreateTransactionChainReply {
+ private final ActorPath transactionChainPath;
+
+ public CreateTransactionChainReply(ActorPath transactionChainPath) {
+ this.transactionChainPath = transactionChainPath;
+ }
+
+ public ActorPath getTransactionChainPath() {
+ return transactionChainPath;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import akka.actor.ActorPath;
+
+public class CreateTransactionReply {
+ private final ActorPath transactionPath;
+
+ public CreateTransactionReply(ActorPath transactionPath) {
+ this.transactionPath = transactionPath;
+ }
+
+ public ActorPath getTransactionPath() {
+ return transactionPath;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+public class DeleteData {
+ private final InstanceIdentifier path;
+
+ public DeleteData(InstanceIdentifier path) {
+ this.path = path;
+ }
+
+ public InstanceIdentifier getPath() {
+ return path;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class DeleteDataReply {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * The FindPrimary message is used to locate the primary of any given shard
+ *
+ * TODO : Make this serializable
+ */
+public class FindPrimary{
+ private final String shardName;
+
+ public FindPrimary(String shardName){
+
+ Preconditions.checkNotNull(shardName, "shardName should not be null");
+
+ this.shardName = shardName;
+ }
+
+ public String getShardName() {
+ return shardName;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class MergeData extends ModifyData {
+ public MergeData(InstanceIdentifier path, NormalizedNode<?, ?> data) {
+ super(path, data);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class MergeDataReply {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public abstract class ModifyData {
+ private final InstanceIdentifier path;
+ private final NormalizedNode<?,?> data;
+
+ public ModifyData(InstanceIdentifier path, NormalizedNode<?, ?> data) {
+ this.path = path;
+ this.data = data;
+ }
+
+ public InstanceIdentifier getPath() {
+ return path;
+ }
+
+ public NormalizedNode<?, ?> getData() {
+ return data;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class PrimaryFound {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import com.google.common.base.Preconditions;
+
+public class PrimaryNotFound {
+
+ private final String shardName;
+
+ public PrimaryNotFound(String shardName){
+
+ Preconditions.checkNotNull(shardName, "shardName should not be null");
+
+ this.shardName = shardName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ PrimaryNotFound that = (PrimaryNotFound) o;
+
+ if (shardName != null ? !shardName.equals(that.shardName) : that.shardName != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return shardName != null ? shardName.hashCode() : 0;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+public class ReadData {
+ private final InstanceIdentifier path;
+
+ public ReadData(InstanceIdentifier path) {
+ this.path = path;
+ }
+
+ public InstanceIdentifier getPath() {
+ return path;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class ReadDataReply {
+ private final NormalizedNode<?, ?> normalizedNode;
+
+ public ReadDataReply(NormalizedNode<?, ?> normalizedNode){
+
+ this.normalizedNode = normalizedNode;
+ }
+
+ public NormalizedNode<?, ?> getNormalizedNode() {
+ return normalizedNode;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class ReadyTransaction {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import akka.actor.ActorPath;
+
+public class ReadyTransactionReply {
+ private final ActorPath path;
+
+ public ReadyTransactionReply(ActorPath path) {
+
+ this.path = path;
+ }
+
+ public ActorPath getPath() {
+ return path;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class RegisterChangeListener {
+ private final InstanceIdentifier path;
+ private final AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> listener;
+ private final AsyncDataBroker.DataChangeScope scope;
+
+
+ public RegisterChangeListener(InstanceIdentifier path, AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> listener, AsyncDataBroker.DataChangeScope scope) {
+ this.path = path;
+ this.listener = listener;
+ this.scope = scope;
+ }
+
+ public InstanceIdentifier getPath() {
+ return path;
+ }
+
+ public AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> getListener() {
+ return listener;
+ }
+
+ public AsyncDataBroker.DataChangeScope getScope() {
+ return scope;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import akka.actor.ActorPath;
+
+public class RegisterChangeListenerReply {
+ private final ActorPath listenerRegistrationPath;
+
+ public RegisterChangeListenerReply(ActorPath listenerRegistrationPath) {
+ this.listenerRegistrationPath = listenerRegistrationPath;
+ }
+
+ public ActorPath getListenerRegistrationPath() {
+ return listenerRegistrationPath;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class UpdateSchemaContext {
+ private final SchemaContext schemaContext;
+
+ public UpdateSchemaContext(SchemaContext schemaContext) {
+ this.schemaContext = schemaContext;
+ }
+
+ public SchemaContext getSchemaContext() {
+ return schemaContext;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class WriteData extends ModifyData{
+
+ public WriteData(InstanceIdentifier path, NormalizedNode<?, ?> data) {
+ super(path, data);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.messages;
+
+public class WriteDataReply {
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.shardstrategy;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+/**
+ * The DefaultShardStrategy basically puts all data into the default Shard
+ * <p>
+ * The default shard stores data for all modules for which a specific set of shards has not been configured
+ * </p>
+ */
+public class DefaultShardStrategy implements ShardStrategy{
+
+ public static final String NAME = "default";
+ public static final String DEFAULT_SHARD = "default";
+
+ @Override
+ public String findShard(InstanceIdentifier path) {
+ return DEFAULT_SHARD;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.shardstrategy;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+/**
+ * The role of ShardStrategy is to figure out which Shards a given piece of data belongs to
+ */
+public interface ShardStrategy {
+ /**
+ * Find the name of the shard in which the data pointed to by the specified path belongs in
+ *
+ * @param path The location of the data in the logical tree
+ * @return
+ */
+ String findShard(InstanceIdentifier path);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore.shardstrategy;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ShardStrategyFactory {
+ private static final Map<String, ShardStrategy> moduleNameToStrategyMap = new ConcurrentHashMap();
+
+ private static final String UNKNOWN_MODULE_NAME = "unknown";
+
+ public static ShardStrategy getStrategy(InstanceIdentifier path){
+ Preconditions.checkNotNull(path, "path should not be null");
+
+ String moduleName = getModuleName(path);
+ ShardStrategy shardStrategy = moduleNameToStrategyMap.get(moduleName);
+ if(shardStrategy == null){
+ return new DefaultShardStrategy();
+ }
+
+ return shardStrategy;
+ }
+
+
+ private static String getModuleName(InstanceIdentifier path){
+ return UNKNOWN_MODULE_NAME;
+ }
+
+ /**
+ * This is to be used in the future to register a custom shard strategy
+ *
+ * @param moduleName
+ * @param shardStrategy
+ */
+ public static void registerShardStrategy(String moduleName, ShardStrategy shardStrategy){
+ throw new UnsupportedOperationException("registering a custom shard strategy not supported yet");
+ }
+}
--- /dev/null
+package org.opendaylight.controller.config.yang.config.distributed_datastore_provider;
+
+import org.opendaylight.controller.cluster.datastore.DistributedDataStore;
+
+public class DistributedDataStoreProviderModule extends org.opendaylight.controller.config.yang.config.distributed_datastore_provider.AbstractDistributedDataStoreProviderModule {
+ public DistributedDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public DistributedDataStoreProviderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.config.distributed_datastore_provider.DistributedDataStoreProviderModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ new DistributedDataStore();
+
+ final class AutoCloseableDistributedDataStore implements AutoCloseable {
+
+ @Override
+ public void close() throws Exception {
+
+ }
+ }
+
+ return new AutoCloseableDistributedDataStore();
+ }
+
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: distributed-datastore-provider yang module local name: distributed-datastore-provider
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu Jun 12 15:23:43 PDT 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.config.distributed_datastore_provider;
+public class DistributedDataStoreProviderModuleFactory extends org.opendaylight.controller.config.yang.config.distributed_datastore_provider.AbstractDistributedDataStoreProviderModuleFactory {
+
+}
--- /dev/null
+// vi: set smarttab et sw=4 tabstop=4:
+module distributed-datastore-provider {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:config:distributed-datastore-provider";
+ prefix "distributed-datastore-provider";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import rpc-context { prefix rpcx; revision-date 2013-06-17; }
+
+ description
+ "This module contains the base YANG definitions for
+ the distributed datastore provider implementation";
+
+ revision "2014-06-12" {
+ description
+ "Initial revision.";
+ }
+
+ // This is the definition of the service implementation as a module identity.
+ identity distributed-datastore-provider {
+ base config:module-type;
+
+ // Specifies the prefix for generated java classes.
+ config:java-name-prefix DistributedDataStoreProvider;
+ }
+
+ // Augments the 'configuration' choice node under modules/module.
+ augment "/config:modules/config:module/config:configuration" {
+ case distributed-datastore-provider {
+ when "/config:modules/config:module/config:type = 'distributed-datastore-provider'";
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorSystem;
+import akka.testkit.JavaTestKit;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+public abstract class AbstractActorTest {
+ private static ActorSystem system;
+
+ @BeforeClass
+ public static void setUp(){
+ system = ActorSystem.create("test");
+ }
+
+ @AfterClass
+ public static void tearDown(){
+ JavaTestKit.shutdownActorSystem(system);
+ system = null;
+ }
+
+ protected ActorSystem getSystem(){
+ return system;
+ }
+
+}
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import junit.framework.Assert;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class DistributedDataStoreTest {
+
+ private DistributedDataStore distributedDataStore;
+
+ @org.junit.Before
+ public void setUp() throws Exception {
+ distributedDataStore = new DistributedDataStore();
+ }
+
+ @org.junit.After
+ public void tearDown() throws Exception {
+
+ }
+
+ @org.junit.Test
+ public void testRegisterChangeListener() throws Exception {
+ ListenerRegistration registration =
+ distributedDataStore.registerChangeListener(InstanceIdentifier.builder().build(), new AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>() {
+ @Override
+ public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> change) {
+ throw new UnsupportedOperationException("onDataChanged");
+ }
+ }, AsyncDataBroker.DataChangeScope.BASE);
+
+ Assert.assertNotNull(registration);
+ }
+
+ @org.junit.Test
+ public void testCreateTransactionChain() throws Exception {
+ final DOMStoreTransactionChain transactionChain = distributedDataStore.createTransactionChain();
+ Assert.assertNotNull(transactionChain);
+ }
+
+ @org.junit.Test
+ public void testNewReadOnlyTransaction() throws Exception {
+ final DOMStoreReadTransaction transaction = distributedDataStore.newReadOnlyTransaction();
+ Assert.assertNotNull(transaction);
+ }
+
+ @org.junit.Test
+ public void testNewWriteOnlyTransaction() throws Exception {
+ final DOMStoreWriteTransaction transaction = distributedDataStore.newWriteOnlyTransaction();
+ Assert.assertNotNull(transaction);
+ }
+
+ @org.junit.Test
+ public void testNewReadWriteTransaction() throws Exception {
+ final DOMStoreReadWriteTransaction transaction = distributedDataStore.newReadWriteTransaction();
+ Assert.assertNotNull(transaction);
+ }
+}
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.testkit.JavaTestKit;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.messages.CloseListenerRegistration;
+import org.opendaylight.controller.cluster.datastore.messages.CloseListenerRegistrationReply;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import static org.junit.Assert.assertEquals;
+
+public class ListenerRegistrationTest extends AbstractActorTest {
+ private static ListeningExecutorService storeExecutor = MoreExecutors.listeningDecorator(MoreExecutors.sameThreadExecutor());
+
+ private static final InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", storeExecutor);
+
+ static {
+ store.onGlobalContextUpdated(TestModel.createTestContext());
+ }
+
+
+ @Test
+ public void testOnReceiveCloseListenerRegistration() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ final Props props = ListenerRegistration.props(store.registerChangeListener(TestModel.TEST_PATH, noOpDataChangeListener(), AsyncDataBroker.DataChangeScope.BASE));
+ final ActorRef subject = getSystem().actorOf(props, "testCloseListenerRegistration");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new CloseListenerRegistration(), getRef());
+
+ final String out = new ExpectMsg<String>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected String match(Object in) {
+ if (in instanceof CloseListenerRegistrationReply) {
+ return "match";
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertEquals("match", out);
+
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+ }
+
+ private AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> noOpDataChangeListener(){
+ return new AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>() {
+ @Override
+ public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> change) {
+
+ }
+ };
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorSystem;
+import akka.actor.Props;
+import akka.testkit.JavaTestKit;
+import akka.testkit.TestActorRef;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.messages.FindPrimary;
+import org.opendaylight.controller.cluster.datastore.messages.PrimaryNotFound;
+import scala.concurrent.duration.Duration;
+
+public class ShardManagerTest {
+ private static ActorSystem system;
+
+ @BeforeClass
+ public static void setUp(){
+ system = ActorSystem.create("test");
+ }
+
+ @AfterClass
+ public static void tearDown(){
+ JavaTestKit.shutdownActorSystem(system);
+ system = null;
+ }
+
+ @Test
+ public void testOnReceiveFindPrimary() throws Exception {
+
+ new JavaTestKit(system) {{
+ final Props props = Props.create(ShardManager.class);
+ final TestActorRef<ShardManager> subject = TestActorRef.create(system, props, "test");
+
+ // can also use JavaTestKit “from the outside”
+ final JavaTestKit probe = new JavaTestKit(system);
+
+ // the run() method needs to finish within 3 seconds
+ new Within(duration("3 seconds")) {
+ protected void run() {
+
+ subject.tell(new FindPrimary("inventory"), getRef());
+
+ expectMsgEquals(Duration.Zero(), new PrimaryNotFound("inventory"));
+
+ // Will wait for the rest of the 3 seconds
+ expectNoMsg();
+ }
+ };
+ }};
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.testkit.JavaTestKit;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChain;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionChainReply;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
+import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListenerReply;
+import org.opendaylight.controller.cluster.datastore.messages.UpdateSchemaContext;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import static org.junit.Assert.assertTrue;
+
+public class ShardTest extends AbstractActorTest{
+ @Test
+ public void testOnReceiveCreateTransactionChain() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ final Props props = Props.create(Shard.class);
+ final ActorRef subject = getSystem().actorOf(props, "testCreateTransactionChain");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new CreateTransactionChain(), getRef());
+
+ final String out = new ExpectMsg<String>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected String match(Object in) {
+ if (in instanceof CreateTransactionChainReply) {
+ CreateTransactionChainReply reply = (CreateTransactionChainReply) in;
+ return reply.getTransactionChainPath().toString();
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertTrue(out.matches("akka:\\/\\/test\\/user\\/testCreateTransactionChain\\/\\$.*"));
+ // Will wait for the rest of the 3 seconds
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+ }
+
+ @Test
+ public void testOnReceiveRegisterListener() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ final Props props = Props.create(Shard.class);
+ final ActorRef subject = getSystem().actorOf(props, "testRegisterChangeListener");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new UpdateSchemaContext(TestModel.createTestContext()), getRef());
+
+ subject.tell(new RegisterChangeListener(InstanceIdentifier.builder().build(), noOpDataChangeListener() , AsyncDataBroker.DataChangeScope.BASE), getRef());
+
+ final String out = new ExpectMsg<String>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected String match(Object in) {
+ if (in instanceof RegisterChangeListenerReply) {
+ RegisterChangeListenerReply reply = (RegisterChangeListenerReply) in;
+ return reply.getListenerRegistrationPath().toString();
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertTrue(out.matches("akka:\\/\\/test\\/user\\/testRegisterChangeListener\\/\\$.*"));
+ // Will wait for the rest of the 3 seconds
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+ }
+
+ private AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>> noOpDataChangeListener(){
+ return new AsyncDataChangeListener<InstanceIdentifier, NormalizedNode<?, ?>>() {
+ @Override
+ public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> change) {
+
+ }
+ };
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.testkit.JavaTestKit;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionChain;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionChainReply;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CreateTransactionReply;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ShardTransactionChainTest extends AbstractActorTest {
+
+ private static ListeningExecutorService storeExecutor = MoreExecutors.listeningDecorator(MoreExecutors.sameThreadExecutor());
+
+ private static final InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", storeExecutor);
+
+ static {
+ store.onGlobalContextUpdated(TestModel.createTestContext());
+ }
+ @Test
+ public void testOnReceiveCreateTransaction() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ final Props props = ShardTransactionChain.props(store.createTransactionChain());
+ final ActorRef subject = getSystem().actorOf(props, "testCreateTransaction");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new CreateTransaction(), getRef());
+
+ final String out = new ExpectMsg<String>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected String match(Object in) {
+ if (in instanceof CreateTransactionReply) {
+ return ((CreateTransactionReply) in).getTransactionPath().toString();
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertTrue(out.matches("akka:\\/\\/test\\/user\\/testCreateTransaction\\/\\$.*"));
+ // Will wait for the rest of the 3 seconds
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+ }
+
+ @Test
+ public void testOnReceiveCloseTransactionChain() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ final Props props = ShardTransactionChain.props(store.createTransactionChain());
+ final ActorRef subject = getSystem().actorOf(props, "testCloseTransactionChain");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new CloseTransactionChain(), getRef());
+
+ final String out = new ExpectMsg<String>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected String match(Object in) {
+ if (in instanceof CloseTransactionChainReply) {
+ return "match";
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertEquals("match", out);
+ // Will wait for the rest of the 3 seconds
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.testkit.JavaTestKit;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Test;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransaction;
+import org.opendaylight.controller.cluster.datastore.messages.CloseTransactionReply;
+import org.opendaylight.controller.cluster.datastore.messages.DeleteData;
+import org.opendaylight.controller.cluster.datastore.messages.DeleteDataReply;
+import org.opendaylight.controller.cluster.datastore.messages.MergeData;
+import org.opendaylight.controller.cluster.datastore.messages.MergeDataReply;
+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.messages.WriteDataReply;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+
+import static org.junit.Assert.assertEquals;
+
+public class ShardTransactionTest extends AbstractActorTest {
+ private static ListeningExecutorService storeExecutor = MoreExecutors.listeningDecorator(MoreExecutors.sameThreadExecutor());
+
+ private static final InMemoryDOMDataStore store = new InMemoryDOMDataStore("OPER", storeExecutor);
+
+ static {
+ store.onGlobalContextUpdated(TestModel.createTestContext());
+ }
+
+ @Test
+ public void testOnReceiveReadData() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef subject = getSystem().actorOf(props, "testReadData");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new ReadData(InstanceIdentifier.builder().build()), getRef());
+
+ final String out = new ExpectMsg<String>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected String match(Object in) {
+ if (in instanceof ReadDataReply) {
+ if (((ReadDataReply) in).getNormalizedNode() != null) {
+ return "match";
+ }
+ return null;
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertEquals("match", out);
+
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+ }
+
+ @Test
+ public void testOnReceiveWriteData() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef subject = getSystem().actorOf(props, "testWriteData");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new WriteData(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME)), getRef());
+
+ final String out = new ExpectMsg<String>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected String match(Object in) {
+ if (in instanceof WriteDataReply) {
+ return "match";
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertEquals("match", out);
+
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+ }
+
+ @Test
+ public void testOnReceiveMergeData() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef subject = getSystem().actorOf(props, "testMergeData");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new MergeData(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME)), getRef());
+
+ final String out = new ExpectMsg<String>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected String match(Object in) {
+ if (in instanceof MergeDataReply) {
+ return "match";
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertEquals("match", out);
+
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+ }
+
+ @Test
+ public void testOnReceiveDeleteData() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef subject = getSystem().actorOf(props, "testDeleteData");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new DeleteData(TestModel.TEST_PATH), getRef());
+
+ final String out = new ExpectMsg<String>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected String match(Object in) {
+ if (in instanceof DeleteDataReply) {
+ return "match";
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertEquals("match", out);
+
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+ }
+
+
+ @Test
+ public void testOnReceiveReadyTransaction() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef subject = getSystem().actorOf(props, "testReadyTransaction");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new ReadyTransaction(), getRef());
+
+ final String out = new ExpectMsg<String>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected String match(Object in) {
+ if (in instanceof ReadyTransactionReply) {
+ return "match";
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertEquals("match", out);
+
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+
+ }
+
+ @Test
+ public void testOnReceiveCloseTransaction() throws Exception {
+ new JavaTestKit(getSystem()) {{
+ final Props props = ShardTransaction.props(store.newReadWriteTransaction());
+ final ActorRef subject = getSystem().actorOf(props, "testCloseTransaction");
+
+ new Within(duration("1 seconds")) {
+ protected void run() {
+
+ subject.tell(new CloseTransaction(), getRef());
+
+ final String out = new ExpectMsg<String>("match hint") {
+ // do not put code outside this method, will run afterwards
+ protected String match(Object in) {
+ if (in instanceof CloseTransactionReply) {
+ return "match";
+ } else {
+ throw noMatch();
+ }
+ }
+ }.get(); // this extracts the received message
+
+ assertEquals("match", out);
+
+ expectNoMsg();
+ }
+
+
+ };
+ }};
+
+ }
+
+
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.cluster.datastore.messages;
+
+import org.junit.Test;
+
+public class FindPrimaryTest {
+
+ @Test
+ public void testNewBuilderForType() throws Exception {
+
+ }
+
+ @Test
+ public void testToBuilder() throws Exception {
+
+ }
+
+ @Test
+ public void testGetDefaultInstanceForType() throws Exception {
+
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.cluster.datastore.shardstrategy;
+
+import junit.framework.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+
+public class DefaultShardStrategyTest {
+
+ @Test
+ public void testFindShard() throws Exception {
+ String shard = new DefaultShardStrategy().findShard(TestModel.TEST_PATH);
+ Assert.assertEquals(DefaultShardStrategy.DEFAULT_SHARD, shard);
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.cluster.datastore.shardstrategy;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+
+import static junit.framework.Assert.assertNotNull;
+
+public class ShardStrategyFactoryTest {
+
+ @Rule
+ public ExpectedException expectedEx = ExpectedException.none();
+
+ @Test
+ public void testGetStrategy(){
+ ShardStrategy strategy = ShardStrategyFactory.getStrategy(TestModel.TEST_PATH);
+ assertNotNull(strategy);
+ }
+
+ @Test
+ public void testGetStrategyNullPointerExceptionWhenPathIsNull(){
+ expectedEx.expect(NullPointerException.class);
+ expectedEx.expectMessage("path should not be null");
+
+ ShardStrategyFactory.getStrategy(null);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.cluster.datastore.model;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Set;
+
+public class TestModel {
+
+ public static final QName TEST_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13",
+ "test");
+ public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list");
+ public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list");
+ public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice");
+ public static final QName ID_QNAME = QName.create(TEST_QNAME, "id");
+ public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name");
+ public static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value");
+ private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
+
+ public static final InstanceIdentifier TEST_PATH = InstanceIdentifier.of(TEST_QNAME);
+ public static final InstanceIdentifier OUTER_LIST_PATH = InstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build();
+ public static final QName TWO_QNAME = QName.create(TEST_QNAME,"two");
+ public static final QName THREE_QNAME = QName.create(TEST_QNAME,"three");
+
+
+ public static final InputStream getDatastoreTestInputStream() {
+ return getInputStream(DATASTORE_TEST_YANG);
+ }
+
+ private static InputStream getInputStream(final String resourceName) {
+ return TestModel.class.getResourceAsStream(DATASTORE_TEST_YANG);
+ }
+
+ public static SchemaContext createTestContext() {
+ YangParserImpl parser = new YangParserImpl();
+ Set<Module> modules = parser.parseYangModelsFromStreams(Collections.singletonList(getDatastoreTestInputStream()));
+ return parser.resolveSchemaContext(modules);
+ }
+}
--- /dev/null
+module odl-datastore-test {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test";
+ prefix "store-test";
+
+ revision "2014-03-13" {
+ description "Initial revision.";
+ }
+
+ container test {
+ list outer-list {
+ key id;
+ leaf id {
+ type uint16;
+ }
+ choice outer-choice {
+ case one {
+ leaf one {
+ type string;
+ }
+ }
+ case two-three {
+ leaf two {
+ type string;
+ }
+ leaf three {
+ type string;
+ }
+ }
+ }
+ list inner-list {
+ key name;
+ leaf name {
+ type string;
+ }
+ leaf value {
+ type string;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
- *
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
package org.opendaylight.controller.md.sal.dom.api;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainFactory;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.sal.core.api.BrokerService;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-public interface DOMDataBroker extends AsyncDataBroker<InstanceIdentifier, NormalizedNode<?, ?>, DOMDataChangeListener>, BrokerService {
+/**
+ * Data Broker which provides data transaction and data change listener fuctionality
+ * using {@link NormalizedNode} data format.
+ *
+ * This interface is type capture of generic interfaces and returns type captures
+ * of results for client-code convenience.
+ *
+ */
+public interface DOMDataBroker extends
+ AsyncDataBroker<InstanceIdentifier, NormalizedNode<?, ?>, DOMDataChangeListener>,
+ TransactionChainFactory<InstanceIdentifier, NormalizedNode<?, ?>>, BrokerService {
+
+ /**
+ * {@inheritDoc}
+ */
@Override
DOMDataReadTransaction newReadOnlyTransaction();
+ /**
+ * {@inheritDoc}
+ */
@Override
DOMDataReadWriteTransaction newReadWriteTransaction();
+ /**
+ * {@inheritDoc}
+ */
@Override
DOMDataWriteTransaction newWriteOnlyTransaction();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ DOMTransactionChain createTransactionChain(TransactionChainListener listener);
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.api;
+
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * A chain of DOM Data 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.
+ *
+ * <p>
+ * This interface is type capture of {@link TransactionChain} for DOM Data Contracts.
+ */
+public interface DOMTransactionChain extends TransactionChain<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+ @Override
+ DOMDataReadTransaction newReadOnlyTransaction();
+
+ @Override
+ DOMDataReadWriteTransaction newReadWriteTransaction();
+
+ @Override
+ DOMDataWriteTransaction newWriteOnlyTransaction();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Composite DOM Transaction backed by {@link DOMStoreTransaction}.
+ *
+ * Abstract base for composite transaction, which provides access only to common
+ * functionality as retrieval of subtransaction, close method and retrieval of
+ * identifier.
+ *
+ * @param <K>
+ * Subtransaction distinguisher
+ * @param <T>
+ * Subtransaction type
+ */
+abstract class AbstractDOMForwardedCompositeTransaction<K, T extends DOMStoreTransaction> implements
+ AsyncTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
+
+ private final ImmutableMap<K, T> backingTxs;
+ private final Object identifier;
+
+ /**
+ *
+ * Creates new composite Transactions.
+ *
+ * @param identifier
+ * Identifier of transaction.
+ * @param backingTxs
+ * Key,value map of backing transactions.
+ */
+ protected AbstractDOMForwardedCompositeTransaction(final Object identifier, final ImmutableMap<K, T> backingTxs) {
+ this.identifier = Preconditions.checkNotNull(identifier, "Identifier should not be null");
+ this.backingTxs = Preconditions.checkNotNull(backingTxs, "Backing transactions should not be null");
+ }
+
+ /**
+ * Returns subtransaction associated with supplied key.
+ *
+ * @param key
+ * @return
+ * @throws NullPointerException
+ * if key is null
+ * @throws IllegalArgumentException
+ * if no subtransaction is associated with key.
+ */
+ protected final T getSubtransaction(final K key) {
+ Preconditions.checkNotNull(key, "key must not be null.");
+ Preconditions.checkArgument(backingTxs.containsKey(key), "No subtransaction associated with %s", key);
+ return backingTxs.get(key);
+ }
+
+ /**
+ * Returns immutable Iterable of all subtransactions.
+ *
+ */
+ protected Iterable<T> getSubtransactions() {
+ return backingTxs.values();
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public void close() {
+ /*
+ * We share one exception for all failures, which are added
+ * as supressedExceptions to it.
+ *
+ */
+ IllegalStateException failure = null;
+ for (T subtransaction : backingTxs.values()) {
+ try {
+ subtransaction.close();
+ } catch (Exception e) {
+ // If we did not allocated failure we allocate it
+ if(failure == null) {
+ failure = new IllegalStateException("Uncaught exception occured during closing transaction.", e);
+ } else {
+ // We update it with addotional exceptions, which occured during error.
+ failure.addSuppressed(e);
+ }
+ }
+ }
+ // If we have failure, we throw it at after all attempts to close.
+ if(failure != null) {
+ throw failure;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+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.DOMStoreTransactionFactory;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+/**
+ *
+ * Abstract composite transaction factory.
+ *
+ * Provides an convenience common implementation for composite DOM Transactions,
+ * where subtransaction is identified by {@link LogicalDatastoreType} type and
+ * implementation of subtransaction is provided by
+ * {@link DOMStoreTransactionFactory}.
+ *
+ * <b>Note:</b>This class does not have thread-safe implementation of {@link #close()},
+ * implementation may allow accessing and allocating new transactions during closing
+ * this instance.
+ *
+ * @param <T>
+ * Type of {@link DOMStoreTransactionFactory} factory.
+ */
+public abstract class AbstractDOMForwardedTransactionFactory<T extends DOMStoreTransactionFactory> implements DOMDataCommitImplementation, AutoCloseable {
+
+ private final ImmutableMap<LogicalDatastoreType, T> storeTxFactories;
+
+ private boolean closed;
+
+ protected AbstractDOMForwardedTransactionFactory(final Map<LogicalDatastoreType, ? extends T> txFactories) {
+ this.storeTxFactories = ImmutableMap.copyOf(txFactories);
+ }
+
+ /**
+ * Implementations must return unique identifier for each and every call of
+ * this method;
+ *
+ * @return new Unique transaction identifier.
+ */
+ protected abstract Object newTransactionIdentifier();
+
+ /**
+ * Creates a new composite read-only transaction
+ *
+ * Creates a new composite read-only transaction backed by one transaction
+ * per factory in {@link #getTxFactories()}.
+ *
+ * Subtransaction for reading is selected by supplied
+ * {@link LogicalDatastoreType} as parameter for
+ * {@link DOMDataReadTransaction#read(LogicalDatastoreType,org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)}
+ * .
+ *
+ * Id of returned transaction is retrieved via
+ * {@link #newTransactionIdentifier()}.
+ *
+ * @return New composite read-only transaction.
+ */
+ public DOMDataReadTransaction newReadOnlyTransaction() {
+ checkNotClosed();
+ ImmutableMap.Builder<LogicalDatastoreType, DOMStoreReadTransaction> builder = ImmutableMap.builder();
+ for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+ builder.put(store.getKey(), store.getValue().newReadOnlyTransaction());
+ }
+ return new DOMForwardedReadOnlyTransaction(newTransactionIdentifier(), builder.build());
+ }
+
+
+
+ /**
+ * Creates a new composite write-only transaction
+ *
+ * <p>
+ * Creates a new composite write-only transaction backed by one write-only
+ * transaction per factory in {@link #getTxFactories()}.
+ *
+ * <p>
+ * Implementation of composite Write-only transaction is following:
+ *
+ * <ul>
+ * <li>
+ * {@link DOMDataWriteTransaction#put(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#write(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#merge(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#merge(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#delete(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#delete(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+ * selected subtransaction.
+ * <li> {@link DOMDataWriteTransaction#commit()} - results in invoking
+ * {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts
+ * and then invoking finalized implementation callback
+ * {@link #commit(DOMDataWriteTransaction, Iterable)} with transaction which
+ * was commited and gathered results.
+ * </ul>
+ *
+ * Id of returned transaction is generated via
+ * {@link #newTransactionIdentifier()}.
+ *
+ * @return New composite write-only transaction associated with this
+ * factory.
+ */
+ public DOMDataWriteTransaction newWriteOnlyTransaction() {
+ checkNotClosed();
+ ImmutableMap.Builder<LogicalDatastoreType, DOMStoreWriteTransaction> builder = ImmutableMap.builder();
+ for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+ builder.put(store.getKey(), store.getValue().newWriteOnlyTransaction());
+ }
+ return new DOMForwardedWriteTransaction<DOMStoreWriteTransaction>(newTransactionIdentifier(), builder.build(),
+ this);
+ }
+
+ /**
+ * Creates a new composite write-only transaction
+ *
+ * <p>
+ * Creates a new composite write-only transaction backed by one write-only
+ * transaction per factory in {@link #getTxFactories()}.
+ * <p>
+ * Implementation of composite Write-only transaction is following:
+ *
+ * <ul>
+ * <li>
+ * {@link DOMDataWriteTransaction#read(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#read(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+ * selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#put(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#write(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#merge(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#merge(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier, org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode)}
+ * is invoked on selected subtransaction.
+ * <li>
+ * {@link DOMDataWriteTransaction#delete(LogicalDatastoreType, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)
+ * - backing subtransaction is selected by {@link LogicalDatastoreType},
+ * {@link DOMStoreWriteTransaction#delete(org.opendaylight.yangtools.yang.data.api.InstanceIdentifier)} is invoked on
+ * selected subtransaction.
+ * <li> {@link DOMDataWriteTransaction#commit()} - results in invoking
+ * {@link DOMStoreWriteTransaction#ready()}, gathering all resulting cohorts
+ * and then invoking finalized implementation callback
+ * {@link #commit(DOMDataWriteTransaction, Iterable)} with transaction which
+ * was commited and gathered results.
+ * <li>
+ * </ul>
+ *
+ * Id of returned transaction is generated via
+ * {@link #newTransactionIdentifier()}.
+ *
+ * @return New composite read-write transaction associated with this
+ * factory.
+ *
+ */
+ public DOMDataReadWriteTransaction newReadWriteTransaction() {
+ checkNotClosed();
+ ImmutableMap.Builder<LogicalDatastoreType, DOMStoreReadWriteTransaction> builder = ImmutableMap.builder();
+ for (Entry<LogicalDatastoreType, T> store : storeTxFactories.entrySet()) {
+ builder.put(store.getKey(), store.getValue().newReadWriteTransaction());
+ }
+ return new DOMForwardedReadWriteTransaction(newTransactionIdentifier(), builder.build(), this);
+ }
+
+ /**
+ * Convenience accessor of backing factories intended to be used only by
+ * finalization of this class.
+ *
+ * <b>Note:</b>
+ * Finalization of this class may want to access other functionality of
+ * supplied Transaction factories.
+ *
+ * @return Map of backing transaction factories.
+ */
+ protected final Map<LogicalDatastoreType, T> getTxFactories() {
+ return storeTxFactories;
+ }
+
+ /**
+ *
+ * Checks if instance is not closed.
+ *
+ * @throws IllegalStateException If instance of this class was closed.
+ *
+ */
+ @GuardedBy("this")
+ protected synchronized void checkNotClosed() {
+ Preconditions.checkState(!closed,"Transaction factory was closed. No further operations allowed.");
+ }
+
+ @Override
+ @GuardedBy("this")
+ public synchronized void close() {
+ closed = true;
+ }
+
+}
*/
package org.opendaylight.controller.md.sal.dom.broker.impl;
-import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
-import java.util.Collections;
-import java.util.List;
import java.util.Map.Entry;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
-import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
-import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.controller.sal.core.spi.data.DOMStore;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Function;
import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
-public class DOMDataBrokerImpl implements DOMDataBroker, AutoCloseable {
+public class DOMDataBrokerImpl extends AbstractDOMForwardedTransactionFactory<DOMStore> implements DOMDataBroker,
+ AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(DOMDataBrokerImpl.class);
- private static final Logger COORDINATOR_LOG = LoggerFactory.getLogger(CommitCoordination.class);
- private final ImmutableMap<LogicalDatastoreType, DOMStore> datastores;
- private final ListeningExecutorService executor;
+
+ private final DOMDataCommitCoordinatorImpl coordinator;
private final AtomicLong txNum = new AtomicLong();
+ private final AtomicLong chainNum = new AtomicLong();
public DOMDataBrokerImpl(final ImmutableMap<LogicalDatastoreType, DOMStore> datastores,
final ListeningExecutorService executor) {
- super();
- this.datastores = datastores;
- this.executor = executor;
+ super(datastores);
+ this.coordinator = new DOMDataCommitCoordinatorImpl(executor);
}
- private static final Function<Iterable<Boolean>, Boolean> AND_FUNCTION = new Function<Iterable<Boolean>, Boolean>() {
-
- @Override
- public Boolean apply(final Iterable<Boolean> input) {
-
- for (Boolean value : input) {
- if (value == false) {
- return Boolean.FALSE;
- }
- }
- return Boolean.TRUE;
- }
- };
-
@Override
- public DOMDataReadTransaction newReadOnlyTransaction() {
- ImmutableMap.Builder<LogicalDatastoreType, DOMStoreReadTransaction> builder = ImmutableMap.builder();
- for (Entry<LogicalDatastoreType, DOMStore> store : datastores.entrySet()) {
- builder.put(store.getKey(), store.getValue().newReadOnlyTransaction());
- }
- return new ReadOnlyTransactionImpl(newTransactionIdentifier(), builder.build());
- }
-
- private Object newTransactionIdentifier() {
+ protected Object newTransactionIdentifier() {
return "DOM-" + txNum.getAndIncrement();
}
- @Override
- public DOMDataReadWriteTransaction newReadWriteTransaction() {
- ImmutableMap.Builder<LogicalDatastoreType, DOMStoreReadWriteTransaction> builder = ImmutableMap.builder();
- for (Entry<LogicalDatastoreType, DOMStore> store : datastores.entrySet()) {
- builder.put(store.getKey(), store.getValue().newReadWriteTransaction());
- }
- return new ReadWriteTransactionImpl(newTransactionIdentifier(), builder.build(), this);
- }
-
- @Override
- public DOMDataWriteTransaction newWriteOnlyTransaction() {
- ImmutableMap.Builder<LogicalDatastoreType, DOMStoreWriteTransaction> builder = ImmutableMap.builder();
- for (Entry<LogicalDatastoreType, DOMStore> store : datastores.entrySet()) {
- builder.put(store.getKey(), store.getValue().newWriteOnlyTransaction());
- }
- return new WriteTransactionImpl<DOMStoreWriteTransaction>(newTransactionIdentifier(), builder.build(), this);
- }
-
@Override
public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
final InstanceIdentifier path, final DOMDataChangeListener listener, final DataChangeScope triggeringScope) {
- DOMStore potentialStore = datastores.get(store);
+ DOMStore potentialStore = getTxFactories().get(store);
checkState(potentialStore != null, "Requested logical data store is not available.");
return potentialStore.registerChangeListener(path, listener, triggeringScope);
}
- private ListenableFuture<RpcResult<TransactionStatus>> submit(
- final WriteTransactionImpl<? extends DOMStoreWriteTransaction> transaction) {
- LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier());
- return executor.submit(new CommitCoordination(transaction));
- }
-
- private abstract static class AbstractCompositeTransaction<K, T extends DOMStoreTransaction> implements
- AsyncTransaction<InstanceIdentifier, NormalizedNode<?, ?>> {
-
- private final ImmutableMap<K, T> backingTxs;
- private final Object identifier;
-
- protected AbstractCompositeTransaction(final Object identifier, final ImmutableMap<K, T> backingTxs) {
- this.identifier = checkNotNull(identifier, "Identifier should not be null");
- this.backingTxs = checkNotNull(backingTxs, "Backing transactions should not be null");
- }
-
- protected T getSubtransaction(final K key) {
- return backingTxs.get(key);
- }
-
- public Iterable<T> getSubtransactions() {
- return backingTxs.values();
- }
-
- @Override
- public Object getIdentifier() {
- return identifier;
- }
-
- @Override
- public void close() {
- try {
- for (T subtransaction : backingTxs.values()) {
- subtransaction.close();
- }
- } catch (Exception e) {
- throw new IllegalStateException("Uncaught exception occured during closing transaction.", e);
- }
- }
-
- }
-
- private static class ReadOnlyTransactionImpl extends
- AbstractCompositeTransaction<LogicalDatastoreType, DOMStoreReadTransaction> implements
- DOMDataReadTransaction {
-
- protected ReadOnlyTransactionImpl(final Object identifier,
- final ImmutableMap<LogicalDatastoreType, DOMStoreReadTransaction> backingTxs) {
- super(identifier, backingTxs);
- }
-
- @Override
- public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
- final InstanceIdentifier path) {
- return getSubtransaction(store).read(path);
- }
-
- }
-
- private static class WriteTransactionImpl<T extends DOMStoreWriteTransaction> extends
- AbstractCompositeTransaction<LogicalDatastoreType, T> implements DOMDataWriteTransaction {
-
- private final DOMDataBrokerImpl broker;
- private ImmutableList<DOMStoreThreePhaseCommitCohort> cohorts;
-
- protected WriteTransactionImpl(final Object identifier, final ImmutableMap<LogicalDatastoreType, T> backingTxs,
- final DOMDataBrokerImpl broker) {
- super(identifier, backingTxs);
- this.broker = broker;
- }
-
- public synchronized Iterable<DOMStoreThreePhaseCommitCohort> ready() {
- checkState(cohorts == null, "Transaction was already marked as ready.");
- ImmutableList.Builder<DOMStoreThreePhaseCommitCohort> cohortsBuilder = ImmutableList.builder();
- for (DOMStoreWriteTransaction subTx : getSubtransactions()) {
- cohortsBuilder.add(subTx.ready());
- }
- cohorts = cohortsBuilder.build();
- return cohorts;
- }
-
- protected ImmutableList<DOMStoreThreePhaseCommitCohort> getCohorts() {
- return cohorts;
- }
-
- @Override
- public void put(final LogicalDatastoreType store, final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
- getSubtransaction(store).write(path, data);
- }
-
- @Override
- public void delete(final LogicalDatastoreType store, final InstanceIdentifier path) {
- getSubtransaction(store).delete(path);
- }
-
- @Override
- public void merge(final LogicalDatastoreType store, final InstanceIdentifier path,
- final NormalizedNode<?, ?> data) {
- getSubtransaction(store).merge(path,data);
- }
-
- @Override
- public void cancel() {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public ListenableFuture<RpcResult<TransactionStatus>> commit() {
-
- ready();
- return broker.submit(this);
- }
-
- }
-
- private static class ReadWriteTransactionImpl extends WriteTransactionImpl<DOMStoreReadWriteTransaction> implements
- DOMDataReadWriteTransaction {
-
- protected ReadWriteTransactionImpl(final Object identifier,
- final ImmutableMap<LogicalDatastoreType, DOMStoreReadWriteTransaction> backingTxs,
- final DOMDataBrokerImpl broker) {
- // super(identifier, backingTxs);
- super(identifier, backingTxs, broker);
- }
-
- @Override
- public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
- final InstanceIdentifier path) {
- return getSubtransaction(store).read(path);
- }
- }
-
- private final class CommitCoordination implements Callable<RpcResult<TransactionStatus>> {
-
- private final WriteTransactionImpl<? extends DOMStoreWriteTransaction> transaction;
-
- public CommitCoordination(final WriteTransactionImpl<? extends DOMStoreWriteTransaction> transaction) {
- this.transaction = transaction;
- }
-
- @Override
- public RpcResult<TransactionStatus> call() throws Exception {
-
- try {
- Boolean canCommit = canCommit().get();
-
- if (canCommit) {
- try {
- preCommit().get();
- try {
- commit().get();
- COORDINATOR_LOG.debug("Tx: {} Is commited.", transaction.getIdentifier());
- return Rpcs.getRpcResult(true, TransactionStatus.COMMITED,
- Collections.<RpcError> emptySet());
-
- } catch (InterruptedException | ExecutionException e) {
- COORDINATOR_LOG.error("Tx: {} Error during commit", transaction.getIdentifier(), e);
- }
-
- } catch (InterruptedException | ExecutionException e) {
- COORDINATOR_LOG.warn("Tx: {} Error during preCommit, starting Abort",
- transaction.getIdentifier(), e);
- }
- } else {
- COORDINATOR_LOG.info("Tx: {} Did not pass canCommit phase.", transaction.getIdentifier());
- abort().get();
- }
- } catch (InterruptedException | ExecutionException e) {
- COORDINATOR_LOG.warn("Tx: {} Error during canCommit, starting Abort", transaction.getIdentifier(), e);
-
- }
- try {
- abort().get();
- } catch (InterruptedException | ExecutionException e) {
- COORDINATOR_LOG.error("Tx: {} Error during abort", transaction.getIdentifier(), e);
- }
- return Rpcs.getRpcResult(false, TransactionStatus.FAILED, Collections.<RpcError> emptySet());
- }
-
- public ListenableFuture<Void> preCommit() {
- COORDINATOR_LOG.debug("Transaction {}: PreCommit Started ", transaction.getIdentifier());
- Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
- for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
- ops.add(cohort.preCommit());
- }
- return (ListenableFuture) Futures.allAsList(ops.build());
- }
-
- public ListenableFuture<Void> commit() {
- COORDINATOR_LOG.debug("Transaction {}: Commit Started ", transaction.getIdentifier());
- Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
- for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
- ops.add(cohort.commit());
- }
- return (ListenableFuture) Futures.allAsList(ops.build());
- }
-
- public ListenableFuture<Boolean> canCommit() {
- COORDINATOR_LOG.debug("Transaction {}: CanCommit Started ", transaction.getIdentifier());
- Builder<ListenableFuture<Boolean>> canCommitOperations = ImmutableList.builder();
- for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
- canCommitOperations.add(cohort.canCommit());
- }
- ListenableFuture<List<Boolean>> allCanCommits = Futures.allAsList(canCommitOperations.build());
- return Futures.transform(allCanCommits, AND_FUNCTION);
- }
-
- public ListenableFuture<Void> abort() {
- COORDINATOR_LOG.debug("Transaction {}: Abort Started ", transaction.getIdentifier());
- Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
- for (DOMStoreThreePhaseCommitCohort cohort : transaction.getCohorts()) {
- ops.add(cohort.abort());
- }
- return (ListenableFuture) Futures.allAsList(ops.build());
- };
+ @Override
+ public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+ ImmutableMap.Builder<LogicalDatastoreType, DOMStoreTransactionChain> backingChainsBuilder = ImmutableMap
+ .builder();
+ for (Entry<LogicalDatastoreType, DOMStore> entry : getTxFactories().entrySet()) {
+ backingChainsBuilder.put(entry.getKey(), entry.getValue().createTransactionChain());
+ }
+ long chainId = chainNum.getAndIncrement();
+ ImmutableMap<LogicalDatastoreType, DOMStoreTransactionChain> backingChains = backingChainsBuilder.build();
+ LOG.debug("Transactoin chain {} created with listener {}, backing store chains {}", chainId, listener,
+ backingChains);
+ return new DOMDataBrokerTransactionChainImpl(chainId, backingChains, coordinator, listener);
}
@Override
- public void close() throws Exception {
-
+ public ListenableFuture<RpcResult<TransactionStatus>> commit(final DOMDataWriteTransaction transaction,
+ final Iterable<DOMStoreThreePhaseCommitCohort> cohorts) {
+ LOG.debug("Transaction: {} submitted with cohorts {}.", transaction.getIdentifier(), cohorts);
+ return coordinator.submit(transaction, cohorts, Optional.<DOMDataCommitErrorListener> absent());
}
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * NormalizedNode implementation of {@link org.opendaylight.controller.md.sal.common.api.data.TransactionChain} which is backed
+ * by several {@link DOMStoreTransactionChain} differentiated by provided
+ * {@link org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType} type.
+ *
+ */
+public class DOMDataBrokerTransactionChainImpl extends AbstractDOMForwardedTransactionFactory<DOMStoreTransactionChain>
+ implements DOMTransactionChain, DOMDataCommitErrorListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DOMDataBrokerTransactionChainImpl.class);
+ private final DOMDataCommitExecutor coordinator;
+ private final TransactionChainListener listener;
+ private final long chainId;
+ private final AtomicLong txNum = new AtomicLong();
+ @GuardedBy("this")
+ private boolean failed = false;
+
+ /**
+ *
+ * @param chainId
+ * ID of transaction chain
+ * @param chains
+ * Backing {@link DOMStoreTransactionChain}s.
+ * @param coordinator
+ * Commit Coordinator which should be used to coordinate commits
+ * of transaction
+ * produced by this chain.
+ * @param listener
+ * Listener, which listens on transaction chain events.
+ * @throws NullPointerException
+ * If any of arguments is null.
+ */
+ public DOMDataBrokerTransactionChainImpl(final long chainId,
+ final ImmutableMap<LogicalDatastoreType, DOMStoreTransactionChain> chains,
+ final DOMDataCommitExecutor coordinator, final TransactionChainListener listener) {
+ super(chains);
+ this.chainId = chainId;
+ this.coordinator = Preconditions.checkNotNull(coordinator);
+ this.listener = Preconditions.checkNotNull(listener);
+ }
+
+ @Override
+ protected Object newTransactionIdentifier() {
+ return "DOM-CHAIN-" + chainId + "-" + txNum.getAndIncrement();
+ }
+
+ @Override
+ public synchronized ListenableFuture<RpcResult<TransactionStatus>> commit(
+ final DOMDataWriteTransaction transaction, final Iterable<DOMStoreThreePhaseCommitCohort> cohorts) {
+ return coordinator.submit(transaction, cohorts, Optional.<DOMDataCommitErrorListener> of(this));
+ }
+
+ @Override
+ public synchronized void close() {
+ super.close();
+ for (DOMStoreTransactionChain subChain : getTxFactories().values()) {
+ subChain.close();
+ }
+
+ if (!failed) {
+ LOG.debug("Transaction chain {} successfully finished.", this);
+ listener.onTransactionChainSuccessful(this);
+ }
+ }
+
+ @Override
+ public synchronized void onCommitFailed(final DOMDataWriteTransaction tx, final Throwable cause) {
+ failed = true;
+ LOG.debug("Transaction chain {} failed.", this, cause);
+ listener.onTransactionChainFailed(this, tx, cause);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+/**
+ *
+ * Implementation of blocking three phase commit coordinator, which which
+ * supports coordination on multiple {@link DOMStoreThreePhaseCommitCohort}.
+ *
+ * This implementation does not support cancelation of commit,
+ *
+ * In order to advance to next phase of three phase commit all subtasks of
+ * previous step must be finish.
+ *
+ * This executor does not have an upper bound on subtask timeout.
+ *
+ *
+ */
+public class DOMDataCommitCoordinatorImpl implements DOMDataCommitExecutor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DOMDataCommitCoordinatorImpl.class);
+
+ /**
+ * Runs AND binary operation between all booleans in supplied iteration of booleans.
+ *
+ * This method will stop evaluating iterables if first found is false.
+ */
+ private static final Function<Iterable<Boolean>, Boolean> AND_FUNCTION = new Function<Iterable<Boolean>, Boolean>() {
+
+ @Override
+ public Boolean apply(final Iterable<Boolean> input) {
+ for(boolean value : input) {
+ if(!value) {
+ return Boolean.FALSE;
+ }
+ }
+ return Boolean.TRUE;
+ }
+ };
+
+ private final ListeningExecutorService executor;
+
+ /**
+ *
+ * Construct DOMDataCommitCoordinator which uses supplied executor to
+ * process commit coordinations.
+ *
+ * @param executor
+ */
+ public DOMDataCommitCoordinatorImpl(final ListeningExecutorService executor) {
+ this.executor = Preconditions.checkNotNull(executor, "executor must not be null.");
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> submit(final DOMDataWriteTransaction transaction,
+ final Iterable<DOMStoreThreePhaseCommitCohort> cohorts, final Optional<DOMDataCommitErrorListener> listener) {
+ Preconditions.checkArgument(transaction != null, "Transaction must not be null.");
+ Preconditions.checkArgument(cohorts != null, "Cohorts must not be null.");
+ Preconditions.checkArgument(listener != null, "Listener must not be null");
+ LOG.debug("Tx: {} is submitted for execution.", transaction.getIdentifier());
+ ListenableFuture<RpcResult<TransactionStatus>> commitFuture = executor.submit(new CommitCoordinationTask(
+ transaction, cohorts, listener));
+ if (listener.isPresent()) {
+ Futures.addCallback(commitFuture, new DOMDataCommitErrorInvoker(transaction, listener.get()));
+ }
+ return commitFuture;
+ }
+
+ /**
+ *
+ * Phase of 3PC commit
+ *
+ * Represents phase of 3PC Commit
+ *
+ *
+ */
+ private static enum CommitPhase {
+ /**
+ *
+ * Commit Coordination Task is submitted for executing
+ *
+ */
+ SUBMITTED,
+ /**
+ * Commit Coordination Task is in can commit phase of 3PC
+ *
+ */
+ CAN_COMMIT,
+ /**
+ * Commit Coordination Task is in pre-commit phase of 3PC
+ *
+ */
+ PRE_COMMIT,
+ /**
+ * Commit Coordination Task is in commit phase of 3PC
+ *
+ */
+ COMMIT,
+ /**
+ * Commit Coordination Task is in abort phase of 3PC
+ *
+ */
+ ABORT
+ }
+
+ /**
+ *
+ * Implementation of blocking three-phase commit-coordination tasks without
+ * support of cancelation.
+ *
+ */
+ private static class CommitCoordinationTask implements Callable<RpcResult<TransactionStatus>> {
+
+ private final DOMDataWriteTransaction tx;
+ private final Iterable<DOMStoreThreePhaseCommitCohort> cohorts;
+
+ @GuardedBy("this")
+ private CommitPhase currentPhase;
+
+ public CommitCoordinationTask(final DOMDataWriteTransaction transaction,
+ final Iterable<DOMStoreThreePhaseCommitCohort> cohorts,
+ final Optional<DOMDataCommitErrorListener> listener) {
+ this.tx = Preconditions.checkNotNull(transaction, "transaction must not be null");
+ this.cohorts = Preconditions.checkNotNull(cohorts, "cohorts must not be null");
+ this.currentPhase = CommitPhase.SUBMITTED;
+ }
+
+ @Override
+ public RpcResult<TransactionStatus> call() throws TransactionCommitFailedException {
+
+ try {
+ canCommitBlocking();
+ preCommitBlocking();
+ return commitBlocking();
+ } catch (TransactionCommitFailedException e) {
+ LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), currentPhase, e);
+ abortBlocking(e);
+ throw e;
+ }
+ }
+
+ /**
+ *
+ * Invokes canCommit on underlying cohorts and blocks till
+ * all results are returned.
+ *
+ * Valid state transition is from SUBMITTED to CAN_COMMIT,
+ * if currentPhase is not SUBMITTED throws IllegalStateException.
+ *
+ * @throws TransactionCommitFailedException
+ * If one of cohorts failed can Commit
+ *
+ */
+ private void canCommitBlocking() throws TransactionCommitFailedException {
+ final Boolean canCommitResult = canCommitAll().checkedGet();
+ if (!canCommitResult) {
+ throw new TransactionCommitFailedException("Can Commit failed, no detailed cause available.");
+ }
+ }
+
+ /**
+ *
+ * Invokes preCommit on underlying cohorts and blocks till
+ * all results are returned.
+ *
+ * Valid state transition is from CAN_COMMIT to PRE_COMMIT, if current
+ * state is not CAN_COMMIT
+ * throws IllegalStateException.
+ *
+ * @throws TransactionCommitFailedException
+ * If one of cohorts failed preCommit
+ *
+ */
+ private void preCommitBlocking() throws TransactionCommitFailedException {
+ preCommitAll().checkedGet();
+ }
+
+ /**
+ *
+ * Invokes commit on underlying cohorts and blocks till
+ * all results are returned.
+ *
+ * Valid state transition is from PRE_COMMIT to COMMIT, if not throws
+ * IllegalStateException.
+ *
+ * @throws TransactionCommitFailedException
+ * If one of cohorts failed preCommit
+ *
+ */
+ private RpcResult<TransactionStatus> commitBlocking() throws TransactionCommitFailedException {
+ commitAll().checkedGet();
+ return Rpcs.getRpcResult(true, TransactionStatus.COMMITED, Collections.<RpcError> emptySet());
+ }
+
+ /**
+ * Aborts transaction.
+ *
+ * Invokes {@link DOMStoreThreePhaseCommitCohort#abort()} on all
+ * cohorts, blocks
+ * for all results. If any of the abort failed throws
+ * IllegalStateException,
+ * which will contains originalCause as suppressed Exception.
+ *
+ * If aborts we're successful throws supplied exception
+ *
+ * @param originalCause
+ * Exception which should be used to fail transaction for
+ * consumers of transaction
+ * future and listeners of transaction failure.
+ * @throws TransactionCommitFailedException
+ * on invocation of this method.
+ * originalCa
+ * @throws IllegalStateException
+ * if abort failed.
+ */
+ private void abortBlocking(final TransactionCommitFailedException originalCause)
+ throws TransactionCommitFailedException {
+ LOG.warn("Tx: {} Error during phase {}, starting Abort", tx.getIdentifier(), currentPhase, originalCause);
+ Exception cause = originalCause;
+ try {
+ abortAsyncAll().get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error("Tx: {} Error during Abort.", tx.getIdentifier(), e);
+ cause = new IllegalStateException("Abort failed.", e);
+ cause.addSuppressed(e);
+ }
+ Throwables.propagateIfPossible(cause, TransactionCommitFailedException.class);
+ }
+
+ /**
+ *
+ * Invokes preCommit on underlying cohorts and returns future
+ * which will complete once all preCommit on cohorts completed or
+ * failed.
+ *
+ *
+ * Valid state transition is from CAN_COMMIT to PRE_COMMIT, if current
+ * state is not CAN_COMMIT
+ * throws IllegalStateException.
+ *
+ * @return Future which will complete once all cohorts completed
+ * preCommit.
+ * Future throws TransactionCommitFailedException
+ * If any of cohorts failed preCommit
+ *
+ */
+ private CheckedFuture<Void, TransactionCommitFailedException> preCommitAll() {
+ changeStateFrom(CommitPhase.CAN_COMMIT, CommitPhase.PRE_COMMIT);
+ Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ ops.add(cohort.preCommit());
+ }
+ /*
+ * We are returing all futures as list, not only succeeded ones in
+ * order to fail composite future if any of them failed.
+ * See Futures.allAsList for this description.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ ListenableFuture<Void> compositeResult = (ListenableFuture) Futures.allAsList(ops.build());
+ return Futures.makeChecked(compositeResult, TransactionCommitFailedExceptionMapper.PRE_COMMIT_MAPPER);
+ }
+
+ /**
+ *
+ * Invokes commit on underlying cohorts and returns future which
+ * completes
+ * once all commits on cohorts are completed.
+ *
+ * Valid state transition is from PRE_COMMIT to COMMIT, if not throws
+ * IllegalStateException
+ *
+ * @return Future which will complete once all cohorts completed
+ * commit.
+ * Future throws TransactionCommitFailedException
+ * If any of cohorts failed preCommit
+ *
+ */
+ private CheckedFuture<Void, TransactionCommitFailedException> commitAll() {
+ changeStateFrom(CommitPhase.PRE_COMMIT, CommitPhase.COMMIT);
+ Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ ops.add(cohort.commit());
+ }
+ /*
+ * We are returing all futures as list, not only succeeded ones in
+ * order to fail composite future if any of them failed.
+ * See Futures.allAsList for this description.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ ListenableFuture<Void> compositeResult = (ListenableFuture) Futures.allAsList(ops.build());
+ return Futures.makeChecked(compositeResult, TransactionCommitFailedExceptionMapper.COMMIT_ERROR_MAPPER);
+ }
+
+ /**
+ *
+ * Invokes canCommit on underlying cohorts and returns composite future
+ * which will contains {@link Boolean#TRUE} only and only if
+ * all cohorts returned true.
+ *
+ * Valid state transition is from SUBMITTED to CAN_COMMIT,
+ * if currentPhase is not SUBMITTED throws IllegalStateException.
+ *
+ * @return Future which will complete once all cohorts completed
+ * preCommit.
+ * Future throws TransactionCommitFailedException
+ * If any of cohorts failed preCommit
+ *
+ */
+ private CheckedFuture<Boolean, TransactionCommitFailedException> canCommitAll() {
+ changeStateFrom(CommitPhase.SUBMITTED, CommitPhase.CAN_COMMIT);
+ Builder<ListenableFuture<Boolean>> canCommitOperations = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ canCommitOperations.add(cohort.canCommit());
+ }
+ ListenableFuture<List<Boolean>> allCanCommits = Futures.allAsList(canCommitOperations.build());
+ ListenableFuture<Boolean> allSuccessFuture = Futures.transform(allCanCommits, AND_FUNCTION);
+ return Futures
+ .makeChecked(allSuccessFuture, TransactionCommitFailedExceptionMapper.CAN_COMMIT_ERROR_MAPPER);
+
+ }
+
+ /**
+ *
+ * Invokes abort on underlying cohorts and returns future which
+ * completes
+ * once all abort on cohorts are completed.
+ *
+ * @return Future which will complete once all cohorts completed
+ * abort.
+ *
+ */
+ private ListenableFuture<Void> abortAsyncAll() {
+ changeStateFrom(currentPhase, CommitPhase.ABORT);
+ Builder<ListenableFuture<Void>> ops = ImmutableList.builder();
+ for (DOMStoreThreePhaseCommitCohort cohort : cohorts) {
+ ops.add(cohort.abort());
+ }
+ /*
+ * We are returing all futures as list, not only succeeded ones in
+ * order to fail composite future if any of them failed.
+ * See Futures.allAsList for this description.
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ ListenableFuture<Void> compositeResult = (ListenableFuture) Futures.allAsList(ops.build());
+ return compositeResult;
+ }
+
+ /**
+ * Change phase / state of transaction from expected value to new value
+ *
+ * This method checks state and updates state to new state of
+ * of this task if current state equals expected state.
+ * If expected state and current state are different raises
+ * IllegalStateException
+ * which means there is probably bug in implementation of commit
+ * coordination.
+ *
+ * If transition is successful, it logs transition on DEBUG level.
+ *
+ * @param currentExpected
+ * Required phase for change of state
+ * @param newState
+ * New Phase which will be entered by transaction.
+ * @throws IllegalStateException
+ * If currentState of task does not match expected state
+ */
+ private synchronized void changeStateFrom(final CommitPhase currentExpected, final CommitPhase newState) {
+ Preconditions.checkState(currentPhase.equals(currentExpected),
+ "Invalid state transition: Tx: %s current state: %s new state: %s", tx.getIdentifier(),
+ currentPhase, newState);
+ LOG.debug("Transaction {}: Phase {} Started ", tx.getIdentifier(), newState);
+ currentPhase = newState;
+ };
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+
+/**
+ *
+ * Utility implemetation of {@link FutureCallback} which is responsible
+ * for invoking {@link DOMDataCommitErrorListener} on TransactionCommit failed.
+ *
+ * When {@link #onFailure(Throwable)} is invoked, supplied {@link DOMDataCommitErrorListener}
+ * callback is invoked with associated transaction and throwable is invoked on listener.
+ *
+ */
+class DOMDataCommitErrorInvoker implements FutureCallback<RpcResult<TransactionStatus>> {
+
+ private final DOMDataWriteTransaction tx;
+ private final DOMDataCommitErrorListener listener;
+
+
+ /**
+ *
+ * Construct new DOMDataCommitErrorInvoker.
+ *
+ * @param transaction Transaction which should be passed as argument to {@link DOMDataCommitErrorListener#onCommitFailed(DOMDataWriteTransaction, Throwable)}
+ * @param listener Listener which should be invoked on error.
+ */
+ public DOMDataCommitErrorInvoker(DOMDataWriteTransaction transaction, DOMDataCommitErrorListener listener) {
+ this.tx = Preconditions.checkNotNull(transaction, "Transaction must not be null");
+ this.listener = Preconditions.checkNotNull(listener, "Listener must not be null");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ listener.onCommitFailed(tx, t);
+ }
+
+ @Override
+ public void onSuccess(RpcResult<TransactionStatus> result) {
+ // NOOP
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import java.util.EventListener;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+
+/**
+ *
+ * Listener on transaction failure which may be passed to
+ * {@link DOMDataCommitExecutor}. This listener is notified during transaction
+ * processing, before result is delivered to other client code outside MD-SAL.
+ * This allows implementors to update their internal state before transaction
+ * failure is visible to client code.
+ *
+ * This is internal API for MD-SAL implementations, for consumer facing error
+ * listeners see {@link org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener}.
+ *
+ */
+interface DOMDataCommitErrorListener extends EventListener {
+
+ /**
+ *
+ * Callback which is invoked on transaction failure during three phase
+ * commit in {@link DOMDataCommitExecutor}.
+ *
+ *
+ * Implementation of this callback MUST NOT do any blocking calls or any
+ * calls to MD-SAL, since this callback is invoked synchronously on MD-SAL
+ * Broker coordination thread.
+ *
+ * @param tx
+ * Transaction which failed
+ * @param cause
+ * Failure reason
+ */
+ void onCommitFailed(DOMDataWriteTransaction tx, Throwable cause);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Executor of Three Phase Commit coordination for
+ * {@link DOMDataWriteTransaction} transactions.
+ *
+ * Implementations are responsible for executing implementation of three-phase
+ * commit protocol on supplied {@link DOMStoreThreePhaseCommitCohort}s.
+ *
+ *
+ */
+interface DOMDataCommitExecutor {
+
+ /**
+ * Submits supplied transaction to be executed in context of provided
+ * cohorts.
+ *
+ * Transaction is used only as a context, cohorts should be associated with
+ * this transaction.
+ *
+ * @param tx
+ * Transaction to be used as context for reporting
+ * @param cohort
+ * DOM Store cohorts representing provided transaction, its
+ * subtransactoins.
+ * @param listener
+ * Error listener which should be notified if transaction failed.
+ * @return ListenableFuture which contains RpcResult with
+ * {@link TransactionStatus#COMMITED} if commit coordination on
+ * cohorts finished successfully.
+ *
+ */
+ ListenableFuture<RpcResult<TransactionStatus>> submit(DOMDataWriteTransaction tx,
+ Iterable<DOMStoreThreePhaseCommitCohort> cohort, Optional<DOMDataCommitErrorListener> listener);
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * Implementation prototype of commit method for
+ * {@link DOMForwardedWriteTransaction}.
+ *
+ */
+public interface DOMDataCommitImplementation {
+
+ /**
+ * User-supplied implementation of {@link DOMDataWriteTransaction#commit()}
+ * for transaction.
+ *
+ * Callback invoked when {@link DOMDataWriteTransaction#commit()} is invoked
+ * on transaction created by this factory.
+ *
+ * @param transaction
+ * Transaction on which {@link DOMDataWriteTransaction#commit()}
+ * was invoked.
+ * @param cohorts
+ * Iteration of cohorts for subtransactions associated with
+ * commited transaction.
+ *
+ */
+ ListenableFuture<RpcResult<TransactionStatus>> commit(final DOMDataWriteTransaction transaction,
+ final Iterable<DOMStoreThreePhaseCommitCohort> cohorts);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * Read Only Transaction, which is composed of several
+ * {@link DOMStoreReadTransaction} transactions. Subtransaction is selected by
+ * {@link LogicalDatastoreType} type parameter in
+ * {@link #read(LogicalDatastoreType, InstanceIdentifier)}.
+ */
+class DOMForwardedReadOnlyTransaction extends
+ AbstractDOMForwardedCompositeTransaction<LogicalDatastoreType, DOMStoreReadTransaction> implements
+ DOMDataReadTransaction {
+
+ protected DOMForwardedReadOnlyTransaction(final Object identifier,
+ final ImmutableMap<LogicalDatastoreType, DOMStoreReadTransaction> backingTxs) {
+ super(identifier, backingTxs);
+ }
+
+ @Override
+ public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
+ final InstanceIdentifier path) {
+ return getSubtransaction(store).read(path);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * Read-Write Transaction, which is composed of several
+ * {@link DOMStoreReadWriteTransaction} transactions. Subtransaction is selected by
+ * {@link LogicalDatastoreType} type parameter in:
+ *
+ * <ul>
+ * <li>{@link #read(LogicalDatastoreType, InstanceIdentifier)}
+ * <li>{@link #put(LogicalDatastoreType, InstanceIdentifier, NormalizedNode)}
+ * <li>{@link #delete(LogicalDatastoreType, InstanceIdentifier)}
+ * <li>{@link #merge(LogicalDatastoreType, InstanceIdentifier, NormalizedNode)}
+ * </ul>
+ * {@link #commit()} will result in invocation of
+ * {@link DOMDataCommitImplementation#commit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
+ * invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying
+ * transactions.
+ *
+ */
+
+class DOMForwardedReadWriteTransaction extends DOMForwardedWriteTransaction<DOMStoreReadWriteTransaction> implements
+ DOMDataReadWriteTransaction {
+
+ protected DOMForwardedReadWriteTransaction(final Object identifier,
+ final ImmutableMap<LogicalDatastoreType, DOMStoreReadWriteTransaction> backingTxs,
+ final DOMDataCommitImplementation commitImpl) {
+ super(identifier, backingTxs, commitImpl);
+ }
+
+ @Override
+ public ListenableFuture<Optional<NormalizedNode<?, ?>>> read(final LogicalDatastoreType store,
+ final InstanceIdentifier path) {
+ return getSubtransaction(store).read(path);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import javax.annotation.concurrent.GuardedBy;
+
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ *
+ * Read-Write Transaction, which is composed of several
+ * {@link DOMStoreWriteTransaction} transactions. Subtransaction is selected by
+ * {@link LogicalDatastoreType} type parameter in:
+ *
+ * <ul>
+ * <li>{@link #put(LogicalDatastoreType, InstanceIdentifier, NormalizedNode)}
+ * <li>{@link #delete(LogicalDatastoreType, InstanceIdentifier)}
+ * <li>{@link #merge(LogicalDatastoreType, InstanceIdentifier, NormalizedNode)}
+ * </ul>
+ * <p>
+ * {@link #commit()} will result in invocation of
+ * {@link DOMDataCommitImplementation#commit(org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction, Iterable)}
+ * invocation with all {@link org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort} for underlying
+ * transactions.
+ *
+ * @param <T>
+ * Subtype of {@link DOMStoreWriteTransaction} which is used as
+ * subtransaction.
+ */
+class DOMForwardedWriteTransaction<T extends DOMStoreWriteTransaction> extends
+ AbstractDOMForwardedCompositeTransaction<LogicalDatastoreType, T> implements DOMDataWriteTransaction {
+
+ @GuardedBy("this")
+ private DOMDataCommitImplementation commitImpl;
+
+ @GuardedBy("this")
+ private boolean canceled;
+ @GuardedBy("this")
+ private ListenableFuture<RpcResult<TransactionStatus>> commitFuture;
+
+ protected DOMForwardedWriteTransaction(final Object identifier,
+ final ImmutableMap<LogicalDatastoreType, T> backingTxs, final DOMDataCommitImplementation commitImpl) {
+ super(identifier, backingTxs);
+ this.commitImpl = Preconditions.checkNotNull(commitImpl, "commitImpl must not be null.");
+ }
+
+ @Override
+ public void put(final LogicalDatastoreType store, final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkNotReady();
+ getSubtransaction(store).write(path, data);
+ }
+
+ @Override
+ public void delete(final LogicalDatastoreType store, final InstanceIdentifier path) {
+ checkNotReady();
+ getSubtransaction(store).delete(path);
+ }
+
+ @Override
+ public void merge(final LogicalDatastoreType store, final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ checkNotReady();
+ getSubtransaction(store).merge(path, data);
+ }
+
+ @Override
+ public synchronized void cancel() {
+ checkState(!canceled, "Transaction was canceled.");
+ if (commitFuture != null) {
+ // FIXME: Implement cancelation of commit future
+ // when Broker impl will support cancelation.
+ throw new UnsupportedOperationException("Not implemented yet.");
+ }
+ canceled = true;
+ commitImpl = null;
+
+ }
+
+ @Override
+ public synchronized ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ checkNotReady();
+
+ ImmutableList.Builder<DOMStoreThreePhaseCommitCohort> cohortsBuilder = ImmutableList.builder();
+ for (DOMStoreWriteTransaction subTx : getSubtransactions()) {
+ cohortsBuilder.add(subTx.ready());
+ }
+ ImmutableList<DOMStoreThreePhaseCommitCohort> cohorts = cohortsBuilder.build();
+ commitFuture = commitImpl.commit(this, cohorts);
+ return commitFuture;
+ }
+
+ private void checkNotReady() {
+ checkNotCanceled();
+ checkNotCommited();
+ }
+
+ private void checkNotCanceled() {
+ Preconditions.checkState(!canceled, "Transaction was canceled.");
+ }
+
+ private void checkNotCommited() {
+ checkState(commitFuture == null, "Transaction was already commited.");
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+
+/**
+ *
+ * Utility exception mapper which translates {@link Exception}
+ * to {@link TransactionCommitFailedException}.
+ *
+ * This mapper is intended to be used with {@link com.google.common.util.concurrent.Futures#makeChecked(com.google.common.util.concurrent.ListenableFuture, Function)}
+ * <ul>
+ * <li>if exception is {@link TransactionCommitFailedException} or one of its subclasses returns original exception.
+ * <li>if exception is {@link ExecutionException} and cause is {@link TransactionCommitFailedException} return cause
+ * <li>otherwise returns {@link TransactionCommitFailedException} with original exception as a cause.
+ * </ul>
+ *
+ */
+
+final class TransactionCommitFailedExceptionMapper implements
+ Function<Exception, TransactionCommitFailedException> {
+
+ static final TransactionCommitFailedExceptionMapper PRE_COMMIT_MAPPER = create("canCommit");
+
+ static final TransactionCommitFailedExceptionMapper CAN_COMMIT_ERROR_MAPPER = create("preCommit");
+
+ static final TransactionCommitFailedExceptionMapper COMMIT_ERROR_MAPPER = create("commit");
+
+ private final String opName;
+
+ private TransactionCommitFailedExceptionMapper(final String opName) {
+ this.opName = Preconditions.checkNotNull(opName);
+ }
+
+ public static final TransactionCommitFailedExceptionMapper create(final String opName) {
+ return new TransactionCommitFailedExceptionMapper(opName);
+ }
+
+ @Override
+ public TransactionCommitFailedException apply(final Exception e) {
+ // If excetion is TransactionCommitFailedException
+ // we reuse it directly.
+ if (e instanceof TransactionCommitFailedException) {
+ return (TransactionCommitFailedException) e;
+ }
+ // If error is ExecutionException which was caused by cause of
+ // TransactionCommitFailedException
+ // we reuse original cause
+ if (e instanceof ExecutionException && e.getCause() instanceof TransactionCommitFailedException) {
+ return (TransactionCommitFailedException) e.getCause();
+ }
+ if (e instanceof InterruptedException) {
+ return new TransactionCommitFailedException(opName + " failed - DOMStore was interupted.", e);
+ }
+ // Otherwise we are using new exception, with original cause
+ return new TransactionCommitFailedException(opName + " failed", e);
+ }
+}
\ No newline at end of file
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ConflictingModificationAppliedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTree;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeCandidate;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeModification;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeSnapshot;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ListenerTree;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.InMemoryDataTreeFactory;
import org.opendaylight.controller.sal.core.spi.data.DOMStore;
public ListenableFuture<Boolean> canCommit() {
return executor.submit(new Callable<Boolean>() {
@Override
- public Boolean call() {
+ public Boolean call() throws TransactionCommitFailedException {
try {
dataTree.validate(modification);
LOG.debug("Store Transaction: {} can be committed", transaction.getIdentifier());
return true;
- } catch (DataPreconditionFailedException e) {
+ } catch (ConflictingModificationAppliedException e) {
+ LOG.warn("Store Tx: {} Conflicting modification for {}.", transaction.getIdentifier(),
+ e.getPath());
+ throw new OptimisticLockFailedException("Optimistic lock failed.",e);
+ } catch (DataValidationFailedException e) {
LOG.warn("Store Tx: {} Data Precondition failed for {}.", transaction.getIdentifier(),
e.getPath(), e);
- return false;
+ throw new TransactionCommitFailedException("Data did not pass validation.",e);
}
}
});
NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>> afterCont = (NormalizedNodeContainer<?, PathArgument, NormalizedNode<PathArgument, ?>>) afterData;
return resolveNodeContainerReplaced(path, listeners, beforeCont, afterCont);
} else if (!beforeData.equals(afterData)) {
- // Node is either of Leaf type (does not contain child nodes)
- // or we do not have listeners, so normal equals method is
- // sufficient for determining change.
+ // 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();
eventBuilder.merge(resolveSameEventRecursivelly(path.node(childId), childListeners, child, eventFactory));
}
propagateEvent = eventBuilder.build();
- } else {
- // We do not dispatch leaf events since Binding Aware components do not support them.
- propagateEvent = builder(DataChangeScope.BASE).build();
}
if (!listeners.isEmpty()) {
addPartialTask(listeners, propagateEvent);
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+/**
+ * Exception thrown when a proposed change fails validation before being
+ * applied into the Data Tree because the Data Tree has been modified
+ * in way that a conflicting
+ * node is present.
+ */
+public class ConflictingModificationAppliedException extends DataValidationFailedException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public ConflictingModificationAppliedException(final InstanceIdentifier path, final String message, final Throwable cause) {
+ super(path, message, cause);
+ }
+
+ public ConflictingModificationAppliedException(final InstanceIdentifier path, final String message) {
+ super(path, message);
+ }
+
+}
/**
* Validate whether a particular modification can be applied to the data tree.
*/
- void validate(DataTreeModification modification) throws DataPreconditionFailedException;
+ void validate(DataTreeModification modification) throws DataValidationFailedException;
/**
* Prepare a modification for commit.
* the datastore has been concurrently modified such that a conflicting
* node is present, or the modification is structurally incorrect.
*/
-public class DataPreconditionFailedException extends Exception {
+public class DataValidationFailedException extends Exception {
private static final long serialVersionUID = 1L;
private final InstanceIdentifier path;
* @param path Object path which caused this exception
* @param message Specific message describing the failure
*/
- public DataPreconditionFailedException(final InstanceIdentifier path, final String message) {
+ public DataValidationFailedException(final InstanceIdentifier path, final String message) {
this(path, message, null);
}
/**
* @param message Specific message describing the failure
* @param cause Exception which triggered this failure, may be null
*/
- public DataPreconditionFailedException(final InstanceIdentifier path, final String message, final Throwable cause) {
+ public DataValidationFailedException(final InstanceIdentifier path, final String message, final Throwable cause) {
super(message, cause);
this.path = Preconditions.checkNotNull(path);
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.store.impl.tree;
+
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+
+/**
+ * Exception thrown when a proposed change fails validation before being
+ * applied into the datastore because of incorrect structure of user supplied
+ * data.
+ *
+ */
+public class IncorrectDataStructureException extends DataValidationFailedException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public IncorrectDataStructureException(final InstanceIdentifier path, final String message, final Throwable cause) {
+ super(path, message, cause);
+ }
+
+ public IncorrectDataStructureException(final InstanceIdentifier path, final String message) {
+ super(path, message);
+ }
+
+}
*/
package org.opendaylight.controller.md.sal.dom.store.impl.tree.data;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTree;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeCandidate;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataTreeModification;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreUtils;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Read-only snapshot of the data tree.
}
@Override
- public void validate(final DataTreeModification modification) throws DataPreconditionFailedException {
+ public void validate(final DataTreeModification modification) throws DataValidationFailedException {
Preconditions.checkArgument(modification instanceof InMemoryDataTreeModification, "Invalid modification class %s", modification.getClass());
final InMemoryDataTreeModification m = (InMemoryDataTreeModification)modification;
*/
package org.opendaylight.controller.md.sal.dom.store.impl.tree.data;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreTreeNode;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.Version;
* @param current Metadata Node to which modification should be applied
* @return true if modification is applicable
* false if modification is no applicable
- * @throws DataPreconditionFailedException
+ * @throws DataValidationFailedException
*/
- void checkApplicable(InstanceIdentifier path, NodeModification modification, Optional<TreeNode> current) throws DataPreconditionFailedException;
+ void checkApplicable(InstanceIdentifier path, NodeModification modification, Optional<TreeNode> current) throws DataValidationFailedException;
}
import java.util.Map;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.ListEntryModificationStrategy;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.ValueNodeModificationStrategy.LeafSetEntryModificationStrategy;
@Override
protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ final Optional<TreeNode> current) throws DataValidationFailedException {
// FIXME: Implement proper write check for replacement of node container
// prerequisite is to have transaction chain available for clients
// otherwise this will break chained writes to same node.
@Override
protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
- checkDataPrecondition(path, current.isPresent(), "Node was deleted by other transaction.");
+ final Optional<TreeNode> current) throws DataValidationFailedException {
+ checkConflicting(path, current.isPresent(), "Node was deleted by other transaction.");
checkChildPreconditions(path, modification, current);
}
- private void checkChildPreconditions(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ private void checkChildPreconditions(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
final TreeNode currentMeta = current.get();
for (NodeModification childMod : modification.getChildren()) {
final PathArgument childId = childMod.getIdentifier();
@Override
protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ final Optional<TreeNode> current) throws DataValidationFailedException {
if(current.isPresent()) {
checkChildPreconditions(path, modification,current);
}
import java.util.List;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.ConflictingModificationAppliedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataValidationFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.IncorrectDataStructureException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.ContainerModificationStrategy;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.data.DataNodeContainerModificationStrategy.UnkeyedListItemModificationStrategy;
return null;
}
- public static boolean checkDataPrecondition(final InstanceIdentifier path, final boolean condition, final String message) throws DataPreconditionFailedException {
+ public static boolean checkConflicting(final InstanceIdentifier path, final boolean condition, final String message) throws ConflictingModificationAppliedException {
if(!condition) {
- throw new DataPreconditionFailedException(path, message);
+ throw new ConflictingModificationAppliedException(path, message);
}
return condition;
}
}
}
- private static final void checkNotConflicting(final InstanceIdentifier path, final TreeNode original, final TreeNode current) throws DataPreconditionFailedException {
- checkDataPrecondition(path, original.getVersion().equals(current.getVersion()),
+ private static final void checkNotConflicting(final InstanceIdentifier path, final TreeNode original, final TreeNode current) throws ConflictingModificationAppliedException {
+ checkConflicting(path, original.getVersion().equals(current.getVersion()),
"Node was replaced by other transaction.");
- checkDataPrecondition(path, original.getSubtreeVersion().equals(current.getSubtreeVersion()),
+ checkConflicting(path, original.getSubtreeVersion().equals(current.getSubtreeVersion()),
"Node children was modified by other transaction");
}
}
@Override
- public final void checkApplicable(final InstanceIdentifier path,final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ public final void checkApplicable(final InstanceIdentifier path,final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
switch (modification.getType()) {
case DELETE:
checkDeleteApplicable(modification, current);
}
- protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ protected void checkMergeApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
Optional<TreeNode> original = modification.getOriginal();
if (original.isPresent() && current.isPresent()) {
/*
}
}
- protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataPreconditionFailedException {
+ protected void checkWriteApplicable(final InstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
Optional<TreeNode> original = modification.getOriginal();
if (original.isPresent() && current.isPresent()) {
checkNotConflicting(path, original.get(), current.get());
} else if(original.isPresent()) {
- throw new DataPreconditionFailedException(path,"Node was deleted by other transaction.");
+ throw new ConflictingModificationAppliedException(path,"Node was deleted by other transaction.");
}
}
protected abstract TreeNode applySubtreeChange(ModifiedNode modification,
TreeNode currentMeta, Version version);
+ /**
+ *
+ * Checks is supplied {@link NodeModification} is applicable for Subtree Modification.
+ *
+ * @param path Path to current node
+ * @param modification Node modification which should be applied.
+ * @param current Current state of data tree
+ * @throws ConflictingModificationAppliedException If subtree was changed in conflicting way
+ * @throws IncorrectDataStructureException If subtree modification is not applicable (e.g. leaf node).
+ */
protected abstract void checkSubtreeModificationApplicable(InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException;
+ final Optional<TreeNode> current) throws DataValidationFailedException;
protected abstract void verifyWrittenStructure(NormalizedNode<?, ?> writtenValue);
@Override
protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
- throw new DataPreconditionFailedException(path, "Subtree modification is not allowed.");
+ final Optional<TreeNode> current) throws IncorrectDataStructureException {
+ throw new IncorrectDataStructureException(path, "Subtree modification is not allowed.");
}
}
}
import static com.google.common.base.Preconditions.checkArgument;
-import org.opendaylight.controller.md.sal.dom.store.impl.tree.DataPreconditionFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.tree.IncorrectDataStructureException;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNode;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.TreeNodeFactory;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.spi.Version;
@Override
protected void checkSubtreeModificationApplicable(final InstanceIdentifier path, final NodeModification modification,
- final Optional<TreeNode> current) throws DataPreconditionFailedException {
- throw new DataPreconditionFailedException(path, "Subtree modification is not allowed.");
+ final Optional<TreeNode> current) throws IncorrectDataStructureException {
+ throw new IncorrectDataStructureException(path, "Subtree modification is not allowed.");
}
public static class LeafSetEntryModificationStrategy extends ValueNodeModificationStrategy<LeafListSchemaNode> {
package org.opendaylight.controller.sal.dom.broker.osgi;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.osgi.framework.ServiceReference;
return getDelegate().registerDataChangeListener(store, path, listener, triggeringScope);
}
+ @Override
+ public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+ return getDelegate().createTransactionChain(listener);
+ }
+
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+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 com.google.common.util.concurrent.SettableFuture;
+
+/**
+ * Simple implementation of {@link TransactionChainListener} for testing.
+ *
+ * This transaction chain listener does not contain any logic, only update
+ * futures ({@link #getFailFuture()} and {@link #getSuccessFuture()} when
+ * transaction chain event is retrieved.
+ *
+ */
+class BlockingTransactionChainListener implements TransactionChainListener {
+
+ private final SettableFuture<Throwable> failFuture = SettableFuture.create();
+ private final SettableFuture<Void> successFuture = SettableFuture.create();
+
+ @Override
+ public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction,
+ final Throwable cause) {
+ failFuture.set(cause);
+ }
+
+ @Override
+ public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+ successFuture.set(null);
+ }
+
+ public SettableFuture<Throwable> getFailFuture() {
+ return failFuture;
+ }
+
+ public SettableFuture<Void> getSuccessFuture() {
+ return successFuture;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.dom.broker.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.md.sal.dom.store.impl.TestModel;
+import org.opendaylight.controller.sal.core.spi.data.DOMStore;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+public class DOMTransactionChainTest {
+
+ private SchemaContext schemaContext;
+ private DOMDataBrokerImpl domBroker;
+
+ @Before
+ public void setupStore() {
+ InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER", MoreExecutors.sameThreadExecutor());
+ InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG", MoreExecutors.sameThreadExecutor());
+ schemaContext = TestModel.createTestContext();
+
+ operStore.onGlobalContextUpdated(schemaContext);
+ configStore.onGlobalContextUpdated(schemaContext);
+
+ ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType, DOMStore> builder() //
+ .put(CONFIGURATION, configStore) //
+ .put(OPERATIONAL, operStore) //
+ .build();
+
+ ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+ domBroker = new DOMDataBrokerImpl(stores, executor);
+ }
+
+ @Test
+ public void testTransactionChainNoConflict() throws InterruptedException, ExecutionException, TimeoutException {
+ BlockingTransactionChainListener listener = new BlockingTransactionChainListener();
+ DOMTransactionChain txChain = domBroker.createTransactionChain(listener);
+ assertNotNull(txChain);
+
+ /**
+ * We alocate new read-write transaction and write /test
+ *
+ *
+ */
+ DOMDataReadWriteTransaction firstTx = allocateAndWrite(txChain);
+
+ /**
+ * First transaction is marked as ready, we are able to allocate chained
+ * transactions
+ */
+ ListenableFuture<RpcResult<TransactionStatus>> firstWriteTxFuture = firstTx.commit();
+
+ /**
+ * We alocate chained transaction - read transaction.
+ */
+ DOMDataReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
+
+ /**
+ *
+ * We test if we are able to read data from tx, read should not fail
+ * since we are using chained transaction.
+ *
+ *
+ */
+ assertTestContainerExists(secondReadTx);
+
+ /**
+ *
+ * We alocate next transaction, which is still based on first one, but
+ * is read-write.
+ *
+ */
+ DOMDataReadWriteTransaction thirdDeleteTx = allocateAndDelete(txChain);
+
+ /**
+ * third transaction is sealed.
+ */
+ ListenableFuture<RpcResult<TransactionStatus>> thirdDeleteTxFuture = thirdDeleteTx.commit();
+
+ /**
+ * We commit first transaction
+ *
+ */
+ assertCommitSuccessful(firstWriteTxFuture);
+
+ // Alocates store transaction
+ DOMDataReadTransaction storeReadTx = domBroker.newReadOnlyTransaction();
+ /**
+ * We verify transaction is commited to store, container should exists
+ * in datastore.
+ */
+ assertTestContainerExists(storeReadTx);
+ /**
+ * We commit third transaction
+ *
+ */
+ assertCommitSuccessful(thirdDeleteTxFuture);
+
+ /**
+ * We close transaction chain.
+ */
+ txChain.close();
+
+ listener.getSuccessFuture().get(1000, TimeUnit.MILLISECONDS);
+ }
+
+ @Test
+ public void testTransactionChainNotSealed() throws InterruptedException, ExecutionException, TimeoutException {
+ BlockingTransactionChainListener listener = new BlockingTransactionChainListener();
+ DOMTransactionChain txChain = domBroker.createTransactionChain(listener);
+ assertNotNull(txChain);
+
+ /**
+ * We alocate new read-write transaction and write /test
+ *
+ *
+ */
+ allocateAndWrite(txChain);
+
+ /**
+ * We alocate chained transaction - read transaction, note first one is
+ * still not commited to datastore, so this allocation should fail with
+ * IllegalStateException.
+ */
+ try {
+ txChain.newReadOnlyTransaction();
+ fail("Allocation of secondReadTx should fail with IllegalStateException");
+ } catch (Exception e) {
+ assertTrue(e instanceof IllegalStateException);
+ }
+ }
+
+ private static DOMDataReadWriteTransaction allocateAndDelete(final DOMTransactionChain txChain)
+ throws InterruptedException, ExecutionException {
+ DOMDataReadWriteTransaction tx = txChain.newReadWriteTransaction();
+
+ /**
+ * We test existence of /test in third transaction container should
+ * still be visible from first one (which is still uncommmited).
+ *
+ */
+ assertTestContainerExists(tx);
+
+ /**
+ * We delete node in third transaction
+ */
+ tx.delete(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
+ return tx;
+ }
+
+ private static DOMDataReadWriteTransaction allocateAndWrite(final DOMTransactionChain txChain)
+ throws InterruptedException, ExecutionException {
+ DOMDataReadWriteTransaction tx = txChain.newReadWriteTransaction();
+ assertTestContainerWrite(tx);
+ return tx;
+ }
+
+ private static void assertCommitSuccessful(final ListenableFuture<RpcResult<TransactionStatus>> future)
+ throws InterruptedException, ExecutionException {
+ RpcResult<TransactionStatus> rpcResult = future.get();
+ assertTrue(rpcResult.isSuccessful());
+ assertEquals(TransactionStatus.COMMITED, rpcResult.getResult());
+ }
+
+ private static void assertTestContainerExists(final DOMDataReadTransaction readTx) throws InterruptedException,
+ ExecutionException {
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> readFuture = readTx.read(OPERATIONAL, TestModel.TEST_PATH);
+ Optional<NormalizedNode<?, ?>> readedData = readFuture.get();
+ assertTrue(readedData.isPresent());
+ }
+
+ private static void assertTestContainerWrite(final DOMDataReadWriteTransaction tx) throws InterruptedException,
+ ExecutionException {
+ tx.put(OPERATIONAL, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+ assertTestContainerExists(tx);
+ }
+}
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>sal-common-util</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Iterator;
+import java.util.List;
import java.util.Queue;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.client.NetconfClientSession;
import org.opendaylight.controller.netconf.client.NetconfClientSessionListener;
import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.controller.sal.common.util.RpcErrors;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceCommunicator.class);
- private static final RpcResult<NetconfMessage> FAILED_RPC_RESULT = new FailedRpcResult<>(RpcErrors.getRpcError(
- null, null, null, RpcError.ErrorSeverity.ERROR, "Netconf session disconnected",
- RpcError.ErrorType.PROTOCOL, null));
-
private final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice;
private final RemoteDeviceId id;
+ private final Lock sessionLock = new ReentrantLock();
public NetconfDeviceCommunicator(final RemoteDeviceId id,
final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice) {
private NetconfClientSession session;
@Override
- public synchronized void onSessionUp(final NetconfClientSession session) {
- logger.debug("{}: Session established", id);
- this.session = session;
+ public void onSessionUp(final NetconfClientSession session) {
+ sessionLock.lock();
+ try {
+ logger.debug("{}: Session established", id);
+ this.session = session;
- final NetconfSessionCapabilities netconfSessionCapabilities = NetconfSessionCapabilities.fromNetconfSession(session);
- logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionCapabilities);
+ final NetconfSessionCapabilities netconfSessionCapabilities =
+ NetconfSessionCapabilities.fromNetconfSession(session);
+ logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionCapabilities);
- remoteDevice.onRemoteSessionUp(netconfSessionCapabilities, this);
+ remoteDevice.onRemoteSessionUp(netconfSessionCapabilities, this);
+ }
+ finally {
+ sessionLock.unlock();
+ }
}
public void initializeRemoteConnection(final NetconfClientDispatcher dispatch,
dispatch.createReconnectingClient(config);
}
- private synchronized void tearDown(final Exception e) {
- remoteDevice.onRemoteSessionDown();
- session = null;
+ private void tearDown( String reason ) {
+ List<UncancellableFuture<RpcResult<NetconfMessage>>> futuresToCancel = Lists.newArrayList();
+ sessionLock.lock();
+ try {
+ if( session != null ) {
+ session = null;
+
+ /*
+ * Walk all requests, check if they have been executing
+ * or cancelled and remove them from the queue.
+ */
+ final Iterator<Request> it = requests.iterator();
+ while (it.hasNext()) {
+ final Request r = it.next();
+ if (r.future.isUncancellable()) {
+ futuresToCancel.add( r.future );
+ it.remove();
+ } else if (r.future.isCancelled()) {
+ // This just does some house-cleaning
+ it.remove();
+ }
+ }
- /*
- * Walk all requests, check if they have been executing
- * or cancelled and remove them from the queue.
- */
- final Iterator<Request> it = requests.iterator();
- while (it.hasNext()) {
- final Request r = it.next();
- if (r.future.isUncancellable()) {
- r.future.setException(e);
- it.remove();
- } else if (r.future.isCancelled()) {
- // This just does some house-cleaning
- it.remove();
+ remoteDevice.onRemoteSessionDown();
+ }
+ }
+ finally {
+ sessionLock.unlock();
+ }
+
+ // Notify pending request futures outside of the sessionLock to avoid unnecessarily
+ // blocking the caller.
+ for( UncancellableFuture<RpcResult<NetconfMessage>> future: futuresToCancel ) {
+ if( Strings.isNullOrEmpty( reason ) ) {
+ future.set( createSessionDownRpcResult() );
+ } else {
+ future.set( createErrorRpcResult( RpcError.ErrorType.TRANSPORT, reason ) );
}
}
}
+ private RpcResult<NetconfMessage> createSessionDownRpcResult()
+ {
+ return createErrorRpcResult( RpcError.ErrorType.TRANSPORT,
+ String.format( "The netconf session to %1$s is disconnected", id.getName() ) );
+ }
+
+ private RpcResult<NetconfMessage> createErrorRpcResult( RpcError.ErrorType errorType, String message )
+ {
+ return new FailedRpcResult<NetconfMessage>( RpcErrors.getRpcError( null,
+ NetconfDocumentedException.ErrorTag.operation_failed.getTagValue(),
+ null, RpcError.ErrorSeverity.ERROR, message, errorType, null ) );
+ }
+
@Override
public void onSessionDown(final NetconfClientSession session, final Exception e) {
logger.warn("{}: Session went down", id, e);
- tearDown(e);
+ tearDown( null );
}
@Override
public void onSessionTerminated(final NetconfClientSession session, final NetconfTerminationReason reason) {
logger.warn("{}: Session terminated {}", id, reason);
- tearDown(new RuntimeException(reason.getErrorMessage()));
+ tearDown( reason.getErrorMessage() );
+ }
+
+ @Override
+ public void close() {
+ tearDown( String.format( "The netconf session to %1$s has been closed", id.getName() ) );
}
@Override
}
}
- private synchronized void processMessage(final NetconfMessage message) {
- final Request r = requests.peek();
- if (r.future.isUncancellable()) {
- requests.poll();
+ private void processMessage(final NetconfMessage message) {
+ Request request = null;
+ sessionLock.lock();
+ try {
+ request = requests.peek();
+ if (request.future.isUncancellable()) {
+ requests.poll();
+ }
+ else {
+ request = null;
+ logger.warn("{}: Ignoring unsolicited message {}", id, msgToS(message));
+ }
+ }
+ finally {
+ sessionLock.unlock();
+ }
+
+ if( request != null ) {
logger.debug("{}: Message received {}", id, message);
if(logger.isTraceEnabled()) {
- logger.trace("{}: Matched request: {} to response: {}", id, msgToS(r.request), msgToS(message));
+ logger.trace( "{}: Matched request: {} to response: {}", id,
+ msgToS( request.request ), msgToS( message ) );
}
try {
- NetconfMessageTransformUtil.checkValidReply(r.request, message);
- } catch (final IllegalStateException e) {
- logger.warn("{}: Invalid request-reply match, reply message contains different message-id, request: {}, response: {}", id,
- msgToS(r.request), msgToS(message), e);
- r.future.setException(e);
+ NetconfMessageTransformUtil.checkValidReply( request.request, message );
+ }
+ catch (final NetconfDocumentedException e) {
+ logger.warn( "{}: Invalid request-reply match, reply message contains different message-id, request: {}, response: {}",
+ id, msgToS( request.request ), msgToS( message ), e );
+
+ request.future.set( new FailedRpcResult<NetconfMessage>(
+ NetconfMessageTransformUtil.toRpcError( e ) ) );
return;
}
try {
NetconfMessageTransformUtil.checkSuccessReply(message);
- } catch (NetconfDocumentedException | IllegalStateException e) {
- logger.warn("{}: Error reply from remote device, request: {}, response: {}", id,
- msgToS(r.request), msgToS(message), e);
- r.future.setException(e);
+ }
+ catch( NetconfDocumentedException e ) {
+ logger.warn( "{}: Error reply from remote device, request: {}, response: {}", id,
+ msgToS( request.request ), msgToS( message ), e );
+
+ request.future.set( new FailedRpcResult<NetconfMessage>(
+ NetconfMessageTransformUtil.toRpcError( e ) ) );
return;
}
- r.future.set(Rpcs.getRpcResult(true, message, Collections.<RpcError>emptySet()));
- } else {
- logger.warn("{}: Ignoring unsolicited message {}", id, msgToS(message));
+ request.future.set(Rpcs.getRpcResult( true, message, Collections.<RpcError>emptySet() ) );
}
}
- @Override
- public void close() {
- tearDown(new RuntimeException("Closed"));
- }
-
private static String msgToS(final NetconfMessage msg) {
return XmlUtil.toString(msg.getDocument());
}
@Override
- public synchronized ListenableFuture<RpcResult<NetconfMessage>> sendRequest(final NetconfMessage message, final QName rpc) {
+ public ListenableFuture<RpcResult<NetconfMessage>> sendRequest(
+ final NetconfMessage message, final QName rpc) {
+ sessionLock.lock();
+ try {
+ return sendRequestWithLock( message, rpc );
+ }
+ finally {
+ sessionLock.unlock();
+ }
+ }
+
+ private ListenableFuture<RpcResult<NetconfMessage>> sendRequestWithLock(
+ final NetconfMessage message, final QName rpc) {
if(logger.isTraceEnabled()) {
logger.trace("{}: Sending message {}", id, msgToS(message));
}
if (session == null) {
logger.warn("{}: Session is disconnected, failing RPC request {}", id, message);
- return Futures.immediateFuture(FAILED_RPC_RESULT);
+ return Futures.immediateFuture( createSessionDownRpcResult() );
}
- final Request req = new Request(new UncancellableFuture<RpcResult<NetconfMessage>>(true), message, rpc);
+ final Request req = new Request( new UncancellableFuture<RpcResult<NetconfMessage>>(true),
+ message );
requests.add(req);
session.sendMessage(req.request).addListener(new FutureListener<Void>() {
@Override
public void operationComplete(final Future<Void> future) throws Exception {
- if (!future.isSuccess()) {
+ if( !future.isSuccess() ) {
// We expect that a session down will occur at this point
- logger.debug("{}: Failed to send request {}", id, XmlUtil.toString(req.request.getDocument()), future.cause());
- req.future.setException(future.cause());
- } else {
- logger.trace("{}: Finished sending request {}", id, req.request);
+ logger.debug( "{}: Failed to send request {}", id,
+ XmlUtil.toString(req.request.getDocument()), future.cause() );
+
+ if( future.cause() != null ) {
+ req.future.set( createErrorRpcResult( RpcError.ErrorType.TRANSPORT,
+ future.cause().getLocalizedMessage() ) );
+ } else {
+ req.future.set( createSessionDownRpcResult() ); // assume session is down
+ }
+ req.future.setException( future.cause() );
+ }
+ else {
+ logger.trace( "Finished sending request {}", req.request );
}
}
});
private static final class Request {
final UncancellableFuture<RpcResult<NetconfMessage>> future;
final NetconfMessage request;
- final QName rpc;
- private Request(final UncancellableFuture<RpcResult<NetconfMessage>> future, final NetconfMessage request, final QName rpc) {
+ private Request(final UncancellableFuture<RpcResult<NetconfMessage>> future,
+ final NetconfMessage request) {
this.future = future;
this.request = request;
- this.rpc = rpc;
}
}
}
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import javax.annotation.Nullable;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.controller.sal.common.util.RpcErrors;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.Node;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
return new CompositeNodeTOImpl(argument.getNodeType(), null, list);
}
- public static void checkValidReply(final NetconfMessage input, final NetconfMessage output) {
+ public static void checkValidReply(final NetconfMessage input, final NetconfMessage output)
+ throws NetconfDocumentedException {
final String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id");
final String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id");
if(inputMsgId.equals(outputMsgId) == false) {
- final String requestXml = XmlUtil.toString(input.getDocument());
- final String responseXml = XmlUtil.toString(output.getDocument());
- throw new IllegalStateException(String.format("Rpc request and reply message IDs must be same. Request: %s, response: %s", requestXml, responseXml));
+ Map<String,String> errorInfo = ImmutableMap.<String,String>builder()
+ .put( "actual-message-id", outputMsgId )
+ .put( "expected-message-id", inputMsgId )
+ .build();
+
+ throw new NetconfDocumentedException( "Response message contained unknown \"message-id\"",
+ null, NetconfDocumentedException.ErrorType.protocol,
+ NetconfDocumentedException.ErrorTag.bad_attribute,
+ NetconfDocumentedException.ErrorSeverity.error, errorInfo );
}
}
public static void checkSuccessReply(final NetconfMessage output) throws NetconfDocumentedException {
if(NetconfMessageUtil.isErrorMessage(output)) {
- throw new IllegalStateException(String.format("Response contains error: %s", XmlUtil.toString(output.getDocument())));
+ throw NetconfDocumentedException.fromXMLDocument( output.getDocument() );
+ }
+ }
+
+ public static RpcError toRpcError( NetconfDocumentedException ex )
+ {
+ StringBuilder infoBuilder = new StringBuilder();
+ Map<String, String> errorInfo = ex.getErrorInfo();
+ if( errorInfo != null )
+ {
+ for( Entry<String,String> e: errorInfo.entrySet() ) {
+ infoBuilder.append( '<' ).append( e.getKey() ).append( '>' ).append( e.getValue() )
+ .append( "</" ).append( e.getKey() ).append( '>' );
+
+ }
+ }
+
+ return RpcErrors.getRpcError( null, ex.getErrorTag().getTagValue(), infoBuilder.toString(),
+ toRpcErrorSeverity( ex.getErrorSeverity() ), ex.getLocalizedMessage(),
+ toRpcErrorType( ex.getErrorType() ), ex.getCause() );
+ }
+
+ private static ErrorSeverity toRpcErrorSeverity( NetconfDocumentedException.ErrorSeverity severity ) {
+ switch( severity ) {
+ case warning:
+ return RpcError.ErrorSeverity.WARNING;
+ default:
+ return RpcError.ErrorSeverity.ERROR;
+ }
+ }
+
+ private static RpcError.ErrorType toRpcErrorType( NetconfDocumentedException.ErrorType type )
+ {
+ switch( type ) {
+ case protocol:
+ return RpcError.ErrorType.PROTOCOL;
+ case rpc:
+ return RpcError.ErrorType.RPC;
+ case transport:
+ return RpcError.ErrorType.TRANSPORT;
+ default:
+ return RpcError.ErrorType.APPLICATION;
}
}
import org.junit.Test;
import org.mockito.Mockito;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.connect.api.MessageTransformer;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.sal.connect.netconf.listener;
+
+import io.netty.channel.ChannelFuture;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+
+import java.io.ByteArrayInputStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
+
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.client.NetconfClientSession;
+import org.opendaylight.controller.sal.connect.api.RemoteDevice;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class NetconfDeviceCommunicatorTest {
+
+ @Mock
+ NetconfClientSession mockSession;
+
+ @Mock
+ RemoteDevice<NetconfSessionCapabilities, NetconfMessage> mockDevice;
+
+ NetconfDeviceCommunicator communicator;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks( this );
+
+ communicator = new NetconfDeviceCommunicator( new RemoteDeviceId( "test" ), mockDevice );
+ }
+
+ @SuppressWarnings("unchecked")
+ void setupSession()
+ {
+ doReturn( Collections.<String>emptySet() ).when( mockSession ).getServerCapabilities();
+ doNothing().when( mockDevice ).onRemoteSessionUp( any( NetconfSessionCapabilities.class ),
+ any( RemoteDeviceCommunicator.class ) );
+ communicator.onSessionUp( mockSession );
+ }
+
+ private ListenableFuture<RpcResult<NetconfMessage>> sendRequest() throws Exception {
+ return sendRequest( UUID.randomUUID().toString() );
+ }
+
+ @SuppressWarnings("unchecked")
+ private ListenableFuture<RpcResult<NetconfMessage>> sendRequest( String messageID ) throws Exception {
+ Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+ Element element = doc.createElement( "request" );
+ element.setAttribute( "message-id", messageID );
+ doc.appendChild( element );
+ NetconfMessage message = new NetconfMessage( doc );
+
+ ChannelFuture mockChannelFuture = mock( ChannelFuture.class );
+ doReturn( mockChannelFuture ).when( mockChannelFuture )
+ .addListener( any( (GenericFutureListener.class ) ) );
+ doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) );
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture =
+ communicator.sendRequest( message, QName.create( "mock rpc" ) );
+
+ assertNotNull( "ListenableFuture is null", resultFuture );
+ return resultFuture;
+ }
+
+ @Test
+ public void testOnSessionUp() {
+ String testCapability = "urn:opendaylight:params:xml:ns:test?module=test-module&revision=2014-06-02";
+ Collection<String> serverCapabilities =
+ Sets.newHashSet( NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString(),
+ NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString(),
+ testCapability );
+ doReturn( serverCapabilities ).when( mockSession ).getServerCapabilities();
+
+ ArgumentCaptor<NetconfSessionCapabilities> netconfSessionCapabilities =
+ ArgumentCaptor.forClass( NetconfSessionCapabilities.class );
+ doNothing().when( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) );
+
+ communicator.onSessionUp( mockSession );
+
+ verify( mockSession ).getServerCapabilities();
+ verify( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) );
+
+ NetconfSessionCapabilities actualCapabilites = netconfSessionCapabilities.getValue();
+ assertEquals( "containsCapability", true, actualCapabilites.containsCapability(
+ NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString() ) );
+ assertEquals( "containsCapability", true, actualCapabilites.containsCapability( testCapability ) );
+ assertEquals( "getModuleBasedCaps", Sets.newHashSet(
+ QName.create( "urn:opendaylight:params:xml:ns:test", "2014-06-02", "test-module" )),
+ actualCapabilites.getModuleBasedCaps() );
+ assertEquals( "isRollbackSupported", true, actualCapabilites.isRollbackSupported() );
+ assertEquals( "isMonitoringSupported", true, actualCapabilites.isMonitoringSupported() );
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test(timeout=5000)
+ public void testOnSessionDown() throws Exception {
+ setupSession();
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture1 = sendRequest();
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture2 = sendRequest();
+
+ doNothing().when( mockDevice ).onRemoteSessionDown();
+
+ communicator.onSessionDown( mockSession, new Exception( "mock ex" ) );
+
+ verifyErrorRpcResult( resultFuture1.get(), RpcError.ErrorType.TRANSPORT, "operation-failed" );
+ verifyErrorRpcResult( resultFuture2.get(), RpcError.ErrorType.TRANSPORT, "operation-failed" );
+
+ verify( mockDevice ).onRemoteSessionDown();
+
+ reset( mockDevice );
+
+ communicator.onSessionDown( mockSession, new Exception( "mock ex" ) );
+
+ verify( mockDevice, never() ).onRemoteSessionDown();
+ }
+
+ @Test
+ public void testOnSessionTerminated() throws Exception {
+ setupSession();
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest();
+
+ doNothing().when( mockDevice ).onRemoteSessionDown();
+
+ String reasonText = "testing terminate";
+ NetconfTerminationReason reason = new NetconfTerminationReason( reasonText );
+ communicator.onSessionTerminated( mockSession, reason );
+
+ RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.TRANSPORT,
+ "operation-failed" );
+ assertEquals( "RpcError message", reasonText, rpcError.getMessage() );
+
+ verify( mockDevice ).onRemoteSessionDown();
+ }
+
+ @Test
+ public void testClose() throws Exception {
+ communicator.close();
+ verify( mockDevice, never() ).onRemoteSessionDown();
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Test
+ public void testSendRequest() throws Exception {
+ setupSession();
+
+ NetconfMessage message = new NetconfMessage(
+ DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() );
+ QName rpc = QName.create( "mock rpc" );
+
+ ArgumentCaptor<GenericFutureListener> futureListener =
+ ArgumentCaptor.forClass( GenericFutureListener.class );
+
+ ChannelFuture mockChannelFuture = mock( ChannelFuture.class );
+ doReturn( mockChannelFuture ).when( mockChannelFuture ).addListener( futureListener.capture() );
+ doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) );
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest( message, rpc );
+
+ verify( mockSession ).sendMessage( same( message ) );
+
+ assertNotNull( "ListenableFuture is null", resultFuture );
+
+ verify( mockChannelFuture ).addListener( futureListener.capture() );
+ Future<Void> operationFuture = mock( Future.class );
+ doReturn( true ).when( operationFuture ).isSuccess();
+ doReturn( true ).when( operationFuture ).isDone();
+ futureListener.getValue().operationComplete( operationFuture );
+
+ try {
+ resultFuture.get( 1, TimeUnit.MILLISECONDS ); // verify it's not cancelled or has an error set
+ }
+ catch( TimeoutException e ) {} // expected
+ }
+
+ @Test
+ public void testSendRequestWithNoSession() throws Exception {
+ NetconfMessage message = new NetconfMessage(
+ DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() );
+ QName rpc = QName.create( "mock rpc" );
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest( message, rpc );
+
+ assertNotNull( "ListenableFuture is null", resultFuture );
+
+ // Should have an immediate result
+ RpcResult<NetconfMessage> rpcResult = resultFuture.get( 3, TimeUnit.MILLISECONDS );
+
+ verifyErrorRpcResult( rpcResult, RpcError.ErrorType.TRANSPORT, "operation-failed" );
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Test
+ public void testSendRequestWithWithSendFailure() throws Exception {
+ setupSession();
+
+ NetconfMessage message = new NetconfMessage(
+ DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() );
+ QName rpc = QName.create( "mock rpc" );
+
+ ArgumentCaptor<GenericFutureListener> futureListener =
+ ArgumentCaptor.forClass( GenericFutureListener.class );
+
+ ChannelFuture mockChannelFuture = mock( ChannelFuture.class );
+ doReturn( mockChannelFuture ).when( mockChannelFuture ).addListener( futureListener.capture() );
+ doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) );
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest( message, rpc );
+
+ assertNotNull( "ListenableFuture is null", resultFuture );
+
+ verify( mockChannelFuture ).addListener( futureListener.capture() );
+
+ Future<Void> operationFuture = mock( Future.class );
+ doReturn( false ).when( operationFuture ).isSuccess();
+ doReturn( true ).when( operationFuture ).isDone();
+ doReturn( new Exception( "mock error" ) ).when( operationFuture ).cause();
+ futureListener.getValue().operationComplete( operationFuture );
+
+ // Should have an immediate result
+ RpcResult<NetconfMessage> rpcResult = resultFuture.get( 3, TimeUnit.MILLISECONDS );
+
+ RpcError rpcError = verifyErrorRpcResult( rpcResult, RpcError.ErrorType.TRANSPORT, "operation-failed" );
+ assertEquals( "RpcError message contains \"mock error\"", true,
+ rpcError.getMessage().contains( "mock error" ) );
+ }
+
+ private NetconfMessage createSuccessResponseMessage( String messageID ) throws ParserConfigurationException {
+ Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+ Element rpcReply = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY );
+ rpcReply.setAttribute( "message-id", messageID );
+ Element element = doc.createElementNS( "ns", "data" );
+ element.setTextContent( messageID );
+ rpcReply.appendChild( element );
+ doc.appendChild( rpcReply );
+
+ return new NetconfMessage( doc );
+ }
+
+ @Test
+ public void testOnSuccessfulResponseMessage() throws Exception {
+ setupSession();
+
+ String messageID1 = UUID.randomUUID().toString();
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture1 = sendRequest( messageID1 );
+
+ String messageID2 = UUID.randomUUID().toString();
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture2 = sendRequest( messageID2 );
+
+ communicator.onMessage( mockSession, createSuccessResponseMessage( messageID1 ) );
+ communicator.onMessage( mockSession, createSuccessResponseMessage( messageID2 ) );
+
+ verifyResponseMessage( resultFuture1.get(), messageID1 );
+ verifyResponseMessage( resultFuture2.get(), messageID2 );
+ }
+
+ @Test
+ public void testOnResponseMessageWithError() throws Exception {
+ setupSession();
+
+ String messageID = UUID.randomUUID().toString();
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest( messageID );
+
+ communicator.onMessage( mockSession, createErrorResponseMessage( messageID ) );
+
+ RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.RPC,
+ "missing-attribute" );
+ assertEquals( "RpcError message", "Missing attribute", rpcError.getMessage() );
+
+ String errorInfo = rpcError.getInfo();
+ assertNotNull( "RpcError info is null", errorInfo );
+ assertEquals( "Error info contains \"foo\"", true,
+ errorInfo.contains( "<bad-attribute>foo</bad-attribute>" ) );
+ assertEquals( "Error info contains \"bar\"", true,
+ errorInfo.contains( "<bad-element>bar</bad-element>" ) );
+ }
+
+ @Test
+ public void testOnResponseMessageWithWrongMessageID() throws Exception {
+ setupSession();
+
+ String messageID = UUID.randomUUID().toString();
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest( messageID );
+
+ communicator.onMessage( mockSession, createSuccessResponseMessage( UUID.randomUUID().toString() ) );
+
+ RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.PROTOCOL,
+ "bad-attribute" );
+ assertEquals( "RpcError message non-empty", true,
+ !Strings.isNullOrEmpty( rpcError.getMessage() ) );
+
+ String errorInfo = rpcError.getInfo();
+ assertNotNull( "RpcError info is null", errorInfo );
+ assertEquals( "Error info contains \"actual-message-id\"", true,
+ errorInfo.contains( "actual-message-id" ) );
+ assertEquals( "Error info contains \"expected-message-id\"", true,
+ errorInfo.contains( "expected-message-id" ) );
+ }
+
+ private NetconfMessage createErrorResponseMessage( String messageID ) throws Exception {
+ String xmlStr =
+ "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"" +
+ " message-id=\"" + messageID + "\">" +
+ " <rpc-error>" +
+ " <error-type>rpc</error-type>" +
+ " <error-tag>missing-attribute</error-tag>" +
+ " <error-severity>error</error-severity>" +
+ " <error-message>Missing attribute</error-message>" +
+ " <error-info>" +
+ " <bad-attribute>foo</bad-attribute>" +
+ " <bad-element>bar</bad-element>" +
+ " </error-info>" +
+ " </rpc-error>" +
+ "</rpc-reply>";
+
+ ByteArrayInputStream bis = new ByteArrayInputStream( xmlStr.getBytes() );
+ Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse( bis );
+ return new NetconfMessage( doc );
+ }
+
+ private void verifyResponseMessage( RpcResult<NetconfMessage> rpcResult, String dataText ) {
+ assertNotNull( "RpcResult is null", rpcResult );
+ assertEquals( "isSuccessful", true, rpcResult.isSuccessful() );
+ NetconfMessage messageResult = rpcResult.getResult();
+ assertNotNull( "getResult", messageResult );
+// List<SimpleNode<?>> nodes = messageResult.getSimpleNodesByName(
+// QName.create( URI.create( "ns" ), null, "data" ) );
+// assertNotNull( "getSimpleNodesByName", nodes );
+// assertEquals( "List<SimpleNode<?>> size", 1, nodes.size() );
+// assertEquals( "SimpleNode value", dataText, nodes.iterator().next().getValue() );
+ }
+
+ private RpcError verifyErrorRpcResult( RpcResult<NetconfMessage> rpcResult,
+ RpcError.ErrorType expErrorType, String expErrorTag ) {
+ assertNotNull( "RpcResult is null", rpcResult );
+ assertEquals( "isSuccessful", false, rpcResult.isSuccessful() );
+ assertNotNull( "RpcResult errors is null", rpcResult.getErrors() );
+ assertEquals( "Errors size", 1, rpcResult.getErrors().size() );
+ RpcError rpcError = rpcResult.getErrors().iterator().next();
+ assertEquals( "getErrorSeverity", RpcError.ErrorSeverity.ERROR, rpcError.getSeverity() );
+ assertEquals( "getErrorType", expErrorType, rpcError.getErrorType() );
+ assertEquals( "getErrorTag", expErrorTag, rpcError.getTag() );
+ assertTrue( "getMessage is empty", StringUtils.isNotEmpty( rpcError.getMessage() ) );
+ return rpcError;
+ }
+}
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.5.2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal</artifactId>
- <version>0.8.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-api</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common-util</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common-util</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-core-api</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-api</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common-util</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-core-api</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.5.2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal</artifactId>
- <version>0.8.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common-util</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-remoterpc-connector</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
</dependencies>
<url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
</scm>
- <properties>
- <jackson.version>2.3.0</jackson.version>
- <exam.version>3.0.0</exam.version>
- <url.version>1.5.0</url.version>
- </properties>
-
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>commons-codec</groupId>
- <artifactId>commons-codec</artifactId>
- <version>1.7</version>
- </dependency>
- </dependencies>
- </dependencyManagement>
-
<build>
<plugins>
<plugin>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>maven-paxexam-plugin</artifactId>
- <version>1.2.4</version>
<executions>
<execution>
<id>generate-config</id>
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
- <version>1.0.0</version>
+ <version>${lifecycle.mapping.version}</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<dependency>
<groupId>org.opendaylight.yangtools.thirdparty</groupId>
<artifactId>xtend-lib-osgi</artifactId>
- <version>2.4.3</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.tests</groupId>
<artifactId>sal-remoterpc-connector-test-provider</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.tests</groupId>
<artifactId>sal-remoterpc-connector-test-consumer</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-broker-impl</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
<dependency>
<groupId>org.ops4j.pax.url</groupId>
<artifactId>pax-url-aether</artifactId>
- <version>1.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>equinoxSDK381</groupId>
<artifactId>org.eclipse.osgi</artifactId>
- <version>3.8.1.v20120830-144521</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
- <version>1.7.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
- <version>1.0.9</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
- <version>1.0.9</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-api</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common-util</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-core-api</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-remoterpc-connector</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.5.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.osgi</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common-util</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools.thirdparty</groupId>
<artifactId>antlr4-runtime-osgi-nohead</artifactId>
- <version>4.0</version>
</dependency>
<!-- routing table dependencies -->
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>zeromq-routingtable.implementation</artifactId>
- <version>0.4.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>clustering.services</artifactId>
- <version>0.4.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal</artifactId>
- <version>0.5.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.osgi</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal.implementation</artifactId>
- <version>0.4.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.5.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>org.osgi</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager.it.implementation</artifactId>
- <version>0.5.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>clustering.stub</artifactId>
- <version>0.4.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.dependencymanager.shell</artifactId>
- <version>3.0.1</version>
<exclusions>
<exclusion>
<groupId>org.osgi</groupId>
<dependency>
<groupId>eclipselink</groupId>
<artifactId>javax.resource</artifactId>
- <version>1.5.0.v200906010428</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal</artifactId>
- <version>0.5.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>ietf-netconf-monitoring</artifactId>
- <version>0.2.5-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<dependency>
<groupId>org.opendaylight.yangtools.model</groupId>
<artifactId>yang-ext</artifactId>
- <version>2013.09.07.3</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools.model</groupId>
<artifactId>opendaylight-l2-types</artifactId>
- <version>2013.08.27.3</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-it</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-config</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-broker-impl</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-broker-impl</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
<artifactId>model-inventory</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-connector-api</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-common-util</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>clustering.services</artifactId>
- <version>0.4.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>equinoxSDK381</groupId>
<artifactId>org.eclipse.osgi</artifactId>
- <version>3.8.1.v20120830-144521</version>
</dependency>
<dependency>
<dependency>
<groupId>org.zeromq</groupId>
<artifactId>jeromq</artifactId>
- <version>0.3.1</version>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools.thirdparty</groupId>
<artifactId>xtend-lib-osgi</artifactId>
- <version>2.4.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-binding-broker-impl</artifactId>
- <version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<dependency>
<groupId>equinoxSDK381</groupId>
<artifactId>org.eclipse.osgi</artifactId>
- <version>3.8.1.v20120830-144521</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
- <version>1.7.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
- <version>1.0.9</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
- <version>1.0.9</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
<artifactId>model-flow-service</artifactId>
- <version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-manager</artifactId>
- <version>0.2.3-SNAPSHOT</version>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
<artifactId>model-flow-management</artifactId>
- <version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.yangtools.thirdparty</groupId>
<artifactId>antlr4-runtime-osgi-nohead</artifactId>
- <version>4.0</version>
</dependency>
</dependencies>
</project>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>containermanager</artifactId>
- <version>0.5.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>commons.northbound</artifactId>
- <version>0.4.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal</artifactId>
- <version>0.5.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.tests</groupId>
<artifactId>sal-remoterpc-connector-test-provider</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.tests</groupId>
<artifactId>sal-remoterpc-connector-test-consumer</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-remoterpc-connector</artifactId>
- <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
- <version>5.0.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>remoterpc-routingtable.implementation</artifactId>
- <version>0.4.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>sal-core-api</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
- <version>3.0.4.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
- <version>2.4</version>
<scope>test</scope>
</dependency>
<dependency>
@Path("/config/{identifier:.+}")
@Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML,
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
- public StructuredData readConfigurationData(@Encoded @PathParam("identifier") String identifier);
+ public StructuredData readConfigurationData(@Encoded @PathParam("identifier") String identifier,
+ @Context UriInfo depth);
@GET
@Path("/operational/{identifier:.+}")
@Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML,
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
- public StructuredData readOperationalData(@Encoded @PathParam("identifier") String identifier);
+ public StructuredData readOperationalData(@Encoded @PathParam("identifier") String identifier,
+ @Context UriInfo depth);
@PUT
@Path("/config/{identifier:.+}")
import java.io.InputStream;
import java.net.URI;
import java.util.Stack;
-
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
-
import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
import org.opendaylight.controller.sal.restconf.impl.EmptyNodeWrapper;
import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException {
checkArgument(event != null, "XML Event cannot be NULL!");
if (event.isStartElement()) {
- if (eventReader.hasNext()) {
- final XMLEvent innerEvent;
- innerEvent = eventReader.peek();
- if (innerEvent.isCharacters()) {
- final Characters chars = innerEvent.asCharacters();
- if (!chars.isWhiteSpace()) {
- return true;
- }
- } else if (innerEvent.isEndElement()) {
- return true;
- }
+ XMLEvent innerEvent = skipCommentsAndWhitespace();
+ if ( innerEvent != null && (innerEvent.isCharacters() || innerEvent.isEndElement())) {
+ return true;
}
}
return false;
private boolean isCompositeNodeEvent(final XMLEvent event) throws XMLStreamException {
checkArgument(event != null, "XML Event cannot be NULL!");
if (event.isStartElement()) {
- if (eventReader.hasNext()) {
- XMLEvent innerEvent;
- innerEvent = eventReader.peek();
- if (innerEvent.isCharacters()) {
- Characters chars = innerEvent.asCharacters();
- if (chars.isWhiteSpace()) {
- eventReader.nextEvent();
- innerEvent = eventReader.peek();
- }
- }
+ XMLEvent innerEvent = skipCommentsAndWhitespace();
+ if( innerEvent != null ) {
if (innerEvent.isStartElement()) {
return true;
}
return false;
}
+ private XMLEvent skipCommentsAndWhitespace() throws XMLStreamException {
+ while( eventReader.hasNext() ) {
+ XMLEvent event = eventReader.peek();
+ if( event.getEventType() == XMLStreamConstants.COMMENT ) {
+ eventReader.nextEvent();
+ continue;
+ }
+
+ if( event.isCharacters() ) {
+ Characters chars = event.asCharacters();
+ if( chars.isWhiteSpace() ) {
+ eventReader.nextEvent();
+ continue;
+ }
+ }
+ return event;
+ }
+ return null;
+ }
+
private CompositeNodeWrapper resolveCompositeNodeFromStartElement(final StartElement startElement) {
checkArgument(startElement != null, "Start Element cannot be NULL!");
return new CompositeNodeWrapper(getNamespaceFor(startElement), getLocalNameFor(startElement));
*/
package org.opendaylight.controller.sal.restconf.impl;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
-
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
-
import org.apache.commons.lang3.StringUtils;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
public class RestconfImpl implements RestconfService {
private final static RestconfImpl INSTANCE = new RestconfImpl();
operationsAsData.add(immutableSimpleNode);
String name = module.getName();
- LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, null);
+ LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName,
+ SchemaPath.create(true, QName.create("dummy")));
final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
fakeRpcSchemaNode.setAugmenting(true);
}
@Override
- public StructuredData readConfigurationData(final String identifier) {
+ public StructuredData readConfigurationData(final String identifier, UriInfo info) {
final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
CompositeNode data = null;
MountInstance mountPoint = iiWithData.getMountPoint();
data = broker.readConfigurationData(iiWithData.getInstanceIdentifier());
}
+ data = pruneDataAtDepth( data, parseDepthParameter( info ) );
return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
}
+ @SuppressWarnings("unchecked")
+ private <T extends Node<?>> T pruneDataAtDepth( T node, Integer depth ) {
+ if( depth == null ) {
+ return node;
+ }
+
+ if( node instanceof CompositeNode ) {
+ ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
+ if( depth > 1 ) {
+ for( Node<?> childNode: ((CompositeNode)node).getValue() ) {
+ newChildNodes.add( pruneDataAtDepth( childNode, depth - 1 ) );
+ }
+ }
+
+ return (T) ImmutableCompositeNode.create( node.getNodeType(), newChildNodes.build() );
+ }
+ else { // SimpleNode
+ return node;
+ }
+ }
+
+ private Integer parseDepthParameter( UriInfo info ) {
+ String param = info.getQueryParameters( false ).getFirst( "depth" );
+ if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) {
+ return null;
+ }
+
+ try {
+ Integer depth = Integer.valueOf( param );
+ if( depth < 1 ) {
+ throw new RestconfDocumentedException( new RestconfError(
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depth,
+ null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
+ }
+
+ return depth;
+ }
+ catch( NumberFormatException e ) {
+ throw new RestconfDocumentedException( new RestconfError(
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+ "Invalid depth parameter: " + e.getMessage(),
+ null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
+ }
+ }
+
@Override
- public StructuredData readOperationalData(final String identifier) {
+ public StructuredData readOperationalData(final String identifier, UriInfo info) {
final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
CompositeNode data = null;
MountInstance mountPoint = iiWithData.getMountPoint();
data = broker.readOperationalData(iiWithData.getInstanceIdentifier());
}
+ data = pruneDataAtDepth( data, parseDepthParameter( info ) );
return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint);
}
import java.io.IOException;
import java.util.Collections;
-
import javax.ws.rs.WebApplicationException;
-
import org.junit.BeforeClass;
import org.junit.Test;
import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
import org.opendaylight.controller.sal.restconf.impl.test.DummyType;
import org.opendaylight.controller.sal.restconf.impl.test.TestUtils;
import org.opendaylight.controller.sal.restconf.impl.test.YangAndXmlAndDataSchemaLoader;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.ModifyAction;
import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
import org.slf4j.Logger;
private DataSchemaNode prepareDataSchemaNode() {
ContainerSchemaNodeBuilder contBuild = new ContainerSchemaNodeBuilder("module", 1, TestUtils.buildQName("cont",
- "simple:uri", "2012-12-17"), null);
+ "simple:uri", "2012-12-17"), SchemaPath.create(true, QName.create("dummy")));
LeafSchemaNodeBuilder leafBuild = new LeafSchemaNodeBuilder("module", 2, TestUtils.buildQName("lf1",
- "simple:uri", "2012-12-17"), null);
+ "simple:uri", "2012-12-17"), SchemaPath.create(true, QName.create("dummy")));
leafBuild.setType(new DummyType());
leafBuild.setConfiguration(true);
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
String uriPrefix = "/config/";
String uriPath = "ietf-interfaces:interfaces";
String uri = uriPrefix + uriPath;
- when(restconfService.readConfigurationData(uriPath)).thenReturn(null);
+ when(restconfService.readConfigurationData(eq(uriPath), any(UriInfo.class))).thenReturn(null);
get(uri, Draft02.MediaTypes.DATA+JSON);
- verify(restconfService, times(1)).readConfigurationData(uriPath);
+ verify(restconfService, times(1)).readConfigurationData(eq(uriPath), any(UriInfo.class));
get(uri, Draft02.MediaTypes.DATA+XML);
- verify(restconfService, times(2)).readConfigurationData(uriPath);
+ verify(restconfService, times(2)).readConfigurationData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.APPLICATION_JSON);
- verify(restconfService, times(3)).readConfigurationData(uriPath);
+ verify(restconfService, times(3)).readConfigurationData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.APPLICATION_XML);
- verify(restconfService, times(4)).readConfigurationData(uriPath);
+ verify(restconfService, times(4)).readConfigurationData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.TEXT_XML);
- verify(restconfService, times(5)).readConfigurationData(uriPath);
+ verify(restconfService, times(5)).readConfigurationData(eq(uriPath), any(UriInfo.class));
// negative tests
get(uri, MediaType.TEXT_PLAIN);
- verify(restconfService, times(5)).readConfigurationData(uriPath);
+ verify(restconfService, times(5)).readConfigurationData(eq(uriPath), any(UriInfo.class));
}
@Test
String uriPrefix = "/operational/";
String uriPath = "ietf-interfaces:interfaces";
String uri = uriPrefix + uriPath;
- when(restconfService.readOperationalData(uriPath)).thenReturn(null);
+ when(restconfService.readOperationalData(eq(uriPath), any(UriInfo.class))).thenReturn(null);
get(uri, Draft02.MediaTypes.DATA+JSON);
- verify(restconfService, times(1)).readOperationalData(uriPath);
+ verify(restconfService, times(1)).readOperationalData(eq(uriPath), any(UriInfo.class));
get(uri, Draft02.MediaTypes.DATA+XML);
- verify(restconfService, times(2)).readOperationalData(uriPath);
+ verify(restconfService, times(2)).readOperationalData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.APPLICATION_JSON);
- verify(restconfService, times(3)).readOperationalData(uriPath);
+ verify(restconfService, times(3)).readOperationalData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.APPLICATION_XML);
- verify(restconfService, times(4)).readOperationalData(uriPath);
+ verify(restconfService, times(4)).readOperationalData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.TEXT_XML);
- verify(restconfService, times(5)).readOperationalData(uriPath);
+ verify(restconfService, times(5)).readOperationalData(eq(uriPath), any(UriInfo.class));
// negative tests
get(uri, MediaType.TEXT_PLAIN);
- verify(restconfService, times(5)).readOperationalData(uriPath);
+ verify(restconfService, times(5)).readOperationalData(eq(uriPath), any(UriInfo.class));
}
@Test
*/
package org.opendaylight.controller.sal.restconf.impl.test;
-import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.FileNotFoundException;
+import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import org.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.controller.sal.core.api.mount.MountService;
import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
public class RestGetOperationTest extends JerseyTest {
+ static class NodeData {
+ Object key;
+ Object data; // List for a CompositeNode, value Object for a SimpleNode
+
+ NodeData( Object key, Object data ) {
+ this.key = key;
+ this.data = data;
+ }
+ }
+
private static BrokerFacade brokerFacade;
private static RestconfImpl restconfImpl;
private static SchemaContext schemaContextYangsIetf;
return null;
}
+ @Test
+ public void getDataWithUriDepthParameterTest() throws UnsupportedEncodingException {
+
+ ControllerContext.getInstance().setGlobalSchema( schemaContextModules );
+
+ CompositeNode depth1Cont = toCompositeNode(
+ toCompositeNodeData( toNestedQName( "depth1-cont" ),
+ toCompositeNodeData( toNestedQName( "depth2-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth3-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth4-cont1" ),
+ toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" )
+ ),
+ toCompositeNodeData( toNestedQName( "depth2-cont2" ),
+ toCompositeNodeData( toNestedQName( "depth3-cont2" ),
+ toCompositeNodeData( toNestedQName( "depth4-cont2" ),
+ toSimpleNodeData( toNestedQName( "depth5-leaf2" ), "depth5-leaf2-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth4-leaf2" ), "depth4-leaf2-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth3-leaf2" ), "depth3-leaf2-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth2-leaf1" ), "depth2-leaf1-value" )
+ ) );
+
+ when( brokerFacade.readConfigurationData( any( InstanceIdentifier.class ) ) )
+ .thenReturn( depth1Cont );
+
+ // Test config with depth 1
+
+ Response response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "1" )
+ .request( "application/xml" ).get();
+
+ verifyXMLResponse( response, expectEmptyContainer( "depth1-cont" ) );
+
+ // Test config with depth 2
+
+ response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "2" )
+ .request( "application/xml" ).get();
+
+// String xml="<depth1-cont><depth2-cont1/><depth2-cont2/><depth2-leaf1>depth2-leaf1-value</depth2-leaf1></depth1-cont>";
+// Response mr=mock(Response.class);
+// when(mr.getEntity()).thenReturn( new java.io.StringBufferInputStream(xml) );
+
+ verifyXMLResponse( response,
+ expectContainer( "depth1-cont",
+ expectEmptyContainer( "depth2-cont1" ),
+ expectEmptyContainer( "depth2-cont2" ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
+
+ // Test config with depth 3
+
+ response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "3" )
+ .request( "application/xml" ).get();
+
+ verifyXMLResponse( response,
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectEmptyContainer( "depth3-cont1" ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectEmptyContainer( "depth3-cont2" ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
+
+ // Test config with depth 4
+
+ response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "4" )
+ .request( "application/xml" ).get();
+
+ verifyXMLResponse( response,
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectEmptyContainer( "depth4-cont1" ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectContainer( "depth3-cont2",
+ expectEmptyContainer( "depth4-cont2" ),
+ expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
+ ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
+
+ // Test config with depth 5
+
+ response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "5" )
+ .request( "application/xml" ).get();
+
+ verifyXMLResponse( response,
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectContainer( "depth4-cont1",
+ expectLeaf( "depth5-leaf1", "depth5-leaf1-value" )
+ ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectContainer( "depth3-cont2",
+ expectContainer( "depth4-cont2",
+ expectLeaf( "depth5-leaf2", "depth5-leaf2-value" )
+ ),
+ expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
+ ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
+
+ // Test config with depth unbounded
+
+ response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "unbounded" )
+ .request( "application/xml" ).get();
+
+ verifyXMLResponse( response,
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectContainer( "depth4-cont1",
+ expectLeaf( "depth5-leaf1", "depth5-leaf1-value" )
+ ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectContainer( "depth3-cont2",
+ expectContainer( "depth4-cont2",
+ expectLeaf( "depth5-leaf2", "depth5-leaf2-value" )
+ ),
+ expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
+ ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
+
+ // Test operational
+
+ CompositeNode depth2Cont1 = toCompositeNode(
+ toCompositeNodeData( toNestedQName( "depth2-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth3-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth4-cont1" ),
+ toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" )
+ ) );
+
+ when( brokerFacade.readOperationalData( any( InstanceIdentifier.class ) ) )
+ .thenReturn( depth2Cont1 );
+
+ response = target( "/operational/nested-module:depth1-cont/depth2-cont1" )
+ .queryParam( "depth", "3" ).request( "application/xml" ).get();
+
+ verifyXMLResponse( response,
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectEmptyContainer( "depth4-cont1" ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ) );
+ }
+
+ @Test
+ public void getDataWithInvalidDepthParameterTest() {
+
+ ControllerContext.getInstance().setGlobalSchema( schemaContextModules );
+
+ final MultivaluedMap<String,String> paramMap = new MultivaluedHashMap<>();
+ paramMap.putSingle( "depth", "1o" );
+ UriInfo mockInfo = mock( UriInfo.class );
+ when( mockInfo.getQueryParameters( false ) ).thenAnswer(
+ new Answer<MultivaluedMap<String,String>>() {
+ @Override
+ public MultivaluedMap<String, String> answer( InvocationOnMock invocation ) {
+ return paramMap;
+ }
+ } );
+
+ getDataWithInvalidDepthParameterTest( mockInfo );
+
+ paramMap.putSingle( "depth", "0" );
+ getDataWithInvalidDepthParameterTest( mockInfo );
+
+ paramMap.putSingle( "depth", "-1" );
+ getDataWithInvalidDepthParameterTest( mockInfo );
+ }
+
+ private void getDataWithInvalidDepthParameterTest( UriInfo uriInfo ) {
+ try {
+ restconfImpl.readConfigurationData( "nested-module:depth1-cont", uriInfo );
+ fail( "Expected RestconfDocumentedException" );
+ }
+ catch( RestconfDocumentedException e ) {
+ assertTrue( "Unexpected error message: " + e.getErrors().get( 0 ).getErrorMessage(),
+ e.getErrors().get( 0 ).getErrorMessage().contains( "depth" ) );
+ }
+ }
+
+ private void verifyXMLResponse( Response response, NodeData nodeData ) {
+
+ Document doc = TestUtils.loadDocumentFrom( (InputStream) response.getEntity() );
+ assertNotNull( "Could not parse XML document", doc );
+
+ //System.out.println(TestUtils.getDocumentInPrintableForm( doc ));
+
+ verifyContainerElement( doc.getDocumentElement(), nodeData );
+ }
+
+ @SuppressWarnings("unchecked")
+ private void verifyContainerElement( Element element, NodeData nodeData ) {
+
+ assertEquals( "Element local name", nodeData.key, element.getNodeName() );
+
+ NodeList childNodes = element.getChildNodes();
+ if( nodeData.data == null ) { // empty container
+ assertTrue( "Expected no child elements for \"" + element.getNodeName() + "\"",
+ childNodes.getLength() == 0 );
+ return;
+ }
+
+ Map<String,NodeData> expChildMap = Maps.newHashMap();
+ for( NodeData expChild: (List<NodeData>)nodeData.data ) {
+ expChildMap.put( expChild.key.toString(), expChild );
+ }
+
+ for( int i = 0; i < childNodes.getLength(); i++ ) {
+ org.w3c.dom.Node actualChild = childNodes.item( i );
+ if( !( actualChild instanceof Element ) ) {
+ continue;
+ }
+
+ Element actualElement = (Element)actualChild;
+ NodeData expChild = expChildMap.remove( actualElement.getNodeName() );
+ assertNotNull( "Unexpected child element for parent \"" + element.getNodeName() +
+ "\": " + actualElement.getNodeName(), expChild );
+
+ if( expChild.data == null || expChild.data instanceof List ) {
+ verifyContainerElement( actualElement, expChild );
+ }
+ else {
+ assertEquals( "Text content for element: " + actualElement.getNodeName(),
+ expChild.data, actualElement.getTextContent() );
+ }
+ }
+
+ if( !expChildMap.isEmpty() ) {
+ fail( "Missing elements for parent \"" + element.getNodeName() +
+ "\": " + expChildMap.keySet() );
+ }
+ }
+
+ private NodeData expectContainer( String name, NodeData... childData ) {
+ return new NodeData( name, Lists.newArrayList( childData ) );
+ }
+
+ private NodeData expectEmptyContainer( String name ) {
+ return new NodeData( name, null );
+ }
+
+ private NodeData expectLeaf( String name, Object value ) {
+ return new NodeData( name, value );
+ }
+
+ private QName toNestedQName( String localName ) {
+ return QName.create( "urn:nested:module", "2014-06-3", localName );
+ }
+
+ @SuppressWarnings("unchecked")
+ private CompositeNode toCompositeNode( NodeData nodeData ) {
+ CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
+ builder.setQName( (QName) nodeData.key );
+
+ for( NodeData child: (List<NodeData>)nodeData.data ) {
+ if( child.data instanceof List ) {
+ builder.add( toCompositeNode( child ) );
+ }
+ else {
+ builder.addLeaf( (QName) child.key, child.data );
+ }
+ }
+
+ return builder.toInstance();
+ }
+
+ private NodeData toCompositeNodeData( QName key, NodeData... childData ) {
+ return new NodeData( key, Lists.newArrayList( childData ) );
+ }
+
+ private NodeData toSimpleNodeData( QName key, Object value ) {
+ return new NodeData( key, value );
+ }
}
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
void stageMockEx( final RestconfDocumentedException ex ) {
reset( mockRestConf );
- when( mockRestConf.readOperationalData( any( String.class ) ) ).thenThrow( ex );
+ when( mockRestConf.readOperationalData( any( String.class ), any( UriInfo.class ) ) ).thenThrow( ex );
}
void testJsonResponse( final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
// The StructuredDataToJsonProvider should throw a RestconfDocumentedException with no data
- when( mockRestConf.readOperationalData( any( String.class ) ) )
- .thenReturn( new StructuredData( null, null, null ) );
+ when( mockRestConf.readOperationalData( any( String.class ), any( UriInfo.class ) ) )
+ .thenReturn( new StructuredData( null, null, null ) );
Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
module basic-module {
- namespace "basic:module";
-
- prefix "basmod";
-
- import referenced-module {prefix refmo; revision-date 2013-12-2;}
-
- revision 2013-12-2 {
- }
-
- container cont {
- container cont1 {
- leaf lf11 {
- type identityref {
- base "refmo:iden";
- }
- }
- }
- leaf lfStr {
- type string;
- }
- leaf lfInt8 {
- type int8;
- }
-
- leaf lfInt16 {
- type int16;
- }
-
- leaf lfInt32 {
- type int32;
- }
-
- leaf lfInt64 {
- type int64;
- }
-
- leaf lfUint8 {
- type uint8;
- }
-
- leaf lfUint16 {
- type uint16;
- }
-
- leaf lfUint32 {
- type uint32;
- }
-
- leaf lfUint64 {
- type uint64;
- }
-
- leaf lfBinary {
- type binary;
- }
-
- leaf lfBits {
- type bits {
- bit one;
- bit two;
- bit three;
- }
- }
-
- leaf lfEnumeration {
- type enumeration {
- enum enum1;
- enum enum2;
- enum enum3;
- }
- }
-
- leaf lfEmpty {
- type empty;
- }
-
- leaf lfBoolean {
- type boolean;
- }
-
- leaf lfUnion {
- type union {
- type int8;
- type string;
- type bits {
- bit first;
- bit second;
- }
- type boolean;
- }
- }
-
- leaf lfLfref {
- type leafref {
- path "/cont/lfBoolean";
- }
- }
-
- leaf lfInIdentifier {
- type instance-identifier;
- }
-
- }
-
-}
\ No newline at end of file
+ namespace "basic:module";
+
+ prefix "basmod";
+
+ import referenced-module {prefix refmo; revision-date 2013-12-2;}
+
+ revision 2013-12-2 {
+ }
+
+ container cont {
+ container cont1 {
+ leaf lf11 {
+ type identityref {
+ base "refmo:iden";
+ }
+ }
+ }
+ leaf lfStr {
+ type string;
+ }
+ leaf lfInt8 {
+ type int8;
+ }
+
+ leaf lfInt16 {
+ type int16;
+ }
+
+ leaf lfInt32 {
+ type int32;
+ }
+
+ leaf lfInt64 {
+ type int64;
+ }
+
+ leaf lfUint8 {
+ type uint8;
+ }
+
+ leaf lfUint16 {
+ type uint16;
+ }
+
+ leaf lfUint32 {
+ type uint32;
+ }
+
+ leaf lfUint64 {
+ type uint64;
+ }
+
+ leaf lfBinary {
+ type binary;
+ }
+
+ leaf lfBits {
+ type bits {
+ bit one;
+ bit two;
+ bit three;
+ }
+ }
+
+ leaf lfEnumeration {
+ type enumeration {
+ enum enum1;
+ enum enum2;
+ enum enum3;
+ }
+ }
+
+ leaf lfEmpty {
+ type empty;
+ }
+
+ leaf lfBoolean {
+ type boolean;
+ }
+
+ leaf lfUnion {
+ type union {
+ type int8;
+ type string;
+ type bits {
+ bit first;
+ bit second;
+ }
+ type boolean;
+ }
+ }
+
+ leaf lfLfref {
+ type leafref {
+ path "/cont/lfBoolean";
+ }
+ }
+
+ leaf lfInIdentifier {
+ type instance-identifier;
+ }
+
+ }
+
+}
--- /dev/null
+module nested-module {
+ namespace "urn:nested:module";
+ prefix "nested";
+ revision "2014-06-3";
+
+ container depth1-cont {
+ container depth2-cont1 {
+ container depth3-cont1 {
+ container depth4-cont1 {
+ leaf depth5-leaf1 {
+ type string;
+ }
+ }
+
+ leaf depth4-leaf1 {
+ type string;
+ }
+ }
+
+ leaf depth3-leaf1 {
+ type string;
+ }
+ }
+
+ container depth2-cont2 {
+ container depth3-cont2 {
+ container depth4-cont2 {
+ leaf depth5-leaf2 {
+ type string;
+ }
+ }
+
+ leaf depth4-leaf2 {
+ type string;
+ }
+ }
+
+ leaf depth3-leaf2 {
+ type string;
+ }
+ }
+
+ leaf depth2-leaf1 {
+ type string;
+ }
+ }
+}
\ No newline at end of file
<artifactId>guava</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+
<!-- Jax rs -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
import org.opendaylight.controller.sal.core.api.Broker;
import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener;
import org.opendaylight.controller.sal.rest.doc.impl.ApiDocGenerator;
+import org.opendaylight.controller.sal.rest.doc.mountpoints.MountPointSwagger;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class DocProvider implements BundleActivator, ServiceTrackerCustomizer<Broker, Broker>, Provider, AutoCloseable {
+public class DocProvider implements BundleActivator, ServiceTrackerCustomizer<Broker, Broker>,
+ Provider, AutoCloseable {
- private static final Logger _logger = LoggerFactory.getLogger(DocProvider.class);
+ private final Logger _logger = LoggerFactory.getLogger(DocProvider.class);
private ServiceTracker<Broker, Broker> brokerServiceTracker;
private BundleContext bundleContext;
private Broker.ProviderSession session;
+ private final List<AutoCloseable> toClose = new LinkedList<>();
+
@Override
public void close() throws Exception {
stop(bundleContext);
}
@Override
- public void onSessionInitiated(final Broker.ProviderSession providerSession) {
+ public void onSessionInitiated(Broker.ProviderSession providerSession) {
SchemaService schemaService = providerSession.getService(SchemaService.class);
ApiDocGenerator.getInstance().setSchemaService(schemaService);
+ MountProvisionService mountService = providerSession
+ .getService(MountProvisionService.class);
+ ListenerRegistration<MountProvisionListener> registration = mountService
+ .registerProvisionListener(MountPointSwagger.getInstance());
+ MountPointSwagger.getInstance().setGlobalSchema(schemaService);
+ synchronized (toClose) {
+ toClose.add(registration);
+ }
+ MountPointSwagger.getInstance().setMountService(mountService);
+
_logger.debug("Restconf API Explorer started");
}
}
@Override
- public void start(final BundleContext context) throws Exception {
+ public void start(BundleContext context) throws Exception {
bundleContext = context;
- brokerServiceTracker = new ServiceTracker<>(context, Broker.class, this);
+ brokerServiceTracker = new ServiceTracker(context, Broker.class, this);
brokerServiceTracker.open();
}
@Override
- public void stop(final BundleContext context) throws Exception {
- if (brokerServiceTracker != null) {
+ public void stop(BundleContext context) throws Exception {
+ if (brokerServiceTracker != null)
brokerServiceTracker.close();
- }
- if (session != null) {
+ if (session != null)
session.close();
+
+ synchronized (toClose) {
+ for (AutoCloseable close : toClose) {
+ close.close();
+ }
}
}
@Override
- public Broker addingService(final ServiceReference<Broker> reference) {
+ public Broker addingService(ServiceReference<Broker> reference) {
Broker broker = bundleContext.getService(reference);
session = broker.registerProvider(this, bundleContext);
return broker;
}
@Override
- public void modifiedService(final ServiceReference<Broker> reference, final Broker service) {
- if (session != null) {
+ public void modifiedService(ServiceReference<Broker> reference, Broker service) {
+ if (session != null)
session.close();
- }
Broker broker = bundleContext.getService(reference);
session = broker.registerProvider(this, bundleContext);
}
@Override
- public void removedService(final ServiceReference<Broker> reference, final Broker service) {
+ public void removedService(ServiceReference<Broker> reference, Broker service) {
bundleContext.ungetService(reference);
}
}
import javax.ws.rs.core.Response;
/**
- * This service generates swagger
- * (See <a href="https://helloreverb.com/developers/swagger">https://helloreverb.com/developers/swagger</a>)
- * compliant documentation for RESTCONF APIs. The output of this is used by embedded Swagger UI.
+ * This service generates swagger (See <a
+ * href="https://helloreverb.com/developers/swagger"
+ * >https://helloreverb.com/developers/swagger</a>) compliant documentation for
+ * RESTCONF APIs. The output of this is used by embedded Swagger UI.
*/
@Path("/")
public interface ApiDocService {
- /**
- * Generates index document for Swagger UI. This document lists out all modules with link to get APIs for
- * each module. The API for each module is served by <code> getDocByModule()</code> method.
- *
- * @param uriInfo
- * @return
- */
- @GET
- @Produces(MediaType.APPLICATION_JSON)
- public Response getRootDoc(@Context javax.ws.rs.core.UriInfo uriInfo);
-
- /**
- * Generates Swagger compliant document listing APIs for module.
- *
- * @param module
- * @param revision
- * @param uriInfo
- * @return
- */
- @GET
- @Path("/{module},{revision}")
- @Produces(MediaType.APPLICATION_JSON)
- public Response getDocByModule(@PathParam("module") String module,
- @PathParam("revision") String revision,
- @Context javax.ws.rs.core.UriInfo uriInfo);
-
- /**
- * Redirects to embedded swagger ui.
- *
- * @param uriInfo
- * @return
- */
- @GET
- @Path("/ui")
- @Produces(MediaType.TEXT_HTML)
- public Response getApiExplorer(@Context javax.ws.rs.core.UriInfo uriInfo);
+ /**
+ * Generates index document for Swagger UI. This document lists out all
+ * modules with link to get APIs for each module. The API for each module is
+ * served by <code> getDocByModule()</code> method.
+ *
+ * @param uriInfo
+ * @return
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getRootDoc(@Context javax.ws.rs.core.UriInfo uriInfo);
+
+ /**
+ * Generates Swagger compliant document listing APIs for module.
+ *
+ * @param module
+ * @param revision
+ * @param uriInfo
+ * @return
+ */
+ @GET
+ @Path("/{module}({revision})")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getDocByModule(@PathParam("module") String module,
+ @PathParam("revision") String revision, @Context javax.ws.rs.core.UriInfo uriInfo);
+
+ /**
+ * Redirects to embedded swagger ui.
+ *
+ * @param uriInfo
+ * @return
+ */
+ @GET
+ @Path("/ui")
+ @Produces(MediaType.TEXT_HTML)
+ public Response getApiExplorer(@Context javax.ws.rs.core.UriInfo uriInfo);
+
+ /**
+ * Generates index document for Swagger UI. This document lists out all
+ * modules with link to get APIs for each module. The API for each module is
+ * served by <code> getDocByModule()</code> method.
+ *
+ * @param uriInfo
+ * @return
+ */
+ @GET
+ @Path("/mounts")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getListOfMounts(@Context javax.ws.rs.core.UriInfo uriInfo);
+
+ @GET
+ @Path("/mounts/{instance}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getMountRootDoc(@PathParam("instance") String instanceNum,
+ @Context javax.ws.rs.core.UriInfo uriInfo);
+
+ /**
+ * Generates Swagger compliant document listing APIs for module.
+ *
+ * @param module
+ * @param revision
+ * @param uriInfo
+ * @return
+ */
+ @GET
+ @Path("/mounts/{instance}/{module}({revision})")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getMountDocByModule(@PathParam("instance") String instanceNum,
+ @PathParam("module") String module, @PathParam("revision") String revision,
+ @Context javax.ws.rs.core.UriInfo uriInfo);
+
}
*/
package org.opendaylight.controller.sal.rest.doc.impl;
-import java.io.IOException;
-import java.net.URI;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
import javax.ws.rs.core.UriInfo;
-import org.json.JSONException;
-import org.json.JSONObject;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
-import org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder;
-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.common.QName;
-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.LeafSchemaNode;
-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.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
import com.google.common.base.Preconditions;
/**
- * This class gathers all yang defined {@link Module}s and generates Swagger compliant documentation.
+ * This class gathers all yang defined {@link Module}s and generates Swagger
+ * compliant documentation.
*/
-public class ApiDocGenerator {
+public class ApiDocGenerator extends BaseYangSwaggerGenerator {
- private static final Logger _logger = LoggerFactory.getLogger(ApiDocGenerator.class);
+ private static Logger _logger = LoggerFactory.getLogger(ApiDocGenerator.class);
private static final ApiDocGenerator INSTANCE = new ApiDocGenerator();
- private final ObjectMapper mapper = new ObjectMapper();
- private final ModelGenerator jsonConverter = new ModelGenerator();
-
private SchemaService schemaService;
- private static final String API_VERSION = "1.0.0";
- private static final String SWAGGER_VERSION = "1.2";
- private static final String RESTCONF_CONTEXT_ROOT = "restconf";
- private final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
-
- //For now its {@link HashMap}. It will be changed to thread-safe Map when schema change listener is implemented.
- private final Map<String, ApiDeclaration> MODULE_DOC_CACHE = new HashMap<String, ApiDeclaration>();
+ public ResourceList getResourceListing(UriInfo uriInfo) {
+ Preconditions.checkState(schemaService != null);
+ SchemaContext schemaContext = schemaService.getGlobalContext();
+ Preconditions.checkState(schemaContext != null);
+ return super.getResourceListing(uriInfo, schemaContext, "");
+ }
- private ApiDocGenerator(){
- mapper.registerModule(new JsonOrgModule());
- mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
+ public ApiDeclaration getApiDeclaration(String module, String revision, UriInfo uriInfo) {
+ SchemaContext schemaContext = schemaService.getGlobalContext();
+ Preconditions.checkState(schemaContext != null);
+ return super.getApiDeclaration(module, revision, uriInfo, schemaContext, "");
}
/**
* Returns singleton instance
+ *
* @return
*/
public static ApiDocGenerator getInstance() {
*
* @param schemaService
*/
- public void setSchemaService(final SchemaService schemaService) {
+ public void setSchemaService(SchemaService schemaService) {
this.schemaService = schemaService;
}
- /**
- *
- * @param uriInfo
- * @return list of modules converted to swagger compliant resource list.
- */
- public ResourceList getResourceListing(final UriInfo uriInfo) {
-
- Preconditions.checkState(schemaService != null);
- SchemaContext schemaContext = schemaService.getGlobalContext();
- Preconditions.checkState(schemaContext != null);
-
- Set<Module> modules = schemaContext.getModules();
-
- ResourceList resourceList = new ResourceList();
- resourceList.setApiVersion(API_VERSION);
- resourceList.setSwaggerVersion(SWAGGER_VERSION);
-
- List<Resource> resources = new ArrayList<>(modules.size());
- _logger.info("Modules found [{}]", modules.size());
-
- for (Module module : modules) {
- Resource resource = new Resource();
- String revisionString = SIMPLE_DATE_FORMAT.format(module.getRevision());
-
- _logger.debug("Working on [{},{}]...", module.getName(), revisionString);
- ApiDeclaration doc = getApiDeclaration(module.getName(), revisionString, uriInfo);
-
- if (doc != null) {
- URI uri = uriInfo.getRequestUriBuilder().
- path(generateCacheKey(module.getName(), revisionString)).
- build();
-
- resource.setPath(uri.toASCIIString());
- resources.add(resource);
- } else {
- _logger.debug("Could not generate doc for {},{}", module.getName(), revisionString);
- }
- }
-
- resourceList.setApis(resources);
-
- return resourceList;
- }
-
- public ApiDeclaration getApiDeclaration(final String module, final String revision, final UriInfo uriInfo) {
-
- //Lookup cache
- String cacheKey = generateCacheKey(module, revision);
-
- if (MODULE_DOC_CACHE.containsKey(cacheKey)) {
- _logger.debug("Serving from cache for {}", cacheKey);
- return MODULE_DOC_CACHE.get(cacheKey);
- }
-
- Date rev = null;
- try {
- rev = SIMPLE_DATE_FORMAT.parse(revision);
- } catch (ParseException e) {
- throw new IllegalArgumentException(e);
- }
-
- SchemaContext schemaContext = schemaService.getGlobalContext();
- Preconditions.checkState(schemaContext != null);
-
- Module m = schemaContext.findModuleByName(module, rev);
- Preconditions.checkArgument(m != null, "Could not find module by name,revision: " + module + "," + revision);
-
- String basePath = new StringBuilder(uriInfo.getBaseUri().getScheme())
- .append("://")
- .append(uriInfo.getBaseUri().getHost())
- .append(":")
- .append(uriInfo.getBaseUri().getPort())
- .append("/")
- .append(RESTCONF_CONTEXT_ROOT)
- .toString();
-
- ApiDeclaration doc = getSwaggerDocSpec(m, basePath);
- MODULE_DOC_CACHE.put(cacheKey, doc);
- return doc;
- }
-
- public ApiDeclaration getSwaggerDocSpec(final Module m, final String basePath) {
- ApiDeclaration doc = new ApiDeclaration();
- doc.setApiVersion(API_VERSION);
- doc.setSwaggerVersion(SWAGGER_VERSION);
- doc.setBasePath(basePath);
- doc.setProduces(Arrays.asList("application/json", "application/xml"));
-
- List<Api> apis = new ArrayList<Api>();
-
- Set<DataSchemaNode> dataSchemaNodes = m.getChildNodes();
- _logger.debug("child nodes size [{}]", dataSchemaNodes.size());
- for (DataSchemaNode node : dataSchemaNodes) {
- if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) {
-
- _logger.debug("Is Configuration node [{}] [{}]", node.isConfiguration(), node.getQName().getLocalName());
-
- List<Parameter> pathParams = null;
- if (node.isConfiguration()) {
- pathParams = new ArrayList<Parameter>();
- String resourcePath = "/config/" + m.getName() + ":";
- addApis(node, apis, resourcePath, pathParams, true);
-
- }
-
- pathParams = new ArrayList<Parameter>();
- String resourcePath = "/operational/" + m.getName() + ":";
- addApis(node, apis, resourcePath, pathParams, false);
- }
- }
-
- Set<RpcDefinition> rpcs = m.getRpcs();
- for (RpcDefinition rpcDefinition : rpcs) {
- String resourcePath = "/operations/" + m.getName() + ":";
- addRpcs(rpcDefinition, apis, resourcePath);
-
- }
- _logger.debug("Number of APIs found [{}]", apis.size());
- doc.setApis(apis);
- JSONObject models = null;
-
- try {
- models = jsonConverter.convertToJsonSchema(m);
- doc.setModels(models);
- _logger.debug(mapper.writeValueAsString(doc));
- } catch (IOException | JSONException e) {
- e.printStackTrace();
- }
-
- return doc;
- }
-
- private String generateCacheKey(final Module m) {
- return generateCacheKey(m.getName(), SIMPLE_DATE_FORMAT.format(m.getRevision()));
- }
-
- private String generateCacheKey(final String module, final String revision) {
- return module + "," + revision;
- }
-
- private void addApis(final DataSchemaNode node,
- final List<Api> apis,
- final String parentPath,
- final List<Parameter> parentPathParams,
- final boolean addConfigApi) {
-
- Api api = new Api();
- List<Parameter> pathParams = new ArrayList<Parameter>(parentPathParams);
-
- String resourcePath = parentPath + createPath(node, pathParams) + "/";
- _logger.debug("Adding path: [{}]", resourcePath);
- api.setPath(resourcePath);
- api.setOperations(operations(node, pathParams, addConfigApi));
- apis.add(api);
- if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) {
- DataNodeContainer schemaNode = (DataNodeContainer) node;
- Set<DataSchemaNode> dataSchemaNodes = schemaNode.getChildNodes();
-
- for (DataSchemaNode childNode : dataSchemaNodes) {
- addApis(childNode, apis, resourcePath, pathParams, addConfigApi);
- }
- }
-
- }
-
- private void addRpcs(final RpcDefinition rpcDefn, final List<Api> apis, final String parentPath) {
- Api rpc = new Api();
- String resourcePath = parentPath + rpcDefn.getQName().getLocalName();
- rpc.setPath(resourcePath);
-
- Operation operationSpec = new Operation();
- operationSpec.setMethod("POST");
- operationSpec.setNotes(rpcDefn.getDescription());
- operationSpec.setNickname(rpcDefn.getQName().getLocalName());
- rpc.setOperations(Arrays.asList(operationSpec));
-
- apis.add(rpc);
- }
-
- /**
- * @param node
- * @param pathParams
- * @return
- */
- private List<Operation> operations(final DataSchemaNode node, final List<Parameter> pathParams, final boolean isConfig) {
- List<Operation> operations = new ArrayList<>();
-
- OperationBuilder.Get getBuilder = new OperationBuilder.Get(node);
- 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);
- operations.add(putBuilder.pathParams(pathParams).build());
-
- OperationBuilder.Delete deleteBuilder = new OperationBuilder.Delete(node);
- operations.add(deleteBuilder.pathParams(pathParams).build());
- }
- return operations;
- }
-
- private String createPath(final DataSchemaNode schemaNode, final List<Parameter> pathParams) {
- ArrayList<LeafSchemaNode> pathListParams = new ArrayList<LeafSchemaNode>();
- StringBuilder path = new StringBuilder();
- QName _qName = schemaNode.getQName();
- String localName = _qName.getLocalName();
- path.append(localName);
-
- if ((schemaNode instanceof ListSchemaNode)) {
- final List<QName> listKeys = ((ListSchemaNode) schemaNode).getKeyDefinition();
- for (final QName listKey : listKeys) {
- {
- DataSchemaNode _dataChildByName = ((DataNodeContainer) schemaNode).getDataChildByName(listKey);
- pathListParams.add(((LeafSchemaNode) _dataChildByName));
-
- String pathParamIdentifier = new StringBuilder("/{").append(listKey.getLocalName()).append("}").toString();
- path.append(pathParamIdentifier);
-
- Parameter pathParam = new Parameter();
- pathParam.setName(listKey.getLocalName());
- pathParam.setDescription(_dataChildByName.getDescription());
- pathParam.setType("string");
- pathParam.setParamType("path");
-
- pathParams.add(pathParam);
- }
- }
- }
- return path.toString();
- }
-
}
*/
package org.opendaylight.controller.sal.rest.doc.impl;
-import org.opendaylight.controller.sal.rest.doc.api.ApiDocService;
-import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
-import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Map.Entry;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+import org.json.JSONWriter;
+import org.opendaylight.controller.sal.rest.doc.api.ApiDocService;
+import org.opendaylight.controller.sal.rest.doc.mountpoints.MountPointSwagger;
+import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
+import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList;
+
/**
- * This service generates swagger
- * (See <a href="https://helloreverb.com/developers/swagger">https://helloreverb.com/developers/swagger</a>)
- * compliant documentation for RESTCONF APIs. The output of this is used by embedded Swagger UI.
+ * This service generates swagger (See <a
+ * href="https://helloreverb.com/developers/swagger"
+ * >https://helloreverb.com/developers/swagger</a>) compliant documentation for
+ * RESTCONF APIs. The output of this is used by embedded Swagger UI.
+ *
+ * NOTE: These API's need to be synchronized due to bug 1198. Thread access to
+ * the SchemaContext is not synchronized properly and thus you can end up with
+ * missing definitions without this synchronization. There are likely otherways
+ * to work around this limitation, but given that this API is a dev only tool
+ * and not dependent UI, this was the fastest work around.
+ *
*/
public class ApiDocServiceImpl implements ApiDocService {
- private static final ApiDocService INSTANCE = new ApiDocServiceImpl();
+ private static final ApiDocService INSTANCE = new ApiDocServiceImpl();
+
+ public static ApiDocService getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Generates index document for Swagger UI. This document lists out all
+ * modules with link to get APIs for each module. The API for each module is
+ * served by <code> getDocByModule()</code> method.
+ *
+ * @param uriInfo
+ * @return
+ */
+ @Override
+ public synchronized Response getRootDoc(UriInfo uriInfo) {
+ ApiDocGenerator generator = ApiDocGenerator.getInstance();
+ ResourceList rootDoc = generator.getResourceListing(uriInfo);
+
+ return Response.ok(rootDoc).build();
+ }
- public static ApiDocService getInstance(){
- return INSTANCE;
- }
+ /**
+ * Generates Swagger compliant document listing APIs for module.
+ *
+ * @param module
+ * @param revision
+ * @param uriInfo
+ * @return
+ */
+ @Override
+ public synchronized Response getDocByModule(String module, String revision, UriInfo uriInfo) {
+ ApiDocGenerator generator = ApiDocGenerator.getInstance();
- /**
- * Generates index document for Swagger UI. This document lists out all modules with link to get APIs for
- * each module. The API for each module is served by <code> getDocByModule()</code> method.
- *
- * @param uriInfo
- * @return
- */
- @Override
- public Response getRootDoc(UriInfo uriInfo) {
+ ApiDeclaration doc = generator.getApiDeclaration(module, revision, uriInfo);
+ return Response.ok(doc).build();
+ }
- ApiDocGenerator generator = ApiDocGenerator.getInstance();
- ResourceList rootDoc = generator.getResourceListing(uriInfo);
+ /**
+ * Redirects to embedded swagger ui.
+ *
+ * @param uriInfo
+ * @return
+ */
+ @Override
+ public synchronized Response getApiExplorer(UriInfo uriInfo) {
+ return Response
+ .seeOther(uriInfo.getBaseUriBuilder().path("../explorer/index.html").build())
+ .build();
+ }
- return Response.ok(rootDoc).build();
- }
+ @Override
+ public synchronized Response getListOfMounts(UriInfo uriInfo) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (OutputStreamWriter streamWriter = new OutputStreamWriter(baos)) {
+ JSONWriter writer = new JSONWriter(streamWriter);
+ writer.array();
+ for (Entry<String, Long> entry : MountPointSwagger.getInstance()
+ .getInstanceIdentifiers().entrySet()) {
+ writer.object();
+ writer.key("instance").value(entry.getKey());
+ writer.key("id").value(entry.getValue());
+ writer.endObject();
+ }
+ writer.endArray();
+ } catch (Exception e) {
+ return Response.status(500).entity(e.getMessage()).build();
+ }
+ return Response.status(200).entity(baos.toString()).build();
+ }
- /**
- * Generates Swagger compliant document listing APIs for module.
- *
- * @param module
- * @param revision
- * @param uriInfo
- * @return
- */
- @Override
- public Response getDocByModule(String module, String revision, UriInfo uriInfo) {
- ApiDocGenerator generator = ApiDocGenerator.getInstance();
+ @Override
+ public synchronized Response getMountRootDoc(String instanceNum, UriInfo uriInfo) {
+ ResourceList resourceList = MountPointSwagger.getInstance().getResourceList(uriInfo,
+ Long.parseLong(instanceNum));
+ return Response.ok(resourceList).build();
+ }
- ApiDeclaration doc = generator.getApiDeclaration(module, revision, uriInfo);
- return Response.ok(doc).build();
- }
+ @Override
+ public synchronized Response getMountDocByModule(String instanceNum, String module,
+ String revision, UriInfo uriInfo) {
+ ApiDeclaration api = MountPointSwagger.getInstance().getMountPointApi(uriInfo,
+ Long.parseLong(instanceNum), module, revision);
+ return Response.ok(api).build();
+ }
- /**
- * Redirects to embedded swagger ui.
- *
- * @param uriInfo
- * @return
- */
- @Override
- public Response getApiExplorer(UriInfo uriInfo) {
- return Response.seeOther(uriInfo.getBaseUriBuilder().path("../explorer/index.html").build()).build();
- }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.rest.doc.impl;
+
+import java.io.IOException;
+import java.net.URI;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.ws.rs.core.UriInfo;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder;
+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.common.QName;
+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.LeafSchemaNode;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
+import com.google.common.base.Preconditions;
+
+public class BaseYangSwaggerGenerator {
+
+ private static Logger _logger = LoggerFactory.getLogger(BaseYangSwaggerGenerator.class);
+
+ 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";
+ protected final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+ private final ModelGenerator jsonConverter = new ModelGenerator();
+
+ // private Map<String, ApiDeclaration> MODULE_DOC_CACHE = new HashMap<>()
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ protected BaseYangSwaggerGenerator() {
+ mapper.registerModule(new JsonOrgModule());
+ mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
+ }
+
+ /**
+ *
+ * @param uriInfo
+ * @param operType
+ * @return list of modules converted to swagger compliant resource list.
+ */
+ public ResourceList getResourceListing(UriInfo uriInfo, SchemaContext schemaContext,
+ String context) {
+
+ ResourceList resourceList = createResourceList();
+
+ Set<Module> modules = getSortedModules(schemaContext);
+
+ List<Resource> resources = new ArrayList<>(modules.size());
+
+ _logger.info("Modules found [{}]", modules.size());
+
+ for (Module module : modules) {
+ String revisionString = SIMPLE_DATE_FORMAT.format(module.getRevision());
+
+ Resource resource = new Resource();
+ _logger.debug("Working on [{},{}]...", module.getName(), revisionString);
+ ApiDeclaration doc = getApiDeclaration(module.getName(), revisionString, uriInfo,
+ schemaContext, context);
+
+ if (doc != null) {
+ resource.setPath(generatePath(uriInfo, module.getName(), revisionString));
+ resources.add(resource);
+ } else {
+ _logger.debug("Could not generate doc for {},{}", module.getName(), revisionString);
+ }
+ }
+
+ resourceList.setApis(resources);
+
+ return resourceList;
+ }
+
+ protected ResourceList createResourceList() {
+ ResourceList resourceList = new ResourceList();
+ resourceList.setApiVersion(API_VERSION);
+ resourceList.setSwaggerVersion(SWAGGER_VERSION);
+ return resourceList;
+ }
+
+ protected String generatePath(UriInfo uriInfo, String name, String revision) {
+ URI uri = uriInfo.getRequestUriBuilder().path(generateCacheKey(name, revision)).build();
+ return uri.toASCIIString();
+ }
+
+ public ApiDeclaration getApiDeclaration(String module, String revision, UriInfo uriInfo,
+ SchemaContext schemaContext, String context) {
+ Date rev = null;
+ try {
+ rev = SIMPLE_DATE_FORMAT.parse(revision);
+ } catch (ParseException e) {
+ throw new IllegalArgumentException(e);
+ }
+ Module m = schemaContext.findModuleByName(module, rev);
+ Preconditions.checkArgument(m != null, "Could not find module by name,revision: " + module
+ + "," + revision);
+
+ return getApiDeclaration(m, rev, uriInfo, schemaContext, context);
+ }
+
+ public ApiDeclaration getApiDeclaration(Module module, Date revision, UriInfo uriInfo,
+ SchemaContext schemaContext, String context) {
+ String basePath = createBasePathFromUriInfo(uriInfo);
+
+ ApiDeclaration doc = getSwaggerDocSpec(module, basePath, context);
+ if (doc != null) {
+ return doc;
+ }
+ return null;
+ }
+
+ protected String createBasePathFromUriInfo(UriInfo uriInfo) {
+ String portPart = "";
+ int port = uriInfo.getBaseUri().getPort();
+ if (port != -1) {
+ portPart = ":" + port;
+ }
+ String basePath = new StringBuilder(uriInfo.getBaseUri().getScheme()).append("://")
+ .append(uriInfo.getBaseUri().getHost()).append(portPart).append("/")
+ .append(RESTCONF_CONTEXT_ROOT).toString();
+ return basePath;
+ }
+
+ public ApiDeclaration getSwaggerDocSpec(Module m, String basePath, String context) {
+ ApiDeclaration doc = createApiDeclaration(basePath);
+
+ List<Api> apis = new ArrayList<Api>();
+
+ Set<DataSchemaNode> dataSchemaNodes = m.getChildNodes();
+ _logger.debug("child nodes size [{}]", dataSchemaNodes.size());
+ for (DataSchemaNode node : dataSchemaNodes) {
+ if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) {
+
+ _logger.debug("Is Configuration node [{}] [{}]", node.isConfiguration(), node
+ .getQName().getLocalName());
+
+ List<Parameter> pathParams = new ArrayList<Parameter>();
+ String resourcePath = getDataStorePath("/config/", context) + m.getName() + ":";
+ addApis(node, apis, resourcePath, pathParams, true);
+
+ pathParams = new ArrayList<Parameter>();
+ resourcePath = getDataStorePath("/operational/", context) + m.getName() + ":";
+ addApis(node, apis, resourcePath, pathParams, false);
+ }
+
+ Set<RpcDefinition> rpcs = m.getRpcs();
+ for (RpcDefinition rpcDefinition : rpcs) {
+ String resourcePath = getDataStorePath("/operations/", context) + m.getName() + ":";
+ addRpcs(rpcDefinition, apis, resourcePath);
+ }
+ }
+
+ _logger.debug("Number of APIs found [{}]", apis.size());
+
+ if (!apis.isEmpty()) {
+ doc.setApis(apis);
+ JSONObject models = null;
+
+ try {
+ models = jsonConverter.convertToJsonSchema(m);
+ doc.setModels(models);
+ if (_logger.isDebugEnabled()) {
+ _logger.debug(mapper.writeValueAsString(doc));
+ }
+ } catch (IOException | JSONException e) {
+ e.printStackTrace();
+ }
+
+ return doc;
+ }
+ return null;
+ }
+
+ protected ApiDeclaration createApiDeclaration(String basePath) {
+ ApiDeclaration doc = new ApiDeclaration();
+ doc.setApiVersion(API_VERSION);
+ doc.setSwaggerVersion(SWAGGER_VERSION);
+ doc.setBasePath(basePath);
+ doc.setProduces(Arrays.asList("application/json", "application/xml"));
+ return doc;
+ }
+
+ protected String getDataStorePath(String dataStore, String context) {
+ return dataStore + context;
+ }
+
+ private String generateCacheKey(Module m) {
+ return generateCacheKey(m.getName(), SIMPLE_DATE_FORMAT.format(m.getRevision()));
+ }
+
+ private String generateCacheKey(String module, String revision) {
+ return module + "(" + revision + ")";
+ }
+
+ private void addApis(DataSchemaNode node, List<Api> apis, String parentPath,
+ List<Parameter> parentPathParams, boolean addConfigApi) {
+
+ Api api = new Api();
+ List<Parameter> pathParams = new ArrayList<Parameter>(parentPathParams);
+
+ String resourcePath = parentPath + createPath(node, pathParams) + "/";
+ _logger.debug("Adding path: [{}]", resourcePath);
+ api.setPath(resourcePath);
+ api.setOperations(operations(node, pathParams, addConfigApi));
+ apis.add(api);
+ if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) {
+ DataNodeContainer schemaNode = (DataNodeContainer) node;
+ Set<DataSchemaNode> dataSchemaNodes = schemaNode.getChildNodes();
+
+ for (DataSchemaNode childNode : dataSchemaNodes) {
+ // 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, addConfigApi);
+ }
+ }
+ }
+ }
+
+ }
+
+ /**
+ * @param node
+ * @param pathParams
+ * @return
+ */
+ private List<Operation> operations(DataSchemaNode node, List<Parameter> pathParams,
+ boolean isConfig) {
+ List<Operation> 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);
+ operations.add(putBuilder.pathParams(pathParams).build());
+
+ OperationBuilder.Delete deleteBuilder = new OperationBuilder.Delete(node);
+ operations.add(deleteBuilder.pathParams(pathParams).build());
+ }
+ return operations;
+ }
+
+ private String createPath(final DataSchemaNode schemaNode, List<Parameter> pathParams) {
+ ArrayList<LeafSchemaNode> pathListParams = new ArrayList<LeafSchemaNode>();
+ StringBuilder path = new StringBuilder();
+ QName _qName = schemaNode.getQName();
+ String localName = _qName.getLocalName();
+ path.append(localName);
+
+ if ((schemaNode instanceof ListSchemaNode)) {
+ final List<QName> listKeys = ((ListSchemaNode) schemaNode).getKeyDefinition();
+ for (final QName listKey : listKeys) {
+ {
+ DataSchemaNode _dataChildByName = ((DataNodeContainer) schemaNode)
+ .getDataChildByName(listKey);
+ pathListParams.add(((LeafSchemaNode) _dataChildByName));
+
+ String pathParamIdentifier = new StringBuilder("/{")
+ .append(listKey.getLocalName()).append("}").toString();
+ path.append(pathParamIdentifier);
+
+ Parameter pathParam = new Parameter();
+ pathParam.setName(listKey.getLocalName());
+ pathParam.setDescription(_dataChildByName.getDescription());
+ pathParam.setType("string");
+ pathParam.setParamType("path");
+
+ pathParams.add(pathParam);
+ }
+ }
+ }
+ return path.toString();
+ }
+
+ protected void addRpcs(RpcDefinition rpcDefn, List<Api> apis, String parentPath) {
+ Api rpc = new Api();
+ String resourcePath = parentPath + rpcDefn.getQName().getLocalName();
+ rpc.setPath(resourcePath);
+
+ Operation operationSpec = new Operation();
+ operationSpec.setMethod("POST");
+ operationSpec.setNotes(rpcDefn.getDescription());
+ operationSpec.setNickname(rpcDefn.getQName().getLocalName());
+ if (rpcDefn.getOutput() != null) {
+ operationSpec.setType("(" + rpcDefn.getQName().getLocalName() + ")output");
+ }
+ if (rpcDefn.getInput() != null) {
+ Parameter payload = new Parameter();
+ payload.setParamType("body");
+ payload.setType("(" + rpcDefn.getQName().getLocalName() + ")input");
+ operationSpec.setParameters(Collections.singletonList(payload));
+ }
+
+ rpc.setOperations(Arrays.asList(operationSpec));
+
+ apis.add(rpc);
+ }
+
+ protected SortedSet<Module> getSortedModules(SchemaContext schemaContext) {
+ if (schemaContext == null) {
+ return new TreeSet<>();
+ }
+
+ Set<Module> modules = schemaContext.getModules();
+
+ SortedSet<Module> sortedModules = new TreeSet<>(new Comparator<Module>() {
+ @Override
+ public int compare(Module o1, Module o2) {
+ int result = o1.getName().compareTo(o2.getName());
+ if (result == 0) {
+ result = o1.getRevision().compareTo(o2.getRevision());
+ }
+ if (result == 0) {
+ result = o1.getNamespace().compareTo(o2.getNamespace());
+ }
+ return result;
+ }
+ });
+ for (Module m : modules) {
+ if (m != null) {
+ sortedModules.add(m);
+ }
+ }
+ return sortedModules;
+ }
+}
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder;
import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
*/
public class ModelGenerator {
- private static final Logger _logger = LoggerFactory.getLogger(ModelGenerator.class);
+ private static Logger _logger = LoggerFactory.getLogger(ModelGenerator.class);
private static final String BASE_64 = "base64";
private static final String BINARY_ENCODING_KEY = "binaryEncoding";
private static final Map<Class<? extends TypeDefinition<?>>, String> YANG_TYPE_TO_JSON_TYPE_MAPPING;
static {
- Map<Class<? extends TypeDefinition<?>>, String> tempMap1 = new HashMap<Class<? extends TypeDefinition<?>>, String>(10);
- tempMap1.put(StringType.class , STRING);
- tempMap1.put(BooleanType.class , BOOLEAN);
- tempMap1.put(Int8.class , INTEGER);
- tempMap1.put(Int16.class , INTEGER);
- tempMap1.put(Int32.class , INTEGER);
- tempMap1.put(Int64.class , INTEGER);
- tempMap1.put(Uint16.class , INTEGER);
- tempMap1.put(Uint32.class , INTEGER);
- tempMap1.put(Uint64.class , INTEGER);
- tempMap1.put(Uint8.class , INTEGER);
- tempMap1.put(Decimal64.class , NUMBER);
- tempMap1.put(EnumerationType.class , ENUM);
- //TODO: Binary type
+ Map<Class<? extends TypeDefinition<?>>, String> tempMap1 = new HashMap<Class<? extends TypeDefinition<?>>, String>(
+ 10);
+ tempMap1.put(StringType.class, STRING);
+ tempMap1.put(BooleanType.class, BOOLEAN);
+ tempMap1.put(Int8.class, INTEGER);
+ tempMap1.put(Int16.class, INTEGER);
+ tempMap1.put(Int32.class, INTEGER);
+ tempMap1.put(Int64.class, INTEGER);
+ tempMap1.put(Uint16.class, INTEGER);
+ tempMap1.put(Uint32.class, INTEGER);
+ tempMap1.put(Uint64.class, INTEGER);
+ tempMap1.put(Uint8.class, INTEGER);
+ tempMap1.put(Decimal64.class, NUMBER);
+ tempMap1.put(EnumerationType.class, ENUM);
+ // TODO: Binary type
YANG_TYPE_TO_JSON_TYPE_MAPPING = Collections.unmodifiableMap(tempMap1);
}
- public ModelGenerator(){
+ public ModelGenerator() {
}
- public JSONObject convertToJsonSchema(final Module module) throws IOException, JSONException {
+ public JSONObject convertToJsonSchema(Module module) throws IOException, JSONException {
JSONObject models = new JSONObject();
processContainers(module, models);
processRPCs(module, models);
-
return models;
}
-
-
- private void processContainers(final Module module, final JSONObject models) throws IOException, JSONException {
+ private void processContainers(Module module, JSONObject models) throws IOException,
+ JSONException {
String moduleName = module.getName();
- Set<DataSchemaNode> childNodes = module.getChildNodes();
+ Set<DataSchemaNode> childNodes = module.getChildNodes();
- for(DataSchemaNode childNode : childNodes){
- JSONObject moduleJSON=null;
- String filename = childNode.getQName().getLocalName();
+ for (DataSchemaNode childNode : childNodes) {
+ JSONObject configModuleJSON = null;
+ JSONObject operationalModuleJSON = null;
+
+ String childNodeName = childNode.getQName().getLocalName();
/*
* For every container in the module
*/
- if(childNode instanceof ContainerSchemaNode) {
- moduleJSON = processContainer((ContainerSchemaNode)childNode, moduleName, true, models);
+ if (childNode instanceof ContainerSchemaNode) {
+ configModuleJSON = processContainer((ContainerSchemaNode) childNode, moduleName,
+ true, models, true);
+ operationalModuleJSON = processContainer((ContainerSchemaNode) childNode,
+ moduleName, true, models, false);
}
- if(moduleJSON!=null) {
- _logger.debug("Adding model for [{}]", filename);
- moduleJSON.put("id", filename);
- models.put(filename, moduleJSON);
+ 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);
}
}
}
-
/**
- * Process the RPCs for a Module
- * Spits out a file each of the name <rpcName>-input.json
- * and <rpcName>-output.json for each RPC that contains
+ * Process the RPCs for a Module Spits out a file each of the name
+ * <rpcName>-input.json and <rpcName>-output.json for each RPC that contains
* input & output elements
*
* @param module
* @throws JSONException
* @throws IOException
*/
- private void processRPCs(final Module module, final JSONObject models) throws JSONException, IOException {
+ private void processRPCs(Module module, JSONObject models) throws JSONException, IOException {
- Set<RpcDefinition> rpcs = module.getRpcs();
+ Set<RpcDefinition> rpcs = module.getRpcs();
String moduleName = module.getName();
- for(RpcDefinition rpc: rpcs) {
+ for (RpcDefinition rpc : rpcs) {
ContainerSchemaNode input = rpc.getInput();
- if(input!=null) {
+ if (input != null) {
JSONObject inputJSON = processContainer(input, moduleName, true, models);
- String filename = rpc.getQName().getLocalName() + "-input";
+ String filename = "(" + rpc.getQName().getLocalName() + ")input";
inputJSON.put("id", filename);
- //writeToFile(filename, inputJSON.toString(2), moduleName);
+ // writeToFile(filename, inputJSON.toString(2), moduleName);
models.put(filename, inputJSON);
}
ContainerSchemaNode output = rpc.getOutput();
- if(output!=null) {
+ if (output != null) {
JSONObject outputJSON = processContainer(output, moduleName, true, models);
- String filename = rpc.getQName().getLocalName() + "-output";
+ String filename = "(" + rpc.getQName().getLocalName() + ")output";
outputJSON.put("id", filename);
models.put(filename, outputJSON);
}
}
}
-
/**
* Processes the container node and populates the moduleJSON
*
* @param container
* @param moduleName
+ * @param isConfig
* @throws JSONException
* @throws IOException
*/
- private JSONObject processContainer(final ContainerSchemaNode container, final String moduleName, final boolean addSchemaStmt, final JSONObject models) throws JSONException, IOException{
+ private JSONObject processContainer(ContainerSchemaNode container, String moduleName,
+ boolean addSchemaStmt, JSONObject models) throws JSONException, IOException {
+ return processContainer(container, moduleName, addSchemaStmt, models, (Boolean) null);
+ }
+
+ private JSONObject processContainer(ContainerSchemaNode container, String moduleName,
+ boolean addSchemaStmt, JSONObject models, Boolean isConfig) throws JSONException,
+ IOException {
JSONObject moduleJSON = getSchemaTemplate();
- if(addSchemaStmt) {
+ if (addSchemaStmt) {
moduleJSON = getSchemaTemplate();
} else {
moduleJSON = new JSONObject();
moduleJSON.put(DESCRIPTION_KEY, containerDescription);
Set<DataSchemaNode> containerChildren = container.getChildNodes();
- JSONObject properties = processChildren(containerChildren, moduleName, models);
+ JSONObject properties = processChildren(containerChildren, moduleName, models, isConfig);
moduleJSON.put(PROPERTIES_KEY, properties);
return moduleJSON;
}
+ private JSONObject processChildren(Set<DataSchemaNode> nodes, String moduleName,
+ JSONObject models) throws JSONException, IOException {
+ return processChildren(nodes, moduleName, models, null);
+ }
+
/**
* Processes the nodes
+ *
* @param nodes
* @param moduleName
+ * @param isConfig
* @return
* @throws JSONException
* @throws IOException
*/
- private JSONObject processChildren(final Set<DataSchemaNode> nodes, final String moduleName, final JSONObject models) throws JSONException, IOException {
+ private JSONObject processChildren(Set<DataSchemaNode> nodes, String moduleName,
+ JSONObject models, Boolean isConfig) throws JSONException, IOException {
JSONObject properties = new JSONObject();
- for(DataSchemaNode node : nodes){
- String name = node.getQName().getLocalName();
- JSONObject property = null;
- if(node instanceof LeafSchemaNode) {
- property = processLeafNode((LeafSchemaNode)node);
- } else if (node instanceof ListSchemaNode) {
- property = processListSchemaNode((ListSchemaNode)node, moduleName, models);
+ for (DataSchemaNode node : nodes) {
+ if (isConfig == null || node.isConfiguration() == isConfig) {
- } else if (node instanceof LeafListSchemaNode) {
- property = processLeafListNode((LeafListSchemaNode)node);
+ String name = node.getQName().getLocalName();
+ JSONObject property = null;
+ if (node instanceof LeafSchemaNode) {
+ property = processLeafNode((LeafSchemaNode) node);
+ } else if (node instanceof ListSchemaNode) {
+ property = processListSchemaNode((ListSchemaNode) node, moduleName, models);
- } else if (node instanceof ChoiceNode) {
- property = processChoiceNode((ChoiceNode)node, moduleName, models);
+ } else if (node instanceof LeafListSchemaNode) {
+ property = processLeafListNode((LeafListSchemaNode) node);
- } else if (node instanceof AnyXmlSchemaNode) {
- property = processAnyXMLNode((AnyXmlSchemaNode)node);
+ } else if (node instanceof ChoiceNode) {
+ property = processChoiceNode((ChoiceNode) node, moduleName, models);
- } else if (node instanceof ContainerSchemaNode) {
- property = processContainer((ContainerSchemaNode)node, moduleName, false, models);
+ } else if (node instanceof AnyXmlSchemaNode) {
+ property = processAnyXMLNode((AnyXmlSchemaNode) node);
- } else {
- throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
- }
+ } else if (node instanceof ContainerSchemaNode) {
+ property = processContainer((ContainerSchemaNode) node, moduleName, false,
+ models, isConfig);
+
+ } else {
+ throw new IllegalArgumentException("Unknown DataSchemaNode type: "
+ + node.getClass());
+ }
- property.putOpt(DESCRIPTION_KEY, node.getDescription());
- properties.put(name, property);
+ property.putOpt(DESCRIPTION_KEY, node.getDescription());
+ properties.put(name, property);
+ }
}
return properties;
}
* @param listNode
* @throws JSONException
*/
- private JSONObject processLeafListNode(final LeafListSchemaNode listNode) throws JSONException {
+ private JSONObject processLeafListNode(LeafListSchemaNode listNode) throws JSONException {
JSONObject props = new JSONObject();
props.put(TYPE_KEY, ARRAY_TYPE);
* @throws JSONException
* @throws IOException
*/
- private JSONObject processChoiceNode(final ChoiceNode choiceNode, final String moduleName, final JSONObject models) throws JSONException, IOException {
+ private JSONObject processChoiceNode(ChoiceNode choiceNode, String moduleName, JSONObject models)
+ throws JSONException, IOException {
Set<ChoiceCaseNode> cases = choiceNode.getCases();
JSONArray choiceProps = new JSONArray();
- for(ChoiceCaseNode choiceCase: cases) {
+ for (ChoiceCaseNode choiceCase : cases) {
String choiceName = choiceCase.getQName().getLocalName();
JSONObject choiceProp = processChildren(choiceCase.getChildNodes(), moduleName, models);
JSONObject choiceObj = new JSONObject();
return oneOfProps;
}
-
/**
*
* @param constraints
* @param props
* @throws JSONException
*/
- private void processConstraints(final ConstraintDefinition constraints, final JSONObject props) throws JSONException {
+ private void processConstraints(ConstraintDefinition constraints, JSONObject props)
+ throws JSONException {
boolean isMandatory = constraints.isMandatory();
props.put(REQUIRED_KEY, isMandatory);
Integer minElements = constraints.getMinElements();
Integer maxElements = constraints.getMaxElements();
- if(minElements !=null) {
+ if (minElements != null) {
props.put(MIN_ITEMS, minElements);
}
- if(maxElements !=null) {
+ if (maxElements != null) {
props.put(MAX_ITEMS, maxElements);
}
}
/**
* 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.
+ * 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
* @throws JSONException
* @throws IOException
*/
- private JSONObject processListSchemaNode(final ListSchemaNode listNode, final String moduleName, final JSONObject models) throws JSONException, IOException {
+ private JSONObject processListSchemaNode(ListSchemaNode listNode, String moduleName,
+ JSONObject models) throws JSONException, IOException {
Set<DataSchemaNode> listChildren = listNode.getChildNodes();
String fileName = listNode.getQName().getLocalName();
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.
+ * 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);
+ // 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 );
+ items.put(REF_KEY, fileName);
listNodeProperties.put(ITEMS_KEY, items);
return listNodeProperties;
* @return
* @throws JSONException
*/
- private JSONObject processLeafNode(final LeafSchemaNode leafNode) throws JSONException {
+ private JSONObject processLeafNode(LeafSchemaNode leafNode) throws JSONException {
JSONObject property = new JSONObject();
String leafDescription = leafNode.getDescription();
* @return
* @throws JSONException
*/
- private JSONObject processAnyXMLNode(final AnyXmlSchemaNode leafNode) throws JSONException {
+ private JSONObject processAnyXMLNode(AnyXmlSchemaNode leafNode) throws JSONException {
JSONObject property = new JSONObject();
String leafDescription = leafNode.getDescription();
* @param property
* @throws JSONException
*/
- private void processTypeDef(final TypeDefinition<?> leafTypeDef, final JSONObject property) throws JSONException {
+ private void processTypeDef(TypeDefinition<?> leafTypeDef, JSONObject property)
+ throws JSONException {
- if(leafTypeDef instanceof ExtendedType){
+ if (leafTypeDef instanceof ExtendedType) {
processExtendedType(leafTypeDef, property);
} else if (leafTypeDef instanceof EnumerationType) {
- processEnumType((EnumerationType)leafTypeDef, property);
+ processEnumType((EnumerationType) leafTypeDef, property);
} else if (leafTypeDef instanceof BitsTypeDefinition) {
- processBitsType((BitsTypeDefinition)leafTypeDef, property);
+ processBitsType((BitsTypeDefinition) leafTypeDef, property);
} else if (leafTypeDef instanceof UnionTypeDefinition) {
- processUnionType((UnionTypeDefinition)leafTypeDef, property);
+ processUnionType((UnionTypeDefinition) leafTypeDef, property);
} else if (leafTypeDef instanceof IdentityrefTypeDefinition) {
property.putOpt(TYPE_KEY, "object");
} else if (leafTypeDef instanceof BinaryTypeDefinition) {
- processBinaryType((BinaryTypeDefinition)leafTypeDef, property);
+ processBinaryType((BinaryTypeDefinition) leafTypeDef, property);
} else {
- //System.out.println("In else: " + leafTypeDef.getClass());
+ // System.out.println("In else: " + leafTypeDef.getClass());
String jsonType = YANG_TYPE_TO_JSON_TYPE_MAPPING.get(leafTypeDef.getClass());
- if(jsonType==null) {
+ if (jsonType == null) {
jsonType = "object";
}
property.putOpt(TYPE_KEY, jsonType);
* @param property
* @throws JSONException
*/
- private void processExtendedType(final TypeDefinition<?> leafTypeDef, final JSONObject property) throws JSONException {
+ private void processExtendedType(TypeDefinition<?> leafTypeDef, JSONObject property)
+ throws JSONException {
Object leafBaseType = leafTypeDef.getBaseType();
- if(leafBaseType instanceof ExtendedType){
- //recursively process an extended type until we hit a base type
- processExtendedType((TypeDefinition<?>)leafBaseType, property);
+ if (leafBaseType instanceof ExtendedType) {
+ // recursively process an extended type until we hit a base type
+ processExtendedType((TypeDefinition<?>) leafBaseType, property);
} else {
- List<LengthConstraint> lengthConstraints = ((ExtendedType) leafTypeDef).getLengthConstraints();
- for(LengthConstraint lengthConstraint: lengthConstraints) {
+ List<LengthConstraint> lengthConstraints = ((ExtendedType) leafTypeDef)
+ .getLengthConstraints();
+ for (LengthConstraint lengthConstraint : lengthConstraints) {
Number min = lengthConstraint.getMin();
Number max = lengthConstraint.getMax();
property.putOpt(MIN_LENGTH_KEY, min);
property.putOpt(MAX_LENGTH_KEY, max);
}
String jsonType = YANG_TYPE_TO_JSON_TYPE_MAPPING.get(leafBaseType.getClass());
- property.putOpt(TYPE_KEY,jsonType );
+ property.putOpt(TYPE_KEY, jsonType);
}
}
/*
- *
- */
- private void processBinaryType(final BinaryTypeDefinition binaryType, final JSONObject property) throws JSONException {
+ *
+ */
+ private void processBinaryType(BinaryTypeDefinition binaryType, JSONObject property)
+ throws JSONException {
property.put(TYPE_KEY, STRING);
JSONObject media = new JSONObject();
media.put(BINARY_ENCODING_KEY, BASE_64);
* @param property
* @throws JSONException
*/
- private void processEnumType(final EnumerationType enumLeafType, final JSONObject property) throws JSONException {
+ private void processEnumType(EnumerationType enumLeafType, JSONObject property)
+ throws JSONException {
List<EnumPair> enumPairs = enumLeafType.getValues();
List<String> enumNames = new ArrayList<String>();
- for(EnumPair enumPair: enumPairs) {
+ for (EnumPair enumPair : enumPairs) {
enumNames.add(enumPair.getName());
}
property.putOpt(ENUM, new JSONArray(enumNames));
* @param property
* @throws JSONException
*/
- private void processBitsType(final BitsTypeDefinition bitsType, final JSONObject property) throws JSONException{
+ private void processBitsType(BitsTypeDefinition bitsType, JSONObject property)
+ throws JSONException {
property.put(TYPE_KEY, ARRAY_TYPE);
property.put(MIN_ITEMS, 0);
property.put(UNIQUE_ITEMS_KEY, true);
JSONArray enumValues = new JSONArray();
List<Bit> bits = bitsType.getBits();
- for(Bit bit: bits) {
+ for (Bit bit : bits) {
enumValues.put(bit.getName());
}
JSONObject itemsValue = new JSONObject();
property.put(ITEMS_KEY, itemsValue);
}
-
/**
*
* @param unionType
* @param property
* @throws JSONException
*/
- private void processUnionType(final UnionTypeDefinition unionType, final JSONObject property) throws JSONException{
+ private void processUnionType(UnionTypeDefinition unionType, JSONObject property)
+ throws JSONException {
List<TypeDefinition<?>> unionTypes = unionType.getTypes();
JSONArray unionArray = new JSONArray();
- for(TypeDefinition<?> typeDef: unionTypes) {
+ for (TypeDefinition<?> typeDef : unionTypes) {
unionArray.put(YANG_TYPE_TO_JSON_TYPE_MAPPING.get(typeDef.getClass()));
}
property.put(TYPE_KEY, unionArray);
}
-
/**
- * Helper method to generate a pre-filled
- * JSON schema object.
+ * Helper method to generate a pre-filled JSON schema object.
+ *
* @return
* @throws JSONException
*/
*/
package org.opendaylight.controller.sal.rest.doc.jaxrs;
-import org.opendaylight.controller.sal.rest.doc.impl.ApiDocServiceImpl;
-
-import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
+import javax.ws.rs.core.Application;
+
+import org.opendaylight.controller.sal.rest.doc.impl.ApiDocServiceImpl;
+
public class ApiDocApplication extends Application {
- @Override
- public Set<Object> getSingletons() {
- Set<Object> singletons = new HashSet<>();
- singletons.add(ApiDocServiceImpl.getInstance());
- singletons.add(new JaxbContextResolver());
- return singletons;
- }
+ @Override
+ public Set<Object> getSingletons() {
+ Set<Object> singletons = new HashSet<>();
+ singletons.add(ApiDocServiceImpl.getInstance());
+ singletons.add(new JaxbContextResolver());
+ return singletons;
+ }
}
*/
package org.opendaylight.controller.sal.rest.doc.jaxrs;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
-import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
-
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
+import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
+
@Provider
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class JaxbContextResolver implements ContextResolver<ObjectMapper> {
- private ObjectMapper ctx;
+ private final ObjectMapper ctx;
- public JaxbContextResolver(){
- ctx = new ObjectMapper();
- ctx.registerModule(new JsonOrgModule());
- ctx.getSerializationConfig().withSerializationInclusion(JsonInclude.Include.ALWAYS);
- }
+ public JaxbContextResolver() {
+ ctx = new ObjectMapper();
+ ctx.registerModule(new JsonOrgModule());
+ ctx.getSerializationConfig().withSerializationInclusion(JsonInclude.Include.ALWAYS);
+ }
- @Override
- public ObjectMapper getContext(Class<?> aClass) {
+ @Override
+ public ObjectMapper getContext(Class<?> aClass) {
- if (ApiDeclaration.class.isAssignableFrom(aClass)){
- return ctx;
- }
+ if (ApiDeclaration.class.isAssignableFrom(aClass)) {
+ return ctx;
+ }
- return null;//must return null so that jax-rs can continue context search
- }
+ return null;// must return null so that jax-rs can continue context
+ // search
+ }
}
*/
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.DataSchemaNode;
-import java.util.ArrayList;
-import java.util.List;
-
/**
*
*/
public final class OperationBuilder {
- /**
+ public static final String OPERATIONAL = "(operational)";
+ public static final String CONFIG = "(config)";
+
+ /**
*
*/
- public static class Get{
-
- protected Operation spec;
- protected DataSchemaNode schemaNode;
- private final String METHOD_NAME = "GET";
-
- public Get(DataSchemaNode node){
- this.schemaNode = node;
- spec = new Operation();
- spec.setMethod(METHOD_NAME);
- spec.setNickname(METHOD_NAME + "-" + node.getQName().getLocalName());
- spec.setType(node.getQName().getLocalName());
- spec.setNotes(node.getDescription());
+ public static class Get {
+
+ protected Operation spec;
+ protected DataSchemaNode schemaNode;
+ private final String METHOD_NAME = "GET";
+
+ public Get(DataSchemaNode node, boolean isConfig) {
+ this.schemaNode = node;
+ spec = new Operation();
+ spec.setMethod(METHOD_NAME);
+ spec.setNickname(METHOD_NAME + "-" + node.getQName().getLocalName());
+ spec.setType((isConfig ? CONFIG : OPERATIONAL) + node.getQName().getLocalName());
+ spec.setNotes(node.getDescription());
+ }
+
+ public Get pathParams(List<Parameter> params) {
+ List<Parameter> pathParameters = new ArrayList<>(params);
+ spec.setParameters(pathParameters);
+ return this;
+ }
+
+ public Operation build() {
+ return spec;
+ }
}
- public Get pathParams(List<Parameter> params){
- List<Parameter> pathParameters = new ArrayList<>(params);
- spec.setParameters(pathParameters);
- return this;
- }
-
- public Operation build(){
- return spec;
- }
- }
-
- /**
+ /**
*
*/
- public static class Put{
- protected Operation spec;
- protected DataSchemaNode schemaNode;
- private final String METHOD_NAME = "PUT";
-
- public Put(DataSchemaNode node){
- this.schemaNode = node;
- spec = new Operation();
- spec.setType(node.getQName().getLocalName());
- spec.setNotes(node.getDescription());
- }
-
- public Put pathParams(List<Parameter> params){
- List<Parameter> parameters = new ArrayList<>(params);
- Parameter payload = new Parameter();
- payload.setParamType("body");
- payload.setType(schemaNode.getQName().getLocalName());
- parameters.add(payload);
- spec.setParameters(parameters);
- return this;
- }
-
- public Operation build(){
- spec.setMethod(METHOD_NAME);
- spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
- return spec;
+ public static class Put {
+ protected Operation spec;
+ protected DataSchemaNode schemaNode;
+ private final String METHOD_NAME = "PUT";
+
+ public Put(DataSchemaNode node) {
+ this.schemaNode = node;
+ spec = new Operation();
+ spec.setType(CONFIG + node.getQName().getLocalName());
+ spec.setNotes(node.getDescription());
+ }
+
+ public Put pathParams(List<Parameter> params) {
+ List<Parameter> parameters = new ArrayList<>(params);
+ Parameter payload = new Parameter();
+ payload.setParamType("body");
+ payload.setType(CONFIG + schemaNode.getQName().getLocalName());
+ parameters.add(payload);
+ spec.setParameters(parameters);
+ return this;
+ }
+
+ public Operation build() {
+ spec.setMethod(METHOD_NAME);
+ spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
+ return spec;
+ }
}
- }
- /**
+ /**
*
*/
- public static final class Post extends Put{
+ public static final class Post extends Put {
- private final String METHOD_NAME = "POST";
+ private final String METHOD_NAME = "POST";
- public Post(DataSchemaNode node){
- super(node);
- }
+ public Post(DataSchemaNode node) {
+ super(node);
+ }
- public Operation build(){
- spec.setMethod(METHOD_NAME);
- spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
- return spec;
+ @Override
+ public Operation build() {
+ spec.setMethod(METHOD_NAME);
+ spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
+ return spec;
+ }
}
- }
- /**
+ /**
*
*/
- public static final class Delete extends Get {
- private final String METHOD_NAME = "DELETE";
-
- public Delete(DataSchemaNode node){
- super(node);
- }
-
- public Operation build(){
- spec.setMethod(METHOD_NAME);
- spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
- spec.setType(null);
- return spec;
+ public static final class Delete extends Get {
+ private final String METHOD_NAME = "DELETE";
+
+ public Delete(DataSchemaNode node) {
+ super(node, false);
+ }
+
+ @Override
+ public Operation build() {
+ spec.setMethod(METHOD_NAME);
+ spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
+ spec.setType(null);
+ return spec;
+ }
}
- }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.rest.doc.mountpoints;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.ws.rs.core.UriInfo;
+
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener;
+import org.opendaylight.controller.sal.rest.doc.impl.BaseYangSwaggerGenerator;
+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.Resource;
+import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class MountPointSwagger extends BaseYangSwaggerGenerator implements MountProvisionListener {
+
+ private static final String DATASTORES_REVISION = "-";
+ private static final String DATASTORES_LABEL = "Datastores";
+
+ private MountProvisionService mountService;
+ private final Map<InstanceIdentifier, Long> instanceIdToLongId = new TreeMap<>(
+ new Comparator<InstanceIdentifier>() {
+ @Override
+ public int compare(InstanceIdentifier o1, InstanceIdentifier o2) {
+ return o1.toString().compareToIgnoreCase(o2.toString());
+ }
+ });
+ private final Map<Long, InstanceIdentifier> longIdToInstanceId = new HashMap<>();
+ private final Object lock = new Object();
+
+ private final AtomicLong idKey = new AtomicLong(0);
+
+ private static AtomicReference<MountPointSwagger> selfRef = new AtomicReference<>();
+ private SchemaService globalSchema;
+
+ public Map<String, Long> getInstanceIdentifiers() {
+ Map<String, Long> urlToId = new HashMap<>();
+ synchronized (lock) {
+ SchemaContext context = globalSchema.getGlobalContext();
+ for (Entry<InstanceIdentifier, Long> entry : instanceIdToLongId.entrySet()) {
+ String modName = findModuleName(entry.getKey(), context);
+ urlToId.put(generateUrlPrefixFromInstanceID(entry.getKey(), modName),
+ entry.getValue());
+ }
+ }
+ return urlToId;
+ }
+
+ public void setGlobalSchema(SchemaService globalSchema) {
+ this.globalSchema = globalSchema;
+ }
+
+ private String findModuleName(InstanceIdentifier id, SchemaContext context) {
+ PathArgument rootQName = id.getPath().get(0);
+ for (Module mod : context.getModules()) {
+ if (mod.getDataChildByName(rootQName.getNodeType()) != null) {
+ return mod.getName();
+ }
+ }
+ return null;
+ }
+
+ private String generateUrlPrefixFromInstanceID(InstanceIdentifier key, String moduleName) {
+ List<PathArgument> path = key.getPath();
+ StringBuilder builder = new StringBuilder();
+ if (moduleName != null) {
+ builder.append(moduleName);
+ builder.append(":");
+ }
+ boolean first = true;
+ for (PathArgument arg : path) {
+
+ String name = arg.getNodeType().getLocalName();
+ if (first) {
+ first = false;
+ } else {
+ builder.append("/");
+ }
+ builder.append(name);
+ if (arg instanceof InstanceIdentifier.NodeIdentifierWithPredicates) {
+ NodeIdentifierWithPredicates nodeId = (NodeIdentifierWithPredicates) arg;
+ for (Entry<QName, Object> entry : nodeId.getKeyValues().entrySet()) {
+ builder.append("/").append(entry.getValue());
+ }
+ }
+ }
+
+ return builder.append("/").toString();
+ }
+
+ private String getYangMountUrl(InstanceIdentifier key) {
+ String modName = findModuleName(key, globalSchema.getGlobalContext());
+ return generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount/";
+ }
+
+ public ResourceList getResourceList(UriInfo uriInfo, Long id) {
+ InstanceIdentifier iid = getInstanceId(id);
+ if (iid == null) {
+ return null; // indicating not found.
+ }
+ SchemaContext context = getSchemaContext(iid);
+ String urlPrefix = getYangMountUrl(iid);
+ if (context == null) {
+ return createResourceList();
+ }
+ List<Resource> resources = new LinkedList<>();
+ Resource dataStores = new Resource();
+ dataStores.setDescription("Provides methods for accessing the data stores.");
+ dataStores.setPath(generatePath(uriInfo, DATASTORES_LABEL, DATASTORES_REVISION));
+ resources.add(dataStores);
+ ResourceList list = super.getResourceListing(uriInfo, context, urlPrefix);
+ resources.addAll(list.getApis());
+ list.setApis(resources);
+ return list;
+ }
+
+ private InstanceIdentifier getInstanceId(Long id) {
+ InstanceIdentifier instanceId;
+ synchronized (lock) {
+ instanceId = longIdToInstanceId.get(id);
+ }
+ return instanceId;
+ }
+
+ private SchemaContext getSchemaContext(InstanceIdentifier id) {
+
+ if (id == null) {
+ return null;
+ }
+
+ MountProvisionInstance mountPoint = mountService.getMountPoint(id);
+ if (mountPoint == null) {
+ return null;
+ }
+
+ SchemaContext context = mountPoint.getSchemaContext();
+ if (context == null) {
+ return null;
+ }
+ return context;
+ }
+
+ public ApiDeclaration getMountPointApi(UriInfo uriInfo, Long id, String module, String revision) {
+ InstanceIdentifier iid = getInstanceId(id);
+ SchemaContext context = getSchemaContext(iid);
+ String urlPrefix = getYangMountUrl(iid);
+ if (context == null) {
+ return null;
+ }
+
+ if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) {
+ return generateDataStoreApiDoc(uriInfo, urlPrefix);
+ }
+ return super.getApiDeclaration(module, revision, uriInfo, context, urlPrefix);
+ }
+
+ private ApiDeclaration generateDataStoreApiDoc(UriInfo uriInfo, String context) {
+
+ ApiDeclaration declaration = super.createApiDeclaration(createBasePathFromUriInfo(uriInfo));
+ List<Api> apis = new LinkedList<>();
+ apis.add(createGetApi("config",
+ "Queries the config (startup) datastore on the mounted hosted.", context));
+ apis.add(createGetApi("operational",
+ "Queries the operational (running) datastore on the mounted hosted.", context));
+ apis.add(createGetApi("operations",
+ "Queries the available operations (RPC calls) on the mounted hosted.", context));
+ declaration.setApis(apis);
+ return declaration;
+
+ }
+
+ private Api createGetApi(String datastore, String note, String context) {
+ Operation getConfig = new Operation();
+ getConfig.setMethod("GET");
+ getConfig.setNickname("GET " + datastore);
+ getConfig.setNotes(note);
+
+ Api api = new Api();
+ api.setPath(getDataStorePath("/" + datastore + "/", context));
+ api.setOperations(Collections.singletonList(getConfig));
+
+ return api;
+ }
+
+ public void setMountService(MountProvisionService mountService) {
+ this.mountService = mountService;
+ }
+
+ @Override
+ public void onMountPointCreated(InstanceIdentifier path) {
+ synchronized (lock) {
+ Long idLong = idKey.incrementAndGet();
+ instanceIdToLongId.put(path, idLong);
+ longIdToInstanceId.put(idLong, path);
+ }
+ }
+
+ @Override
+ public void onMountPointRemoved(InstanceIdentifier path) {
+ synchronized (lock) {
+ Long id = instanceIdToLongId.remove(path);
+ longIdToInstanceId.remove(id);
+ }
+ }
+
+ public static MountPointSwagger getInstance() {
+ MountPointSwagger swagger = selfRef.get();
+ if (swagger == null) {
+ selfRef.compareAndSet(null, new MountPointSwagger());
+ swagger = selfRef.get();
+ }
+ return swagger;
+ }
+
+}
import java.util.List;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#522-api-object">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#522-api-object</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#522-api-object"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#522-api
+ * -object</a>)
*/
public class Api {
- private String path;
- private List<Operation> operations;
+ private String path;
+ private List<Operation> operations;
- public String getPath() {
- return path;
- }
+ public String getPath() {
+ return path;
+ }
- public void setPath(String path) {
- this.path = path;
- }
+ public void setPath(String path) {
+ this.path = path;
+ }
- public List<Operation> getOperations() {
- return operations;
- }
+ public List<Operation> getOperations() {
+ return operations;
+ }
- public void setOperations(List<Operation> operations) {
- this.operations = operations;
- }
+ public void setOperations(List<Operation> operations) {
+ this.operations = operations;
+ }
}
*/
package org.opendaylight.controller.sal.rest.doc.swagger;
-import org.json.JSONObject;
-
import java.util.List;
+import org.json.JSONObject;
+
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-declaration">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-declaration</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-declaration"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-
+ * declaration</a>)
*/
public class ApiDeclaration {
- private String apiVersion;
- private String swaggerVersion;
- private String basePath;
- private String resourcePath;
- private List<String> produces;
- private List<Api> apis;
- private JSONObject models;
-
- public JSONObject getModels() {
- return models;
- }
-
- public void setModels(JSONObject models) {
- this.models = models;
- }
-
- public String getApiVersion() {
- return apiVersion;
- }
-
- public void setApiVersion(String apiVersion) {
- this.apiVersion = apiVersion;
- }
-
- public String getSwaggerVersion() {
- return swaggerVersion;
- }
-
- public void setSwaggerVersion(String swaggerVersion) {
- this.swaggerVersion = swaggerVersion;
- }
-
- public String getBasePath() {
- return basePath;
- }
-
- public void setBasePath(String basePath) {
- this.basePath = basePath;
- }
-
- public String getResourcePath() {
- return resourcePath;
- }
-
- public void setResourcePath(String resourcePath) {
- this.resourcePath = resourcePath;
- }
-
- public List<String> getProduces() {
- return produces;
- }
-
- public void setProduces(List<String> produces) {
- this.produces = produces;
- }
-
- public List<Api> getApis() {
- return apis;
- }
-
- public void setApis(List<Api> apis) {
- this.apis = apis;
- }
-
- public boolean hasApi(){
- return (apis != null && !apis.isEmpty());
- }
-
- public boolean hasModel(){
- return (models != null && models.length() > 0);
- }
+ private String apiVersion;
+ private String swaggerVersion;
+ private String basePath;
+ private String resourcePath;
+ private List<String> produces;
+ private List<Api> apis;
+ private JSONObject models;
+
+ public JSONObject getModels() {
+ return models;
+ }
+
+ public void setModels(JSONObject models) {
+ this.models = models;
+ }
+
+ public String getApiVersion() {
+ return apiVersion;
+ }
+
+ public void setApiVersion(String apiVersion) {
+ this.apiVersion = apiVersion;
+ }
+
+ public String getSwaggerVersion() {
+ return swaggerVersion;
+ }
+
+ public void setSwaggerVersion(String swaggerVersion) {
+ this.swaggerVersion = swaggerVersion;
+ }
+
+ public String getBasePath() {
+ return basePath;
+ }
+
+ public void setBasePath(String basePath) {
+ this.basePath = basePath;
+ }
+
+ public String getResourcePath() {
+ return resourcePath;
+ }
+
+ public void setResourcePath(String resourcePath) {
+ this.resourcePath = resourcePath;
+ }
+
+ public List<String> getProduces() {
+ return produces;
+ }
+
+ public void setProduces(List<String> produces) {
+ this.produces = produces;
+ }
+
+ public List<Api> getApis() {
+ return apis;
+ }
+
+ public void setApis(List<Api> apis) {
+ this.apis = apis;
+ }
+
+ public boolean hasApi() {
+ return (apis != null && !apis.isEmpty());
+ }
+
+ public boolean hasModel() {
+ return (models != null && models.length() > 0);
+ }
}
import java.util.List;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-operation-object">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-operation-object</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-operation-object"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-
+ * operation-object</a>)
*/
public class Operation {
- private String method;
- private String summary;
- private String notes;
- private String type;
- private String nickname;
- private List<String> consumes;
- private List<Parameter> parameters;
- private List<ResponseMessage> responseMessages;
-
- public String getMethod() {
- return method;
- }
-
- public void setMethod(String method) {
- this.method = method;
- }
-
- public String getSummary() {
- return summary;
- }
-
- public void setSummary(String summary) {
- this.summary = summary;
- }
-
- public String getNotes() {
- return notes;
- }
-
- public void setNotes(String notes) {
- this.notes = notes;
- }
-
- public String getType() {
- return type;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-
- public String getNickname() {
- return nickname;
- }
-
- public void setNickname(String nickname) {
- this.nickname = nickname;
- }
-
- public List<String> getConsumes() {
- return consumes;
- }
-
- public void setConsumes(List<String> consumes) {
- this.consumes = consumes;
- }
-
- public List<Parameter> getParameters() {
- return parameters;
- }
-
- public void setParameters(List<Parameter> parameters) {
- this.parameters = parameters;
- }
-
- public List<ResponseMessage> getResponseMessages() {
- return responseMessages;
- }
-
- public void setResponseMessages(List<ResponseMessage> responseMessages) {
- this.responseMessages = responseMessages;
- }
+ private String method;
+ private String summary;
+ private String notes;
+ private String type;
+ private String nickname;
+ private List<String> consumes;
+ private List<Parameter> parameters;
+ private List<ResponseMessage> responseMessages;
+
+ public String getMethod() {
+ return method;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public String getSummary() {
+ return summary;
+ }
+
+ public void setSummary(String summary) {
+ this.summary = summary;
+ }
+
+ public String getNotes() {
+ return notes;
+ }
+
+ public void setNotes(String notes) {
+ this.notes = notes;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getNickname() {
+ return nickname;
+ }
+
+ public void setNickname(String nickname) {
+ this.nickname = nickname;
+ }
+
+ public List<String> getConsumes() {
+ return consumes;
+ }
+
+ public void setConsumes(List<String> consumes) {
+ this.consumes = consumes;
+ }
+
+ public List<Parameter> getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(List<Parameter> parameters) {
+ this.parameters = parameters;
+ }
+
+ public List<ResponseMessage> getResponseMessages() {
+ return responseMessages;
+ }
+
+ public void setResponseMessages(List<ResponseMessage> responseMessages) {
+ this.responseMessages = responseMessages;
+ }
}
package org.opendaylight.controller.sal.rest.doc.swagger;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#524-parameter-object">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#524-parameter-object</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#524-parameter-object"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#524-
+ * parameter-object</a>)
*/
public class Parameter {
- private String name;
- private String description;
- private boolean required;
- private String type;
- private String paramType;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getDescription() {
- return description;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- public boolean isRequired() {
- return required;
- }
-
- public void setRequired(boolean required) {
- this.required = required;
- }
-
- public String getType() {
- return type;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-
- public String getParamType() {
- return paramType;
- }
-
- public void setParamType(String paramType) {
- this.paramType = paramType;
- }
+ private String name;
+ private String description;
+ private boolean required;
+ private String type;
+ private String paramType;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public boolean isRequired() {
+ return required;
+ }
+
+ public void setRequired(boolean required) {
+ this.required = required;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getParamType() {
+ return paramType;
+ }
+
+ public void setParamType(String paramType) {
+ this.paramType = paramType;
+ }
}
package org.opendaylight.controller.sal.rest.doc.swagger;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512-resource-object">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512-resource-object</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512-resource-object"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512-
+ * resource-object</a>)
*/
public class Resource {
- private String path;
- private String description;
+ private String path;
+ private String description;
- public String getPath() {
- return path;
- }
+ public String getPath() {
+ return path;
+ }
- public void setPath(String path) {
- this.path = path;
- }
+ public void setPath(String path) {
+ this.path = path;
+ }
- public String getDescription() {
- return description;
- }
+ public String getDescription() {
+ return description;
+ }
- public void setDescription(String description) {
- this.description = description;
- }
+ public void setDescription(String description) {
+ this.description = description;
+ }
}
import java.util.List;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51-resource-listing">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51-resource-listing</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51-resource-listing"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51-
+ * resource-listing</a>)
*/
public class ResourceList {
- private String apiVersion;
- private String swaggerVersion;
- private List<Resource> apis;
+ private String apiVersion;
+ private String swaggerVersion;
+ private List<Resource> apis;
- public String getApiVersion() {
- return apiVersion;
- }
+ public String getApiVersion() {
+ return apiVersion;
+ }
- public void setApiVersion(String apiVersion) {
- this.apiVersion = apiVersion;
- }
+ public void setApiVersion(String apiVersion) {
+ this.apiVersion = apiVersion;
+ }
- public String getSwaggerVersion() {
- return swaggerVersion;
- }
+ public String getSwaggerVersion() {
+ return swaggerVersion;
+ }
- public void setSwaggerVersion(String swaggerVersion) {
- this.swaggerVersion = swaggerVersion;
- }
+ public void setSwaggerVersion(String swaggerVersion) {
+ this.swaggerVersion = swaggerVersion;
+ }
- public List<Resource> getApis() {
- return apis;
- }
+ public List<Resource> getApis() {
+ return apis;
+ }
- public void setApis(List<Resource> apis) {
- this.apis = apis;
- }
+ public void setApis(List<Resource> apis) {
+ this.apis = apis;
+ }
}
package org.opendaylight.controller.sal.rest.doc.swagger;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#525-response-message-object">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#525-response-message-object</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#525-response-message-object"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#525-
+ * response-message-object</a>)
*/
public class ResponseMessage {
- private int code;
- private String message;
+ private int code;
+ private String message;
- public int getCode() {
- return code;
- }
+ public int getCode() {
+ return code;
+ }
- public void setCode(int code) {
- this.code = code;
- }
+ public void setCode(int code) {
+ this.code = code;
+ }
- public String getMessage() {
- return message;
- }
+ public String getMessage() {
+ return message;
+ }
- public void setMessage(String message) {
- this.message = message;
- }
+ public void setMessage(String message) {
+ this.message = message;
+ }
}
--- /dev/null
+/*! jQuery UI - v1.10.4 - 2014-06-03
+* http://jqueryui.com
+* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=gloss_wave&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=highlight_soft&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=glass&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=glass&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=highlight_soft&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=diagonals_thick&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=flat&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
+* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden {
+ display: none;
+}
+.ui-helper-hidden-accessible {
+ border: 0;
+ clip: rect(0 0 0 0);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 1px;
+}
+.ui-helper-reset {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ line-height: 1.3;
+ text-decoration: none;
+ font-size: 100%;
+ list-style: none;
+}
+.ui-helper-clearfix:before,
+.ui-helper-clearfix:after {
+ content: "";
+ display: table;
+ border-collapse: collapse;
+}
+.ui-helper-clearfix:after {
+ clear: both;
+}
+.ui-helper-clearfix {
+ min-height: 0; /* support: IE7 */
+}
+.ui-helper-zfix {
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ position: absolute;
+ opacity: 0;
+ filter:Alpha(Opacity=0);
+}
+
+.ui-front {
+ z-index: 100;
+}
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled {
+ cursor: default !important;
+}
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon {
+ display: block;
+ text-indent: -99999px;
+ overflow: hidden;
+ background-repeat: no-repeat;
+}
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+.ui-resizable {
+ position: relative;
+}
+.ui-resizable-handle {
+ position: absolute;
+ font-size: 0.1px;
+ display: block;
+}
+.ui-resizable-disabled .ui-resizable-handle,
+.ui-resizable-autohide .ui-resizable-handle {
+ display: none;
+}
+.ui-resizable-n {
+ cursor: n-resize;
+ height: 7px;
+ width: 100%;
+ top: -5px;
+ left: 0;
+}
+.ui-resizable-s {
+ cursor: s-resize;
+ height: 7px;
+ width: 100%;
+ bottom: -5px;
+ left: 0;
+}
+.ui-resizable-e {
+ cursor: e-resize;
+ width: 7px;
+ right: -5px;
+ top: 0;
+ height: 100%;
+}
+.ui-resizable-w {
+ cursor: w-resize;
+ width: 7px;
+ left: -5px;
+ top: 0;
+ height: 100%;
+}
+.ui-resizable-se {
+ cursor: se-resize;
+ width: 12px;
+ height: 12px;
+ right: 1px;
+ bottom: 1px;
+}
+.ui-resizable-sw {
+ cursor: sw-resize;
+ width: 9px;
+ height: 9px;
+ left: -5px;
+ bottom: -5px;
+}
+.ui-resizable-nw {
+ cursor: nw-resize;
+ width: 9px;
+ height: 9px;
+ left: -5px;
+ top: -5px;
+}
+.ui-resizable-ne {
+ cursor: ne-resize;
+ width: 9px;
+ height: 9px;
+ right: -5px;
+ top: -5px;
+}
+.ui-selectable-helper {
+ position: absolute;
+ z-index: 100;
+ border: 1px dotted black;
+}
+.ui-accordion .ui-accordion-header {
+ display: block;
+ cursor: pointer;
+ position: relative;
+ margin-top: 2px;
+ padding: .5em .5em .5em .7em;
+ min-height: 0; /* support: IE7 */
+}
+.ui-accordion .ui-accordion-icons {
+ padding-left: 2.2em;
+}
+.ui-accordion .ui-accordion-noicons {
+ padding-left: .7em;
+}
+.ui-accordion .ui-accordion-icons .ui-accordion-icons {
+ padding-left: 2.2em;
+}
+.ui-accordion .ui-accordion-header .ui-accordion-header-icon {
+ position: absolute;
+ left: .5em;
+ top: 50%;
+ margin-top: -8px;
+}
+.ui-accordion .ui-accordion-content {
+ padding: 1em 2.2em;
+ border-top: 0;
+ overflow: auto;
+}
+.ui-autocomplete {
+ position: absolute;
+ top: 0;
+ left: 0;
+ cursor: default;
+}
+.ui-button {
+ display: inline-block;
+ position: relative;
+ padding: 0;
+ line-height: normal;
+ margin-right: .1em;
+ cursor: pointer;
+ vertical-align: middle;
+ text-align: center;
+ overflow: visible; /* removes extra width in IE */
+}
+.ui-button,
+.ui-button:link,
+.ui-button:visited,
+.ui-button:hover,
+.ui-button:active {
+ text-decoration: none;
+}
+/* to make room for the icon, a width needs to be set here */
+.ui-button-icon-only {
+ width: 2.2em;
+}
+/* button elements seem to need a little more width */
+button.ui-button-icon-only {
+ width: 2.4em;
+}
+.ui-button-icons-only {
+ width: 3.4em;
+}
+button.ui-button-icons-only {
+ width: 3.7em;
+}
+
+/* button text element */
+.ui-button .ui-button-text {
+ display: block;
+ line-height: normal;
+}
+.ui-button-text-only .ui-button-text {
+ padding: .4em 1em;
+}
+.ui-button-icon-only .ui-button-text,
+.ui-button-icons-only .ui-button-text {
+ padding: .4em;
+ text-indent: -9999999px;
+}
+.ui-button-text-icon-primary .ui-button-text,
+.ui-button-text-icons .ui-button-text {
+ padding: .4em 1em .4em 2.1em;
+}
+.ui-button-text-icon-secondary .ui-button-text,
+.ui-button-text-icons .ui-button-text {
+ padding: .4em 2.1em .4em 1em;
+}
+.ui-button-text-icons .ui-button-text {
+ padding-left: 2.1em;
+ padding-right: 2.1em;
+}
+/* no icon support for input elements, provide padding by default */
+input.ui-button {
+ padding: .4em 1em;
+}
+
+/* button icon element(s) */
+.ui-button-icon-only .ui-icon,
+.ui-button-text-icon-primary .ui-icon,
+.ui-button-text-icon-secondary .ui-icon,
+.ui-button-text-icons .ui-icon,
+.ui-button-icons-only .ui-icon {
+ position: absolute;
+ top: 50%;
+ margin-top: -8px;
+}
+.ui-button-icon-only .ui-icon {
+ left: 50%;
+ margin-left: -8px;
+}
+.ui-button-text-icon-primary .ui-button-icon-primary,
+.ui-button-text-icons .ui-button-icon-primary,
+.ui-button-icons-only .ui-button-icon-primary {
+ left: .5em;
+}
+.ui-button-text-icon-secondary .ui-button-icon-secondary,
+.ui-button-text-icons .ui-button-icon-secondary,
+.ui-button-icons-only .ui-button-icon-secondary {
+ right: .5em;
+}
+
+/* button sets */
+.ui-buttonset {
+ margin-right: 7px;
+}
+.ui-buttonset .ui-button {
+ margin-left: 0;
+ margin-right: -.3em;
+}
+
+/* workarounds */
+/* reset extra padding in Firefox, see h5bp.com/l */
+input.ui-button::-moz-focus-inner,
+button.ui-button::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+.ui-datepicker {
+ width: 17em;
+ padding: .2em .2em 0;
+ display: none;
+}
+.ui-datepicker .ui-datepicker-header {
+ position: relative;
+ padding: .2em 0;
+}
+.ui-datepicker .ui-datepicker-prev,
+.ui-datepicker .ui-datepicker-next {
+ position: absolute;
+ top: 2px;
+ width: 1.8em;
+ height: 1.8em;
+}
+.ui-datepicker .ui-datepicker-prev-hover,
+.ui-datepicker .ui-datepicker-next-hover {
+ top: 1px;
+}
+.ui-datepicker .ui-datepicker-prev {
+ left: 2px;
+}
+.ui-datepicker .ui-datepicker-next {
+ right: 2px;
+}
+.ui-datepicker .ui-datepicker-prev-hover {
+ left: 1px;
+}
+.ui-datepicker .ui-datepicker-next-hover {
+ right: 1px;
+}
+.ui-datepicker .ui-datepicker-prev span,
+.ui-datepicker .ui-datepicker-next span {
+ display: block;
+ position: absolute;
+ left: 50%;
+ margin-left: -8px;
+ top: 50%;
+ margin-top: -8px;
+}
+.ui-datepicker .ui-datepicker-title {
+ margin: 0 2.3em;
+ line-height: 1.8em;
+ text-align: center;
+}
+.ui-datepicker .ui-datepicker-title select {
+ font-size: 1em;
+ margin: 1px 0;
+}
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year {
+ width: 49%;
+}
+.ui-datepicker table {
+ width: 100%;
+ font-size: .9em;
+ border-collapse: collapse;
+ margin: 0 0 .4em;
+}
+.ui-datepicker th {
+ padding: .7em .3em;
+ text-align: center;
+ font-weight: bold;
+ border: 0;
+}
+.ui-datepicker td {
+ border: 0;
+ padding: 1px;
+}
+.ui-datepicker td span,
+.ui-datepicker td a {
+ display: block;
+ padding: .2em;
+ text-align: right;
+ text-decoration: none;
+}
+.ui-datepicker .ui-datepicker-buttonpane {
+ background-image: none;
+ margin: .7em 0 0 0;
+ padding: 0 .2em;
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 0;
+}
+.ui-datepicker .ui-datepicker-buttonpane button {
+ float: right;
+ margin: .5em .2em .4em;
+ cursor: pointer;
+ padding: .2em .6em .3em .6em;
+ width: auto;
+ overflow: visible;
+}
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
+ float: left;
+}
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi {
+ width: auto;
+}
+.ui-datepicker-multi .ui-datepicker-group {
+ float: left;
+}
+.ui-datepicker-multi .ui-datepicker-group table {
+ width: 95%;
+ margin: 0 auto .4em;
+}
+.ui-datepicker-multi-2 .ui-datepicker-group {
+ width: 50%;
+}
+.ui-datepicker-multi-3 .ui-datepicker-group {
+ width: 33.3%;
+}
+.ui-datepicker-multi-4 .ui-datepicker-group {
+ width: 25%;
+}
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
+ border-left-width: 0;
+}
+.ui-datepicker-multi .ui-datepicker-buttonpane {
+ clear: left;
+}
+.ui-datepicker-row-break {
+ clear: both;
+ width: 100%;
+ font-size: 0;
+}
+
+/* RTL support */
+.ui-datepicker-rtl {
+ direction: rtl;
+}
+.ui-datepicker-rtl .ui-datepicker-prev {
+ right: 2px;
+ left: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-next {
+ left: 2px;
+ right: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-prev:hover {
+ right: 1px;
+ left: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-next:hover {
+ left: 1px;
+ right: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane {
+ clear: right;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane button {
+ float: left;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
+.ui-datepicker-rtl .ui-datepicker-group {
+ float: right;
+}
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
+ border-right-width: 0;
+ border-left-width: 1px;
+}
+.ui-dialog {
+ overflow: hidden;
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: .2em;
+ outline: 0;
+}
+.ui-dialog .ui-dialog-titlebar {
+ padding: .4em 1em;
+ position: relative;
+}
+.ui-dialog .ui-dialog-title {
+ float: left;
+ margin: .1em 0;
+ white-space: nowrap;
+ width: 90%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.ui-dialog .ui-dialog-titlebar-close {
+ position: absolute;
+ right: .3em;
+ top: 50%;
+ width: 20px;
+ margin: -10px 0 0 0;
+ padding: 1px;
+ height: 20px;
+}
+.ui-dialog .ui-dialog-content {
+ position: relative;
+ border: 0;
+ padding: .5em 1em;
+ background: none;
+ overflow: auto;
+}
+.ui-dialog .ui-dialog-buttonpane {
+ text-align: left;
+ border-width: 1px 0 0 0;
+ background-image: none;
+ margin-top: .5em;
+ padding: .3em 1em .5em .4em;
+}
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
+ float: right;
+}
+.ui-dialog .ui-dialog-buttonpane button {
+ margin: .5em .4em .5em 0;
+ cursor: pointer;
+}
+.ui-dialog .ui-resizable-se {
+ width: 12px;
+ height: 12px;
+ right: -5px;
+ bottom: -5px;
+ background-position: 16px 16px;
+}
+.ui-draggable .ui-dialog-titlebar {
+ cursor: move;
+}
+.ui-menu {
+ list-style: none;
+ padding: 2px;
+ margin: 0;
+ display: block;
+ outline: none;
+}
+.ui-menu .ui-menu {
+ margin-top: -3px;
+ position: absolute;
+}
+.ui-menu .ui-menu-item {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ /* support: IE10, see #8844 */
+ list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
+}
+.ui-menu .ui-menu-divider {
+ margin: 5px -2px 5px -2px;
+ height: 0;
+ font-size: 0;
+ line-height: 0;
+ border-width: 1px 0 0 0;
+}
+.ui-menu .ui-menu-item a {
+ text-decoration: none;
+ display: block;
+ padding: 2px .4em;
+ line-height: 1.5;
+ min-height: 0; /* support: IE7 */
+ font-weight: normal;
+}
+.ui-menu .ui-menu-item a.ui-state-focus,
+.ui-menu .ui-menu-item a.ui-state-active {
+ font-weight: normal;
+ margin: -1px;
+}
+
+.ui-menu .ui-state-disabled {
+ font-weight: normal;
+ margin: .4em 0 .2em;
+ line-height: 1.5;
+}
+.ui-menu .ui-state-disabled a {
+ cursor: default;
+}
+
+/* icon support */
+.ui-menu-icons {
+ position: relative;
+}
+.ui-menu-icons .ui-menu-item a {
+ position: relative;
+ padding-left: 2em;
+}
+
+/* left-aligned */
+.ui-menu .ui-icon {
+ position: absolute;
+ top: .2em;
+ left: .2em;
+}
+
+/* right-aligned */
+.ui-menu .ui-menu-icon {
+ position: static;
+ float: right;
+}
+.ui-progressbar {
+ height: 2em;
+ text-align: left;
+ overflow: hidden;
+}
+.ui-progressbar .ui-progressbar-value {
+ margin: -1px;
+ height: 100%;
+}
+.ui-progressbar .ui-progressbar-overlay {
+ background: url("images/animated-overlay.gif");
+ height: 100%;
+ filter: alpha(opacity=25);
+ opacity: 0.25;
+}
+.ui-progressbar-indeterminate .ui-progressbar-value {
+ background-image: none;
+}
+.ui-slider {
+ position: relative;
+ text-align: left;
+}
+.ui-slider .ui-slider-handle {
+ position: absolute;
+ z-index: 2;
+ width: 1.2em;
+ height: 1.2em;
+ cursor: default;
+}
+.ui-slider .ui-slider-range {
+ position: absolute;
+ z-index: 1;
+ font-size: .7em;
+ display: block;
+ border: 0;
+ background-position: 0 0;
+}
+
+/* For IE8 - See #6727 */
+.ui-slider.ui-state-disabled .ui-slider-handle,
+.ui-slider.ui-state-disabled .ui-slider-range {
+ filter: inherit;
+}
+
+.ui-slider-horizontal {
+ height: .8em;
+}
+.ui-slider-horizontal .ui-slider-handle {
+ top: -.3em;
+ margin-left: -.6em;
+}
+.ui-slider-horizontal .ui-slider-range {
+ top: 0;
+ height: 100%;
+}
+.ui-slider-horizontal .ui-slider-range-min {
+ left: 0;
+}
+.ui-slider-horizontal .ui-slider-range-max {
+ right: 0;
+}
+
+.ui-slider-vertical {
+ width: .8em;
+ height: 100px;
+}
+.ui-slider-vertical .ui-slider-handle {
+ left: -.3em;
+ margin-left: 0;
+ margin-bottom: -.6em;
+}
+.ui-slider-vertical .ui-slider-range {
+ left: 0;
+ width: 100%;
+}
+.ui-slider-vertical .ui-slider-range-min {
+ bottom: 0;
+}
+.ui-slider-vertical .ui-slider-range-max {
+ top: 0;
+}
+.ui-spinner {
+ position: relative;
+ display: inline-block;
+ overflow: hidden;
+ padding: 0;
+ vertical-align: middle;
+}
+.ui-spinner-input {
+ border: none;
+ background: none;
+ color: inherit;
+ padding: 0;
+ margin: .2em 0;
+ vertical-align: middle;
+ margin-left: .4em;
+ margin-right: 22px;
+}
+.ui-spinner-button {
+ width: 16px;
+ height: 50%;
+ font-size: .5em;
+ padding: 0;
+ margin: 0;
+ text-align: center;
+ position: absolute;
+ cursor: default;
+ display: block;
+ overflow: hidden;
+ right: 0;
+}
+/* more specificity required here to override default borders */
+.ui-spinner a.ui-spinner-button {
+ border-top: none;
+ border-bottom: none;
+ border-right: none;
+}
+/* vertically center icon */
+.ui-spinner .ui-icon {
+ position: absolute;
+ margin-top: -8px;
+ top: 50%;
+ left: 0;
+}
+.ui-spinner-up {
+ top: 0;
+}
+.ui-spinner-down {
+ bottom: 0;
+}
+
+/* TR overrides */
+.ui-spinner .ui-icon-triangle-1-s {
+ /* need to fix icons sprite */
+ background-position: -65px -16px;
+}
+.ui-tabs {
+ position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+ padding: .2em;
+}
+.ui-tabs .ui-tabs-nav {
+ margin: 0;
+ padding: .2em .2em 0;
+}
+.ui-tabs .ui-tabs-nav li {
+ list-style: none;
+ float: left;
+ position: relative;
+ top: 0;
+ margin: 1px .2em 0 0;
+ border-bottom-width: 0;
+ padding: 0;
+ white-space: nowrap;
+}
+.ui-tabs .ui-tabs-nav .ui-tabs-anchor {
+ float: left;
+ padding: .5em 1em;
+ text-decoration: none;
+}
+.ui-tabs .ui-tabs-nav li.ui-tabs-active {
+ margin-bottom: -1px;
+ padding-bottom: 1px;
+}
+.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
+.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
+.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
+ cursor: text;
+}
+.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
+ cursor: pointer;
+}
+.ui-tabs .ui-tabs-panel {
+ display: block;
+ border-width: 0;
+ padding: 1em 1.4em;
+ background: none;
+}
+.ui-tooltip {
+ padding: 8px;
+ position: absolute;
+ z-index: 9999;
+ max-width: 300px;
+ -webkit-box-shadow: 0 0 5px #aaa;
+ box-shadow: 0 0 5px #aaa;
+}
+body .ui-tooltip {
+ border-width: 2px;
+}
+
+/* Component containers
+----------------------------------*/
+.ui-widget {
+ font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;
+ font-size: 1.1em;
+}
+.ui-widget .ui-widget {
+ font-size: 1em;
+}
+.ui-widget input,
+.ui-widget select,
+.ui-widget textarea,
+.ui-widget button {
+ font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;
+ font-size: 1em;
+}
+.ui-widget-content {
+ border: 1px solid #dddddd;
+ background: #eeeeee url("images/ui-bg_highlight-soft_100_eeeeee_1x100.png") 50% top repeat-x;
+ color: #333333;
+}
+.ui-widget-content a {
+ color: #333333;
+}
+.ui-widget-header {
+ border: 1px solid #e78f08;
+ background: #f6a828 url("images/ui-bg_gloss-wave_35_f6a828_500x100.png") 50% 50% repeat-x;
+ color: #ffffff;
+ font-weight: bold;
+}
+.ui-widget-header a {
+ color: #ffffff;
+}
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default,
+.ui-widget-content .ui-state-default,
+.ui-widget-header .ui-state-default {
+ border: 1px solid #cccccc;
+ background: #f6f6f6 url("images/ui-bg_glass_100_f6f6f6_1x400.png") 50% 50% repeat-x;
+ font-weight: bold;
+ color: #1c94c4;
+}
+.ui-state-default a,
+.ui-state-default a:link,
+.ui-state-default a:visited {
+ color: #1c94c4;
+ text-decoration: none;
+}
+.ui-state-hover,
+.ui-widget-content .ui-state-hover,
+.ui-widget-header .ui-state-hover,
+.ui-state-focus,
+.ui-widget-content .ui-state-focus,
+.ui-widget-header .ui-state-focus {
+ border: 1px solid #fbcb09;
+ background: #fdf5ce url("images/ui-bg_glass_100_fdf5ce_1x400.png") 50% 50% repeat-x;
+ font-weight: bold;
+ color: #c77405;
+}
+.ui-state-hover a,
+.ui-state-hover a:hover,
+.ui-state-hover a:link,
+.ui-state-hover a:visited,
+.ui-state-focus a,
+.ui-state-focus a:hover,
+.ui-state-focus a:link,
+.ui-state-focus a:visited {
+ color: #c77405;
+ text-decoration: none;
+}
+.ui-state-active,
+.ui-widget-content .ui-state-active,
+.ui-widget-header .ui-state-active {
+ border: 1px solid #fbd850;
+ background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;
+ font-weight: bold;
+ color: #eb8f00;
+}
+.ui-state-active a,
+.ui-state-active a:link,
+.ui-state-active a:visited {
+ color: #eb8f00;
+ text-decoration: none;
+}
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight,
+.ui-widget-content .ui-state-highlight,
+.ui-widget-header .ui-state-highlight {
+ border: 1px solid #fed22f;
+ background: #ffe45c url("images/ui-bg_highlight-soft_75_ffe45c_1x100.png") 50% top repeat-x;
+ color: #363636;
+}
+.ui-state-highlight a,
+.ui-widget-content .ui-state-highlight a,
+.ui-widget-header .ui-state-highlight a {
+ color: #363636;
+}
+.ui-state-error,
+.ui-widget-content .ui-state-error,
+.ui-widget-header .ui-state-error {
+ border: 1px solid #cd0a0a;
+ background: #b81900 url("images/ui-bg_diagonals-thick_18_b81900_40x40.png") 50% 50% repeat;
+ color: #ffffff;
+}
+.ui-state-error a,
+.ui-widget-content .ui-state-error a,
+.ui-widget-header .ui-state-error a {
+ color: #ffffff;
+}
+.ui-state-error-text,
+.ui-widget-content .ui-state-error-text,
+.ui-widget-header .ui-state-error-text {
+ color: #ffffff;
+}
+.ui-priority-primary,
+.ui-widget-content .ui-priority-primary,
+.ui-widget-header .ui-priority-primary {
+ font-weight: bold;
+}
+.ui-priority-secondary,
+.ui-widget-content .ui-priority-secondary,
+.ui-widget-header .ui-priority-secondary {
+ opacity: .7;
+ filter:Alpha(Opacity=70);
+ font-weight: normal;
+}
+.ui-state-disabled,
+.ui-widget-content .ui-state-disabled,
+.ui-widget-header .ui-state-disabled {
+ opacity: .35;
+ filter:Alpha(Opacity=35);
+ background-image: none;
+}
+.ui-state-disabled .ui-icon {
+ filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
+}
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon {
+ width: 16px;
+ height: 16px;
+}
+.ui-icon,
+.ui-widget-content .ui-icon {
+ background-image: url("images/ui-icons_222222_256x240.png");
+}
+.ui-widget-header .ui-icon {
+ background-image: url("images/ui-icons_ffffff_256x240.png");
+}
+.ui-state-default .ui-icon {
+ background-image: url("images/ui-icons_ef8c08_256x240.png");
+}
+.ui-state-hover .ui-icon,
+.ui-state-focus .ui-icon {
+ background-image: url("images/ui-icons_ef8c08_256x240.png");
+}
+.ui-state-active .ui-icon {
+ background-image: url("images/ui-icons_ef8c08_256x240.png");
+}
+.ui-state-highlight .ui-icon {
+ background-image: url("images/ui-icons_228ef1_256x240.png");
+}
+.ui-state-error .ui-icon,
+.ui-state-error-text .ui-icon {
+ background-image: url("images/ui-icons_ffd27a_256x240.png");
+}
+
+/* positioning */
+.ui-icon-blank { background-position: 16px 16px; }
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-on { background-position: -96px -144px; }
+.ui-icon-radio-off { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-all,
+.ui-corner-top,
+.ui-corner-left,
+.ui-corner-tl {
+ border-top-left-radius: 4px;
+}
+.ui-corner-all,
+.ui-corner-top,
+.ui-corner-right,
+.ui-corner-tr {
+ border-top-right-radius: 4px;
+}
+.ui-corner-all,
+.ui-corner-bottom,
+.ui-corner-left,
+.ui-corner-bl {
+ border-bottom-left-radius: 4px;
+}
+.ui-corner-all,
+.ui-corner-bottom,
+.ui-corner-right,
+.ui-corner-br {
+ border-bottom-right-radius: 4px;
+}
+
+/* Overlays */
+.ui-widget-overlay {
+ background: #666666 url("images/ui-bg_diagonals-thick_20_666666_40x40.png") 50% 50% repeat;
+ opacity: .5;
+ filter: Alpha(Opacity=50);
+}
+.ui-widget-shadow {
+ margin: -5px 0 0 -5px;
+ padding: 5px;
+ background: #000000 url("images/ui-bg_flat_10_000000_40x100.png") 50% 50% repeat-x;
+ opacity: .2;
+ filter: Alpha(Opacity=20);
+ border-radius: 5px;
+}
--- /dev/null
+/*! jQuery UI - v1.10.4 - 2014-06-03
+* http://jqueryui.com
+* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=gloss_wave&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=highlight_soft&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=glass&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=glass&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=highlight_soft&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=diagonals_thick&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=flat&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
+* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
+
+.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:none}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;width:100%;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.ui-menu .ui-menu-divider{margin:5px -2px 5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;min-height:0;font-weight:normal}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{font-weight:normal;margin:-1px}.ui-menu .ui-state-disabled{font-weight:normal;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("images/animated-overlay.gif");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #ddd;background:#eee url("images/ui-bg_highlight-soft_100_eeeeee_1x100.png") 50% top repeat-x;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #e78f08;background:#f6a828 url("images/ui-bg_gloss-wave_35_f6a828_500x100.png") 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #ccc;background:#f6f6f6 url("images/ui-bg_glass_100_f6f6f6_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#1c94c4}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#1c94c4;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #fbcb09;background:#fdf5ce url("images/ui-bg_glass_100_fdf5ce_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#c77405}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#c77405;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #fbd850;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#eb8f00}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#eb8f00;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fed22f;background:#ffe45c url("images/ui-bg_highlight-soft_75_ffe45c_1x100.png") 50% top repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#b81900 url("images/ui-bg_diagonals-thick_18_b81900_40x40.png") 50% 50% repeat;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_228ef1_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_ffd27a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#666 url("images/ui-bg_diagonals-thick_20_666666_40x40.png") 50% 50% repeat;opacity:.5;filter:Alpha(Opacity=50)}.ui-widget-shadow{margin:-5px 0 0 -5px;padding:5px;background:#000 url("images/ui-bg_flat_10_000000_40x100.png") 50% 50% repeat-x;opacity:.2;filter:Alpha(Opacity=20);border-radius:5px}
\ No newline at end of file
<!DOCTYPE html>\r
<html>\r
<head>\r
- <title>Swagger UI</title>\r
- <link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>\r
- <link href='css/highlight.default.css' media='screen' rel='stylesheet' type='text/css'/>\r
- <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>\r
- <script type="text/javascript" src="lib/shred.bundle.js" /></script> \r
- <script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>\r
- <script src='lib/jquery.slideto.min.js' type='text/javascript'></script>\r
- <script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>\r
- <script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>\r
- <script src='lib/handlebars-1.0.0.js' type='text/javascript'></script>\r
- <script src='lib/underscore-min.js' type='text/javascript'></script>\r
- <script src='lib/backbone-min.js' type='text/javascript'></script>\r
- <script src='lib/swagger.js' type='text/javascript'></script>\r
- <script src='swagger-ui.js' type='text/javascript'></script>\r
- <script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>\r
- <script type="text/javascript">\r
- $(function () {\r
- window.swaggerUi = new SwaggerUi({\r
- url: "http://localhost:8080/apidoc/apis",\r
- dom_id: "swagger-ui-container",\r
- supportedSubmitMethods: ['get', 'post', 'put', 'delete'],\r
- onComplete: function(swaggerApi, swaggerUi){\r
- if(console) {\r
- console.log("Loaded SwaggerUI")\r
- }\r
- $('pre code').each(function(i, e) {hljs.highlightBlock(e)});\r
- },\r
- onFailure: function(data) {\r
- if(console) {\r
- console.log("Unable to Load SwaggerUI");\r
- console.log(data);\r
- }\r
- },\r
- docExpansion: "none"\r
- });\r
+<title>RestConf Documentation</title>\r
+<link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700'\r
+ rel='stylesheet' type='text/css' />\r
+<link href='css/highlight.default.css' media='screen' rel='stylesheet'\r
+ type='text/css' />\r
+<link href='css/screen.css' media='screen' rel='stylesheet'\r
+ type='text/css' />\r
+<link rel="stylesheet" type="text/css" href="/css/opendaylight.css">\r
+<link rel="stylesheet" type="text/css"\r
+ href="css/ui-lightness/jquery-ui-1.10.4.custom.min.css">\r
+<script type="text/javascript" src="lib/shred.bundle.js"></script>\r
+<script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>\r
+<script src='lib/jquery-ui-1.11.0.min.js' type="text/javascript"></script>\r
+<script src='lib/jquery.slideto.min.js' type='text/javascript'></script>\r
+<script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>\r
+<script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>\r
+<script src='lib/handlebars-1.0.0.js' type='text/javascript'></script>\r
+<script src='lib/underscore-min.js' type='text/javascript'></script>\r
+<script src='lib/backbone-min.js' type='text/javascript'></script>\r
+<script src='lib/swagger.js' type='text/javascript'></script>\r
+<script src='swagger-ui.js' type='text/javascript'></script>\r
+<script src='lib/odl/list_mounts.js' type='text/javascript'></script>\r
+<script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>\r
+<script src='lib/odl/rest-tree.js' type='text/javascript'></script>\r
+<script src='lib/odl/swagger.js' type='text/javascript'></script>\r
\r
- $('#input_apiKey').change(function() {\r
- var key = $('#input_apiKey')[0].value;\r
- console.log("key: " + key);\r
- if(key && key.trim() != "") {\r
- console.log("added key " + key);\r
- window.authorizations.add("key", new ApiKeyAuthorization("api_key", key, "query"));\r
- }\r
- })\r
- window.swaggerUi.load();\r
- });\r
+<script type="text/javascript">\r
+ \r
+ //reloads the swagger UI documentation for the specified mount.\r
+ var loadMount = function(mountIndex, mountPath) {\r
+ $("#message").empty();\r
+ $("#message").append( "<p>Loading...</p>" );\r
+ loadSwagger("http://localhost:8080/apidoc/apis/mounts/" + mountIndex,\r
+ "swagger-ui-container");\r
+ $("#message").empty();\r
+ $("#message").append( "<h2><b>Showing mount points for " + mountPath + "</b></h2>");\r
+ }\r
\r
- </script>\r
+ //clears the swagger UI and adds text prompting use to select a mount point.\r
+ var selectAMount = function(string) {\r
+ $("#swagger-ui-container").empty();\r
+ $("#message").empty();\r
+ $("#message").append("<p>Select a mount point.</p>");\r
+ }\r
+ \r
+ //loads the root swagger documenation (which comes from RestConf)\r
+ var loadRootSwagger = function() {\r
+ $("#message").empty();\r
+ loadSwagger("http://localhost:8080/apidoc/apis", "swagger-ui-container");\r
+ }\r
+\r
+ //main method to initialize the mount list / swagger docs / tabs on page load\r
+ $(function() {\r
+ $("#tabs").tabs();\r
+\r
+ loadMountList($("#mountlist"));\r
+\r
+ loadRootSwagger();\r
+ });\r
+</script>\r
</head>\r
\r
<body>\r
-<div id='header'>\r
- <div class="swagger-ui-wrap">\r
- <a id="logo" href="http://swagger.wordnik.com">swagger</a>\r
-\r
- <form id='api_selector'>\r
- <div class='input icon-btn'>\r
- <img id="show-pet-store-icon" src="images/pet_store_api.png" title="Show Swagger Petstore Example Apis">\r
- </div>\r
- <div class='input icon-btn'>\r
- <img id="show-wordnik-dev-icon" src="images/wordnik_api.png" title="Show Wordnik Developer Apis">\r
- </div>\r
- <div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div>\r
- <div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>\r
- <div class='input'><a id="explore" href="#">Explore</a></div>\r
- </form>\r
- </div>\r
-</div>\r
+ <div>\r
+ <!-- style="background-color: #FCA000;" -->\r
+ <div class="swagger-ui-wrap ui-tabs">\r
+ <table>\r
+ <tr>\r
+ <td><img src="images/logo_small.png" /></td>\r
+ <td><h1 width="100%">OpenDaylight RestConf API\r
+ Documentation</h1></td>\r
+ </tr>\r
+ </table>\r
+ </div>\r
+ </div>\r
\r
-<div id="message-bar" class="swagger-ui-wrap">\r
- \r
-</div>\r
+ <div class="navbar-inner">\r
+ <div class="brand"></div>\r
+ </div>\r
\r
-<div id="swagger-ui-container" class="swagger-ui-wrap">\r
+ <!-- defines the div shells which represent the jquery tabs -->\r
+ <div id="tabs" class="swagger-ui-wrap">\r
+ <ul>\r
+ <li><a href="#Controller" onclick="loadRootSwagger()">Controller\r
+ Resources</a></li>\r
+ <li><a href="#Mounts" onclick="selectAMount()">Mounted\r
+ Resources</a></li>\r
+ </ul>\r
\r
-</div>\r
+ <div id="Controller">\r
+ <div>\r
+ <h3>Below are the list of APIs supported by the Controller.</h3>\r
+ </div>\r
+ </div>\r
+ <div id="Mounts">\r
+ <div>\r
+ <h3>Mount Points - Select an API below for details on available\r
+ queries.</h3>\r
+ </div>\r
+ <div id="mountlist"></div>\r
+ </div>\r
+ </div>\r
+ \r
+ <div class="swagger-ui-wrap"><hr/></div>\r
+ \r
+ <!-- messages -->\r
+ <div id="message" class="swagger-ui-wrap"></div>\r
+ \r
+ <!-- the swagger is always loaded in this div -->\r
+ <div id="swagger-ui-container" class="swagger-ui-wrap"></div>\r
\r
+ <div id="message-bar" class="swagger-ui-wrap"> </div>\r
</body>\r
\r
</html>\r
--- /dev/null
+/*! jQuery UI - v1.11.0-beta.2 - 2014-06-08
+* http://jqueryui.com
+* Includes: core.js, widget.js, mouse.js, position.js, draggable.js, droppable.js, resizable.js, selectable.js, sortable.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, menu.js, progressbar.js, selectmenu.js, slider.js, spinner.js, tabs.js, tooltip.js, effect.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js
+* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
+
+(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(t,i){var r,s,n,o=t.nodeName.toLowerCase();return"area"===o?(r=t.parentNode,s=r.name,t.href&&s&&"map"===r.nodeName.toLowerCase()?(n=e("img[usemap=#"+s+"]")[0],!!n&&a(n)):!1):(/input|select|textarea|button|object/.test(o)?!t.disabled:"a"===o?t.href||i:i)&&a(t)}function a(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}function i(e){for(var t,a;e.length&&e[0]!==document;){if(t=e.css("position"),("absolute"===t||"relative"===t||"fixed"===t)&&(a=parseInt(e.css("zIndex"),10),!isNaN(a)&&0!==a))return a;e=e.parent()}return 0}function r(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},e.extend(this._defaults,this.regional[""]),this.dpDiv=s(e("<div id='"+this._mainDivId+"' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"))}function s(t){var a="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return t.delegate(a,"mouseout",function(){e(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).removeClass("ui-datepicker-next-hover")}).delegate(a,"mouseover",function(){e.datepicker._isDisabledDatepicker(g.inline?t.parent()[0]:g.input[0])||(e(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),e(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).addClass("ui-datepicker-next-hover"))})}function n(t,a){e.extend(t,a);for(var i in a)null==a[i]&&(t[i]=a[i]);return t}function o(e){return function(){var t=this.element.val();e.apply(this,arguments),this._refresh(),t!==this.element.val()&&this._trigger("change")}}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.0-beta.2",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(){var t=this.css("position"),a="absolute"===t,i=this.parents().filter(function(){var t=e(this);return a&&"static"===t.css("position")?!1:/(auto|scroll)/.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==t&&i.length?i:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(a){return!!e.data(a,t)}}):function(t,a,i){return!!e.data(t,i[3])},focusable:function(a){return t(a,!isNaN(e.attr(a,"tabindex")))},tabbable:function(a){var i=e.attr(a,"tabindex"),r=isNaN(i);return(r||i>=0)&&t(a,!r)}}),e("<a>").outerWidth(1).jquery||e.each(["Width","Height"],function(t,a){function i(t,a,i,s){return e.each(r,function(){a-=parseFloat(e.css(t,"padding"+this))||0,i&&(a-=parseFloat(e.css(t,"border"+this+"Width"))||0),s&&(a-=parseFloat(e.css(t,"margin"+this))||0)}),a}var r="Width"===a?["Left","Right"]:["Top","Bottom"],s=a.toLowerCase(),n={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+a]=function(t){return void 0===t?n["inner"+a].call(this):this.each(function(){e(this).css(s,i(this,t)+"px")})},e.fn["outer"+a]=function(t,r){return"number"!=typeof t?n["outer"+a].call(this,t):this.each(function(){e(this).css(s,i(this,t,!0,r)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(a){return arguments.length?t.call(this,e.camelCase(a)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(a,i){return"number"==typeof a?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),i&&i.call(t)},a)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var a,i,r=e(this[0]);r.length&&r[0]!==document;){if(a=r.css("position"),("absolute"===a||"relative"===a||"fixed"===a)&&(i=parseInt(r.css("zIndex"),10),!isNaN(i)&&0!==i))return i;r=r.parent()}return 0}}),e.ui.plugin={add:function(t,a,i){var r,s=e.ui[t].prototype;for(r in i)s.plugins[r]=s.plugins[r]||[],s.plugins[r].push([a,i[r]])},call:function(e,t,a,i){var r,s=e.plugins[t];if(s&&(i||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(r=0;s.length>r;r++)e.options[s[r][0]]&&s[r][1].apply(e.element,a)}};var d=0,u=Array.prototype.slice;e.cleanData=function(t){return function(a){for(var i,r=0;null!=(i=a[r]);r++)try{e(i).triggerHandler("remove")}catch(s){}t(a)}}(e.cleanData),e.widget=function(t,a,i){var r,s,n,o,d={},u=t.split(".")[0];return t=t.split(".")[1],r=u+"-"+t,i||(i=a,a=e.Widget),e.expr[":"][r.toLowerCase()]=function(t){return!!e.data(t,r)},e[u]=e[u]||{},s=e[u][t],n=e[u][t]=function(e,t){return this._createWidget?(arguments.length&&this._createWidget(e,t),void 0):new n(e,t)},e.extend(n,s,{version:i.version,_proto:e.extend({},i),_childConstructors:[]}),o=new a,o.options=e.widget.extend({},o.options),e.each(i,function(t,i){return e.isFunction(i)?(d[t]=function(){var e=function(){return a.prototype[t].apply(this,arguments)},r=function(e){return a.prototype[t].apply(this,e)};return function(){var t,a=this._super,s=this._superApply;return this._super=e,this._superApply=r,t=i.apply(this,arguments),this._super=a,this._superApply=s,t}}(),void 0):(d[t]=i,void 0)}),n.prototype=e.widget.extend(o,{widgetEventPrefix:s?o.widgetEventPrefix||t:t},d,{constructor:n,namespace:u,widgetName:t,widgetFullName:r}),s?(e.each(s._childConstructors,function(t,a){var i=a.prototype;e.widget(i.namespace+"."+i.widgetName,n,a._proto)}),delete s._childConstructors):a._childConstructors.push(n),e.widget.bridge(t,n),n},e.widget.extend=function(t){for(var a,i,r=u.call(arguments,1),s=0,n=r.length;n>s;s++)for(a in r[s])i=r[s][a],r[s].hasOwnProperty(a)&&void 0!==i&&(t[a]=e.isPlainObject(i)?e.isPlainObject(t[a])?e.widget.extend({},t[a],i):e.widget.extend({},i):i);return t},e.widget.bridge=function(t,a){var i=a.prototype.widgetFullName||t;e.fn[t]=function(r){var s="string"==typeof r,n=u.call(arguments,1),o=this;return r=!s&&n.length?e.widget.extend.apply(null,[r].concat(n)):r,s?this.each(function(){var a,s=e.data(this,i);return"instance"===r?(o=s,!1):s?e.isFunction(s[r])&&"_"!==r.charAt(0)?(a=s[r].apply(s,n),a!==s&&void 0!==a?(o=a&&a.jquery?o.pushStack(a.get()):a,!1):void 0):e.error("no such method '"+r+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; "+"attempted to call method '"+r+"'")}):this.each(function(){var t=e.data(this,i);t?(t.option(r||{}),t._init&&t._init()):e.data(this,i,new a(r,this))}),o}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(t,a){a=e(a||this.defaultElement||this)[0],this.element=e(a),this.uuid=d++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),a!==this&&(e.data(a,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===a&&this.destroy()}}),this.document=e(a.style?a.ownerDocument:a.document||a),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,a){var i,r,s,n=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(n={},i=t.split("."),t=i.shift(),i.length){for(r=n[t]=e.widget.extend({},this.options[t]),s=0;i.length-1>s;s++)r[i[s]]=r[i[s]]||{},r=r[i[s]];if(t=i.pop(),1===arguments.length)return void 0===r[t]?null:r[t];r[t]=a}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];n[t]=a}return this._setOptions(n),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,a,i){var r,s=this;"boolean"!=typeof t&&(i=a,a=t,t=!1),i?(a=r=e(a),this.bindings=this.bindings.add(a)):(i=a,a=this.element,r=this.widget()),e.each(i,function(i,n){function o(){return t||s.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof n?s[n]:n).apply(s,arguments):void 0}"string"!=typeof n&&(o.guid=n.guid=n.guid||o.guid||e.guid++);var d=i.match(/^([\w:-]*)\s*(.*)$/),u=d[1]+s.eventNamespace,h=d[2];h?r.delegate(h,u,o):a.bind(u,o)})},_off:function(e,t){t=(t||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function a(){return("string"==typeof e?i[e]:e).apply(i,arguments)}var i=this;return setTimeout(a,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,a,i){var r,s,n=this.options[t];if(i=i||{},a=e.Event(a),a.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),a.target=this.element[0],s=a.originalEvent)for(r in s)r in a||(a[r]=s[r]);return this.element.trigger(a,i),!(e.isFunction(n)&&n.apply(this.element[0],[a].concat(i))===!1||a.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,a){e.Widget.prototype["_"+t]=function(i,r,s){"string"==typeof r&&(r={effect:r});var n,o=r?r===!0||"number"==typeof r?a:r.effect||a:t;r=r||{},"number"==typeof r&&(r={duration:r}),n=!e.isEmptyObject(r),r.complete=s,r.delay&&i.delay(r.delay),n&&e.effects&&e.effects.effect[o]?i[t](r):o!==t&&i[o]?i[o](r.duration,r.easing,s):i.queue(function(a){e(this)[t](),s&&s.call(i[0]),a()})}}),e.widget;var h=!1;e(document).mouseup(function(){h=!1}),e.widget("ui.mouse",{version:"1.11.0-beta.2",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(a){return!0===e.data(a.target,t.widgetName+".preventClickEvent")?(e.removeData(a.target,t.widgetName+".preventClickEvent"),a.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(!h){this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var a=this,i=1===t.which,r="string"==typeof this.options.cancel&&t.target.nodeName?e(t.target).closest(this.options.cancel).length:!1;return i&&!r&&this._mouseCapture(t)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){a.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(t)!==!1,!this._mouseStarted)?(t.preventDefault(),!0):(!0===e.data(t.target,this.widgetName+".preventClickEvent")&&e.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return a._mouseMove(e)},this._mouseUpDelegate=function(e){return a._mouseUp(e)},this.document.bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),h=!0,!0)):!0}},_mouseMove:function(t){return e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button?this._mouseUp(t):t.which?this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted):this._mouseUp(t)},_mouseUp:function(t){return this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),h=!1,!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),function(){function t(e,t,a){return[parseFloat(e[0])*(c.test(e[0])?t/100:1),parseFloat(e[1])*(c.test(e[1])?a/100:1)]}function a(t,a){return parseInt(e.css(t,a),10)||0}function i(t){var a=t[0];return 9===a.nodeType?{width:t.width(),height:t.height(),offset:{top:0,left:0}}:e.isWindow(a)?{width:t.width(),height:t.height(),offset:{top:t.scrollTop(),left:t.scrollLeft()}}:a.preventDefault?{width:0,height:0,offset:{top:a.pageY,left:a.pageX}}:{width:t.outerWidth(),height:t.outerHeight(),offset:t.offset()}}e.ui=e.ui||{};var r,s,n=Math.max,o=Math.abs,d=Math.round,u=/left|center|right/,h=/top|center|bottom/,l=/[\+\-]\d+(\.[\d]+)?%?/,m=/^\w+/,c=/%$/,p=e.fn.position;e.position={scrollbarWidth:function(){if(void 0!==r)return r;var t,a,i=e("<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),s=i.children()[0];return e("body").append(i),t=s.offsetWidth,i.css("overflow","scroll"),a=s.offsetWidth,t===a&&(a=i[0].clientWidth),i.remove(),r=t-a},getScrollInfo:function(t){var a=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),i=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),r="scroll"===a||"auto"===a&&t.width<t.element[0].scrollWidth,s="scroll"===i||"auto"===i&&t.height<t.element[0].scrollHeight;return{width:s?e.position.scrollbarWidth():0,height:r?e.position.scrollbarWidth():0}},getWithinInfo:function(t){var a=e(t||window),i=e.isWindow(a[0]),r=!!a[0]&&9===a[0].nodeType;return{element:a,isWindow:i,isDocument:r,offset:a.offset()||{left:0,top:0},scrollLeft:a.scrollLeft(),scrollTop:a.scrollTop(),width:i?a.width():a.outerWidth(),height:i?a.height():a.outerHeight()}}},e.fn.position=function(r){if(!r||!r.of)return p.apply(this,arguments);r=e.extend({},r);var c,f,g,y,x,v,T=e(r.of),k=e.position.getWithinInfo(r.within),S=e.position.getScrollInfo(k),b=(r.collision||"flip").split(" "),N={};return v=i(T),T[0].preventDefault&&(r.at="left top"),f=v.width,g=v.height,y=v.offset,x=e.extend({},y),e.each(["my","at"],function(){var e,t,a=(r[this]||"").split(" ");1===a.length&&(a=u.test(a[0])?a.concat(["center"]):h.test(a[0])?["center"].concat(a):["center","center"]),a[0]=u.test(a[0])?a[0]:"center",a[1]=h.test(a[1])?a[1]:"center",e=l.exec(a[0]),t=l.exec(a[1]),N[this]=[e?e[0]:0,t?t[0]:0],r[this]=[m.exec(a[0])[0],m.exec(a[1])[0]]}),1===b.length&&(b[1]=b[0]),"right"===r.at[0]?x.left+=f:"center"===r.at[0]&&(x.left+=f/2),"bottom"===r.at[1]?x.top+=g:"center"===r.at[1]&&(x.top+=g/2),c=t(N.at,f,g),x.left+=c[0],x.top+=c[1],this.each(function(){var i,u,h=e(this),l=h.outerWidth(),m=h.outerHeight(),p=a(this,"marginLeft"),v=a(this,"marginTop"),M=l+p+a(this,"marginRight")+S.width,D=m+v+a(this,"marginBottom")+S.height,w=e.extend({},x),_=t(N.my,h.outerWidth(),h.outerHeight());"right"===r.my[0]?w.left-=l:"center"===r.my[0]&&(w.left-=l/2),"bottom"===r.my[1]?w.top-=m:"center"===r.my[1]&&(w.top-=m/2),w.left+=_[0],w.top+=_[1],s||(w.left=d(w.left),w.top=d(w.top)),i={marginLeft:p,marginTop:v},e.each(["left","top"],function(t,a){e.ui.position[b[t]]&&e.ui.position[b[t]][a](w,{targetWidth:f,targetHeight:g,elemWidth:l,elemHeight:m,collisionPosition:i,collisionWidth:M,collisionHeight:D,offset:[c[0]+_[0],c[1]+_[1]],my:r.my,at:r.at,within:k,elem:h})}),r.using&&(u=function(e){var t=y.left-w.left,a=t+f-l,i=y.top-w.top,s=i+g-m,d={target:{element:T,left:y.left,top:y.top,width:f,height:g},element:{element:h,left:w.left,top:w.top,width:l,height:m},horizontal:0>a?"left":t>0?"right":"center",vertical:0>s?"top":i>0?"bottom":"middle"};l>f&&f>o(t+a)&&(d.horizontal="center"),m>g&&g>o(i+s)&&(d.vertical="middle"),d.important=n(o(t),o(a))>n(o(i),o(s))?"horizontal":"vertical",r.using.call(this,e,d)}),h.offset(e.extend(w,{using:u}))})},e.ui.position={fit:{left:function(e,t){var a,i=t.within,r=i.isWindow?i.scrollLeft:i.offset.left,s=i.width,o=e.left-t.collisionPosition.marginLeft,d=r-o,u=o+t.collisionWidth-s-r;t.collisionWidth>s?d>0&&0>=u?(a=e.left+d+t.collisionWidth-s-r,e.left+=d-a):e.left=u>0&&0>=d?r:d>u?r+s-t.collisionWidth:r:d>0?e.left+=d:u>0?e.left-=u:e.left=n(e.left-o,e.left)},top:function(e,t){var a,i=t.within,r=i.isWindow?i.scrollTop:i.offset.top,s=t.within.height,o=e.top-t.collisionPosition.marginTop,d=r-o,u=o+t.collisionHeight-s-r;t.collisionHeight>s?d>0&&0>=u?(a=e.top+d+t.collisionHeight-s-r,e.top+=d-a):e.top=u>0&&0>=d?r:d>u?r+s-t.collisionHeight:r:d>0?e.top+=d:u>0?e.top-=u:e.top=n(e.top-o,e.top)}},flip:{left:function(e,t){var a,i,r=t.within,s=r.offset.left+r.scrollLeft,n=r.width,d=r.isWindow?r.scrollLeft:r.offset.left,u=e.left-t.collisionPosition.marginLeft,h=u-d,l=u+t.collisionWidth-n-d,m="left"===t.my[0]?-t.elemWidth:"right"===t.my[0]?t.elemWidth:0,c="left"===t.at[0]?t.targetWidth:"right"===t.at[0]?-t.targetWidth:0,p=-2*t.offset[0];0>h?(a=e.left+m+c+p+t.collisionWidth-n-s,(0>a||o(h)>a)&&(e.left+=m+c+p)):l>0&&(i=e.left-t.collisionPosition.marginLeft+m+c+p-d,(i>0||l>o(i))&&(e.left+=m+c+p))},top:function(e,t){var a,i,r=t.within,s=r.offset.top+r.scrollTop,n=r.height,d=r.isWindow?r.scrollTop:r.offset.top,u=e.top-t.collisionPosition.marginTop,h=u-d,l=u+t.collisionHeight-n-d,m="top"===t.my[1],c=m?-t.elemHeight:"bottom"===t.my[1]?t.elemHeight:0,p="top"===t.at[1]?t.targetHeight:"bottom"===t.at[1]?-t.targetHeight:0,f=-2*t.offset[1];0>h?(i=e.top+c+p+f+t.collisionHeight-n-s,e.top+c+p+f>h&&(0>i||o(h)>i)&&(e.top+=c+p+f)):l>0&&(a=e.top-t.collisionPosition.marginTop+c+p+f-d,e.top+c+p+f>l&&(a>0||l>o(a))&&(e.top+=c+p+f))}},flipfit:{left:function(){e.ui.position.flip.left.apply(this,arguments),e.ui.position.fit.left.apply(this,arguments)},top:function(){e.ui.position.flip.top.apply(this,arguments),e.ui.position.fit.top.apply(this,arguments)}}},function(){var t,a,i,r,n,o=document.getElementsByTagName("body")[0],d=document.createElement("div");t=document.createElement(o?"div":"body"),i={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},o&&e.extend(i,{position:"absolute",left:"-1000px",top:"-1000px"});for(n in i)t.style[n]=i[n];t.appendChild(d),a=o||document.documentElement,a.insertBefore(t,a.firstChild),d.style.cssText="position: absolute; left: 10.7432222px;",r=e(d).offset().left,s=r>10&&11>r,t.innerHTML="",a.removeChild(t)}()}(),e.ui.position,e.widget("ui.draggable",e.ui.mouse,{version:"1.11.0-beta.2",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"!==this.options.helper||/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._setHandleClassName(),this._mouseInit()},_setOption:function(e,t){this._super(e,t),"handle"===e&&this._setHandleClassName()},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(t){var a=this.document[0],i=this.options;try{a.activeElement&&"body"!==a.activeElement.nodeName.toLowerCase()&&e(a.activeElement).blur()}catch(r){}return this.helper||i.disabled||e(t.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(t),this.handle?(e(i.iframeFix===!0?"iframe":i.iframeFix).each(function(){e("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>").css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(e(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(t){var a=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offsetParent=this.helper.offsetParent(),this.offsetParentCssPosition=this.offsetParent.css("position"),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.offset.scroll=!1,e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(t,!1),this.originalPageX=t.pageX,this.originalPageY=t.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!a.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_mouseDrag:function(t,a){if("fixed"===this.offsetParentCssPosition&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(t,!0),this.positionAbs=this._convertPositionTo("absolute"),!a){var i=this._uiHash();if(this._trigger("drag",t,i)===!1)return this._mouseUp({}),!1;this.position=i.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var a=this,i=!1;return e.ui.ddmanager&&!this.options.dropBehaviour&&(i=e.ui.ddmanager.drop(this,t)),this.dropped&&(i=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!i||"valid"===this.options.revert&&i||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,i)?e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){a._trigger("stop",t)!==!1&&a._clear()}):this._trigger("stop",t)!==!1&&this._clear(),!1},_mouseUp:function(t){return e("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),this.element.focus(),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){return this.options.handle?!!e(t.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this._removeHandleClassName(),e(this.options.handle||this.element).addClass("ui-draggable-handle")},_removeHandleClassName:function(){this.element.find(".ui-draggable-handle").addBack().removeClass("ui-draggable-handle")},_createHelper:function(t){var a=this.options,i=e.isFunction(a.helper)?e(a.helper.apply(this.element[0],[t])):"clone"===a.helper?this.element.clone().removeAttr("id"):this.element;return i.parents("body").length||i.appendTo("parent"===a.appendTo?this.element[0].parentNode:a.appendTo),i[0]===this.element[0]||/(fixed|absolute)/.test(i.css("position"))||i.css("position","absolute"),i},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_isRootNode:function(e){return/(html|body)/i.test(e.tagName)||e===this.document[0]},_getParentOffset:function(){var t=this.offsetParent.offset(),a=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==a&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var e=this.element.position(),t=this._isRootNode(this.scrollParent[0]);return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+(t?0:this.scrollParent.scrollTop()),left:e.left-(parseInt(this.helper.css("left"),10)||0)+(t?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,a,i,r=this.options,s=this.document[0];return this.relative_container=null,r.containment?"window"===r.containment?(this.containment=[e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,e(window).scrollLeft()+e(window).width()-this.helperProportions.width-this.margins.left,e(window).scrollTop()+(e(window).height()||s.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===r.containment?(this.containment=[0,0,e(s).width()-this.helperProportions.width-this.margins.left,(e(s).height()||s.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):r.containment.constructor===Array?(this.containment=r.containment,void 0):("parent"===r.containment&&(r.containment=this.helper[0].parentNode),a=e(r.containment),i=a[0],i&&(t="hidden"!==a.css("overflow"),this.containment=[(parseInt(a.css("borderLeftWidth"),10)||0)+(parseInt(a.css("paddingLeft"),10)||0),(parseInt(a.css("borderTopWidth"),10)||0)+(parseInt(a.css("paddingTop"),10)||0),(t?Math.max(i.scrollWidth,i.offsetWidth):i.offsetWidth)-(parseInt(a.css("borderRightWidth"),10)||0)-(parseInt(a.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(t?Math.max(i.scrollHeight,i.offsetHeight):i.offsetHeight)-(parseInt(a.css("borderBottomWidth"),10)||0)-(parseInt(a.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=a),void 0):(this.containment=null,void 0)},_convertPositionTo:function(e,t){t||(t=this.position);var a="absolute"===e?1:-1,i=this._isRootNode(this.scrollParent[0]);return{top:t.top+this.offset.relative.top*a+this.offset.parent.top*a-("fixed"===this.cssPosition?-this.offset.scroll.top:i?0:this.offset.scroll.top)*a,left:t.left+this.offset.relative.left*a+this.offset.parent.left*a-("fixed"===this.cssPosition?-this.offset.scroll.left:i?0:this.offset.scroll.left)*a}},_generatePosition:function(e,t){var a,i,r,s,n=this.options,o=this._isRootNode(this.scrollParent[0]),d=e.pageX,u=e.pageY;return o&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),t&&(this.containment&&(this.relative_container?(i=this.relative_container.offset(),a=[this.containment[0]+i.left,this.containment[1]+i.top,this.containment[2]+i.left,this.containment[3]+i.top]):a=this.containment,e.pageX-this.offset.click.left<a[0]&&(d=a[0]+this.offset.click.left),e.pageY-this.offset.click.top<a[1]&&(u=a[1]+this.offset.click.top),e.pageX-this.offset.click.left>a[2]&&(d=a[2]+this.offset.click.left),e.pageY-this.offset.click.top>a[3]&&(u=a[3]+this.offset.click.top)),n.grid&&(r=n.grid[1]?this.originalPageY+Math.round((u-this.originalPageY)/n.grid[1])*n.grid[1]:this.originalPageY,u=a?r-this.offset.click.top>=a[1]||r-this.offset.click.top>a[3]?r:r-this.offset.click.top>=a[1]?r-n.grid[1]:r+n.grid[1]:r,s=n.grid[0]?this.originalPageX+Math.round((d-this.originalPageX)/n.grid[0])*n.grid[0]:this.originalPageX,d=a?s-this.offset.click.left>=a[0]||s-this.offset.click.left>a[2]?s:s-this.offset.click.left>=a[0]?s-n.grid[0]:s+n.grid[0]:s),"y"===n.axis&&(d=this.originalPageX),"x"===n.axis&&(u=this.originalPageY)),{top:u-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:o?0:this.offset.scroll.top),left:d-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:o?0:this.offset.scroll.left)}
+},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_trigger:function(t,a,i){return i=i||this._uiHash(),e.ui.plugin.call(this,t,[a,i,this],!0),"drag"===t&&(this.positionAbs=this._convertPositionTo("absolute")),e.Widget.prototype._trigger.call(this,t,a,i)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),e.ui.plugin.add("draggable","connectToSortable",{start:function(t,a,i){var r=i.options,s=e.extend({},a,{item:i.element});i.sortables=[],e(r.connectToSortable).each(function(){var a=e(this).sortable("instance");a&&!a.options.disabled&&(i.sortables.push({instance:a,shouldRevert:a.options.revert}),a.refreshPositions(),a._trigger("activate",t,s))})},stop:function(t,a,i){var r=e.extend({},a,{item:i.element});e.each(i.sortables,function(){this.instance.isOver?(this.instance.isOver=0,i.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=this.shouldRevert),this.instance._mouseStop(t),this.instance.options.helper=this.instance.options._helper,"original"===i.options.helper&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",t,r))})},drag:function(t,a,i){var r=this;e.each(i.sortables,function(){var s=!1,n=this;this.instance.positionAbs=i.positionAbs,this.instance.helperProportions=i.helperProportions,this.instance.offset.click=i.offset.click,this.instance._intersectsWith(this.instance.containerCache)&&(s=!0,e.each(i.sortables,function(){return this.instance.positionAbs=i.positionAbs,this.instance.helperProportions=i.helperProportions,this.instance.offset.click=i.offset.click,this!==n&&this.instance._intersectsWith(this.instance.containerCache)&&e.contains(n.instance.element[0],this.instance.element[0])&&(s=!1),s})),s?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=e(r).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return a.helper[0]},t.target=this.instance.currentItem[0],this.instance._mouseCapture(t,!0),this.instance._mouseStart(t,!0,!0),this.instance.offset.click.top=i.offset.click.top,this.instance.offset.click.left=i.offset.click.left,this.instance.offset.parent.left-=i.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=i.offset.parent.top-this.instance.offset.parent.top,i._trigger("toSortable",t),i.dropped=this.instance.element,i.currentItem=i.element,this.instance.fromOutside=i),this.instance.currentItem&&this.instance._mouseDrag(t)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",t,this.instance._uiHash(this.instance)),this.instance._mouseStop(t,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),i._trigger("fromSortable",t),i.dropped=!1)})}}),e.ui.plugin.add("draggable","cursor",{start:function(t,a,i){var r=e("body"),s=i.options;r.css("cursor")&&(s._cursor=r.css("cursor")),r.css("cursor",s.cursor)},stop:function(t,a,i){var r=i.options;r._cursor&&e("body").css("cursor",r._cursor)}}),e.ui.plugin.add("draggable","opacity",{start:function(t,a,i){var r=e(a.helper),s=i.options;r.css("opacity")&&(s._opacity=r.css("opacity")),r.css("opacity",s.opacity)},stop:function(t,a,i){var r=i.options;r._opacity&&e(a.helper).css("opacity",r._opacity)}}),e.ui.plugin.add("draggable","scroll",{start:function(e,t,a){a.scrollParent[0]!==a.document[0]&&"HTML"!==a.scrollParent[0].tagName&&(a.overflowOffset=a.scrollParent.offset())},drag:function(t,a,i){var r=i.options,s=!1,n=i.document[0];i.scrollParent[0]!==n&&"HTML"!==i.scrollParent[0].tagName?(r.axis&&"x"===r.axis||(i.overflowOffset.top+i.scrollParent[0].offsetHeight-t.pageY<r.scrollSensitivity?i.scrollParent[0].scrollTop=s=i.scrollParent[0].scrollTop+r.scrollSpeed:t.pageY-i.overflowOffset.top<r.scrollSensitivity&&(i.scrollParent[0].scrollTop=s=i.scrollParent[0].scrollTop-r.scrollSpeed)),r.axis&&"y"===r.axis||(i.overflowOffset.left+i.scrollParent[0].offsetWidth-t.pageX<r.scrollSensitivity?i.scrollParent[0].scrollLeft=s=i.scrollParent[0].scrollLeft+r.scrollSpeed:t.pageX-i.overflowOffset.left<r.scrollSensitivity&&(i.scrollParent[0].scrollLeft=s=i.scrollParent[0].scrollLeft-r.scrollSpeed))):(r.axis&&"x"===r.axis||(t.pageY-e(n).scrollTop()<r.scrollSensitivity?s=e(n).scrollTop(e(n).scrollTop()-r.scrollSpeed):e(window).height()-(t.pageY-e(n).scrollTop())<r.scrollSensitivity&&(s=e(n).scrollTop(e(n).scrollTop()+r.scrollSpeed))),r.axis&&"y"===r.axis||(t.pageX-e(n).scrollLeft()<r.scrollSensitivity?s=e(n).scrollLeft(e(n).scrollLeft()-r.scrollSpeed):e(window).width()-(t.pageX-e(n).scrollLeft())<r.scrollSensitivity&&(s=e(n).scrollLeft(e(n).scrollLeft()+r.scrollSpeed)))),s!==!1&&e.ui.ddmanager&&!r.dropBehaviour&&e.ui.ddmanager.prepareOffsets(i,t)}}),e.ui.plugin.add("draggable","snap",{start:function(t,a,i){var r=i.options;i.snapElements=[],e(r.snap.constructor!==String?r.snap.items||":data(ui-draggable)":r.snap).each(function(){var t=e(this),a=t.offset();this!==i.element[0]&&i.snapElements.push({item:this,width:t.outerWidth(),height:t.outerHeight(),top:a.top,left:a.left})})},drag:function(t,a,i){var r,s,n,o,d,u,h,l,m,c,p=i.options,f=p.snapTolerance,g=a.offset.left,y=g+i.helperProportions.width,x=a.offset.top,v=x+i.helperProportions.height;for(m=i.snapElements.length-1;m>=0;m--)d=i.snapElements[m].left,u=d+i.snapElements[m].width,h=i.snapElements[m].top,l=h+i.snapElements[m].height,d-f>y||g>u+f||h-f>v||x>l+f||!e.contains(i.snapElements[m].item.ownerDocument,i.snapElements[m].item)?(i.snapElements[m].snapping&&i.options.snap.release&&i.options.snap.release.call(i.element,t,e.extend(i._uiHash(),{snapItem:i.snapElements[m].item})),i.snapElements[m].snapping=!1):("inner"!==p.snapMode&&(r=f>=Math.abs(h-v),s=f>=Math.abs(l-x),n=f>=Math.abs(d-y),o=f>=Math.abs(u-g),r&&(a.position.top=i._convertPositionTo("relative",{top:h-i.helperProportions.height,left:0}).top-i.margins.top),s&&(a.position.top=i._convertPositionTo("relative",{top:l,left:0}).top-i.margins.top),n&&(a.position.left=i._convertPositionTo("relative",{top:0,left:d-i.helperProportions.width}).left-i.margins.left),o&&(a.position.left=i._convertPositionTo("relative",{top:0,left:u}).left-i.margins.left)),c=r||s||n||o,"outer"!==p.snapMode&&(r=f>=Math.abs(h-x),s=f>=Math.abs(l-v),n=f>=Math.abs(d-g),o=f>=Math.abs(u-y),r&&(a.position.top=i._convertPositionTo("relative",{top:h,left:0}).top-i.margins.top),s&&(a.position.top=i._convertPositionTo("relative",{top:l-i.helperProportions.height,left:0}).top-i.margins.top),n&&(a.position.left=i._convertPositionTo("relative",{top:0,left:d}).left-i.margins.left),o&&(a.position.left=i._convertPositionTo("relative",{top:0,left:u-i.helperProportions.width}).left-i.margins.left)),!i.snapElements[m].snapping&&(r||s||n||o||c)&&i.options.snap.snap&&i.options.snap.snap.call(i.element,t,e.extend(i._uiHash(),{snapItem:i.snapElements[m].item})),i.snapElements[m].snapping=r||s||n||o||c)}}),e.ui.plugin.add("draggable","stack",{start:function(t,a,i){var r,s=i.options,n=e.makeArray(e(s.stack)).sort(function(t,a){return(parseInt(e(t).css("zIndex"),10)||0)-(parseInt(e(a).css("zIndex"),10)||0)});n.length&&(r=parseInt(e(n[0]).css("zIndex"),10)||0,e(n).each(function(t){e(this).css("zIndex",r+t)}),this.css("zIndex",r+n.length))}}),e.ui.plugin.add("draggable","zIndex",{start:function(t,a,i){var r=e(a.helper),s=i.options;r.css("zIndex")&&(s._zIndex=r.css("zIndex")),r.css("zIndex",s.zIndex)},stop:function(t,a,i){var r=i.options;r._zIndex&&e(a.helper).css("zIndex",r._zIndex)}}),e.ui.draggable,e.widget("ui.droppable",{version:"1.11.0-beta.2",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var t,a=this.options,i=a.accept;this.isover=!1,this.isout=!0,this.accept=e.isFunction(i)?i:function(e){return e.is(i)},this.proportions=function(){return arguments.length?(t=arguments[0],void 0):t?t:t={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},this._addToManager(a.scope),a.addClasses&&this.element.addClass("ui-droppable")},_addToManager:function(t){e.ui.ddmanager.droppables[t]=e.ui.ddmanager.droppables[t]||[],e.ui.ddmanager.droppables[t].push(this)},_splice:function(e){for(var t=0;e.length>t;t++)e[t]===this&&e.splice(t,1)},_destroy:function(){var t=e.ui.ddmanager.droppables[this.options.scope];this._splice(t),this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(t,a){if("accept"===t)this.accept=e.isFunction(a)?a:function(e){return e.is(a)};else if("scope"===t){var i=e.ui.ddmanager.droppables[this.options.scope];this._splice(i),this._addToManager(a)}this._super(t,a)},_activate:function(t){var a=e.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),a&&this._trigger("activate",t,this.ui(a))},_deactivate:function(t){var a=e.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),a&&this._trigger("deactivate",t,this.ui(a))},_over:function(t){var a=e.ui.ddmanager.current;a&&(a.currentItem||a.element)[0]!==this.element[0]&&this.accept.call(this.element[0],a.currentItem||a.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",t,this.ui(a)))},_out:function(t){var a=e.ui.ddmanager.current;a&&(a.currentItem||a.element)[0]!==this.element[0]&&this.accept.call(this.element[0],a.currentItem||a.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",t,this.ui(a)))},_drop:function(t,a){var i=a||e.ui.ddmanager.current,r=!1;return i&&(i.currentItem||i.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var t=e(this).droppable("instance");return t.options.greedy&&!t.options.disabled&&t.options.scope===i.options.scope&&t.accept.call(t.element[0],i.currentItem||i.element)&&e.ui.intersect(i,e.extend(t,{offset:t.element.offset()}),t.options.tolerance)?(r=!0,!1):void 0}),r?!1:this.accept.call(this.element[0],i.currentItem||i.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",t,this.ui(i)),this.element):!1):!1},ui:function(e){return{draggable:e.currentItem||e.element,helper:e.helper,position:e.position,offset:e.positionAbs}}}),e.ui.intersect=function(){function e(e,t,a){return e>=t&&t+a>e}return function(t,a,i){if(!a.offset)return!1;var r,s,n=(t.positionAbs||t.position.absolute).left,o=(t.positionAbs||t.position.absolute).top,d=n+t.helperProportions.width,u=o+t.helperProportions.height,h=a.offset.left,l=a.offset.top,m=h+a.proportions().width,c=l+a.proportions().height;switch(i){case"fit":return n>=h&&m>=d&&o>=l&&c>=u;case"intersect":return n+t.helperProportions.width/2>h&&m>d-t.helperProportions.width/2&&o+t.helperProportions.height/2>l&&c>u-t.helperProportions.height/2;case"pointer":return r=(t.positionAbs||t.position.absolute).left+(t.clickOffset||t.offset.click).left,s=(t.positionAbs||t.position.absolute).top+(t.clickOffset||t.offset.click).top,e(s,l,a.proportions().height)&&e(r,h,a.proportions().width);case"touch":return(o>=l&&c>=o||u>=l&&c>=u||l>o&&u>c)&&(n>=h&&m>=n||d>=h&&m>=d||h>n&&d>m);default:return!1}}}(),e.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(t,a){var i,r,s=e.ui.ddmanager.droppables[t.options.scope]||[],n=a?a.type:null,o=(t.currentItem||t.element).find(":data(ui-droppable)").addBack();e:for(i=0;s.length>i;i++)if(!(s[i].options.disabled||t&&!s[i].accept.call(s[i].element[0],t.currentItem||t.element))){for(r=0;o.length>r;r++)if(o[r]===s[i].element[0]){s[i].proportions().height=0;continue e}s[i].visible="none"!==s[i].element.css("display"),s[i].visible&&("mousedown"===n&&s[i]._activate.call(s[i],a),s[i].offset=s[i].element.offset(),s[i].proportions({width:s[i].element[0].offsetWidth,height:s[i].element[0].offsetHeight}))}},drop:function(t,a){var i=!1;return e.each((e.ui.ddmanager.droppables[t.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&e.ui.intersect(t,this,this.options.tolerance)&&(i=this._drop.call(this,a)||i),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],t.currentItem||t.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,a)))}),i},dragStart:function(t,a){t.element.parentsUntil("body").bind("scroll.droppable",function(){t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,a)})},drag:function(t,a){t.options.refreshPositions&&e.ui.ddmanager.prepareOffsets(t,a),e.each(e.ui.ddmanager.droppables[t.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var i,r,s,n=e.ui.intersect(t,this,this.options.tolerance),o=!n&&this.isover?"isout":n&&!this.isover?"isover":null;o&&(this.options.greedy&&(r=this.options.scope,s=this.element.parents(":data(ui-droppable)").filter(function(){return e(this).droppable("instance").options.scope===r}),s.length&&(i=e(s[0]).droppable("instance"),i.greedyChild="isover"===o)),i&&"isover"===o&&(i.isover=!1,i.isout=!0,i._out.call(i,a)),this[o]=!0,this["isout"===o?"isover":"isout"]=!1,this["isover"===o?"_over":"_out"].call(this,a),i&&"isout"===o&&(i.isout=!1,i.isover=!0,i._over.call(i,a)))}})},dragStop:function(t,a){t.element.parentsUntil("body").unbind("scroll.droppable"),t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,a)}},e.ui.droppable,e.widget("ui.resizable",e.ui.mouse,{version:"1.11.0-beta.2",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(e){return parseInt(e,10)||0},_isNumber:function(e){return!isNaN(parseInt(e,10))},_hasScroll:function(t,a){if("hidden"===e(t).css("overflow"))return!1;var i=a&&"left"===a?"scrollLeft":"scrollTop",r=!1;return t[i]>0?!0:(t[i]=1,r=t[i]>0,t[i]=0,r)},_create:function(){var t,a,i,r,s,n=this,o=this.options;if(this.element.addClass("ui-resizable"),e.extend(this,{_aspectRatio:!!o.aspectRatio,aspectRatio:o.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:o.helper||o.ghost||o.animate?o.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(e("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=o.handles||(e(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),t=this.handles.split(","),this.handles={},a=0;t.length>a;a++)i=e.trim(t[a]),s="ui-resizable-"+i,r=e("<div class='ui-resizable-handle "+s+"'></div>"),r.css({zIndex:o.zIndex}),"se"===i&&r.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[i]=".ui-resizable-"+i,this.element.append(r);this._renderAxis=function(t){var a,i,r,s;t=t||this.element;for(a in this.handles)this.handles[a].constructor===String&&(this.handles[a]=this.element.children(this.handles[a]).first().show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(i=e(this.handles[a],this.element),s=/sw|ne|nw|se|n|s/.test(a)?i.outerHeight():i.outerWidth(),r=["padding",/ne|nw|n/.test(a)?"Top":/se|sw|s/.test(a)?"Bottom":/^e$/.test(a)?"Right":"Left"].join(""),t.css(r,s),this._proportionallyResize()),e(this.handles[a]).length},this._renderAxis(this.element),this._handles=e(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){n.resizing||(this.className&&(r=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),n.axis=r&&r[1]?r[1]:"se")}),o.autoHide&&(this._handles.hide(),e(this.element).addClass("ui-resizable-autohide").mouseenter(function(){o.disabled||(e(this).removeClass("ui-resizable-autohide"),n._handles.show())}).mouseleave(function(){o.disabled||n.resizing||(e(this).addClass("ui-resizable-autohide"),n._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t,a=function(t){e(t).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(a(this.element),t=this.element,this.originalElement.css({position:t.css("position"),width:t.outerWidth(),height:t.outerHeight(),top:t.css("top"),left:t.css("left")}).insertAfter(t),t.remove()),this.originalElement.css("resize",this.originalResizeStyle),a(this.originalElement),this},_mouseCapture:function(t){var a,i,r=!1;for(a in this.handles)i=e(this.handles[a])[0],(i===t.target||e.contains(i,t.target))&&(r=!0);return!this.options.disabled&&r},_mouseStart:function(t){var a,i,r,s=this.options,n=this.element;return this.resizing=!0,this._renderProxy(),a=this._num(this.helper.css("left")),i=this._num(this.helper.css("top")),s.containment&&(a+=e(s.containment).scrollLeft()||0,i+=e(s.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:a,top:i},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:n.width(),height:n.height()},this.originalSize=this._helper?{width:n.outerWidth(),height:n.outerHeight()}:{width:n.width(),height:n.height()},this.originalPosition={left:a,top:i},this.sizeDiff={width:n.outerWidth()-n.width(),height:n.outerHeight()-n.height()},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio="number"==typeof s.aspectRatio?s.aspectRatio:this.originalSize.width/this.originalSize.height||1,r=e(".ui-resizable-"+this.axis).css("cursor"),e("body").css("cursor","auto"===r?this.axis+"-resize":r),n.addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(t){var a,i=this.helper,r={},s=this.originalMousePosition,n=this.axis,o=t.pageX-s.left||0,d=t.pageY-s.top||0,u=this._change[n];return this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height},u?(a=u.apply(this,[t,o,d]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(a=this._updateRatio(a,t)),a=this._respectSize(a,t),this._updateCache(a),this._propagate("resize",t),this.position.top!==this.prevPosition.top&&(r.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(r.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(r.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(r.height=this.size.height+"px"),i.css(r),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),e.isEmptyObject(r)||this._trigger("resize",t,this.ui()),!1):!1},_mouseStop:function(t){this.resizing=!1;var a,i,r,s,n,o,d,u=this.options,h=this;return this._helper&&(a=this._proportionallyResizeElements,i=a.length&&/textarea/i.test(a[0].nodeName),r=i&&this._hasScroll(a[0],"left")?0:h.sizeDiff.height,s=i?0:h.sizeDiff.width,n={width:h.helper.width()-s,height:h.helper.height()-r},o=parseInt(h.element.css("left"),10)+(h.position.left-h.originalPosition.left)||null,d=parseInt(h.element.css("top"),10)+(h.position.top-h.originalPosition.top)||null,u.animate||this.element.css(e.extend(n,{top:d,left:o})),h.helper.height(h.size.height),h.helper.width(h.size.width),this._helper&&!u.animate&&this._proportionallyResize()),e("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(e){var t,a,i,r,s,n=this.options;s={minWidth:this._isNumber(n.minWidth)?n.minWidth:0,maxWidth:this._isNumber(n.maxWidth)?n.maxWidth:1/0,minHeight:this._isNumber(n.minHeight)?n.minHeight:0,maxHeight:this._isNumber(n.maxHeight)?n.maxHeight:1/0},(this._aspectRatio||e)&&(t=s.minHeight*this.aspectRatio,i=s.minWidth/this.aspectRatio,a=s.maxHeight*this.aspectRatio,r=s.maxWidth/this.aspectRatio,t>s.minWidth&&(s.minWidth=t),i>s.minHeight&&(s.minHeight=i),s.maxWidth>a&&(s.maxWidth=a),s.maxHeight>r&&(s.maxHeight=r)),this._vBoundaries=s},_updateCache:function(e){this.offset=this.helper.offset(),this._isNumber(e.left)&&(this.position.left=e.left),this._isNumber(e.top)&&(this.position.top=e.top),this._isNumber(e.height)&&(this.size.height=e.height),this._isNumber(e.width)&&(this.size.width=e.width)},_updateRatio:function(e){var t=this.position,a=this.size,i=this.axis;return this._isNumber(e.height)?e.width=e.height*this.aspectRatio:this._isNumber(e.width)&&(e.height=e.width/this.aspectRatio),"sw"===i&&(e.left=t.left+(a.width-e.width),e.top=null),"nw"===i&&(e.top=t.top+(a.height-e.height),e.left=t.left+(a.width-e.width)),e},_respectSize:function(e){var t=this._vBoundaries,a=this.axis,i=this._isNumber(e.width)&&t.maxWidth&&t.maxWidth<e.width,r=this._isNumber(e.height)&&t.maxHeight&&t.maxHeight<e.height,s=this._isNumber(e.width)&&t.minWidth&&t.minWidth>e.width,n=this._isNumber(e.height)&&t.minHeight&&t.minHeight>e.height,o=this.originalPosition.left+this.originalSize.width,d=this.position.top+this.size.height,u=/sw|nw|w/.test(a),h=/nw|ne|n/.test(a);return s&&(e.width=t.minWidth),n&&(e.height=t.minHeight),i&&(e.width=t.maxWidth),r&&(e.height=t.maxHeight),s&&u&&(e.left=o-t.minWidth),i&&u&&(e.left=o-t.maxWidth),n&&h&&(e.top=d-t.minHeight),r&&h&&(e.top=d-t.maxHeight),e.width||e.height||e.left||!e.top?e.width||e.height||e.top||!e.left||(e.left=null):e.top=null,e},_proportionallyResize:function(){if(this._proportionallyResizeElements.length){var e,t,a,i,r,s=this.helper||this.element;for(e=0;this._proportionallyResizeElements.length>e;e++){if(r=this._proportionallyResizeElements[e],!this.borderDif)for(this.borderDif=[],a=[r.css("borderTopWidth"),r.css("borderRightWidth"),r.css("borderBottomWidth"),r.css("borderLeftWidth")],i=[r.css("paddingTop"),r.css("paddingRight"),r.css("paddingBottom"),r.css("paddingLeft")],t=0;a.length>t;t++)this.borderDif[t]=(parseInt(a[t],10)||0)+(parseInt(i[t],10)||0);r.css({height:s.height()-this.borderDif[0]-this.borderDif[2]||0,width:s.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var t=this.element,a=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||e("<div style='overflow:hidden;'></div>"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++a.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(e,t){return{width:this.originalSize.width+t}},w:function(e,t){var a=this.originalSize,i=this.originalPosition;return{left:i.left+t,width:a.width-t}},n:function(e,t,a){var i=this.originalSize,r=this.originalPosition;return{top:r.top+a,height:i.height-a}},s:function(e,t,a){return{height:this.originalSize.height+a}},se:function(t,a,i){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,a,i]))},sw:function(t,a,i){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,a,i]))},ne:function(t,a,i){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,a,i]))},nw:function(t,a,i){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,a,i]))}},_propagate:function(t,a){e.ui.plugin.call(this,t,[a,this.ui()]),"resize"!==t&&this._trigger(t,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition,prevSize:this.prevSize,prevPosition:this.prevPosition}}}),e.ui.plugin.add("resizable","animate",{stop:function(t){var a=e(this).resizable("instance"),i=a.options,r=a._proportionallyResizeElements,s=r.length&&/textarea/i.test(r[0].nodeName),n=s&&a._hasScroll(r[0],"left")?0:a.sizeDiff.height,o=s?0:a.sizeDiff.width,d={width:a.size.width-o,height:a.size.height-n},u=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null,h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(d,h&&u?{top:h,left:u}:{}),{duration:i.animateDuration,easing:i.animateEasing,step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};r&&r.length&&e(r[0]).css({width:i.width,height:i.height}),a._updateCache(i),a._propagate("resize",t)}})}}),e.ui.plugin.add("resizable","containment",{start:function(){var t,a,i,r,s,n,o,d=e(this).resizable("instance"),u=d.options,h=d.element,l=u.containment,m=l instanceof e?l.get(0):/parent/.test(l)?h.parent().get(0):l;m&&(d.containerElement=e(m),/document/.test(l)||l===document?(d.containerOffset={left:0,top:0},d.containerPosition={left:0,top:0},d.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}):(t=e(m),a=[],e(["Top","Right","Left","Bottom"]).each(function(e,i){a[e]=d._num(t.css("padding"+i))}),d.containerOffset=t.offset(),d.containerPosition=t.position(),d.containerSize={height:t.innerHeight()-a[3],width:t.innerWidth()-a[1]},i=d.containerOffset,r=d.containerSize.height,s=d.containerSize.width,n=d._hasScroll(m,"left")?m.scrollWidth:s,o=d._hasScroll(m)?m.scrollHeight:r,d.parentData={element:m,left:i.left,top:i.top,width:n,height:o}))},resize:function(t,a){var i,r,s,n,o=e(this).resizable("instance"),d=o.options,u=o.containerOffset,h=o.position,l=o._aspectRatio||t.shiftKey,m={top:0,left:0},c=o.containerElement,p=!0;c[0]!==document&&/static/.test(c.css("position"))&&(m=u),h.left<(o._helper?u.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-u.left:o.position.left-m.left),l&&(o.size.height=o.size.width/o.aspectRatio,p=!1),o.position.left=d.helper?u.left:0),h.top<(o._helper?u.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-u.top:o.position.top),l&&(o.size.width=o.size.height*o.aspectRatio,p=!1),o.position.top=o._helper?u.top:0),o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top,i=Math.abs((o._helper?o.offset.left-m.left:o.offset.left-u.left)+o.sizeDiff.width),r=Math.abs((o._helper?o.offset.top-m.top:o.offset.top-u.top)+o.sizeDiff.height),s=o.containerElement.get(0)===o.element.parent().get(0),n=/relative|absolute/.test(o.containerElement.css("position")),s&&n&&(i-=Math.abs(o.parentData.left)),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,l&&(o.size.height=o.size.width/o.aspectRatio,p=!1)),r+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-r,l&&(o.size.width=o.size.height*o.aspectRatio,p=!1)),p||(o.position.left=a.prevPosition.left,o.position.top=a.prevPosition.top,o.size.width=a.prevSize.width,o.size.height=a.prevSize.height)},stop:function(){var t=e(this).resizable("instance"),a=t.options,i=t.containerOffset,r=t.containerPosition,s=t.containerElement,n=e(t.helper),o=n.offset(),d=n.outerWidth()-t.sizeDiff.width,u=n.outerHeight()-t.sizeDiff.height;t._helper&&!a.animate&&/relative/.test(s.css("position"))&&e(this).css({left:o.left-r.left-i.left,width:d,height:u}),t._helper&&!a.animate&&/static/.test(s.css("position"))&&e(this).css({left:o.left-r.left-i.left,width:d,height:u})}}),e.ui.plugin.add("resizable","alsoResize",{start:function(){var t=e(this).resizable("instance"),a=t.options,i=function(t){e(t).each(function(){var t=e(this);t.data("ui-resizable-alsoresize",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)})})};"object"!=typeof a.alsoResize||a.alsoResize.parentNode?i(a.alsoResize):a.alsoResize.length?(a.alsoResize=a.alsoResize[0],i(a.alsoResize)):e.each(a.alsoResize,function(e){i(e)})},resize:function(t,a){var i=e(this).resizable("instance"),r=i.options,s=i.originalSize,n=i.originalPosition,o={height:i.size.height-s.height||0,width:i.size.width-s.width||0,top:i.position.top-n.top||0,left:i.position.left-n.left||0},d=function(t,i){e(t).each(function(){var t=e(this),r=e(this).data("ui-resizable-alsoresize"),s={},n=i&&i.length?i:t.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(n,function(e,t){var a=(r[t]||0)+(o[t]||0);a&&a>=0&&(s[t]=a||null)}),t.css(s)})};"object"!=typeof r.alsoResize||r.alsoResize.nodeType?d(r.alsoResize):e.each(r.alsoResize,function(e,t){d(e,t)})},stop:function(){e(this).removeData("resizable-alsoresize")}}),e.ui.plugin.add("resizable","ghost",{start:function(){var t=e(this).resizable("instance"),a=t.options,i=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:i.height,width:i.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof a.ghost?a.ghost:""),t.ghost.appendTo(t.helper)},resize:function(){var t=e(this).resizable("instance");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=e(this).resizable("instance");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),e.ui.plugin.add("resizable","grid",{resize:function(){var t=e(this).resizable("instance"),a=t.options,i=t.size,r=t.originalSize,s=t.originalPosition,n=t.axis,o="number"==typeof a.grid?[a.grid,a.grid]:a.grid,d=o[0]||1,u=o[1]||1,h=Math.round((i.width-r.width)/d)*d,l=Math.round((i.height-r.height)/u)*u,m=r.width+h,c=r.height+l,p=a.maxWidth&&m>a.maxWidth,f=a.maxHeight&&c>a.maxHeight,g=a.minWidth&&a.minWidth>m,y=a.minHeight&&a.minHeight>c;a.grid=o,g&&(m+=d),y&&(c+=u),p&&(m-=d),f&&(c-=u),/^(se|s|e)$/.test(n)?(t.size.width=m,t.size.height=c):/^(ne)$/.test(n)?(t.size.width=m,t.size.height=c,t.position.top=s.top-l):/^(sw)$/.test(n)?(t.size.width=m,t.size.height=c,t.position.left=s.left-h):(c-u>0?(t.size.height=c,t.position.top=s.top-l):(t.size.height=u,t.position.top=s.top+r.height-u),m-d>0?(t.size.width=m,t.position.left=s.left-h):(t.size.width=d,t.position.left=s.left+r.width-d))}}),e.ui.resizable,e.widget("ui.selectable",e.ui.mouse,{version:"1.11.0-beta.2",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var t,a=this;
+this.element.addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){t=e(a.options.filter,a.element[0]),t.addClass("ui-selectee"),t.each(function(){var t=e(this),a=t.offset();e.data(this,"selectable-item",{element:this,$element:t,left:a.left,top:a.top,right:a.left+t.outerWidth(),bottom:a.top+t.outerHeight(),startselected:!1,selected:t.hasClass("ui-selected"),selecting:t.hasClass("ui-selecting"),unselecting:t.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=t.addClass("ui-selectee"),this._mouseInit(),this.helper=e("<div class='ui-selectable-helper'></div>")},_destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled"),this._mouseDestroy()},_mouseStart:function(t){var a=this,i=this.options;this.opos=[t.pageX,t.pageY],this.options.disabled||(this.selectees=e(i.filter,this.element[0]),this._trigger("start",t),e(i.appendTo).append(this.helper),this.helper.css({left:t.pageX,top:t.pageY,width:0,height:0}),i.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var i=e.data(this,"selectable-item");i.startselected=!0,t.metaKey||t.ctrlKey||(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,a._trigger("unselecting",t,{unselecting:i.element}))}),e(t.target).parents().addBack().each(function(){var i,r=e.data(this,"selectable-item");return r?(i=!t.metaKey&&!t.ctrlKey||!r.$element.hasClass("ui-selected"),r.$element.removeClass(i?"ui-unselecting":"ui-selected").addClass(i?"ui-selecting":"ui-unselecting"),r.unselecting=!i,r.selecting=i,r.selected=i,i?a._trigger("selecting",t,{selecting:r.element}):a._trigger("unselecting",t,{unselecting:r.element}),!1):void 0}))},_mouseDrag:function(t){if(this.dragged=!0,!this.options.disabled){var a,i=this,r=this.options,s=this.opos[0],n=this.opos[1],o=t.pageX,d=t.pageY;return s>o&&(a=o,o=s,s=a),n>d&&(a=d,d=n,n=a),this.helper.css({left:s,top:n,width:o-s,height:d-n}),this.selectees.each(function(){var a=e.data(this,"selectable-item"),u=!1;a&&a.element!==i.element[0]&&("touch"===r.tolerance?u=!(a.left>o||s>a.right||a.top>d||n>a.bottom):"fit"===r.tolerance&&(u=a.left>s&&o>a.right&&a.top>n&&d>a.bottom),u?(a.selected&&(a.$element.removeClass("ui-selected"),a.selected=!1),a.unselecting&&(a.$element.removeClass("ui-unselecting"),a.unselecting=!1),a.selecting||(a.$element.addClass("ui-selecting"),a.selecting=!0,i._trigger("selecting",t,{selecting:a.element}))):(a.selecting&&((t.metaKey||t.ctrlKey)&&a.startselected?(a.$element.removeClass("ui-selecting"),a.selecting=!1,a.$element.addClass("ui-selected"),a.selected=!0):(a.$element.removeClass("ui-selecting"),a.selecting=!1,a.startselected&&(a.$element.addClass("ui-unselecting"),a.unselecting=!0),i._trigger("unselecting",t,{unselecting:a.element}))),a.selected&&(t.metaKey||t.ctrlKey||a.startselected||(a.$element.removeClass("ui-selected"),a.selected=!1,a.$element.addClass("ui-unselecting"),a.unselecting=!0,i._trigger("unselecting",t,{unselecting:a.element})))))}),!1}},_mouseStop:function(t){var a=this;return this.dragged=!1,e(".ui-unselecting",this.element[0]).each(function(){var i=e.data(this,"selectable-item");i.$element.removeClass("ui-unselecting"),i.unselecting=!1,i.startselected=!1,a._trigger("unselected",t,{unselected:i.element})}),e(".ui-selecting",this.element[0]).each(function(){var i=e.data(this,"selectable-item");i.$element.removeClass("ui-selecting").addClass("ui-selected"),i.selecting=!1,i.selected=!0,i.startselected=!0,a._trigger("selected",t,{selected:i.element})}),this._trigger("stop",t),this.helper.remove(),!1}}),e.widget("ui.sortable",e.ui.mouse,{version:"1.11.0-beta.2",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(e,t,a){return e>=t&&t+a>e},_isFloating:function(e){return/left|right/.test(e.css("float"))||/inline|table-cell/.test(e.css("display"))},_create:function(){var e=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?"x"===e.axis||this._isFloating(this.items[0].item):!1,this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(e,t){this._super(e,t),"handle"===e&&this._setHandleClassName()},_setHandleClassName:function(){this.element.find(".ui-sortable-handle").removeClass("ui-sortable-handle"),e.each(this.items,function(){(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item).addClass("ui-sortable-handle")})},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").find(".ui-sortable-handle").removeClass("ui-sortable-handle"),this._mouseDestroy();for(var e=this.items.length-1;e>=0;e--)this.items[e].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(t,a){var i=null,r=!1,s=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(t),e(t.target).parents().each(function(){return e.data(this,s.widgetName+"-item")===s?(i=e(this),!1):void 0}),e.data(t.target,s.widgetName+"-item")===s&&(i=e(t.target)),i?!this.options.handle||a||(e(this.options.handle,i).find("*").addBack().each(function(){this===t.target&&(r=!0)}),r)?(this.currentItem=i,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(t,a,i){var r,s,n=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(t),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,n.cursorAt&&this._adjustOffsetFromHelper(n.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),n.containment&&this._setContainment(),n.cursor&&"auto"!==n.cursor&&(s=this.document.find("body"),this.storedCursor=s.css("cursor"),s.css("cursor",n.cursor),this.storedStylesheet=e("<style>*{ cursor: "+n.cursor+" !important; }</style>").appendTo(s)),n.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",n.opacity)),n.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",n.zIndex)),this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",t,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!i)for(r=this.containers.length-1;r>=0;r--)this.containers[r]._trigger("activate",t,this._uiHash(this));return e.ui.ddmanager&&(e.ui.ddmanager.current=this),e.ui.ddmanager&&!n.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(t),!0},_mouseDrag:function(t){var a,i,r,s,n=this.options,o=!1;for(this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-t.pageY<n.scrollSensitivity?this.scrollParent[0].scrollTop=o=this.scrollParent[0].scrollTop+n.scrollSpeed:t.pageY-this.overflowOffset.top<n.scrollSensitivity&&(this.scrollParent[0].scrollTop=o=this.scrollParent[0].scrollTop-n.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-t.pageX<n.scrollSensitivity?this.scrollParent[0].scrollLeft=o=this.scrollParent[0].scrollLeft+n.scrollSpeed:t.pageX-this.overflowOffset.left<n.scrollSensitivity&&(this.scrollParent[0].scrollLeft=o=this.scrollParent[0].scrollLeft-n.scrollSpeed)):(t.pageY-e(document).scrollTop()<n.scrollSensitivity?o=e(document).scrollTop(e(document).scrollTop()-n.scrollSpeed):e(window).height()-(t.pageY-e(document).scrollTop())<n.scrollSensitivity&&(o=e(document).scrollTop(e(document).scrollTop()+n.scrollSpeed)),t.pageX-e(document).scrollLeft()<n.scrollSensitivity?o=e(document).scrollLeft(e(document).scrollLeft()-n.scrollSpeed):e(window).width()-(t.pageX-e(document).scrollLeft())<n.scrollSensitivity&&(o=e(document).scrollLeft(e(document).scrollLeft()+n.scrollSpeed))),o!==!1&&e.ui.ddmanager&&!n.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t)),this.positionAbs=this._convertPositionTo("absolute"),this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),a=this.items.length-1;a>=0;a--)if(i=this.items[a],r=i.item[0],s=this._intersectsWithPointer(i),s&&i.instance===this.currentContainer&&r!==this.currentItem[0]&&this.placeholder[1===s?"next":"prev"]()[0]!==r&&!e.contains(this.placeholder[0],r)&&("semi-dynamic"===this.options.type?!e.contains(this.element[0],r):!0)){if(this.direction=1===s?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(i))break;this._rearrange(t,i),this._trigger("change",t,this._uiHash());break}return this._contactContainers(t),e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),this._trigger("sort",t,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(t,a){if(t){if(e.ui.ddmanager&&!this.options.dropBehaviour&&e.ui.ddmanager.drop(this,t),this.options.revert){var i=this,r=this.placeholder.offset(),s=this.options.axis,n={};s&&"x"!==s||(n.left=r.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollLeft)),s&&"y"!==s||(n.top=r.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,e(this.helper).animate(n,parseInt(this.options.revert,10)||500,function(){i._clear(t)})}else this._clear(t,a);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var t=this.containers.length-1;t>=0;t--)this.containers[t]._trigger("deactivate",null,this._uiHash(this)),this.containers[t].containerCache.over&&(this.containers[t]._trigger("out",null,this._uiHash(this)),this.containers[t].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),e.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?e(this.domPosition.prev).after(this.currentItem):e(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(t){var a=this._getItemsAsjQuery(t&&t.connected),i=[];return t=t||{},e(a).each(function(){var a=(e(t.item||this).attr(t.attribute||"id")||"").match(t.expression||/(.+)[\-=_](.+)/);a&&i.push((t.key||a[1]+"[]")+"="+(t.key&&t.expression?a[1]:a[2]))}),!i.length&&t.key&&i.push(t.key+"="),i.join("&")},toArray:function(t){var a=this._getItemsAsjQuery(t&&t.connected),i=[];return t=t||{},a.each(function(){i.push(e(t.item||this).attr(t.attribute||"id")||"")}),i},_intersectsWith:function(e){var t=this.positionAbs.left,a=t+this.helperProportions.width,i=this.positionAbs.top,r=i+this.helperProportions.height,s=e.left,n=s+e.width,o=e.top,d=o+e.height,u=this.offset.click.top,h=this.offset.click.left,l="x"===this.options.axis||i+u>o&&d>i+u,m="y"===this.options.axis||t+h>s&&n>t+h,c=l&&m;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>e[this.floating?"width":"height"]?c:t+this.helperProportions.width/2>s&&n>a-this.helperProportions.width/2&&i+this.helperProportions.height/2>o&&d>r-this.helperProportions.height/2},_intersectsWithPointer:function(e){var t="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top,e.height),a="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left,e.width),i=t&&a,r=this._getDragVerticalDirection(),s=this._getDragHorizontalDirection();return i?this.floating?s&&"right"===s||"down"===r?2:1:r&&("down"===r?2:1):!1},_intersectsWithSides:function(e){var t=this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top+e.height/2,e.height),a=this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left+e.width/2,e.width),i=this._getDragVerticalDirection(),r=this._getDragHorizontalDirection();return this.floating&&r?"right"===r&&a||"left"===r&&!a:i&&("down"===i&&t||"up"===i&&!t)},_getDragVerticalDirection:function(){var e=this.positionAbs.top-this.lastPositionAbs.top;return 0!==e&&(e>0?"down":"up")},_getDragHorizontalDirection:function(){var e=this.positionAbs.left-this.lastPositionAbs.left;return 0!==e&&(e>0?"right":"left")},refresh:function(e){return this._refreshItems(e),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var e=this.options;return e.connectWith.constructor===String?[e.connectWith]:e.connectWith},_getItemsAsjQuery:function(t){function a(){o.push(this)}var i,r,s,n,o=[],d=[],u=this._connectWith();if(u&&t)for(i=u.length-1;i>=0;i--)for(s=e(u[i]),r=s.length-1;r>=0;r--)n=e.data(s[r],this.widgetFullName),n&&n!==this&&!n.options.disabled&&d.push([e.isFunction(n.options.items)?n.options.items.call(n.element):e(n.options.items,n.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),n]);for(d.push([e.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):e(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),i=d.length-1;i>=0;i--)d[i][0].each(a);return e(o)},_removeCurrentsFromItems:function(){var t=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=e.grep(this.items,function(e){for(var a=0;t.length>a;a++)if(t[a]===e.item[0])return!1;return!0})},_refreshItems:function(t){this.items=[],this.containers=[this];var a,i,r,s,n,o,d,u,h=this.items,l=[[e.isFunction(this.options.items)?this.options.items.call(this.element[0],t,{item:this.currentItem}):e(this.options.items,this.element),this]],m=this._connectWith();if(m&&this.ready)for(a=m.length-1;a>=0;a--)for(r=e(m[a]),i=r.length-1;i>=0;i--)s=e.data(r[i],this.widgetFullName),s&&s!==this&&!s.options.disabled&&(l.push([e.isFunction(s.options.items)?s.options.items.call(s.element[0],t,{item:this.currentItem}):e(s.options.items,s.element),s]),this.containers.push(s));for(a=l.length-1;a>=0;a--)for(n=l[a][1],o=l[a][0],i=0,u=o.length;u>i;i++)d=e(o[i]),d.data(this.widgetName+"-item",n),h.push({item:d,instance:n,width:0,height:0,left:0,top:0})},refreshPositions:function(t){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var a,i,r,s;for(a=this.items.length-1;a>=0;a--)i=this.items[a],i.instance!==this.currentContainer&&this.currentContainer&&i.item[0]!==this.currentItem[0]||(r=this.options.toleranceElement?e(this.options.toleranceElement,i.item):i.item,t||(i.width=r.outerWidth(),i.height=r.outerHeight()),s=r.offset(),i.left=s.left,i.top=s.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(a=this.containers.length-1;a>=0;a--)s=this.containers[a].element.offset(),this.containers[a].containerCache.left=s.left,this.containers[a].containerCache.top=s.top,this.containers[a].containerCache.width=this.containers[a].element.outerWidth(),this.containers[a].containerCache.height=this.containers[a].element.outerHeight();return this},_createPlaceholder:function(t){t=t||this;var a,i=t.options;i.placeholder&&i.placeholder.constructor!==String||(a=i.placeholder,i.placeholder={element:function(){var i=t.currentItem[0].nodeName.toLowerCase(),r=e("<"+i+">",t.document[0]).addClass(a||t.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");return"tr"===i?t.currentItem.children().each(function(){e("<td> </td>",t.document[0]).attr("colspan",e(this).attr("colspan")||1).appendTo(r)}):"img"===i&&r.attr("src",t.currentItem.attr("src")),a||r.css("visibility","hidden"),r},update:function(e,r){(!a||i.forcePlaceholderSize)&&(r.height()||r.height(t.currentItem.innerHeight()-parseInt(t.currentItem.css("paddingTop")||0,10)-parseInt(t.currentItem.css("paddingBottom")||0,10)),r.width()||r.width(t.currentItem.innerWidth()-parseInt(t.currentItem.css("paddingLeft")||0,10)-parseInt(t.currentItem.css("paddingRight")||0,10)))}}),t.placeholder=e(i.placeholder.element.call(t.element,t.currentItem)),t.currentItem.after(t.placeholder),i.placeholder.update(t,t.placeholder)},_contactContainers:function(t){var a,i,r,s,n,o,d,u,h,l,m=null,c=null;for(a=this.containers.length-1;a>=0;a--)if(!e.contains(this.currentItem[0],this.containers[a].element[0]))if(this._intersectsWith(this.containers[a].containerCache)){if(m&&e.contains(this.containers[a].element[0],m.element[0]))continue;m=this.containers[a],c=a}else this.containers[a].containerCache.over&&(this.containers[a]._trigger("out",t,this._uiHash(this)),this.containers[a].containerCache.over=0);if(m)if(1===this.containers.length)this.containers[c].containerCache.over||(this.containers[c]._trigger("over",t,this._uiHash(this)),this.containers[c].containerCache.over=1);else{for(r=1e4,s=null,h=m.floating||this._isFloating(this.currentItem),n=h?"left":"top",o=h?"width":"height",l=h?"clientX":"clientY",i=this.items.length-1;i>=0;i--)e.contains(this.containers[c].element[0],this.items[i].item[0])&&this.items[i].item[0]!==this.currentItem[0]&&(d=this.items[i].item.offset()[n],u=!1,t[l]-d>this.items[i][o]/2&&(u=!0),r>Math.abs(t[l]-d)&&(r=Math.abs(t[l]-d),s=this.items[i],this.direction=u?"up":"down"));if(!s&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[c])return;s?this._rearrange(t,s,null,!0):this._rearrange(t,null,this.containers[c].element,!0),this._trigger("change",t,this._uiHash()),this.containers[c]._trigger("change",t,this._uiHash(this)),this.currentContainer=this.containers[c],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[c]._trigger("over",t,this._uiHash(this)),this.containers[c].containerCache.over=1}},_createHelper:function(t){var a=this.options,i=e.isFunction(a.helper)?e(a.helper.apply(this.element[0],[t,this.currentItem])):"clone"===a.helper?this.currentItem.clone():this.currentItem;return i.parents("body").length||e("parent"!==a.appendTo?a.appendTo:this.currentItem[0].parentNode)[0].appendChild(i[0]),i[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!i[0].style.width||a.forceHelperSize)&&i.width(this.currentItem.width()),(!i[0].style.height||a.forceHelperSize)&&i.height(this.currentItem.height()),i},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var t=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&e.ui.ie)&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var e=this.currentItem.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,a,i,r=this.options;"parent"===r.containment&&(r.containment=this.helper[0].parentNode),("document"===r.containment||"window"===r.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,e("document"===r.containment?document:window).width()-this.helperProportions.width-this.margins.left,(e("document"===r.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(r.containment)||(t=e(r.containment)[0],a=e(r.containment).offset(),i="hidden"!==e(t).css("overflow"),this.containment=[a.left+(parseInt(e(t).css("borderLeftWidth"),10)||0)+(parseInt(e(t).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(e(t).css("borderTopWidth"),10)||0)+(parseInt(e(t).css("paddingTop"),10)||0)-this.margins.top,a.left+(i?Math.max(t.scrollWidth,t.offsetWidth):t.offsetWidth)-(parseInt(e(t).css("borderLeftWidth"),10)||0)-(parseInt(e(t).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(i?Math.max(t.scrollHeight,t.offsetHeight):t.offsetHeight)-(parseInt(e(t).css("borderTopWidth"),10)||0)-(parseInt(e(t).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(t,a){a||(a=this.position);var i="absolute"===t?1:-1,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,s=/(html|body)/i.test(r[0].tagName);return{top:a.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():s?0:r.scrollTop())*i,left:a.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():s?0:r.scrollLeft())*i}},_generatePosition:function(t){var a,i,r=this.options,s=t.pageX,n=t.pageY,o="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,d=/(html|body)/i.test(o[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(t.pageX-this.offset.click.left<this.containment[0]&&(s=this.containment[0]+this.offset.click.left),t.pageY-this.offset.click.top<this.containment[1]&&(n=this.containment[1]+this.offset.click.top),t.pageX-this.offset.click.left>this.containment[2]&&(s=this.containment[2]+this.offset.click.left),t.pageY-this.offset.click.top>this.containment[3]&&(n=this.containment[3]+this.offset.click.top)),r.grid&&(a=this.originalPageY+Math.round((n-this.originalPageY)/r.grid[1])*r.grid[1],n=this.containment?a-this.offset.click.top>=this.containment[1]&&a-this.offset.click.top<=this.containment[3]?a:a-this.offset.click.top>=this.containment[1]?a-r.grid[1]:a+r.grid[1]:a,i=this.originalPageX+Math.round((s-this.originalPageX)/r.grid[0])*r.grid[0],s=this.containment?i-this.offset.click.left>=this.containment[0]&&i-this.offset.click.left<=this.containment[2]?i:i-this.offset.click.left>=this.containment[0]?i-r.grid[0]:i+r.grid[0]:i)),{top:n-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():d?0:o.scrollTop()),left:s-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():d?0:o.scrollLeft())}},_rearrange:function(e,t,a,i){a?a[0].appendChild(this.placeholder[0]):t.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?t.item[0]:t.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var r=this.counter;this._delay(function(){r===this.counter&&this.refreshPositions(!i)})},_clear:function(e,t){function a(e,t,a){return function(i){a._trigger(e,i,t._uiHash(t))}}this.reverting=!1;var i,r=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(i in this._storedCSS)("auto"===this._storedCSS[i]||"static"===this._storedCSS[i])&&(this._storedCSS[i]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!t&&r.push(function(e){this._trigger("receive",e,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||t||r.push(function(e){this._trigger("update",e,this._uiHash())}),this!==this.currentContainer&&(t||(r.push(function(e){this._trigger("remove",e,this._uiHash())}),r.push(function(e){return function(t){e._trigger("receive",t,this._uiHash(this))}}.call(this,this.currentContainer)),r.push(function(e){return function(t){e._trigger("update",t,this._uiHash(this))}}.call(this,this.currentContainer)))),i=this.containers.length-1;i>=0;i--)t||r.push(a("deactivate",this,this.containers[i])),this.containers[i].containerCache.over&&(r.push(a("out",this,this.containers[i])),this.containers[i].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,this.cancelHelperRemoval){if(!t){for(this._trigger("beforeStop",e,this._uiHash()),i=0;r.length>i;i++)r[i].call(this,e);this._trigger("stop",e,this._uiHash())}return this.fromOutside=!1,!1}if(t||this._trigger("beforeStop",e,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null,!t){for(i=0;r.length>i;i++)r[i].call(this,e);this._trigger("stop",e,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){e.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(t){var a=t||this;return{helper:a.helper,placeholder:a.placeholder||e([]),position:a.position,originalPosition:a.originalPosition,offset:a.positionAbs,item:a.currentItem,sender:t?t.element:null}}}),e.widget("ui.accordion",{version:"1.11.0-beta.2",options:{active:0,animate:{},collapsible:!1,event:"click",header:"> li > :first-child,> :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var t=this.options;this.prevShow=this.prevHide=e(),this.element.addClass("ui-accordion ui-widget ui-helper-reset").attr("role","tablist"),t.collapsible||t.active!==!1&&null!=t.active||(t.active=0),this._processPanels(),0>t.active&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():e()}},_createIcons:function(){var t=this.options.icons;t&&(e("<span>").addClass("ui-accordion-header-icon ui-icon "+t.header).prependTo(this.headers),this.active.children(".ui-accordion-header-icon").removeClass(t.header).addClass(t.activeHeader),this.headers.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.removeClass("ui-accordion-icons").children(".ui-accordion-header-icon").remove()},_destroy:function(){var e;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.removeClass("ui-accordion-header ui-accordion-header-active ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("aria-controls").removeAttr("tabIndex").removeUniqueId(),this._destroyIcons(),e=this.headers.next().removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled").css("display","").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&e.css("height","")},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):("event"===e&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(t)),this._super(e,t),"collapsible"!==e||t||this.options.active!==!1||this._activate(0),"icons"===e&&(this._destroyIcons(),t&&this._createIcons()),"disabled"===e&&(this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this.headers.add(this.headers.next()).toggleClass("ui-state-disabled",!!t)),void 0)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var a=e.ui.keyCode,i=this.headers.length,r=this.headers.index(t.target),s=!1;switch(t.keyCode){case a.RIGHT:case a.DOWN:s=this.headers[(r+1)%i];break;case a.LEFT:case a.UP:s=this.headers[(r-1+i)%i];break;case a.SPACE:case a.ENTER:this._eventHandler(t);break;case a.HOME:s=this.headers[0];break;case a.END:s=this.headers[i-1]}s&&(e(t.target).attr("tabIndex",-1),e(s).attr("tabIndex",0),s.focus(),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===e.ui.keyCode.UP&&t.ctrlKey&&e(t.currentTarget).prev().focus()},refresh:function(){var t=this.options;this._processPanels(),t.active===!1&&t.collapsible===!0||!this.headers.length?(t.active=!1,this.active=e()):t.active===!1?this._activate(0):this.active.length&&!e.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=e()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){this.headers=this.element.find(this.options.header).addClass("ui-accordion-header ui-state-default ui-corner-all"),this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").filter(":not(.ui-accordion-content-active)").hide()},_refresh:function(){var t,a=this.options,i=a.heightStyle,r=this.element.parent();this.active=this._findActive(a.active).addClass("ui-accordion-header-active ui-state-active ui-corner-top").removeClass("ui-corner-all"),this.active.next().addClass("ui-accordion-content-active").show(),this.headers.attr("role","tab").each(function(){var t=e(this),a=t.uniqueId().attr("id"),i=t.next(),r=i.uniqueId().attr("id");
+t.attr("aria-controls",r),i.attr("aria-labelledby",a)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(a.event),"fill"===i?(t=r.height(),this.element.siblings(":visible").each(function(){var a=e(this),i=a.css("position");"absolute"!==i&&"fixed"!==i&&(t-=a.outerHeight(!0))}),this.headers.each(function(){t-=e(this).outerHeight(!0)}),this.headers.next().each(function(){e(this).height(Math.max(0,t-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===i&&(t=0,this.headers.next().each(function(){t=Math.max(t,e(this).css("height","").height())}).height(t))},_activate:function(t){var a=this._findActive(t)[0];a!==this.active[0]&&(a=a||this.active[0],this._eventHandler({target:a,currentTarget:a,preventDefault:e.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):e()},_setupEvents:function(t){var a={keydown:"_keydown"};t&&e.each(t.split(" "),function(e,t){a[t]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,a),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var a=this.options,i=this.active,r=e(t.currentTarget),s=r[0]===i[0],n=s&&a.collapsible,o=n?e():r.next(),d=i.next(),u={oldHeader:i,oldPanel:d,newHeader:n?e():r,newPanel:o};t.preventDefault(),s&&!a.collapsible||this._trigger("beforeActivate",t,u)===!1||(a.active=n?!1:this.headers.index(r),this.active=s?e():r,this._toggle(u),i.removeClass("ui-accordion-header-active ui-state-active"),a.icons&&i.children(".ui-accordion-header-icon").removeClass(a.icons.activeHeader).addClass(a.icons.header),s||(r.removeClass("ui-corner-all").addClass("ui-accordion-header-active ui-state-active ui-corner-top"),a.icons&&r.children(".ui-accordion-header-icon").removeClass(a.icons.header).addClass(a.icons.activeHeader),r.next().addClass("ui-accordion-content-active")))},_toggle:function(t){var a=t.newPanel,i=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=a,this.prevHide=i,this.options.animate?this._animate(a,i,t):(i.hide(),a.show(),this._toggleComplete(t)),i.attr({"aria-hidden":"true"}),i.prev().attr("aria-selected","false"),a.length&&i.length?i.prev().attr({tabIndex:-1,"aria-expanded":"false"}):a.length&&this.headers.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),a.attr("aria-hidden","false").prev().attr({"aria-selected":"true",tabIndex:0,"aria-expanded":"true"})},_animate:function(e,t,a){var i,r,s,n=this,o=0,d=e.length&&(!t.length||e.index()<t.index()),u=this.options.animate||{},h=d&&u.down||u,l=function(){n._toggleComplete(a)};return"number"==typeof h&&(s=h),"string"==typeof h&&(r=h),r=r||h.easing||u.easing,s=s||h.duration||u.duration,t.length?e.length?(i=e.show().outerHeight(),t.animate(this.hideProps,{duration:s,easing:r,step:function(e,t){t.now=Math.round(e)}}),e.hide().animate(this.showProps,{duration:s,easing:r,complete:l,step:function(e,a){a.now=Math.round(e),"height"!==a.prop?o+=a.now:"content"!==n.options.heightStyle&&(a.now=Math.round(i-t.outerHeight()-o),o=0)}}),void 0):t.animate(this.hideProps,s,r,l):e.animate(this.showProps,s,r,l)},_toggleComplete:function(e){var t=e.oldPanel;t.removeClass("ui-accordion-content-active").prev().removeClass("ui-corner-top").addClass("ui-corner-all"),t.length&&(t.parent()[0].className=t.parent()[0].className),this._trigger("activate",null,e)}}),e.widget("ui.menu",{version:"1.11.0-beta.2",defaultElement:"<ul>",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},items:"> *",menus:"ul",position:{my:"left-1 top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item":function(e){e.preventDefault()},"click .ui-menu-item":function(t){var a=e(t.target);!this.mouseHandled&&a.not(".ui-state-disabled").length&&(this.select(t),t.isPropagationStopped()||(this.mouseHandled=!0),a.has(".ui-menu").length?this.expand(t):!this.element.is(":focus")&&e(this.document[0].activeElement).closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(t){var a=e(t.currentTarget);a.siblings(".ui-state-active").removeClass("ui-state-active"),this.focus(t,a)},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(e,t){var a=this.active||this.element.find(this.options.items).eq(0);t||this.focus(e,a)},blur:function(t){this._delay(function(){e.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){this._closeOnDocumentClick(e)&&this.collapseAll(e),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-menu-icons ui-front").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").removeUniqueId().removeClass("ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var t=e(this);t.data("ui-menu-submenu-carat")&&t.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(t){function a(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var i,r,s,n,o,d=!0;switch(t.keyCode){case e.ui.keyCode.PAGE_UP:this.previousPage(t);break;case e.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case e.ui.keyCode.HOME:this._move("first","first",t);break;case e.ui.keyCode.END:this._move("last","last",t);break;case e.ui.keyCode.UP:this.previous(t);break;case e.ui.keyCode.DOWN:this.next(t);break;case e.ui.keyCode.LEFT:this.collapse(t);break;case e.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case e.ui.keyCode.ENTER:case e.ui.keyCode.SPACE:this._activate(t);break;case e.ui.keyCode.ESCAPE:this.collapse(t);break;default:d=!1,r=this.previousFilter||"",s=String.fromCharCode(t.keyCode),n=!1,clearTimeout(this.filterTimer),s===r?n=!0:s=r+s,o=RegExp("^"+a(s),"i"),i=this.activeMenu.find(this.options.items).filter(function(){return o.test(e(this).text())}),i=n&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(s=String.fromCharCode(t.keyCode),o=RegExp("^"+a(s),"i"),i=this.activeMenu.find(this.options.items).filter(function(){return o.test(e(this).text())})),i.length?(this.focus(t,i),i.length>1?(this.previousFilter=s,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter):delete this.previousFilter}d&&t.preventDefault()},_activate:function(e){this.active.is(".ui-state-disabled")||(this.active.is("[aria-haspopup='true']")?this.expand(e):this.select(e))},refresh:function(){var t,a,i=this,r=this.options.icons.submenu,s=this.element.find(this.options.menus);this.element.toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length),s.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-front").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=e(this),a=t.parent(),i=e("<span>").addClass("ui-menu-icon ui-icon "+r).data("ui-menu-submenu-carat",!0);a.attr("aria-haspopup","true").prepend(i),t.attr("aria-labelledby",a.attr("id"))}),t=s.add(this.element),a=t.find(this.options.items),a.not(".ui-menu-item").each(function(){var t=e(this);i._isDivider(t)&&t.addClass("ui-widget-content ui-menu-divider")}),a.not(".ui-menu-item, .ui-menu-divider").addClass("ui-menu-item").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),a.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!e.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(e,t){"icons"===e&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(t.submenu),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},focus:function(e,t){var a,i;this.blur(e,e&&"focus"===e.type),this._scrollIntoView(t),this.active=t.first(),i=this.active.addClass("ui-state-focus").removeClass("ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",i.attr("id")),this.active.parent().closest(".ui-menu-item").addClass("ui-state-active"),e&&"keydown"===e.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),a=t.children(".ui-menu"),a.length&&e&&/^mouse/.test(e.type)&&this._startOpening(a),this.activeMenu=t.parent(),this._trigger("focus",e,{item:t})},_scrollIntoView:function(t){var a,i,r,s,n,o;this._hasScroll()&&(a=parseFloat(e.css(this.activeMenu[0],"borderTopWidth"))||0,i=parseFloat(e.css(this.activeMenu[0],"paddingTop"))||0,r=t.offset().top-this.activeMenu.offset().top-a-i,s=this.activeMenu.scrollTop(),n=this.activeMenu.height(),o=t.outerHeight(),0>r?this.activeMenu.scrollTop(s+r):r+o>n&&this.activeMenu.scrollTop(s+r-n+o))},blur:function(e,t){t||clearTimeout(this.timer),this.active&&(this.active.removeClass("ui-state-focus"),this.active=null,this._trigger("blur",e,{item:this.active}))},_startOpening:function(e){clearTimeout(this.timer),"true"===e.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(e)},this.delay))},_open:function(t){var a=e.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(t.parents(".ui-menu")).hide().attr("aria-hidden","true"),t.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(a)},collapseAll:function(t,a){clearTimeout(this.timer),this.timer=this._delay(function(){var i=a?this.element:e(t&&t.target).closest(this.element.find(".ui-menu"));i.length||(i=this.element),this._close(i),this.blur(t),this.activeMenu=i},this.delay)},_close:function(e){e||(e=this.active?this.active.parent():this.element),e.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find(".ui-state-active").not(".ui-state-focus").removeClass("ui-state-active")},_closeOnDocumentClick:function(t){return!e(t.target).closest(".ui-menu").length},_isDivider:function(e){return!/[^\-\u2014\u2013\s]/.test(e.text())},collapse:function(e){var t=this.active&&this.active.parent().closest(".ui-menu-item",this.element);t&&t.length&&(this._close(),this.focus(e,t))},expand:function(e){var t=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();t&&t.length&&(this._open(t.parent()),this._delay(function(){this.focus(e,t)}))},next:function(e){this._move("next","first",e)},previous:function(e){this._move("prev","last",e)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(e,t,a){var i;this.active&&(i="first"===e||"last"===e?this.active["first"===e?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[e+"All"](".ui-menu-item").eq(0)),i&&i.length&&this.active||(i=this.activeMenu.find(this.options.items)[t]()),this.focus(a,i)},nextPage:function(t){var a,i,r;return this.active?(this.isLastItem()||(this._hasScroll()?(i=this.active.offset().top,r=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return a=e(this),0>a.offset().top-i-r}),this.focus(t,a)):this.focus(t,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(t),void 0)},previousPage:function(t){var a,i,r;return this.active?(this.isFirstItem()||(this._hasScroll()?(i=this.active.offset().top,r=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return a=e(this),a.offset().top-i+r>0}),this.focus(t,a)):this.focus(t,this.activeMenu.find(this.options.items).first())),void 0):(this.next(t),void 0)},_hasScroll:function(){return this.element.outerHeight()<this.element.prop("scrollHeight")},select:function(t){this.active=this.active||e(t.target).closest(".ui-menu-item");var a={item:this.active};this.active.has(".ui-menu").length||this.collapseAll(t,!0),this._trigger("select",t,a)}}),e.widget("ui.autocomplete",{version:"1.11.0-beta.2",defaultElement:"<input>",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var t,a,i,r=this.element[0].nodeName.toLowerCase(),s="textarea"===r,n="input"===r;this.isMultiLine=s?!0:n?!1:this.element.prop("isContentEditable"),this.valueMethod=this.element[s||n?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(r){if(this.element.prop("readOnly"))return t=!0,i=!0,a=!0,void 0;t=!1,i=!1,a=!1;var s=e.ui.keyCode;switch(r.keyCode){case s.PAGE_UP:t=!0,this._move("previousPage",r);break;case s.PAGE_DOWN:t=!0,this._move("nextPage",r);break;case s.UP:t=!0,this._keyEvent("previous",r);break;case s.DOWN:t=!0,this._keyEvent("next",r);break;case s.ENTER:this.menu.active&&(t=!0,r.preventDefault(),this.menu.select(r));break;case s.TAB:this.menu.active&&this.menu.select(r);break;case s.ESCAPE:this.menu.element.is(":visible")&&(this._value(this.term),this.close(r),r.preventDefault());break;default:a=!0,this._searchTimeout(r)}},keypress:function(i){if(t)return t=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&i.preventDefault(),void 0;if(!a){var r=e.ui.keyCode;switch(i.keyCode){case r.PAGE_UP:this._move("previousPage",i);break;case r.PAGE_DOWN:this._move("nextPage",i);break;case r.UP:this._keyEvent("previous",i);break;case r.DOWN:this._keyEvent("next",i)}}},input:function(e){return i?(i=!1,e.preventDefault(),void 0):(this._searchTimeout(e),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(e),this._change(e),void 0)}}),this._initSource(),this.menu=e("<ul>").addClass("ui-autocomplete ui-front").appendTo(this._appendTo()).menu({role:null}).hide().menu("instance"),this._on(this.menu.element,{mousedown:function(t){t.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur});var a=this.menu.element[0];e(t.target).closest(".ui-menu-item").length||this._delay(function(){var t=this;this.document.one("mousedown",function(i){i.target===t.element[0]||i.target===a||e.contains(a,i.target)||t.close()})})},menufocus:function(t,a){var i,r;return this.isNewMenu&&(this.isNewMenu=!1,t.originalEvent&&/^mouse/.test(t.originalEvent.type))?(this.menu.blur(),this.document.one("mousemove",function(){e(t.target).trigger(t.originalEvent)}),void 0):(r=a.item.data("ui-autocomplete-item"),!1!==this._trigger("focus",t,{item:r})&&t.originalEvent&&/^key/.test(t.originalEvent.type)&&this._value(r.value),i=a.item.attr("aria-label")||r.value,i&&jQuery.trim(i).length&&(this.liveRegion.children().hide(),e("<div>").text(i).appendTo(this.liveRegion)),void 0)},menuselect:function(e,t){var a=t.item.data("ui-autocomplete-item"),i=this.previous;this.element[0]!==this.document[0].activeElement&&(this.element.focus(),this.previous=i,this._delay(function(){this.previous=i,this.selectedItem=a})),!1!==this._trigger("select",e,{item:a})&&this._value(a.value),this.term=this._value(),this.close(e),this.selectedItem=a}}),this.liveRegion=e("<span>",{role:"status","aria-live":"assertive","aria-relevant":"additions"}).addClass("ui-helper-hidden-accessible").appendTo(this.document[0].body),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(e,t){this._super(e,t),"source"===e&&this._initSource(),"appendTo"===e&&this.menu.element.appendTo(this._appendTo()),"disabled"===e&&t&&this.xhr&&this.xhr.abort()},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t&&t[0]||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_initSource:function(){var t,a,i=this;e.isArray(this.options.source)?(t=this.options.source,this.source=function(a,i){i(e.ui.autocomplete.filter(t,a.term))}):"string"==typeof this.options.source?(a=this.options.source,this.source=function(t,r){i.xhr&&i.xhr.abort(),i.xhr=e.ajax({url:a,data:t,dataType:"json",success:function(e){r(e)},error:function(){r([])}})}):this.source=this.options.source},_searchTimeout:function(e){clearTimeout(this.searching),this.searching=this._delay(function(){var t=this.term===this._value(),a=this.menu.element.is(":visible"),i=e.altKey||e.ctrlKey||e.metaKey||e.shiftKey;(!t||t&&!a&&!i)&&(this.selectedItem=null,this.search(null,e))},this.options.delay)},search:function(e,t){return e=null!=e?e:this._value(),this.term=this._value(),e.length<this.options.minLength?this.close(t):this._trigger("search",t)!==!1?this._search(e):void 0},_search:function(e){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.cancelSearch=!1,this.source({term:e},this._response())},_response:function(){var t=++this.requestIndex;return e.proxy(function(e){t===this.requestIndex&&this.__response(e),this.pending--,this.pending||this.element.removeClass("ui-autocomplete-loading")},this)},__response:function(e){e&&(e=this._normalize(e)),this._trigger("response",null,{content:e}),!this.options.disabled&&e&&e.length&&!this.cancelSearch?(this._suggest(e),this._trigger("open")):this._close()},close:function(e){this.cancelSearch=!0,this._close(e)},_close:function(e){this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.blur(),this.isNewMenu=!0,this._trigger("close",e))},_change:function(e){this.previous!==this._value()&&this._trigger("change",e,{item:this.selectedItem})},_normalize:function(t){return t.length&&t[0].label&&t[0].value?t:e.map(t,function(t){return"string"==typeof t?{label:t,value:t}:e.extend({},t,{label:t.label||t.value,value:t.value||t.label})})},_suggest:function(t){var a=this.menu.element.empty();this._renderMenu(a,t),this.isNewMenu=!0,this.menu.refresh(),a.show(),this._resizeMenu(),a.position(e.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next()},_resizeMenu:function(){var e=this.menu.element;e.outerWidth(Math.max(e.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(t,a){var i=this;e.each(a,function(e,a){i._renderItemData(t,a)})},_renderItemData:function(e,t){return this._renderItem(e,t).data("ui-autocomplete-item",t)},_renderItem:function(t,a){return e("<li>").text(a.label).appendTo(t)},_move:function(e,t){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(e)||this.menu.isLastItem()&&/^next/.test(e)?(this.isMultiLine||this._value(this.term),this.menu.blur(),void 0):(this.menu[e](t),void 0):(this.search(null,t),void 0)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(e,t){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(e,t),t.preventDefault())}}),e.extend(e.ui.autocomplete,{escapeRegex:function(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(t,a){var i=RegExp(e.ui.autocomplete.escapeRegex(a),"i");return e.grep(t,function(e){return i.test(e.label||e.value||e)})}}),e.widget("ui.autocomplete",e.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(e){return e+(e>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(t){var a;this._superApply(arguments),this.options.disabled||this.cancelSearch||(a=t&&t.length?this.options.messages.results(t.length):this.options.messages.noResults,this.liveRegion.children().hide(),e("<div>").text(a).appendTo(this.liveRegion))}}),e.ui.autocomplete;var l,m="ui-button ui-widget ui-state-default ui-corner-all",c="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",p=function(){var t=e(this);setTimeout(function(){t.find(":ui-button").button("refresh")},1)},f=function(t){var a=t.name,i=t.form,r=e([]);return a&&(a=a.replace(/'/g,"\\'"),r=i?e(i).find("[name='"+a+"'][type=radio]"):e("[name='"+a+"'][type=radio]",t.ownerDocument).filter(function(){return!this.form})),r};e.widget("ui.button",{version:"1.11.0-beta.2",defaultElement:"<button>",options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset"+this.eventNamespace).bind("reset"+this.eventNamespace,p),"boolean"!=typeof this.options.disabled?this.options.disabled=!!this.element.prop("disabled"):this.element.prop("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var t=this,a=this.options,i="checkbox"===this.type||"radio"===this.type,r=i?"":"ui-state-active";null===a.label&&(a.label="input"===this.type?this.buttonElement.val():this.buttonElement.html()),this._hoverable(this.buttonElement),this.buttonElement.addClass(m).attr("role","button").bind("mouseenter"+this.eventNamespace,function(){a.disabled||this===l&&e(this).addClass("ui-state-active")}).bind("mouseleave"+this.eventNamespace,function(){a.disabled||e(this).removeClass(r)}).bind("click"+this.eventNamespace,function(e){a.disabled&&(e.preventDefault(),e.stopImmediatePropagation())}),this._on({focus:function(){this.buttonElement.addClass("ui-state-focus")},blur:function(){this.buttonElement.removeClass("ui-state-focus")}}),i&&this.element.bind("change"+this.eventNamespace,function(){t.refresh()}),"checkbox"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){return a.disabled?!1:void 0}):"radio"===this.type?this.buttonElement.bind("click"+this.eventNamespace,function(){if(a.disabled)return!1;e(this).addClass("ui-state-active"),t.buttonElement.attr("aria-pressed","true");var i=t.element[0];f(i).not(i).map(function(){return e(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown"+this.eventNamespace,function(){return a.disabled?!1:(e(this).addClass("ui-state-active"),l=this,t.document.one("mouseup",function(){l=null}),void 0)}).bind("mouseup"+this.eventNamespace,function(){return a.disabled?!1:(e(this).removeClass("ui-state-active"),void 0)}).bind("keydown"+this.eventNamespace,function(t){return a.disabled?!1:((t.keyCode===e.ui.keyCode.SPACE||t.keyCode===e.ui.keyCode.ENTER)&&e(this).addClass("ui-state-active"),void 0)}).bind("keyup"+this.eventNamespace+" blur"+this.eventNamespace,function(){e(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(t){t.keyCode===e.ui.keyCode.SPACE&&e(this).click()})),this._setOption("disabled",a.disabled),this._resetButton()},_determineButtonType:function(){var e,t,a;this.type=this.element.is("[type=checkbox]")?"checkbox":this.element.is("[type=radio]")?"radio":this.element.is("input")?"input":"button","checkbox"===this.type||"radio"===this.type?(e=this.element.parents().last(),t="label[for='"+this.element.attr("id")+"']",this.buttonElement=e.find(t),this.buttonElement.length||(e=e.length?e.siblings():this.element.siblings(),this.buttonElement=e.filter(t),this.buttonElement.length||(this.buttonElement=e.find(t))),this.element.addClass("ui-helper-hidden-accessible"),a=this.element.is(":checked"),a&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.prop("aria-pressed",a)):this.buttonElement=this.element},widget:function(){return this.buttonElement},_destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(m+" ui-state-active "+c).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title")},_setOption:function(e,t){return this._super(e,t),"disabled"===e?(this.widget().toggleClass("ui-state-disabled",!!t),this.element.prop("disabled",!!t),t&&("checkbox"===this.type||"radio"===this.type?this.buttonElement.removeClass("ui-state-focus"):this.buttonElement.removeClass("ui-state-focus ui-state-active")),void 0):(this._resetButton(),void 0)},refresh:function(){var t=this.element.is("input, button")?this.element.is(":disabled"):this.element.hasClass("ui-button-disabled");t!==this.options.disabled&&this._setOption("disabled",t),"radio"===this.type?f(this.element[0]).each(function(){e(this).is(":checked")?e(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):e(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):"checkbox"===this.type&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if("input"===this.type)return this.options.label&&this.element.val(this.options.label),void 0;var t=this.buttonElement.removeClass(c),a=e("<span></span>",this.document[0]).addClass("ui-button-text").html(this.options.label).appendTo(t.empty()).text(),i=this.options.icons,r=i.primary&&i.secondary,s=[];i.primary||i.secondary?(this.options.text&&s.push("ui-button-text-icon"+(r?"s":i.primary?"-primary":"-secondary")),i.primary&&t.prepend("<span class='ui-button-icon-primary ui-icon "+i.primary+"'></span>"),i.secondary&&t.append("<span class='ui-button-icon-secondary ui-icon "+i.secondary+"'></span>"),this.options.text||(s.push(r?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||t.attr("title",e.trim(a)))):s.push("ui-button-text-only"),t.addClass(s.join(" "))}}),e.widget("ui.buttonset",{version:"1.11.0-beta.2",options:{items:"button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(e,t){"disabled"===e&&this.buttons.button("option",e,t),this._super(e,t)},refresh:function(){var t="rtl"===this.element.css("direction"),a=this.element.find(this.options.items),i=a.filter(":ui-button");a.not(":ui-button").button(),i.button("refresh"),this.buttons=a.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(t?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(t?"ui-corner-left":"ui-corner-right").end().end()},_destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return e(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy")}}),e.ui.button,e.extend(e.ui,{datepicker:{version:"1.11.0-beta.2"}});var g;e.extend(r.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(e){return n(this._defaults,e||{}),this},_attachDatepicker:function(t,a){var i,r,s;i=t.nodeName.toLowerCase(),r="div"===i||"span"===i,t.id||(this.uuid+=1,t.id="dp"+this.uuid),s=this._newInst(e(t),r),s.settings=e.extend({},a||{}),"input"===i?this._connectDatepicker(t,s):r&&this._inlineDatepicker(t,s)},_newInst:function(t,a){var i=t[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:i,input:t,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:a,dpDiv:a?s(e("<div class='"+this._inlineClass+" ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")):this.dpDiv}},_connectDatepicker:function(t,a){var i=e(t);a.append=e([]),a.trigger=e([]),i.hasClass(this.markerClassName)||(this._attachments(i,a),i.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp),this._autoSize(a),e.data(t,"datepicker",a),a.settings.disabled&&this._disableDatepicker(t))},_attachments:function(t,a){var i,r,s,n=this._get(a,"appendText"),o=this._get(a,"isRTL");a.append&&a.append.remove(),n&&(a.append=e("<span class='"+this._appendClass+"'>"+n+"</span>"),t[o?"before":"after"](a.append)),t.unbind("focus",this._showDatepicker),a.trigger&&a.trigger.remove(),i=this._get(a,"showOn"),("focus"===i||"both"===i)&&t.focus(this._showDatepicker),("button"===i||"both"===i)&&(r=this._get(a,"buttonText"),s=this._get(a,"buttonImage"),a.trigger=e(this._get(a,"buttonImageOnly")?e("<img/>").addClass(this._triggerClass).attr({src:s,alt:r,title:r}):e("<button type='button'></button>").addClass(this._triggerClass).html(s?e("<img/>").attr({src:s,alt:r,title:r}):r)),t[o?"before":"after"](a.trigger),a.trigger.click(function(){return e.datepicker._datepickerShowing&&e.datepicker._lastInput===t[0]?e.datepicker._hideDatepicker():e.datepicker._datepickerShowing&&e.datepicker._lastInput!==t[0]?(e.datepicker._hideDatepicker(),e.datepicker._showDatepicker(t[0])):e.datepicker._showDatepicker(t[0]),!1}))},_autoSize:function(e){if(this._get(e,"autoSize")&&!e.inline){var t,a,i,r,s=new Date(2009,11,20),n=this._get(e,"dateFormat");n.match(/[DM]/)&&(t=function(e){for(a=0,i=0,r=0;e.length>r;r++)e[r].length>a&&(a=e[r].length,i=r);return i},s.setMonth(t(this._get(e,n.match(/MM/)?"monthNames":"monthNamesShort"))),s.setDate(t(this._get(e,n.match(/DD/)?"dayNames":"dayNamesShort"))+20-s.getDay())),e.input.attr("size",this._formatDate(e,s).length)}},_inlineDatepicker:function(t,a){var i=e(t);i.hasClass(this.markerClassName)||(i.addClass(this.markerClassName).append(a.dpDiv),e.data(t,"datepicker",a),this._setDate(a,this._getDefaultDate(a),!0),this._updateDatepicker(a),this._updateAlternate(a),a.settings.disabled&&this._disableDatepicker(t),a.dpDiv.css("display","block"))},_dialogDatepicker:function(t,a,i,r,s){var o,d,u,h,l,m=this._dialogInst;return m||(this.uuid+=1,o="dp"+this.uuid,this._dialogInput=e("<input type='text' id='"+o+"' style='position: absolute; top: -100px; width: 0px;'/>"),this._dialogInput.keydown(this._doKeyDown),e("body").append(this._dialogInput),m=this._dialogInst=this._newInst(this._dialogInput,!1),m.settings={},e.data(this._dialogInput[0],"datepicker",m)),n(m.settings,r||{}),a=a&&a.constructor===Date?this._formatDate(m,a):a,this._dialogInput.val(a),this._pos=s?s.length?s:[s.pageX,s.pageY]:null,this._pos||(d=document.documentElement.clientWidth,u=document.documentElement.clientHeight,h=document.documentElement.scrollLeft||document.body.scrollLeft,l=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[d/2-100+h,u/2-150+l]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),m.settings.onSelect=i,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),e.blockUI&&e.blockUI(this.dpDiv),e.data(this._dialogInput[0],"datepicker",m),this},_destroyDatepicker:function(t){var a,i=e(t),r=e.data(t,"datepicker");i.hasClass(this.markerClassName)&&(a=t.nodeName.toLowerCase(),e.removeData(t,"datepicker"),"input"===a?(r.append.remove(),r.trigger.remove(),i.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):("div"===a||"span"===a)&&i.removeClass(this.markerClassName).empty())
+},_enableDatepicker:function(t){var a,i,r=e(t),s=e.data(t,"datepicker");r.hasClass(this.markerClassName)&&(a=t.nodeName.toLowerCase(),"input"===a?(t.disabled=!1,s.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):("div"===a||"span"===a)&&(i=r.children("."+this._inlineClass),i.children().removeClass("ui-state-disabled"),i.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}))},_disableDatepicker:function(t){var a,i,r=e(t),s=e.data(t,"datepicker");r.hasClass(this.markerClassName)&&(a=t.nodeName.toLowerCase(),"input"===a?(t.disabled=!0,s.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):("div"===a||"span"===a)&&(i=r.children("."+this._inlineClass),i.children().addClass("ui-state-disabled"),i.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}),this._disabledInputs[this._disabledInputs.length]=t)},_isDisabledDatepicker:function(e){if(!e)return!1;for(var t=0;this._disabledInputs.length>t;t++)if(this._disabledInputs[t]===e)return!0;return!1},_getInst:function(t){try{return e.data(t,"datepicker")}catch(a){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(t,a,i){var r,s,o,d,u=this._getInst(t);return 2===arguments.length&&"string"==typeof a?"defaults"===a?e.extend({},e.datepicker._defaults):u?"all"===a?e.extend({},u.settings):this._get(u,a):null:(r=a||{},"string"==typeof a&&(r={},r[a]=i),u&&(this._curInst===u&&this._hideDatepicker(),s=this._getDateDatepicker(t,!0),o=this._getMinMaxDate(u,"min"),d=this._getMinMaxDate(u,"max"),n(u.settings,r),null!==o&&void 0!==r.dateFormat&&void 0===r.minDate&&(u.settings.minDate=this._formatDate(u,o)),null!==d&&void 0!==r.dateFormat&&void 0===r.maxDate&&(u.settings.maxDate=this._formatDate(u,d)),"disabled"in r&&(r.disabled?this._disableDatepicker(t):this._enableDatepicker(t)),this._attachments(e(t),u),this._autoSize(u),this._setDate(u,s),this._updateAlternate(u),this._updateDatepicker(u)),void 0)},_changeDatepicker:function(e,t,a){this._optionDatepicker(e,t,a)},_refreshDatepicker:function(e){var t=this._getInst(e);t&&this._updateDatepicker(t)},_setDateDatepicker:function(e,t){var a=this._getInst(e);a&&(this._setDate(a,t),this._updateDatepicker(a),this._updateAlternate(a))},_getDateDatepicker:function(e,t){var a=this._getInst(e);return a&&!a.inline&&this._setDateFromField(a,t),a?this._getDate(a):null},_doKeyDown:function(t){var a,i,r,s=e.datepicker._getInst(t.target),n=!0,o=s.dpDiv.is(".ui-datepicker-rtl");if(s._keyEvent=!0,e.datepicker._datepickerShowing)switch(t.keyCode){case 9:e.datepicker._hideDatepicker(),n=!1;break;case 13:return r=e("td."+e.datepicker._dayOverClass+":not(."+e.datepicker._currentClass+")",s.dpDiv),r[0]&&e.datepicker._selectDay(t.target,s.selectedMonth,s.selectedYear,r[0]),a=e.datepicker._get(s,"onSelect"),a?(i=e.datepicker._formatDate(s),a.apply(s.input?s.input[0]:null,[i,s])):e.datepicker._hideDatepicker(),!1;case 27:e.datepicker._hideDatepicker();break;case 33:e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(s,"stepBigMonths"):-e.datepicker._get(s,"stepMonths"),"M");break;case 34:e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(s,"stepBigMonths"):+e.datepicker._get(s,"stepMonths"),"M");break;case 35:(t.ctrlKey||t.metaKey)&&e.datepicker._clearDate(t.target),n=t.ctrlKey||t.metaKey;break;case 36:(t.ctrlKey||t.metaKey)&&e.datepicker._gotoToday(t.target),n=t.ctrlKey||t.metaKey;break;case 37:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,o?1:-1,"D"),n=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(s,"stepBigMonths"):-e.datepicker._get(s,"stepMonths"),"M");break;case 38:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,-7,"D"),n=t.ctrlKey||t.metaKey;break;case 39:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,o?-1:1,"D"),n=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(s,"stepBigMonths"):+e.datepicker._get(s,"stepMonths"),"M");break;case 40:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,7,"D"),n=t.ctrlKey||t.metaKey;break;default:n=!1}else 36===t.keyCode&&t.ctrlKey?e.datepicker._showDatepicker(this):n=!1;n&&(t.preventDefault(),t.stopPropagation())},_doKeyPress:function(t){var a,i,r=e.datepicker._getInst(t.target);return e.datepicker._get(r,"constrainInput")?(a=e.datepicker._possibleChars(e.datepicker._get(r,"dateFormat")),i=String.fromCharCode(null==t.charCode?t.keyCode:t.charCode),t.ctrlKey||t.metaKey||" ">i||!a||a.indexOf(i)>-1):void 0},_doKeyUp:function(t){var a,i=e.datepicker._getInst(t.target);if(i.input.val()!==i.lastVal)try{a=e.datepicker.parseDate(e.datepicker._get(i,"dateFormat"),i.input?i.input.val():null,e.datepicker._getFormatConfig(i)),a&&(e.datepicker._setDateFromField(i),e.datepicker._updateAlternate(i),e.datepicker._updateDatepicker(i))}catch(r){}return!0},_showDatepicker:function(t){if(t=t.target||t,"input"!==t.nodeName.toLowerCase()&&(t=e("input",t.parentNode)[0]),!e.datepicker._isDisabledDatepicker(t)&&e.datepicker._lastInput!==t){var a,r,s,o,d,u,h;a=e.datepicker._getInst(t),e.datepicker._curInst&&e.datepicker._curInst!==a&&(e.datepicker._curInst.dpDiv.stop(!0,!0),a&&e.datepicker._datepickerShowing&&e.datepicker._hideDatepicker(e.datepicker._curInst.input[0])),r=e.datepicker._get(a,"beforeShow"),s=r?r.apply(t,[t,a]):{},s!==!1&&(n(a.settings,s),a.lastVal=null,e.datepicker._lastInput=t,e.datepicker._setDateFromField(a),e.datepicker._inDialog&&(t.value=""),e.datepicker._pos||(e.datepicker._pos=e.datepicker._findPos(t),e.datepicker._pos[1]+=t.offsetHeight),o=!1,e(t).parents().each(function(){return o|="fixed"===e(this).css("position"),!o}),d={left:e.datepicker._pos[0],top:e.datepicker._pos[1]},e.datepicker._pos=null,a.dpDiv.empty(),a.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),e.datepicker._updateDatepicker(a),d=e.datepicker._checkOffset(a,d,o),a.dpDiv.css({position:e.datepicker._inDialog&&e.blockUI?"static":o?"fixed":"absolute",display:"none",left:d.left+"px",top:d.top+"px"}),a.inline||(u=e.datepicker._get(a,"showAnim"),h=e.datepicker._get(a,"duration"),a.dpDiv.css("z-index",i(e(t))+1),e.datepicker._datepickerShowing=!0,e.effects&&e.effects.effect[u]?a.dpDiv.show(u,e.datepicker._get(a,"showOptions"),h):a.dpDiv[u||"show"](u?h:null),e.datepicker._shouldFocusInput(a)&&a.input.focus(),e.datepicker._curInst=a))}},_updateDatepicker:function(t){this.maxRows=4,g=t,t.dpDiv.empty().append(this._generateHTML(t)),this._attachHandlers(t),t.dpDiv.find("."+this._dayOverClass+" a");var a,i=this._getNumberOfMonths(t),r=i[1],s=17;t.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),r>1&&t.dpDiv.addClass("ui-datepicker-multi-"+r).css("width",s*r+"em"),t.dpDiv[(1!==i[0]||1!==i[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),t.dpDiv[(this._get(t,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),t===e.datepicker._curInst&&e.datepicker._datepickerShowing&&e.datepicker._shouldFocusInput(t)&&t.input.focus(),t.yearshtml&&(a=t.yearshtml,setTimeout(function(){a===t.yearshtml&&t.yearshtml&&t.dpDiv.find("select.ui-datepicker-year:first").replaceWith(t.yearshtml),a=t.yearshtml=null},0))},_shouldFocusInput:function(e){return e.input&&e.input.is(":visible")&&!e.input.is(":disabled")&&!e.input.is(":focus")},_checkOffset:function(t,a,i){var r=t.dpDiv.outerWidth(),s=t.dpDiv.outerHeight(),n=t.input?t.input.outerWidth():0,o=t.input?t.input.outerHeight():0,d=document.documentElement.clientWidth+(i?0:e(document).scrollLeft()),u=document.documentElement.clientHeight+(i?0:e(document).scrollTop());return a.left-=this._get(t,"isRTL")?r-n:0,a.left-=i&&a.left===t.input.offset().left?e(document).scrollLeft():0,a.top-=i&&a.top===t.input.offset().top+o?e(document).scrollTop():0,a.left-=Math.min(a.left,a.left+r>d&&d>r?Math.abs(a.left+r-d):0),a.top-=Math.min(a.top,a.top+s>u&&u>s?Math.abs(s+o):0),a},_findPos:function(t){for(var a,i=this._getInst(t),r=this._get(i,"isRTL");t&&("hidden"===t.type||1!==t.nodeType||e.expr.filters.hidden(t));)t=t[r?"previousSibling":"nextSibling"];return a=e(t).offset(),[a.left,a.top]},_hideDatepicker:function(t){var a,i,r,s,n=this._curInst;!n||t&&n!==e.data(t,"datepicker")||this._datepickerShowing&&(a=this._get(n,"showAnim"),i=this._get(n,"duration"),r=function(){e.datepicker._tidyDialog(n)},e.effects&&(e.effects.effect[a]||e.effects[a])?n.dpDiv.hide(a,e.datepicker._get(n,"showOptions"),i,r):n.dpDiv["slideDown"===a?"slideUp":"fadeIn"===a?"fadeOut":"hide"](a?i:null,r),a||r(),this._datepickerShowing=!1,s=this._get(n,"onClose"),s&&s.apply(n.input?n.input[0]:null,[n.input?n.input.val():"",n]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),e.blockUI&&(e.unblockUI(),e("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(e){e.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(t){if(e.datepicker._curInst){var a=e(t.target),i=e.datepicker._getInst(a[0]);(a[0].id!==e.datepicker._mainDivId&&0===a.parents("#"+e.datepicker._mainDivId).length&&!a.hasClass(e.datepicker.markerClassName)&&!a.closest("."+e.datepicker._triggerClass).length&&e.datepicker._datepickerShowing&&(!e.datepicker._inDialog||!e.blockUI)||a.hasClass(e.datepicker.markerClassName)&&e.datepicker._curInst!==i)&&e.datepicker._hideDatepicker()}},_adjustDate:function(t,a,i){var r=e(t),s=this._getInst(r[0]);this._isDisabledDatepicker(r[0])||(this._adjustInstDate(s,a+("M"===i?this._get(s,"showCurrentAtPos"):0),i),this._updateDatepicker(s))},_gotoToday:function(t){var a,i=e(t),r=this._getInst(i[0]);this._get(r,"gotoCurrent")&&r.currentDay?(r.selectedDay=r.currentDay,r.drawMonth=r.selectedMonth=r.currentMonth,r.drawYear=r.selectedYear=r.currentYear):(a=new Date,r.selectedDay=a.getDate(),r.drawMonth=r.selectedMonth=a.getMonth(),r.drawYear=r.selectedYear=a.getFullYear()),this._notifyChange(r),this._adjustDate(i)},_selectMonthYear:function(t,a,i){var r=e(t),s=this._getInst(r[0]);s["selected"+("M"===i?"Month":"Year")]=s["draw"+("M"===i?"Month":"Year")]=parseInt(a.options[a.selectedIndex].value,10),this._notifyChange(s),this._adjustDate(r)},_selectDay:function(t,a,i,r){var s,n=e(t);e(r).hasClass(this._unselectableClass)||this._isDisabledDatepicker(n[0])||(s=this._getInst(n[0]),s.selectedDay=s.currentDay=e("a",r).html(),s.selectedMonth=s.currentMonth=a,s.selectedYear=s.currentYear=i,this._selectDate(t,this._formatDate(s,s.currentDay,s.currentMonth,s.currentYear)))},_clearDate:function(t){var a=e(t);this._selectDate(a,"")},_selectDate:function(t,a){var i,r=e(t),s=this._getInst(r[0]);a=null!=a?a:this._formatDate(s),s.input&&s.input.val(a),this._updateAlternate(s),i=this._get(s,"onSelect"),i?i.apply(s.input?s.input[0]:null,[a,s]):s.input&&s.input.trigger("change"),s.inline?this._updateDatepicker(s):(this._hideDatepicker(),this._lastInput=s.input[0],"object"!=typeof s.input[0]&&s.input.focus(),this._lastInput=null)},_updateAlternate:function(t){var a,i,r,s=this._get(t,"altField");s&&(a=this._get(t,"altFormat")||this._get(t,"dateFormat"),i=this._getDate(t),r=this.formatDate(a,i,this._getFormatConfig(t)),e(s).each(function(){e(this).val(r)}))},noWeekends:function(e){var t=e.getDay();return[t>0&&6>t,""]},iso8601Week:function(e){var t,a=new Date(e.getTime());return a.setDate(a.getDate()+4-(a.getDay()||7)),t=a.getTime(),a.setMonth(0),a.setDate(1),Math.floor(Math.round((t-a)/864e5)/7)+1},parseDate:function(t,a,i){if(null==t||null==a)throw"Invalid arguments";if(a="object"==typeof a?""+a:a+"",""===a)return null;var r,s,n,o,d=0,u=(i?i.shortYearCutoff:null)||this._defaults.shortYearCutoff,h="string"!=typeof u?u:(new Date).getFullYear()%100+parseInt(u,10),l=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,m=(i?i.dayNames:null)||this._defaults.dayNames,c=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,p=(i?i.monthNames:null)||this._defaults.monthNames,f=-1,g=-1,y=-1,x=-1,v=!1,T=function(e){var a=t.length>r+1&&t.charAt(r+1)===e;return a&&r++,a},k=function(e){var t=T(e),i="@"===e?14:"!"===e?20:"y"===e&&t?4:"o"===e?3:2,r=RegExp("^\\d{1,"+i+"}"),s=a.substring(d).match(r);if(!s)throw"Missing number at position "+d;return d+=s[0].length,parseInt(s[0],10)},S=function(t,i,r){var s=-1,n=e.map(T(t)?r:i,function(e,t){return[[t,e]]}).sort(function(e,t){return-(e[1].length-t[1].length)});if(e.each(n,function(e,t){var i=t[1];return a.substr(d,i.length).toLowerCase()===i.toLowerCase()?(s=t[0],d+=i.length,!1):void 0}),-1!==s)return s+1;throw"Unknown name at position "+d},b=function(){if(a.charAt(d)!==t.charAt(r))throw"Unexpected literal at position "+d;d++};for(r=0;t.length>r;r++)if(v)"'"!==t.charAt(r)||T("'")?b():v=!1;else switch(t.charAt(r)){case"d":y=k("d");break;case"D":S("D",l,m);break;case"o":x=k("o");break;case"m":g=k("m");break;case"M":g=S("M",c,p);break;case"y":f=k("y");break;case"@":o=new Date(k("@")),f=o.getFullYear(),g=o.getMonth()+1,y=o.getDate();break;case"!":o=new Date((k("!")-this._ticksTo1970)/1e4),f=o.getFullYear(),g=o.getMonth()+1,y=o.getDate();break;case"'":T("'")?b():v=!0;break;default:b()}if(a.length>d&&(n=a.substr(d),!/^\s+/.test(n)))throw"Extra/unparsed characters found in date: "+n;if(-1===f?f=(new Date).getFullYear():100>f&&(f+=(new Date).getFullYear()-(new Date).getFullYear()%100+(h>=f?0:-100)),x>-1)for(g=1,y=x;;){if(s=this._getDaysInMonth(f,g-1),s>=y)break;g++,y-=s}if(o=this._daylightSavingAdjust(new Date(f,g-1,y)),o.getFullYear()!==f||o.getMonth()+1!==g||o.getDate()!==y)throw"Invalid date";return o},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:1e7*60*60*24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925)),formatDate:function(e,t,a){if(!t)return"";var i,r=(a?a.dayNamesShort:null)||this._defaults.dayNamesShort,s=(a?a.dayNames:null)||this._defaults.dayNames,n=(a?a.monthNamesShort:null)||this._defaults.monthNamesShort,o=(a?a.monthNames:null)||this._defaults.monthNames,d=function(t){var a=e.length>i+1&&e.charAt(i+1)===t;return a&&i++,a},u=function(e,t,a){var i=""+t;if(d(e))for(;a>i.length;)i="0"+i;return i},h=function(e,t,a,i){return d(e)?i[t]:a[t]},l="",m=!1;if(t)for(i=0;e.length>i;i++)if(m)"'"!==e.charAt(i)||d("'")?l+=e.charAt(i):m=!1;else switch(e.charAt(i)){case"d":l+=u("d",t.getDate(),2);break;case"D":l+=h("D",t.getDay(),r,s);break;case"o":l+=u("o",Math.round((new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()-new Date(t.getFullYear(),0,0).getTime())/864e5),3);break;case"m":l+=u("m",t.getMonth()+1,2);break;case"M":l+=h("M",t.getMonth(),n,o);break;case"y":l+=d("y")?t.getFullYear():(10>t.getYear()%100?"0":"")+t.getYear()%100;break;case"@":l+=t.getTime();break;case"!":l+=1e4*t.getTime()+this._ticksTo1970;break;case"'":d("'")?l+="'":m=!0;break;default:l+=e.charAt(i)}return l},_possibleChars:function(e){var t,a="",i=!1,r=function(a){var i=e.length>t+1&&e.charAt(t+1)===a;return i&&t++,i};for(t=0;e.length>t;t++)if(i)"'"!==e.charAt(t)||r("'")?a+=e.charAt(t):i=!1;else switch(e.charAt(t)){case"d":case"m":case"y":case"@":a+="0123456789";break;case"D":case"M":return null;case"'":r("'")?a+="'":i=!0;break;default:a+=e.charAt(t)}return a},_get:function(e,t){return void 0!==e.settings[t]?e.settings[t]:this._defaults[t]},_setDateFromField:function(e,t){if(e.input.val()!==e.lastVal){var a=this._get(e,"dateFormat"),i=e.lastVal=e.input?e.input.val():null,r=this._getDefaultDate(e),s=r,n=this._getFormatConfig(e);try{s=this.parseDate(a,i,n)||r}catch(o){i=t?"":i}e.selectedDay=s.getDate(),e.drawMonth=e.selectedMonth=s.getMonth(),e.drawYear=e.selectedYear=s.getFullYear(),e.currentDay=i?s.getDate():0,e.currentMonth=i?s.getMonth():0,e.currentYear=i?s.getFullYear():0,this._adjustInstDate(e)}},_getDefaultDate:function(e){return this._restrictMinMax(e,this._determineDate(e,this._get(e,"defaultDate"),new Date))},_determineDate:function(t,a,i){var r=function(e){var t=new Date;return t.setDate(t.getDate()+e),t},s=function(a){try{return e.datepicker.parseDate(e.datepicker._get(t,"dateFormat"),a,e.datepicker._getFormatConfig(t))}catch(i){}for(var r=(a.toLowerCase().match(/^c/)?e.datepicker._getDate(t):null)||new Date,s=r.getFullYear(),n=r.getMonth(),o=r.getDate(),d=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,u=d.exec(a);u;){switch(u[2]||"d"){case"d":case"D":o+=parseInt(u[1],10);break;case"w":case"W":o+=7*parseInt(u[1],10);break;case"m":case"M":n+=parseInt(u[1],10),o=Math.min(o,e.datepicker._getDaysInMonth(s,n));break;case"y":case"Y":s+=parseInt(u[1],10),o=Math.min(o,e.datepicker._getDaysInMonth(s,n))}u=d.exec(a)}return new Date(s,n,o)},n=null==a||""===a?i:"string"==typeof a?s(a):"number"==typeof a?isNaN(a)?i:r(a):new Date(a.getTime());return n=n&&"Invalid Date"==""+n?i:n,n&&(n.setHours(0),n.setMinutes(0),n.setSeconds(0),n.setMilliseconds(0)),this._daylightSavingAdjust(n)},_daylightSavingAdjust:function(e){return e?(e.setHours(e.getHours()>12?e.getHours()+2:0),e):null},_setDate:function(e,t,a){var i=!t,r=e.selectedMonth,s=e.selectedYear,n=this._restrictMinMax(e,this._determineDate(e,t,new Date));e.selectedDay=e.currentDay=n.getDate(),e.drawMonth=e.selectedMonth=e.currentMonth=n.getMonth(),e.drawYear=e.selectedYear=e.currentYear=n.getFullYear(),r===e.selectedMonth&&s===e.selectedYear||a||this._notifyChange(e),this._adjustInstDate(e),e.input&&e.input.val(i?"":this._formatDate(e))},_getDate:function(e){var t=!e.currentYear||e.input&&""===e.input.val()?null:this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return t},_attachHandlers:function(t){var a=this._get(t,"stepMonths"),i="#"+t.id.replace(/\\\\/g,"\\");t.dpDiv.find("[data-handler]").map(function(){var t={prev:function(){e.datepicker._adjustDate(i,-a,"M")},next:function(){e.datepicker._adjustDate(i,+a,"M")},hide:function(){e.datepicker._hideDatepicker()},today:function(){e.datepicker._gotoToday(i)},selectDay:function(){return e.datepicker._selectDay(i,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return e.datepicker._selectMonthYear(i,this,"M"),!1},selectYear:function(){return e.datepicker._selectMonthYear(i,this,"Y"),!1}};e(this).bind(this.getAttribute("data-event"),t[this.getAttribute("data-handler")])})},_generateHTML:function(e){var t,a,i,r,s,n,o,d,u,h,l,m,c,p,f,g,y,x,v,T,k,S,b,N,M,D,w,_,A,j,F,C,L,H,J,P,z,R,E,O=new Date,Y=this._daylightSavingAdjust(new Date(O.getFullYear(),O.getMonth(),O.getDate())),I=this._get(e,"isRTL"),Q=this._get(e,"showButtonPanel"),W=this._get(e,"hideIfNoPrevNext"),B=this._get(e,"navigationAsDateFormat"),V=this._getNumberOfMonths(e),K=this._get(e,"showCurrentAtPos"),G=this._get(e,"stepMonths"),U=1!==V[0]||1!==V[1],q=this._daylightSavingAdjust(e.currentDay?new Date(e.currentYear,e.currentMonth,e.currentDay):new Date(9999,9,9)),Z=this._getMinMaxDate(e,"min"),X=this._getMinMaxDate(e,"max"),$=e.drawMonth-K,et=e.drawYear;if(0>$&&($+=12,et--),X)for(t=this._daylightSavingAdjust(new Date(X.getFullYear(),X.getMonth()-V[0]*V[1]+1,X.getDate())),t=Z&&Z>t?Z:t;this._daylightSavingAdjust(new Date(et,$,1))>t;)$--,0>$&&($=11,et--);for(e.drawMonth=$,e.drawYear=et,a=this._get(e,"prevText"),a=B?this.formatDate(a,this._daylightSavingAdjust(new Date(et,$-G,1)),this._getFormatConfig(e)):a,i=this._canAdjustMonth(e,-1,et,$)?"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click' title='"+a+"'><span class='ui-icon ui-icon-circle-triangle-"+(I?"e":"w")+"'>"+a+"</span></a>":W?"":"<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+a+"'><span class='ui-icon ui-icon-circle-triangle-"+(I?"e":"w")+"'>"+a+"</span></a>",r=this._get(e,"nextText"),r=B?this.formatDate(r,this._daylightSavingAdjust(new Date(et,$+G,1)),this._getFormatConfig(e)):r,s=this._canAdjustMonth(e,1,et,$)?"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click' title='"+r+"'><span class='ui-icon ui-icon-circle-triangle-"+(I?"w":"e")+"'>"+r+"</span></a>":W?"":"<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+r+"'><span class='ui-icon ui-icon-circle-triangle-"+(I?"w":"e")+"'>"+r+"</span></a>",n=this._get(e,"currentText"),o=this._get(e,"gotoCurrent")&&e.currentDay?q:Y,n=B?this.formatDate(n,o,this._getFormatConfig(e)):n,d=e.inline?"":"<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>"+this._get(e,"closeText")+"</button>",u=Q?"<div class='ui-datepicker-buttonpane ui-widget-content'>"+(I?d:"")+(this._isInRange(e,o)?"<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'>"+n+"</button>":"")+(I?"":d)+"</div>":"",h=parseInt(this._get(e,"firstDay"),10),h=isNaN(h)?0:h,l=this._get(e,"showWeek"),m=this._get(e,"dayNames"),c=this._get(e,"dayNamesMin"),p=this._get(e,"monthNames"),f=this._get(e,"monthNamesShort"),g=this._get(e,"beforeShowDay"),y=this._get(e,"showOtherMonths"),x=this._get(e,"selectOtherMonths"),v=this._getDefaultDate(e),T="",S=0;V[0]>S;S++){for(b="",this.maxRows=4,N=0;V[1]>N;N++){if(M=this._daylightSavingAdjust(new Date(et,$,e.selectedDay)),D=" ui-corner-all",w="",U){if(w+="<div class='ui-datepicker-group",V[1]>1)switch(N){case 0:w+=" ui-datepicker-group-first",D=" ui-corner-"+(I?"right":"left");break;case V[1]-1:w+=" ui-datepicker-group-last",D=" ui-corner-"+(I?"left":"right");break;default:w+=" ui-datepicker-group-middle",D=""}w+="'>"}for(w+="<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix"+D+"'>"+(/all|left/.test(D)&&0===S?I?s:i:"")+(/all|right/.test(D)&&0===S?I?i:s:"")+this._generateMonthYearHeader(e,$,et,Z,X,S>0||N>0,p,f)+"</div><table class='ui-datepicker-calendar'><thead>"+"<tr>",_=l?"<th class='ui-datepicker-week-col'>"+this._get(e,"weekHeader")+"</th>":"",k=0;7>k;k++)A=(k+h)%7,_+="<th scope='col'"+((k+h+6)%7>=5?" class='ui-datepicker-week-end'":"")+">"+"<span title='"+m[A]+"'>"+c[A]+"</span></th>";for(w+=_+"</tr></thead><tbody>",j=this._getDaysInMonth(et,$),et===e.selectedYear&&$===e.selectedMonth&&(e.selectedDay=Math.min(e.selectedDay,j)),F=(this._getFirstDayOfMonth(et,$)-h+7)%7,C=Math.ceil((F+j)/7),L=U?this.maxRows>C?this.maxRows:C:C,this.maxRows=L,H=this._daylightSavingAdjust(new Date(et,$,1-F)),J=0;L>J;J++){for(w+="<tr>",P=l?"<td class='ui-datepicker-week-col'>"+this._get(e,"calculateWeek")(H)+"</td>":"",k=0;7>k;k++)z=g?g.apply(e.input?e.input[0]:null,[H]):[!0,""],R=H.getMonth()!==$,E=R&&!x||!z[0]||Z&&Z>H||X&&H>X,P+="<td class='"+((k+h+6)%7>=5?" ui-datepicker-week-end":"")+(R?" ui-datepicker-other-month":"")+(H.getTime()===M.getTime()&&$===e.selectedMonth&&e._keyEvent||v.getTime()===H.getTime()&&v.getTime()===M.getTime()?" "+this._dayOverClass:"")+(E?" "+this._unselectableClass+" ui-state-disabled":"")+(R&&!y?"":" "+z[1]+(H.getTime()===q.getTime()?" "+this._currentClass:"")+(H.getTime()===Y.getTime()?" ui-datepicker-today":""))+"'"+(R&&!y||!z[2]?"":" title='"+z[2].replace(/'/g,"'")+"'")+(E?"":" data-handler='selectDay' data-event='click' data-month='"+H.getMonth()+"' data-year='"+H.getFullYear()+"'")+">"+(R&&!y?" ":E?"<span class='ui-state-default'>"+H.getDate()+"</span>":"<a class='ui-state-default"+(H.getTime()===Y.getTime()?" ui-state-highlight":"")+(H.getTime()===q.getTime()?" ui-state-active":"")+(R?" ui-priority-secondary":"")+"' href='#'>"+H.getDate()+"</a>")+"</td>",H.setDate(H.getDate()+1),H=this._daylightSavingAdjust(H);w+=P+"</tr>"}$++,$>11&&($=0,et++),w+="</tbody></table>"+(U?"</div>"+(V[0]>0&&N===V[1]-1?"<div class='ui-datepicker-row-break'></div>":""):""),b+=w}T+=b}return T+=u,e._keyEvent=!1,T},_generateMonthYearHeader:function(e,t,a,i,r,s,n,o){var d,u,h,l,m,c,p,f,g=this._get(e,"changeMonth"),y=this._get(e,"changeYear"),x=this._get(e,"showMonthAfterYear"),v="<div class='ui-datepicker-title'>",T="";if(s||!g)T+="<span class='ui-datepicker-month'>"+n[t]+"</span>";else{for(d=i&&i.getFullYear()===a,u=r&&r.getFullYear()===a,T+="<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>",h=0;12>h;h++)(!d||h>=i.getMonth())&&(!u||r.getMonth()>=h)&&(T+="<option value='"+h+"'"+(h===t?" selected='selected'":"")+">"+o[h]+"</option>");T+="</select>"}if(x||(v+=T+(!s&&g&&y?"":" ")),!e.yearshtml)if(e.yearshtml="",s||!y)v+="<span class='ui-datepicker-year'>"+a+"</span>";else{for(l=this._get(e,"yearRange").split(":"),m=(new Date).getFullYear(),c=function(e){var t=e.match(/c[+\-].*/)?a+parseInt(e.substring(1),10):e.match(/[+\-].*/)?m+parseInt(e,10):parseInt(e,10);return isNaN(t)?m:t},p=c(l[0]),f=Math.max(p,c(l[1]||"")),p=i?Math.max(p,i.getFullYear()):p,f=r?Math.min(f,r.getFullYear()):f,e.yearshtml+="<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";f>=p;p++)e.yearshtml+="<option value='"+p+"'"+(p===a?" selected='selected'":"")+">"+p+"</option>";e.yearshtml+="</select>",v+=e.yearshtml,e.yearshtml=null}return v+=this._get(e,"yearSuffix"),x&&(v+=(!s&&g&&y?"":" ")+T),v+="</div>"},_adjustInstDate:function(e,t,a){var i=e.drawYear+("Y"===a?t:0),r=e.drawMonth+("M"===a?t:0),s=Math.min(e.selectedDay,this._getDaysInMonth(i,r))+("D"===a?t:0),n=this._restrictMinMax(e,this._daylightSavingAdjust(new Date(i,r,s)));e.selectedDay=n.getDate(),e.drawMonth=e.selectedMonth=n.getMonth(),e.drawYear=e.selectedYear=n.getFullYear(),("M"===a||"Y"===a)&&this._notifyChange(e)},_restrictMinMax:function(e,t){var a=this._getMinMaxDate(e,"min"),i=this._getMinMaxDate(e,"max"),r=a&&a>t?a:t;return i&&r>i?i:r},_notifyChange:function(e){var t=this._get(e,"onChangeMonthYear");t&&t.apply(e.input?e.input[0]:null,[e.selectedYear,e.selectedMonth+1,e])},_getNumberOfMonths:function(e){var t=this._get(e,"numberOfMonths");return null==t?[1,1]:"number"==typeof t?[1,t]:t},_getMinMaxDate:function(e,t){return this._determineDate(e,this._get(e,t+"Date"),null)},_getDaysInMonth:function(e,t){return 32-this._daylightSavingAdjust(new Date(e,t,32)).getDate()},_getFirstDayOfMonth:function(e,t){return new Date(e,t,1).getDay()},_canAdjustMonth:function(e,t,a,i){var r=this._getNumberOfMonths(e),s=this._daylightSavingAdjust(new Date(a,i+(0>t?t:r[0]*r[1]),1));return 0>t&&s.setDate(this._getDaysInMonth(s.getFullYear(),s.getMonth())),this._isInRange(e,s)},_isInRange:function(e,t){var a,i,r=this._getMinMaxDate(e,"min"),s=this._getMinMaxDate(e,"max"),n=null,o=null,d=this._get(e,"yearRange");return d&&(a=d.split(":"),i=(new Date).getFullYear(),n=parseInt(a[0],10),o=parseInt(a[1],10),a[0].match(/[+\-].*/)&&(n+=i),a[1].match(/[+\-].*/)&&(o+=i)),(!r||t.getTime()>=r.getTime())&&(!s||t.getTime()<=s.getTime())&&(!n||t.getFullYear()>=n)&&(!o||o>=t.getFullYear())},_getFormatConfig:function(e){var t=this._get(e,"shortYearCutoff");return t="string"!=typeof t?t:(new Date).getFullYear()%100+parseInt(t,10),{shortYearCutoff:t,dayNamesShort:this._get(e,"dayNamesShort"),dayNames:this._get(e,"dayNames"),monthNamesShort:this._get(e,"monthNamesShort"),monthNames:this._get(e,"monthNames")}},_formatDate:function(e,t,a,i){t||(e.currentDay=e.selectedDay,e.currentMonth=e.selectedMonth,e.currentYear=e.selectedYear);var r=t?"object"==typeof t?t:this._daylightSavingAdjust(new Date(i,a,t)):this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return this.formatDate(this._get(e,"dateFormat"),r,this._getFormatConfig(e))}}),e.fn.datepicker=function(t){if(!this.length)return this;e.datepicker.initialized||(e(document).mousedown(e.datepicker._checkExternalClick),e.datepicker.initialized=!0),0===e("#"+e.datepicker._mainDivId).length&&e("body").append(e.datepicker.dpDiv);var a=Array.prototype.slice.call(arguments,1);return"string"!=typeof t||"isDisabled"!==t&&"getDate"!==t&&"widget"!==t?"option"===t&&2===arguments.length&&"string"==typeof arguments[1]?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(a)):this.each(function(){"string"==typeof t?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this].concat(a)):e.datepicker._attachDatepicker(this,t)}):e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(a))},e.datepicker=new r,e.datepicker.initialized=!1,e.datepicker.uuid=(new Date).getTime(),e.datepicker.version="1.11.0-beta.2",e.datepicker,e.widget("ui.dialog",{version:"1.11.0-beta.2",options:{appendTo:"body",autoOpen:!0,buttons:[],closeOnEscape:!0,closeText:"Close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(t){var a=e(this).css(t).offset().top;0>a&&e(this).css("top",t.top-a)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},sizeRelatedOptions:{buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},resizableRelatedOptions:{maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),this.options.title=this.options.title||this.originalTitle,this._createWrapper(),this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(this.uiDialog),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&e.fn.draggable&&this._makeDraggable(),this.options.resizable&&e.fn.resizable&&this._makeResizable(),this._isOpen=!1,this._trackFocus()},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var t=this.options.appendTo;return t&&(t.jquery||t.nodeType)?e(t):this.document.find(t||"body").eq(0)},_destroy:function(){var e,t=this.originalPosition;this._destroyOverlay(),this.element.removeUniqueId().removeClass("ui-dialog-content ui-widget-content").css(this.originalCss).detach(),this.uiDialog.stop(!0,!0).remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),e=t.parent.children().eq(t.index),e.length&&e[0]!==this.element[0]?e.before(this.element):t.parent.append(this.element)},widget:function(){return this.uiDialog},disable:e.noop,enable:e.noop,close:function(t){var a,i=this;if(this._isOpen&&this._trigger("beforeClose",t)!==!1){if(this._isOpen=!1,this._focusedElement=null,this._destroyOverlay(),this._untrackInstance(),!this.opener.filter(":focusable").focus().length)try{a=this.document[0].activeElement,a&&"body"!==a.nodeName.toLowerCase()&&e(a).blur()}catch(r){}this._hide(this.uiDialog,this.options.hide,function(){i._trigger("close",t)})}},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(t,a){var i=!1,r=this.uiDialog.siblings(".ui-front:visible").map(function(){return+e(this).css("z-index")}).get(),s=Math.max.apply(null,r);return s>=+this.uiDialog.css("z-index")&&(this.uiDialog.css("z-index",s+1),i=!0),i&&!a&&this._trigger("focus",t),i},open:function(){var t=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),void 0):(this._isOpen=!0,this.opener=e(this.document[0].activeElement),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this._show(this.uiDialog,this.options.show,function(){t._focusTabbable(),t._trigger("focus")}),this._trigger("open"),void 0)},_focusTabbable:function(){var e=this._focusedElement;e||(e=this.element.find("[autofocus]")),e.length||(e=this.element.find(":tabbable")),e.length||(e=this.uiDialogButtonPane.find(":tabbable")),e.length||(e=this.uiDialogTitlebarClose.filter(":tabbable")),e.length||(e=this.uiDialog),e.eq(0).focus()
+},_keepFocus:function(t){function a(){var t=this.document[0].activeElement,a=this.uiDialog[0]===t||e.contains(this.uiDialog[0],t);a||this._focusTabbable()}t.preventDefault(),a.call(this),this._delay(a)},_createWrapper:function(){this.uiDialog=e("<div>").addClass("ui-dialog ui-widget ui-widget-content ui-corner-all ui-front "+this.options.dialogClass).hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._on(this.uiDialog,{keydown:function(t){if(this.options.closeOnEscape&&!t.isDefaultPrevented()&&t.keyCode&&t.keyCode===e.ui.keyCode.ESCAPE)return t.preventDefault(),this.close(t),void 0;if(t.keyCode===e.ui.keyCode.TAB){var a=this.uiDialog.find(":tabbable"),i=a.filter(":first"),r=a.filter(":last");t.target!==r[0]&&t.target!==this.uiDialog[0]||t.shiftKey?t.target!==i[0]&&t.target!==this.uiDialog[0]||!t.shiftKey||(this._delay(function(){i.focus()}),t.preventDefault()):(this._delay(function(){i.focus()}),t.preventDefault())}},mousedown:function(e){this._moveToTop(e)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var t;this.uiDialogTitlebar=e("<div>").addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(this.uiDialog),this._on(this.uiDialogTitlebar,{mousedown:function(t){e(t.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.focus()}}),this.uiDialogTitlebarClose=e("<button type='button'></button>").button({label:this.options.closeText,icons:{primary:"ui-icon-closethick"},text:!1}).addClass("ui-dialog-titlebar-close").appendTo(this.uiDialogTitlebar),this._on(this.uiDialogTitlebarClose,{click:function(e){e.preventDefault(),this.close(e)}}),t=e("<span>").uniqueId().addClass("ui-dialog-title").prependTo(this.uiDialogTitlebar),this._title(t),this.uiDialog.attr({"aria-labelledby":t.attr("id")})},_title:function(e){this.options.title||e.html(" "),e.text(this.options.title)},_createButtonPane:function(){this.uiDialogButtonPane=e("<div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),this.uiButtonSet=e("<div>").addClass("ui-dialog-buttonset").appendTo(this.uiDialogButtonPane),this._createButtons()},_createButtons:function(){var t=this,a=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),e.isEmptyObject(a)||e.isArray(a)&&!a.length?(this.uiDialog.removeClass("ui-dialog-buttons"),void 0):(e.each(a,function(a,i){var r,s;i=e.isFunction(i)?{click:i,text:a}:i,i=e.extend({type:"button"},i),r=i.click,i.click=function(){r.apply(t.element[0],arguments)},s={icons:i.icons,text:i.showText},delete i.icons,delete i.showText,e("<button></button>",i).button(s).appendTo(t.uiButtonSet)}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),void 0)},_makeDraggable:function(){function t(e){return{position:e.position,offset:e.offset}}var a=this,i=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(i,r){e(this).addClass("ui-dialog-dragging"),a._blockFrames(),a._trigger("dragStart",i,t(r))},drag:function(e,i){a._trigger("drag",e,t(i))},stop:function(r,s){var n=s.offset.left-a.document.scrollLeft(),o=s.offset.top-a.document.scrollTop();i.position={my:"left top",at:"left"+(n>=0?"+":"")+n+" "+"top"+(o>=0?"+":"")+o,of:a.window},e(this).removeClass("ui-dialog-dragging"),a._unblockFrames(),a._trigger("dragStop",r,t(s))}})},_makeResizable:function(){function t(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}var a=this,i=this.options,r=i.resizable,s=this.uiDialog.css("position"),n="string"==typeof r?r:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:i.maxWidth,maxHeight:i.maxHeight,minWidth:i.minWidth,minHeight:this._minHeight(),handles:n,start:function(i,r){e(this).addClass("ui-dialog-resizing"),a._blockFrames(),a._trigger("resizeStart",i,t(r))},resize:function(e,i){a._trigger("resize",e,t(i))},stop:function(r,s){var n=a.uiDialog.offset(),o=n.left-a.document.scrollLeft(),d=n.top-a.document.scrollTop();i.height=a.uiDialog.height(),i.width=a.uiDialog.width(),i.position={my:"left top",at:"left"+(o>=0?"+":"")+o+" "+"top"+(d>=0?"+":"")+d,of:a.window},e(this).removeClass("ui-dialog-resizing"),a._unblockFrames(),a._trigger("resizeStop",r,t(s))}}).css("position",s)},_trackFocus:function(){this._on(this.widget(),{focusin:function(t){this._untrackInstance(),this._trackingInstances().unshift(this),this._focusedElement=e(t.target)}})},_untrackInstance:function(){var t=this._trackingInstances(),a=e.inArray(this,t);-1!==a&&t.splice(a,1)},_trackingInstances:function(){var e=this.document.data("ui-dialog-instances");return e||(e=[],this.document.data("ui-dialog-instances",e)),e},_minHeight:function(){var e=this.options;return"auto"===e.height?e.minHeight:Math.min(e.minHeight,e.height)},_position:function(){var e=this.uiDialog.is(":visible");e||this.uiDialog.show(),this.uiDialog.position(this.options.position),e||this.uiDialog.hide()},_setOptions:function(t){var a=this,i=!1,r={};e.each(t,function(e,t){a._setOption(e,t),e in a.sizeRelatedOptions&&(i=!0),e in a.resizableRelatedOptions&&(r[e]=t)}),i&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",r)},_setOption:function(e,t){var a,i,r=this.uiDialog;"dialogClass"===e&&r.removeClass(this.options.dialogClass).addClass(t),"disabled"!==e&&(this._super(e,t),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:""+t}),"draggable"===e&&(a=r.is(":data(ui-draggable)"),a&&!t&&r.draggable("destroy"),!a&&t&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(i=r.is(":data(ui-resizable)"),i&&!t&&r.resizable("destroy"),i&&"string"==typeof t&&r.resizable("option","handles",t),i||t===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var e,t,a,i=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),i.minWidth>i.width&&(i.width=i.minWidth),e=this.uiDialog.css({height:"auto",width:i.width}).outerHeight(),t=Math.max(0,i.minHeight-e),a="number"==typeof i.maxHeight?Math.max(0,i.maxHeight-e):"none","auto"===i.height?this.element.css({minHeight:t,maxHeight:a,height:"auto"}):this.element.height(Math.max(0,i.height-e)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var t=e(this);return e("<div>").css({position:"absolute",width:t.outerWidth(),height:t.outerHeight()}).appendTo(t.parent()).offset(t.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(t){return e(t.target).closest(".ui-dialog").length?!0:!!e(t.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var t=!0;this._delay(function(){t=!1}),this.document.data("ui-dialog-overlays")||this._on(this.document,{focusin:function(e){t||this._allowInteraction(e)||(e.preventDefault(),this._trackingInstances()[0]._focusTabbable())}}),this.overlay=e("<div>").addClass("ui-widget-overlay ui-front").appendTo(this._appendTo()),this._on(this.overlay,{mousedown:"_keepFocus"}),this.document.data("ui-dialog-overlays",(this.document.data("ui-dialog-overlays")||0)+1)}},_destroyOverlay:function(){if(this.options.modal&&this.overlay){var e=this.document.data("ui-dialog-overlays")-1;e?this.document.data("ui-dialog-overlays",e):this.document.off("focusin").removeData("ui-dialog-overlays"),this.overlay.remove(),this.overlay=null}}}),e.widget("ui.progressbar",{version:"1.11.0-beta.2",options:{max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min}),this.valueDiv=e("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this._refreshValue()},_destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove()},value:function(e){return void 0===e?this.options.value:(this.options.value=this._constrainedValue(e),this._refreshValue(),void 0)},_constrainedValue:function(e){return void 0===e&&(e=this.options.value),this.indeterminate=e===!1,"number"!=typeof e&&(e=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,e))},_setOptions:function(e){var t=e.value;delete e.value,this._super(e),this.options.value=this._constrainedValue(t),this._refreshValue()},_setOption:function(e,t){"max"===e&&(t=Math.max(this.min,t)),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var t=this.options.value,a=this._percentage();this.valueDiv.toggle(this.indeterminate||t>this.min).toggleClass("ui-corner-right",t===this.options.max).width(a.toFixed(0)+"%"),this.element.toggleClass("ui-progressbar-indeterminate",this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=e("<div class='ui-progressbar-overlay'></div>").appendTo(this.valueDiv))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":t}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==t&&(this.oldValue=t,this._trigger("change")),t===this.options.max&&this._trigger("complete")}}),e.widget("ui.selectmenu",{version:"1.11.0-beta.2",defaultElement:"<select>",options:{appendTo:null,disabled:null,icons:{button:"ui-icon-triangle-1-s"},position:{my:"left top",at:"left bottom",collision:"none"},width:null,change:null,close:null,focus:null,open:null,select:null},_create:function(){var e=this.element.uniqueId().attr("id");this.ids={element:e,button:e+"-button",menu:e+"-menu"},this._drawButton(),this._drawMenu(),this.options.disabled&&this.disable()},_drawButton:function(){var t=this,a=this.element.attr("tabindex");this.label=e("label[for='"+this.ids.element+"']").attr("for",this.ids.button),this._on(this.label,{click:function(e){this.button.focus(),e.preventDefault()}}),this.element.hide(),this.button=e("<span>",{"class":"ui-selectmenu-button ui-widget ui-state-default ui-corner-all",tabindex:a||this.options.disabled?-1:0,id:this.ids.button,role:"combobox","aria-expanded":"false","aria-autocomplete":"list","aria-owns":this.ids.menu,"aria-haspopup":"true"}).insertAfter(this.element),e("<span>",{"class":"ui-icon "+this.options.icons.button}).prependTo(this.button),this.buttonText=e("<span>",{"class":"ui-selectmenu-text"}).appendTo(this.button),this._setText(this.buttonText,this.element.find("option:selected").text()),this._setOption("width",this.options.width),this._on(this.button,this._buttonEvents),this.button.one("focusin",function(){t.menuItems||t._refreshMenu()}),this._hoverable(this.button),this._focusable(this.button)},_drawMenu:function(){var t=this;this.menu=e("<ul>",{"aria-hidden":"true","aria-labelledby":this.ids.button,id:this.ids.menu}),this.menuWrap=e("<div>",{"class":"ui-selectmenu-menu ui-front"}).append(this.menu).appendTo(this._appendTo()),this.menuInstance=this.menu.menu({role:"listbox",select:function(e,a){e.preventDefault(),t._select(a.item.data("ui-selectmenu-item"),e)},focus:function(e,a){var i=a.item.data("ui-selectmenu-item");null!=t.focusIndex&&i.index!==t.focusIndex&&(t._trigger("focus",e,{item:i}),t.isOpen||t._select(i,e)),t.focusIndex=i.index,t.button.attr("aria-activedescendant",t.menuItems.eq(i.index).attr("id"))}}).menu("instance"),this.menu.addClass("ui-corner-bottom").removeClass("ui-corner-all"),this.menuInstance._off(this.menu,"mouseleave"),this.menuInstance._closeOnDocumentClick=function(){return!1},this.menuInstance._isDivider=function(){return!1}},refresh:function(){this._refreshMenu(),this._setText(this.buttonText,this._getSelectedItem().text()),this._setOption("width",this.options.width)},_refreshMenu:function(){this.menu.empty();var e,t=this.element.find("option");t.length&&(this._parseOptions(t),this._renderMenu(this.menu,this.items),this.menuInstance.refresh(),this.menuItems=this.menu.find("li").not(".ui-selectmenu-optgroup"),e=this._getSelectedItem(),this.menuInstance.focus(null,e),this._setAria(e.data("ui-selectmenu-item")),this._setOption("disabled",this.element.prop("disabled")))},open:function(e){this.options.disabled||(this.menuItems?(this.menu.find(".ui-state-focus").removeClass("ui-state-focus"),this.menuInstance.focus(null,this._getSelectedItem())):this._refreshMenu(),this.isOpen=!0,this._toggleAttr(),this._resizeMenu(),this._position(),this._on(this.document,this._documentClick),this._trigger("open",e))},_position:function(){this.menuWrap.position(e.extend({of:this.button},this.options.position))},close:function(e){this.isOpen&&(this.isOpen=!1,this._toggleAttr(),this._off(this.document),this._trigger("close",e))},widget:function(){return this.button},menuWidget:function(){return this.menu},_renderMenu:function(t,a){var i=this,r="";e.each(a,function(a,s){s.optgroup!==r&&(e("<li>",{"class":"ui-selectmenu-optgroup ui-menu-divider"+(s.element.parent("optgroup").prop("disabled")?" ui-state-disabled":""),text:s.optgroup}).appendTo(t),r=s.optgroup),i._renderItemData(t,s)})},_renderItemData:function(e,t){return this._renderItem(e,t).data("ui-selectmenu-item",t)},_renderItem:function(t,a){var i=e("<li>");return a.disabled&&i.addClass("ui-state-disabled"),this._setText(i,a.label),i.appendTo(t)},_setText:function(e,t){t?e.text(t):e.html(" ")},_move:function(e,t){var a,i,r=".ui-menu-item";this.isOpen?a=this.menuItems.eq(this.focusIndex):(a=this.menuItems.eq(this.element[0].selectedIndex),r+=":not(.ui-state-disabled)"),i="first"===e||"last"===e?a["first"===e?"prevAll":"nextAll"](r).eq(-1):a[e+"All"](r).eq(0),i.length&&this.menuInstance.focus(t,i)},_getSelectedItem:function(){return this.menuItems.eq(this.element[0].selectedIndex)},_toggle:function(e){this[this.isOpen?"close":"open"](e)},_documentClick:{mousedown:function(t){this.isOpen&&(e(t.target).closest(".ui-selectmenu-menu, #"+this.ids.button).length||this.close(t))}},_buttonEvents:{click:"_toggle",keydown:function(t){var a=!0;switch(t.keyCode){case e.ui.keyCode.TAB:case e.ui.keyCode.ESCAPE:this.close(t),a=!1;break;case e.ui.keyCode.ENTER:this.isOpen&&this._selectFocusedItem(t);break;case e.ui.keyCode.UP:t.altKey?this._toggle(t):this._move("prev",t);break;case e.ui.keyCode.DOWN:t.altKey?this._toggle(t):this._move("next",t);break;case e.ui.keyCode.SPACE:this.isOpen?this._selectFocusedItem(t):this._toggle(t);break;case e.ui.keyCode.LEFT:this._move("prev",t);break;case e.ui.keyCode.RIGHT:this._move("next",t);break;case e.ui.keyCode.HOME:case e.ui.keyCode.PAGE_UP:this._move("first",t);break;case e.ui.keyCode.END:case e.ui.keyCode.PAGE_DOWN:this._move("last",t);break;default:this.menu.trigger(t),a=!1}a&&t.preventDefault()}},_selectFocusedItem:function(e){var t=this.menuItems.eq(this.focusIndex);t.hasClass("ui-state-disabled")||this._select(t.data("ui-selectmenu-item"),e)},_select:function(e,t){var a=this.element[0].selectedIndex;this.element[0].selectedIndex=e.index,this._setText(this.buttonText,e.label),this._setAria(e),this._trigger("select",t,{item:e}),e.index!==a&&this._trigger("change",t,{item:e}),this.close(t)},_setAria:function(e){var t=this.menuItems.eq(e.index).attr("id");this.button.attr({"aria-labelledby":t,"aria-activedescendant":t}),this.menu.attr("aria-activedescendant",t)},_setOption:function(e,t){"icons"===e&&this.button.find("span.ui-icon").removeClass(this.options.icons.button).addClass(t.button),this._super(e,t),"appendTo"===e&&this.menuWrap.appendTo(this._appendTo()),"disabled"===e&&(this.menuInstance.option("disabled",t),this.button.toggleClass("ui-state-disabled",t).attr("aria-disabled",t),this.element.prop("disabled",t),t?(this.button.attr("tabindex",-1),this.close()):this.button.attr("tabindex",0)),"width"===e&&(t||(t=this.element.outerWidth()),this.button.outerWidth(t))},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t&&t[0]||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_toggleAttr:function(){this.button.toggleClass("ui-corner-top",this.isOpen).toggleClass("ui-corner-all",!this.isOpen).attr("aria-expanded",this.isOpen),this.menuWrap.toggleClass("ui-selectmenu-open",this.isOpen),this.menu.attr("aria-hidden",!this.isOpen)},_resizeMenu:function(){this.menu.outerWidth(Math.max(this.button.outerWidth(),this.menu.width("").outerWidth()+1))},_getCreateOptions:function(){return{disabled:this.element.prop("disabled")}},_parseOptions:function(t){var a=[];t.each(function(t,i){var r=e(i),s=r.parent("optgroup");a.push({element:r,index:t,value:r.attr("value"),label:r.text(),optgroup:s.attr("label")||"",disabled:s.prop("disabled")||r.prop("disabled")})}),this.items=a},_destroy:function(){this.menuWrap.remove(),this.button.remove(),this.element.show(),this.element.removeUniqueId(),this.label.attr("for",this.ids.element)}}),e.widget("ui.slider",e.ui.mouse,{version:"1.11.0-beta.2",widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null,change:null,slide:null,start:null,stop:null},numPages:5,_create:function(){this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"),this._refresh(),this._setOption("disabled",this.options.disabled),this._animateOff=!1},_refresh:function(){this._createRange(),this._createHandles(),this._setupEvents(),this._refreshValue()},_createHandles:function(){var t,a,i=this.options,r=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),s="<span class='ui-slider-handle ui-state-default ui-corner-all' tabindex='0'></span>",n=[];for(a=i.values&&i.values.length||1,r.length>a&&(r.slice(a).remove(),r=r.slice(0,a)),t=r.length;a>t;t++)n.push(s);this.handles=r.add(e(n.join("")).appendTo(this.element)),this.handle=this.handles.eq(0),this.handles.each(function(t){e(this).data("ui-slider-handle-index",t)})},_createRange:function(){var t=this.options,a="";t.range?(t.range===!0&&(t.values?t.values.length&&2!==t.values.length?t.values=[t.values[0],t.values[0]]:e.isArray(t.values)&&(t.values=t.values.slice(0)):t.values=[this._valueMin(),this._valueMin()]),this.range&&this.range.length?this.range.removeClass("ui-slider-range-min ui-slider-range-max").css({left:"",bottom:""}):(this.range=e("<div></div>").appendTo(this.element),a="ui-slider-range ui-widget-header ui-corner-all"),this.range.addClass(a+("min"===t.range||"max"===t.range?" ui-slider-range-"+t.range:""))):(this.range&&this.range.remove(),this.range=null)},_setupEvents:function(){this._off(this.handles),this._on(this.handles,this._handleEvents),this._hoverable(this.handles),this._focusable(this.handles)},_destroy:function(){this.handles.remove(),this.range&&this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-widget ui-widget-content ui-corner-all"),this._mouseDestroy()},_mouseCapture:function(t){var a,i,r,s,n,o,d,u,h=this,l=this.options;return l.disabled?!1:(this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),a={x:t.pageX,y:t.pageY},i=this._normValueFromMouse(a),r=this._valueMax()-this._valueMin()+1,this.handles.each(function(t){var a=Math.abs(i-h.values(t));(r>a||r===a&&(t===h._lastChangedValue||h.values(t)===l.min))&&(r=a,s=e(this),n=t)}),o=this._start(t,n),o===!1?!1:(this._mouseSliding=!0,this._handleIndex=n,s.addClass("ui-state-active").focus(),d=s.offset(),u=!e(t.target).parents().addBack().is(".ui-slider-handle"),this._clickOffset=u?{left:0,top:0}:{left:t.pageX-d.left-s.width()/2,top:t.pageY-d.top-s.height()/2-(parseInt(s.css("borderTopWidth"),10)||0)-(parseInt(s.css("borderBottomWidth"),10)||0)+(parseInt(s.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(t,n,i),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(e){var t={x:e.pageX,y:e.pageY},a=this._normValueFromMouse(t);return this._slide(e,this._handleIndex,a),!1},_mouseStop:function(e){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(e,this._handleIndex),this._change(e,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation="vertical"===this.options.orientation?"vertical":"horizontal"},_normValueFromMouse:function(e){var t,a,i,r,s;return"horizontal"===this.orientation?(t=this.elementSize.width,a=e.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(t=this.elementSize.height,a=e.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),i=a/t,i>1&&(i=1),0>i&&(i=0),"vertical"===this.orientation&&(i=1-i),r=this._valueMax()-this._valueMin(),s=this._valueMin()+i*r,this._trimAlignValue(s)},_start:function(e,t){var a={handle:this.handles[t],value:this.value()};return this.options.values&&this.options.values.length&&(a.value=this.values(t),a.values=this.values()),this._trigger("start",e,a)},_slide:function(e,t,a){var i,r,s;this.options.values&&this.options.values.length?(i=this.values(t?0:1),2===this.options.values.length&&this.options.range===!0&&(0===t&&a>i||1===t&&i>a)&&(a=i),a!==this.values(t)&&(r=this.values(),r[t]=a,s=this._trigger("slide",e,{handle:this.handles[t],value:a,values:r}),i=this.values(t?0:1),s!==!1&&this.values(t,a))):a!==this.value()&&(s=this._trigger("slide",e,{handle:this.handles[t],value:a}),s!==!1&&this.value(a))},_stop:function(e,t){var a={handle:this.handles[t],value:this.value()};this.options.values&&this.options.values.length&&(a.value=this.values(t),a.values=this.values()),this._trigger("stop",e,a)},_change:function(e,t){if(!this._keySliding&&!this._mouseSliding){var a={handle:this.handles[t],value:this.value()};this.options.values&&this.options.values.length&&(a.value=this.values(t),a.values=this.values()),this._lastChangedValue=t,this._trigger("change",e,a)}},value:function(e){return arguments.length?(this.options.value=this._trimAlignValue(e),this._refreshValue(),this._change(null,0),void 0):this._value()},values:function(t,a){var i,r,s;if(arguments.length>1)return this.options.values[t]=this._trimAlignValue(a),this._refreshValue(),this._change(null,t),void 0;if(!arguments.length)return this._values();if(!e.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(t):this.value();for(i=this.options.values,r=arguments[0],s=0;i.length>s;s+=1)i[s]=this._trimAlignValue(r[s]),this._change(null,s);this._refreshValue()},_setOption:function(t,a){var i,r=0;switch("range"===t&&this.options.range===!0&&("min"===a?(this.options.value=this._values(0),this.options.values=null):"max"===a&&(this.options.value=this._values(this.options.values.length-1),this.options.values=null)),e.isArray(this.options.values)&&(r=this.options.values.length),"disabled"===t&&this.element.toggleClass("ui-state-disabled",!!a),this._super(t,a),t){case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":for(this._animateOff=!0,this._refreshValue(),i=0;r>i;i+=1)this._change(null,i);this._animateOff=!1;break;case"min":case"max":this._animateOff=!0,this._refreshValue(),this._animateOff=!1;break;case"range":this._animateOff=!0,this._refresh(),this._animateOff=!1}},_value:function(){var e=this.options.value;return e=this._trimAlignValue(e)},_values:function(e){var t,a,i;if(arguments.length)return t=this.options.values[e],t=this._trimAlignValue(t);if(this.options.values&&this.options.values.length){for(a=this.options.values.slice(),i=0;a.length>i;i+=1)a[i]=this._trimAlignValue(a[i]);return a}return[]},_trimAlignValue:function(e){if(this._valueMin()>=e)return this._valueMin();if(e>=this._valueMax())return this._valueMax();var t=this.options.step>0?this.options.step:1,a=(e-this._valueMin())%t,i=e-a;return 2*Math.abs(a)>=t&&(i+=a>0?t:-t),parseFloat(i.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var t,a,i,r,s,n=this.options.range,o=this.options,d=this,u=this._animateOff?!1:o.animate,h={};this.options.values&&this.options.values.length?this.handles.each(function(i){a=100*((d.values(i)-d._valueMin())/(d._valueMax()-d._valueMin())),h["horizontal"===d.orientation?"left":"bottom"]=a+"%",e(this).stop(1,1)[u?"animate":"css"](h,o.animate),d.options.range===!0&&("horizontal"===d.orientation?(0===i&&d.range.stop(1,1)[u?"animate":"css"]({left:a+"%"},o.animate),1===i&&d.range[u?"animate":"css"]({width:a-t+"%"},{queue:!1,duration:o.animate})):(0===i&&d.range.stop(1,1)[u?"animate":"css"]({bottom:a+"%"},o.animate),1===i&&d.range[u?"animate":"css"]({height:a-t+"%"},{queue:!1,duration:o.animate}))),t=a}):(i=this.value(),r=this._valueMin(),s=this._valueMax(),a=s!==r?100*((i-r)/(s-r)):0,h["horizontal"===this.orientation?"left":"bottom"]=a+"%",this.handle.stop(1,1)[u?"animate":"css"](h,o.animate),"min"===n&&"horizontal"===this.orientation&&this.range.stop(1,1)[u?"animate":"css"]({width:a+"%"},o.animate),"max"===n&&"horizontal"===this.orientation&&this.range[u?"animate":"css"]({width:100-a+"%"},{queue:!1,duration:o.animate}),"min"===n&&"vertical"===this.orientation&&this.range.stop(1,1)[u?"animate":"css"]({height:a+"%"},o.animate),"max"===n&&"vertical"===this.orientation&&this.range[u?"animate":"css"]({height:100-a+"%"},{queue:!1,duration:o.animate}))},_handleEvents:{keydown:function(t){var a,i,r,s,n=e(t.target).data("ui-slider-handle-index");switch(t.keyCode){case e.ui.keyCode.HOME:case e.ui.keyCode.END:case e.ui.keyCode.PAGE_UP:case e.ui.keyCode.PAGE_DOWN:case e.ui.keyCode.UP:case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:case e.ui.keyCode.LEFT:if(t.preventDefault(),!this._keySliding&&(this._keySliding=!0,e(t.target).addClass("ui-state-active"),a=this._start(t,n),a===!1))return}switch(s=this.options.step,i=r=this.options.values&&this.options.values.length?this.values(n):this.value(),t.keyCode){case e.ui.keyCode.HOME:r=this._valueMin();break;case e.ui.keyCode.END:r=this._valueMax();break;case e.ui.keyCode.PAGE_UP:r=this._trimAlignValue(i+(this._valueMax()-this._valueMin())/this.numPages);break;case e.ui.keyCode.PAGE_DOWN:r=this._trimAlignValue(i-(this._valueMax()-this._valueMin())/this.numPages);break;case e.ui.keyCode.UP:case e.ui.keyCode.RIGHT:if(i===this._valueMax())return;r=this._trimAlignValue(i+s);break;case e.ui.keyCode.DOWN:case e.ui.keyCode.LEFT:if(i===this._valueMin())return;r=this._trimAlignValue(i-s)}this._slide(t,n,r)},keyup:function(t){var a=e(t.target).data("ui-slider-handle-index");this._keySliding&&(this._keySliding=!1,this._stop(t,a),this._change(t,a),e(t.target).removeClass("ui-state-active"))}}}),e.widget("ui.spinner",{version:"1.11.0-beta.2",defaultElement:"<input>",widgetEventPrefix:"spin",options:{culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),""!==this.value()&&this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var t={},a=this.element;return e.each(["min","max","step"],function(e,i){var r=a.attr(i);void 0!==r&&r.length&&(t[i]=r)}),t},_events:{keydown:function(e){this._start(e)&&this._keydown(e)&&e.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(this._stop(),this._refresh(),this.previous!==this.element.val()&&this._trigger("change",e),void 0)},mousewheel:function(e,t){if(t){if(!this.spinning&&!this._start(e))return!1;this._spin((t>0?1:-1)*this.options.step,e),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(e)},100),e.preventDefault()}},"mousedown .ui-spinner-button":function(t){function a(){var e=this.element[0]===this.document[0].activeElement;e||(this.element.focus(),this.previous=i,this._delay(function(){this.previous=i}))}var i;i=this.element[0]===this.document[0].activeElement?this.previous:this.element.val(),t.preventDefault(),a.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,a.call(this)}),this._start(t)!==!1&&this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(t){return e(t.currentTarget).hasClass("ui-state-active")?this._start(t)===!1?!1:(this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t),void 0):void 0},"mouseleave .ui-spinner-button":"_stop"},_draw:function(){var e=this.uiSpinner=this.element.addClass("ui-spinner-input").attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml());this.element.attr("role","spinbutton"),this.buttons=e.find(".ui-spinner-button").attr("tabIndex",-1).button().removeClass("ui-corner-all"),this.buttons.height()>Math.ceil(.5*e.height())&&e.height()>0&&e.height(e.height()),this.options.disabled&&this.disable()},_keydown:function(t){var a=this.options,i=e.ui.keyCode;switch(t.keyCode){case i.UP:return this._repeat(null,1,t),!0;case i.DOWN:return this._repeat(null,-1,t),!0;case i.PAGE_UP:return this._repeat(null,a.page,t),!0;case i.PAGE_DOWN:return this._repeat(null,-a.page,t),!0}return!1},_uiSpinnerHtml:function(){return"<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>"},_buttonHtml:function(){return"<a class='ui-spinner-button ui-spinner-up ui-corner-tr'><span class='ui-icon "+this.options.icons.up+"'>▲</span>"+"</a>"+"<a class='ui-spinner-button ui-spinner-down ui-corner-br'>"+"<span class='ui-icon "+this.options.icons.down+"'>▼</span>"+"</a>"},_start:function(e){return this.spinning||this._trigger("start",e)!==!1?(this.counter||(this.counter=1),this.spinning=!0,!0):!1},_repeat:function(e,t,a){e=e||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,t,a)},e),this._spin(t*this.options.step,a)},_spin:function(e,t){var a=this.value()||0;this.counter||(this.counter=1),a=this._adjustValue(a+e*this._increment(this.counter)),this.spinning&&this._trigger("spin",t,{value:a})===!1||(this._value(a),this.counter++)},_increment:function(t){var a=this.options.incremental;return a?e.isFunction(a)?a(t):Math.floor(t*t*t/5e4-t*t/500+17*t/200+1):1},_precision:function(){var e=this._precisionOf(this.options.step);return null!==this.options.min&&(e=Math.max(e,this._precisionOf(this.options.min))),e},_precisionOf:function(e){var t=""+e,a=t.indexOf(".");return-1===a?0:t.length-a-1},_adjustValue:function(e){var t,a,i=this.options;return t=null!==i.min?i.min:0,a=e-t,a=Math.round(a/i.step)*i.step,e=t+a,e=parseFloat(e.toFixed(this._precision())),null!==i.max&&e>i.max?i.max:null!==i.min&&i.min>e?i.min:e
+},_stop:function(e){this.spinning&&(clearTimeout(this.timer),clearTimeout(this.mousewheelTimer),this.counter=0,this.spinning=!1,this._trigger("stop",e))},_setOption:function(e,t){if("culture"===e||"numberFormat"===e){var a=this._parse(this.element.val());return this.options[e]=t,this.element.val(this._format(a)),void 0}("max"===e||"min"===e||"step"===e)&&"string"==typeof t&&(t=this._parse(t)),"icons"===e&&(this.buttons.first().find(".ui-icon").removeClass(this.options.icons.up).addClass(t.up),this.buttons.last().find(".ui-icon").removeClass(this.options.icons.down).addClass(t.down)),this._super(e,t),"disabled"===e&&(this.widget().toggleClass("ui-state-disabled",!!t),this.element.prop("disabled",!!t),this.buttons.button(t?"disable":"enable"))},_setOptions:o(function(e){this._super(e),this._value(this.element.val())}),_parse:function(e){return"string"==typeof e&&""!==e&&(e=window.Globalize&&this.options.numberFormat?Globalize.parseFloat(e,10,this.options.culture):+e),""===e||isNaN(e)?null:e},_format:function(e){return""===e?"":window.Globalize&&this.options.numberFormat?Globalize.format(e,this.options.numberFormat,this.options.culture):e},_refresh:function(){this.element.attr({"aria-valuemin":this.options.min,"aria-valuemax":this.options.max,"aria-valuenow":this._parse(this.element.val())})},isValid:function(){var e=this.value();return null===e?!1:e===this._adjustValue(e)},_value:function(e,t){var a;""!==e&&(a=this._parse(e),null!==a&&(t||(a=this._adjustValue(a)),e=this._format(a))),this.element.val(e),this._refresh()},_destroy:function(){this.element.removeClass("ui-spinner-input").prop("disabled",!1).removeAttr("autocomplete").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.uiSpinner.replaceWith(this.element)},stepUp:o(function(e){this._stepUp(e)}),_stepUp:function(e){this._start()&&(this._spin((e||1)*this.options.step),this._stop())},stepDown:o(function(e){this._stepDown(e)}),_stepDown:function(e){this._start()&&(this._spin((e||1)*-this.options.step),this._stop())},pageUp:o(function(e){this._stepUp((e||1)*this.options.page)}),pageDown:o(function(e){this._stepDown((e||1)*this.options.page)}),value:function(e){return arguments.length?(o(this._value).call(this,e),void 0):this._parse(this.element.val())},widget:function(){return this.uiSpinner}}),e.widget("ui.tabs",{version:"1.11.0-beta.2",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:function(){var e=/#.*$/;return function(t){var a,i;t=t.cloneNode(!1),a=t.href.replace(e,""),i=location.href.replace(e,"");try{a=decodeURIComponent(a)}catch(r){}try{i=decodeURIComponent(i)}catch(r){}return t.hash.length>1&&a===i}}(),_create:function(){var t=this,a=this.options;this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",a.collapsible).delegate(".ui-tabs-nav > li","mousedown"+this.eventNamespace,function(t){e(this).is(".ui-state-disabled")&&t.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){e(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this._processTabs(),a.active=this._initialActive(),e.isArray(a.disabled)&&(a.disabled=e.unique(a.disabled.concat(e.map(this.tabs.filter(".ui-state-disabled"),function(e){return t.tabs.index(e)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(a.active):e(),this._refresh(),this.active.length&&this.load(a.active)},_initialActive:function(){var t=this.options.active,a=this.options.collapsible,i=location.hash.substring(1);return null===t&&(i&&this.tabs.each(function(a,r){return e(r).attr("aria-controls")===i?(t=a,!1):void 0}),null===t&&(t=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===t||-1===t)&&(t=this.tabs.length?0:!1)),t!==!1&&(t=this.tabs.index(this.tabs.eq(t)),-1===t&&(t=a?!1:0)),!a&&t===!1&&this.anchors.length&&(t=0),t},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):e()}},_tabKeydown:function(t){var a=e(this.document[0].activeElement).closest("li"),i=this.tabs.index(a),r=!0;if(!this._handlePageNav(t)){switch(t.keyCode){case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:i++;break;case e.ui.keyCode.UP:case e.ui.keyCode.LEFT:r=!1,i--;break;case e.ui.keyCode.END:i=this.anchors.length-1;break;case e.ui.keyCode.HOME:i=0;break;case e.ui.keyCode.SPACE:return t.preventDefault(),clearTimeout(this.activating),this._activate(i),void 0;case e.ui.keyCode.ENTER:return t.preventDefault(),clearTimeout(this.activating),this._activate(i===this.options.active?!1:i),void 0;default:return}t.preventDefault(),clearTimeout(this.activating),i=this._focusNextTab(i,r),t.ctrlKey||(a.attr("aria-selected","false"),this.tabs.eq(i).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",i)},this.delay))}},_panelKeydown:function(t){this._handlePageNav(t)||t.ctrlKey&&t.keyCode===e.ui.keyCode.UP&&(t.preventDefault(),this.active.focus())},_handlePageNav:function(t){return t.altKey&&t.keyCode===e.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):t.altKey&&t.keyCode===e.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(t,a){function i(){return t>r&&(t=0),0>t&&(t=r),t}for(var r=this.tabs.length-1;-1!==e.inArray(i(),this.options.disabled);)t=a?t+1:t-1;return t},_focusNextTab:function(e,t){return e=this._findNextTab(e,t),this.tabs.eq(e).focus(),e},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):"disabled"===e?(this._setupDisabled(t),void 0):(this._super(e,t),"collapsible"===e&&(this.element.toggleClass("ui-tabs-collapsible",t),t||this.options.active!==!1||this._activate(0)),"event"===e&&this._setupEvents(t),"heightStyle"===e&&this._setupHeightStyle(t),void 0)},_sanitizeSelector:function(e){return e?e.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var t=this.options,a=this.tablist.children(":has(a[href])");t.disabled=e.map(a.filter(".ui-state-disabled"),function(e){return a.index(e)}),this._processTabs(),t.active!==!1&&this.anchors.length?this.active.length&&!e.contains(this.tablist[0],this.active[0])?this.tabs.length===t.disabled.length?(t.active=!1,this.active=e()):this._activate(this._findNextTab(Math.max(0,t.active-1),!1)):t.active=this.tabs.index(this.active):(t.active=!1,this.active=e()),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var t=this;this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist"),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return e("a",this)[0]}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=e(),this.anchors.each(function(a,i){var r,s,n,o=e(i).uniqueId().attr("id"),d=e(i).closest("li"),u=d.attr("aria-controls");t._isLocal(i)?(r=i.hash,n=r.substring(1),s=t.element.find(t._sanitizeSelector(r))):(n=d.attr("aria-controls")||e({}).uniqueId()[0].id,r="#"+n,s=t.element.find(r),s.length||(s=t._createPanel(n),s.insertAfter(t.panels[a-1]||t.tablist)),s.attr("aria-live","polite")),s.length&&(t.panels=t.panels.add(s)),u&&d.data("ui-tabs-aria-controls",u),d.attr({"aria-controls":n,"aria-labelledby":o}),s.attr("aria-labelledby",o)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel")},_getList:function(){return this.tablist||this.element.find("ol,ul").eq(0)},_createPanel:function(t){return e("<div>").attr("id",t).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(t){e.isArray(t)&&(t.length?t.length===this.anchors.length&&(t=!0):t=!1);for(var a,i=0;a=this.tabs[i];i++)t===!0||-1!==e.inArray(i,t)?e(a).addClass("ui-state-disabled").attr("aria-disabled","true"):e(a).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=t},_setupEvents:function(t){var a={};t&&e.each(t.split(" "),function(e,t){a[t]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(e){e.preventDefault()}}),this._on(this.anchors,a),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(t){var a,i=this.element.parent();"fill"===t?(a=i.height(),a-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var t=e(this),i=t.css("position");"absolute"!==i&&"fixed"!==i&&(a-=t.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){a-=e(this).outerHeight(!0)}),this.panels.each(function(){e(this).height(Math.max(0,a-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===t&&(a=0,this.panels.each(function(){a=Math.max(a,e(this).height("").height())}).height(a))},_eventHandler:function(t){var a=this.options,i=this.active,r=e(t.currentTarget),s=r.closest("li"),n=s[0]===i[0],o=n&&a.collapsible,d=o?e():this._getPanelForTab(s),u=i.length?this._getPanelForTab(i):e(),h={oldTab:i,oldPanel:u,newTab:o?e():s,newPanel:d};t.preventDefault(),s.hasClass("ui-state-disabled")||s.hasClass("ui-tabs-loading")||this.running||n&&!a.collapsible||this._trigger("beforeActivate",t,h)===!1||(a.active=o?!1:this.tabs.index(s),this.active=n?e():s,this.xhr&&this.xhr.abort(),u.length||d.length||e.error("jQuery UI Tabs: Mismatching fragment identifier."),d.length&&this.load(this.tabs.index(s),t),this._toggle(t,h))},_toggle:function(t,a){function i(){s.running=!1,s._trigger("activate",t,a)}function r(){a.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),n.length&&s.options.show?s._show(n,s.options.show,i):(n.show(),i())}var s=this,n=a.newPanel,o=a.oldPanel;this.running=!0,o.length&&this.options.hide?this._hide(o,this.options.hide,function(){a.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),r()}):(a.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),o.hide(),r()),o.attr("aria-hidden","true"),a.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),n.length&&o.length?a.oldTab.attr("tabIndex",-1):n.length&&this.tabs.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),n.attr("aria-hidden","false"),a.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(t){var a,i=this._findActive(t);i[0]!==this.active[0]&&(i.length||(i=this.active),a=i.find(".ui-tabs-anchor")[0],this._eventHandler({target:a,currentTarget:a,preventDefault:e.noop}))},_findActive:function(t){return t===!1?e():this.tabs.eq(t)},_getIndex:function(e){return"string"==typeof e&&(e=this.anchors.index(this.anchors.filter("[href$='"+e+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){e.data(this,"ui-tabs-destroy")?e(this).remove():e(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var t=e(this),a=t.data("ui-tabs-aria-controls");a?t.attr("aria-controls",a).removeData("ui-tabs-aria-controls"):t.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(t){var a=this.options.disabled;a!==!1&&(void 0===t?a=!1:(t=this._getIndex(t),a=e.isArray(a)?e.map(a,function(e){return e!==t?e:null}):e.map(this.tabs,function(e,a){return a!==t?a:null})),this._setupDisabled(a))},disable:function(t){var a=this.options.disabled;if(a!==!0){if(void 0===t)a=!0;else{if(t=this._getIndex(t),-1!==e.inArray(t,a))return;a=e.isArray(a)?e.merge([t],a).sort():[t]}this._setupDisabled(a)}},load:function(t,a){t=this._getIndex(t);var i=this,r=this.tabs.eq(t),s=r.find(".ui-tabs-anchor"),n=this._getPanelForTab(r),o={tab:r,panel:n};this._isLocal(s[0])||(this.xhr=e.ajax(this._ajaxSettings(s,a,o)),this.xhr&&"canceled"!==this.xhr.statusText&&(r.addClass("ui-tabs-loading"),n.attr("aria-busy","true"),this.xhr.success(function(e){setTimeout(function(){n.html(e),i._trigger("load",a,o)},1)}).complete(function(e,t){setTimeout(function(){"abort"===t&&i.panels.stop(!1,!0),r.removeClass("ui-tabs-loading"),n.removeAttr("aria-busy"),e===i.xhr&&delete i.xhr},1)})))},_ajaxSettings:function(t,a,i){var r=this;return{url:t.attr("href"),beforeSend:function(t,s){return r._trigger("beforeLoad",a,e.extend({jqXHR:t,ajaxSettings:s},i))}}},_getPanelForTab:function(t){var a=e(t).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+a))}}),e.widget("ui.tooltip",{version:"1.11.0-beta.2",options:{content:function(){var t=e(this).attr("title")||"";return e("<a>").text(t).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_addDescribedBy:function(t,a){var i=(t.attr("aria-describedby")||"").split(/\s+/);i.push(a),t.data("ui-tooltip-id",a).attr("aria-describedby",e.trim(i.join(" ")))},_removeDescribedBy:function(t){var a=t.data("ui-tooltip-id"),i=(t.attr("aria-describedby")||"").split(/\s+/),r=e.inArray(a,i);-1!==r&&i.splice(r,1),t.removeData("ui-tooltip-id"),i=e.trim(i.join(" ")),i?t.attr("aria-describedby",i):t.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable(),this.liveRegion=e("<div>").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).addClass("ui-helper-hidden-accessible").appendTo(this.document[0].body)},_setOption:function(t,a){var i=this;return"disabled"===t?(this[a?"_disable":"_enable"](),this.options[t]=a,void 0):(this._super(t,a),"content"===t&&e.each(this.tooltips,function(e,t){i._updateContent(t)}),void 0)},_disable:function(){var t=this;e.each(this.tooltips,function(a,i){var r=e.Event("blur");r.target=r.currentTarget=i[0],t.close(r,!0)}),this.element.find(this.options.items).addBack().each(function(){var t=e(this);t.is("[title]")&&t.data("ui-tooltip-title",t.attr("title")).removeAttr("title")})},_enable:function(){this.element.find(this.options.items).addBack().each(function(){var t=e(this);t.data("ui-tooltip-title")&&t.attr("title",t.data("ui-tooltip-title"))})},open:function(t){var a=this,i=e(t?t.target:this.element).closest(this.options.items);i.length&&!i.data("ui-tooltip-id")&&(i.attr("title")&&i.data("ui-tooltip-title",i.attr("title")),i.data("ui-tooltip-open",!0),t&&"mouseover"===t.type&&i.parents().each(function(){var t,i=e(this);i.data("ui-tooltip-open")&&(t=e.Event("blur"),t.target=t.currentTarget=this,a.close(t,!0)),i.attr("title")&&(i.uniqueId(),a.parents[this.id]={element:this,title:i.attr("title")},i.attr("title",""))}),this._updateContent(i,t))},_updateContent:function(e,t){var a,i=this.options.content,r=this,s=t?t.type:null;return"string"==typeof i?this._open(t,e,i):(a=i.call(e[0],function(a){e.data("ui-tooltip-open")&&r._delay(function(){t&&(t.type=s),this._open(t,e,a)})}),a&&this._open(t,e,a),void 0)},_open:function(t,a,i){function r(e){u.of=e,s.is(":hidden")||s.position(u)}var s,n,o,d,u=e.extend({},this.options.position);if(i){if(s=this._find(a),s.length)return s.find(".ui-tooltip-content").html(i),void 0;a.is("[title]")&&(t&&"mouseover"===t.type?a.attr("title",""):a.removeAttr("title")),s=this._tooltip(a),this._addDescribedBy(a,s.attr("id")),s.find(".ui-tooltip-content").html(i),this.liveRegion.children().hide(),i.clone?(d=i.clone(),d.removeAttr("id").find("[id]").removeAttr("id")):d=i,e("<div>").html(d).appendTo(this.liveRegion),this.options.track&&t&&/^mouse/.test(t.type)?(this._on(this.document,{mousemove:r}),r(t)):s.position(e.extend({of:a},this.options.position)),s.hide(),this._show(s,this.options.show),this.options.show&&this.options.show.delay&&(o=this.delayedShow=setInterval(function(){s.is(":visible")&&(r(u.of),clearInterval(o))},e.fx.interval)),this._trigger("open",t,{tooltip:s}),n={keyup:function(t){if(t.keyCode===e.ui.keyCode.ESCAPE){var i=e.Event(t);i.currentTarget=a[0],this.close(i,!0)}}},a[0]!==this.element[0]&&(n.remove=function(){this._removeTooltip(s)}),t&&"mouseover"!==t.type||(n.mouseleave="close"),t&&"focusin"!==t.type||(n.focusout="close"),this._on(!0,a,n)}},close:function(t){var a=this,i=e(t?t.currentTarget:this.element),r=this._find(i);this.closing||(clearInterval(this.delayedShow),i.data("ui-tooltip-title")&&!i.attr("title")&&i.attr("title",i.data("ui-tooltip-title")),this._removeDescribedBy(i),r.stop(!0),this._hide(r,this.options.hide,function(){a._removeTooltip(e(this))}),i.removeData("ui-tooltip-open"),this._off(i,"mouseleave focusout keyup"),i[0]!==this.element[0]&&this._off(i,"remove"),this._off(this.document,"mousemove"),t&&"mouseleave"===t.type&&e.each(this.parents,function(t,i){e(i.element).attr("title",i.title),delete a.parents[t]}),this.closing=!0,this._trigger("close",t,{tooltip:r}),this.closing=!1)},_tooltip:function(t){var a=e("<div>").attr("role","tooltip").addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||"")),i=a.uniqueId().attr("id");return e("<div>").addClass("ui-tooltip-content").appendTo(a),a.appendTo(this.document[0].body),this.tooltips[i]=t,a},_find:function(t){var a=t.data("ui-tooltip-id");return a?e("#"+a):e()},_removeTooltip:function(e){e.remove(),delete this.tooltips[e.attr("id")]},_destroy:function(){var t=this;e.each(this.tooltips,function(a,i){var r=e.Event("blur");r.target=r.currentTarget=i[0],t.close(r,!0),e("#"+a).remove(),i.data("ui-tooltip-title")&&(i.attr("title")||i.attr("title",i.data("ui-tooltip-title")),i.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}});var y="ui-effects-";e.effects={effect:{}},function(e,t){function a(e,t,a){var i=l[t.type]||{};return null==e?a||!t.def?null:t.def:(e=i.floor?~~e:parseFloat(e),isNaN(e)?t.def:i.mod?(e+i.mod)%i.mod:0>e?0:e>i.max?i.max:e)}function i(a){var i=u(),r=i._rgba=[];return a=a.toLowerCase(),p(d,function(e,s){var n,o=s.re.exec(a),d=o&&s.parse(o),u=s.space||"rgba";return d?(n=i[u](d),i[h[u].cache]=n[h[u].cache],r=i._rgba=n._rgba,!1):t}),r.length?("0,0,0,0"===r.join()&&e.extend(r,s.transparent),i):s[a]}function r(e,t,a){return a=(a+1)%1,1>6*a?e+6*(t-e)*a:1>2*a?t:2>3*a?e+6*(t-e)*(2/3-a):e}var s,n="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",o=/^([\-+])=\s*(\d+\.?\d*)/,d=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[e[1],e[2],e[3],e[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[2.55*e[1],2.55*e[2],2.55*e[3],e[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(e){return[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(e){return[parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16),parseInt(e[3]+e[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(e){return[e[1],e[2]/100,e[3]/100,e[4]]}}],u=e.Color=function(t,a,i,r){return new e.Color.fn.parse(t,a,i,r)},h={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},l={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},m=u.support={},c=e("<p>")[0],p=e.each;c.style.cssText="background-color:rgba(1,1,1,.5)",m.rgba=c.style.backgroundColor.indexOf("rgba")>-1,p(h,function(e,t){t.cache="_"+e,t.props.alpha={idx:3,type:"percent",def:1}}),u.fn=e.extend(u.prototype,{parse:function(r,n,o,d){if(r===t)return this._rgba=[null,null,null,null],this;(r.jquery||r.nodeType)&&(r=e(r).css(n),n=t);var l=this,m=e.type(r),c=this._rgba=[];return n!==t&&(r=[r,n,o,d],m="array"),"string"===m?this.parse(i(r)||s._default):"array"===m?(p(h.rgba.props,function(e,t){c[t.idx]=a(r[t.idx],t)}),this):"object"===m?(r instanceof u?p(h,function(e,t){r[t.cache]&&(l[t.cache]=r[t.cache].slice())}):p(h,function(t,i){var s=i.cache;p(i.props,function(e,t){if(!l[s]&&i.to){if("alpha"===e||null==r[e])return;l[s]=i.to(l._rgba)}l[s][t.idx]=a(r[e],t,!0)}),l[s]&&0>e.inArray(null,l[s].slice(0,3))&&(l[s][3]=1,i.from&&(l._rgba=i.from(l[s])))}),this):t},is:function(e){var a=u(e),i=!0,r=this;return p(h,function(e,s){var n,o=a[s.cache];return o&&(n=r[s.cache]||s.to&&s.to(r._rgba)||[],p(s.props,function(e,a){return null!=o[a.idx]?i=o[a.idx]===n[a.idx]:t})),i}),i},_space:function(){var e=[],t=this;return p(h,function(a,i){t[i.cache]&&e.push(a)}),e.pop()},transition:function(e,t){var i=u(e),r=i._space(),s=h[r],n=0===this.alpha()?u("transparent"):this,o=n[s.cache]||s.to(n._rgba),d=o.slice();return i=i[s.cache],p(s.props,function(e,r){var s=r.idx,n=o[s],u=i[s],h=l[r.type]||{};null!==u&&(null===n?d[s]=u:(h.mod&&(u-n>h.mod/2?n+=h.mod:n-u>h.mod/2&&(n-=h.mod)),d[s]=a((u-n)*t+n,r)))}),this[r](d)},blend:function(t){if(1===this._rgba[3])return this;var a=this._rgba.slice(),i=a.pop(),r=u(t)._rgba;return u(e.map(a,function(e,t){return(1-i)*r[t]+i*e}))},toRgbaString:function(){var t="rgba(",a=e.map(this._rgba,function(e,t){return null==e?t>2?1:0:e});return 1===a[3]&&(a.pop(),t="rgb("),t+a.join()+")"},toHslaString:function(){var t="hsla(",a=e.map(this.hsla(),function(e,t){return null==e&&(e=t>2?1:0),t&&3>t&&(e=Math.round(100*e)+"%"),e});return 1===a[3]&&(a.pop(),t="hsl("),t+a.join()+")"},toHexString:function(t){var a=this._rgba.slice(),i=a.pop();return t&&a.push(~~(255*i)),"#"+e.map(a,function(e){return e=(e||0).toString(16),1===e.length?"0"+e:e}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),u.fn.parse.prototype=u.fn,h.hsla.to=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t,a,i=e[0]/255,r=e[1]/255,s=e[2]/255,n=e[3],o=Math.max(i,r,s),d=Math.min(i,r,s),u=o-d,h=o+d,l=.5*h;return t=d===o?0:i===o?60*(r-s)/u+360:r===o?60*(s-i)/u+120:60*(i-r)/u+240,a=0===u?0:.5>=l?u/h:u/(2-h),[Math.round(t)%360,a,l,null==n?1:n]},h.hsla.from=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t=e[0]/360,a=e[1],i=e[2],s=e[3],n=.5>=i?i*(1+a):i+a-i*a,o=2*i-n;return[Math.round(255*r(o,n,t+1/3)),Math.round(255*r(o,n,t)),Math.round(255*r(o,n,t-1/3)),s]},p(h,function(i,r){var s=r.props,n=r.cache,d=r.to,h=r.from;u.fn[i]=function(i){if(d&&!this[n]&&(this[n]=d(this._rgba)),i===t)return this[n].slice();var r,o=e.type(i),l="array"===o||"object"===o?i:arguments,m=this[n].slice();return p(s,function(e,t){var i=l["object"===o?e:t.idx];null==i&&(i=m[t.idx]),m[t.idx]=a(i,t)}),h?(r=u(h(m)),r[n]=m,r):u(m)},p(s,function(t,a){u.fn[t]||(u.fn[t]=function(r){var s,n=e.type(r),d="alpha"===t?this._hsla?"hsla":"rgba":i,u=this[d](),h=u[a.idx];return"undefined"===n?h:("function"===n&&(r=r.call(this,h),n=e.type(r)),null==r&&a.empty?this:("string"===n&&(s=o.exec(r),s&&(r=h+parseFloat(s[2])*("+"===s[1]?1:-1))),u[a.idx]=r,this[d](u)))})})}),u.hook=function(t){var a=t.split(" ");p(a,function(t,a){e.cssHooks[a]={set:function(t,r){var s,n,o="";if("transparent"!==r&&("string"!==e.type(r)||(s=i(r)))){if(r=u(s||r),!m.rgba&&1!==r._rgba[3]){for(n="backgroundColor"===a?t.parentNode:t;(""===o||"transparent"===o)&&n&&n.style;)try{o=e.css(n,"backgroundColor"),n=n.parentNode}catch(d){}r=r.blend(o&&"transparent"!==o?o:"_default")}r=r.toRgbaString()}try{t.style[a]=r}catch(d){}}},e.fx.step[a]=function(t){t.colorInit||(t.start=u(t.elem,a),t.end=u(t.end),t.colorInit=!0),e.cssHooks[a].set(t.elem,t.start.transition(t.end,t.pos))}})},u.hook(n),e.cssHooks.borderColor={expand:function(e){var t={};return p(["Top","Right","Bottom","Left"],function(a,i){t["border"+i+"Color"]=e}),t}},s=e.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(jQuery),function(){function t(t){var a,i,r=t.ownerDocument.defaultView?t.ownerDocument.defaultView.getComputedStyle(t,null):t.currentStyle,s={};if(r&&r.length&&r[0]&&r[r[0]])for(i=r.length;i--;)a=r[i],"string"==typeof r[a]&&(s[e.camelCase(a)]=r[a]);else for(a in r)"string"==typeof r[a]&&(s[a]=r[a]);return s}function a(t,a){var i,s,n={};for(i in a)s=a[i],t[i]!==s&&(r[i]||(e.fx.step[i]||!isNaN(parseFloat(s)))&&(n[i]=s));return n}var i=["add","remove","toggle"],r={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};e.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(t,a){e.fx.step[a]=function(e){("none"!==e.end&&!e.setAttr||1===e.pos&&!e.setAttr)&&(jQuery.style(e.elem,a,e.end),e.setAttr=!0)}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e.effects.animateClass=function(r,s,n,o){var d=e.speed(s,n,o);return this.queue(function(){var s,n=e(this),o=n.attr("class")||"",u=d.children?n.find("*").addBack():n;u=u.map(function(){var a=e(this);return{el:a,start:t(this)}}),s=function(){e.each(i,function(e,t){r[t]&&n[t+"Class"](r[t])})},s(),u=u.map(function(){return this.end=t(this.el[0]),this.diff=a(this.start,this.end),this}),n.attr("class",o),u=u.map(function(){var t=this,a=e.Deferred(),i=e.extend({},d,{queue:!1,complete:function(){a.resolve(t)}});return this.el.animate(this.diff,i),a.promise()}),e.when.apply(e,u.get()).done(function(){s(),e.each(arguments,function(){var t=this.el;e.each(this.diff,function(e){t.css(e,"")})}),d.complete.call(n[0])})})},e.fn.extend({addClass:function(t){return function(a,i,r,s){return i?e.effects.animateClass.call(this,{add:a},i,r,s):t.apply(this,arguments)}}(e.fn.addClass),removeClass:function(t){return function(a,i,r,s){return arguments.length>1?e.effects.animateClass.call(this,{remove:a},i,r,s):t.apply(this,arguments)}}(e.fn.removeClass),toggleClass:function(t){return function(a,i,r,s,n){return"boolean"==typeof i||void 0===i?r?e.effects.animateClass.call(this,i?{add:a}:{remove:a},r,s,n):t.apply(this,arguments):e.effects.animateClass.call(this,{toggle:a},i,r,s)}}(e.fn.toggleClass),switchClass:function(t,a,i,r,s){return e.effects.animateClass.call(this,{add:a,remove:t},i,r,s)}})}(),function(){function t(t,a,i,r){return e.isPlainObject(t)&&(a=t,t=t.effect),t={effect:t},null==a&&(a={}),e.isFunction(a)&&(r=a,i=null,a={}),("number"==typeof a||e.fx.speeds[a])&&(r=i,i=a,a={}),e.isFunction(i)&&(r=i,i=null),a&&e.extend(t,a),i=i||a.duration,t.duration=e.fx.off?0:"number"==typeof i?i:i in e.fx.speeds?e.fx.speeds[i]:e.fx.speeds._default,t.complete=r||a.complete,t}function a(t){return!t||"number"==typeof t||e.fx.speeds[t]?!0:"string"!=typeof t||e.effects.effect[t]?e.isFunction(t)?!0:"object"!=typeof t||t.effect?!1:!0:!0}e.extend(e.effects,{version:"1.11.0-beta.2",save:function(e,t){for(var a=0;t.length>a;a++)null!==t[a]&&e.data(y+t[a],e[0].style[t[a]])},restore:function(e,t){var a,i;for(i=0;t.length>i;i++)null!==t[i]&&(a=e.data(y+t[i]),void 0===a&&(a=""),e.css(t[i],a))},setMode:function(e,t){return"toggle"===t&&(t=e.is(":hidden")?"show":"hide"),t},getBaseline:function(e,t){var a,i;switch(e[0]){case"top":a=0;break;case"middle":a=.5;break;case"bottom":a=1;break;default:a=e[0]/t.height}switch(e[1]){case"left":i=0;break;case"center":i=.5;break;case"right":i=1;break;default:i=e[1]/t.width}return{x:i,y:a}},createWrapper:function(t){if(t.parent().is(".ui-effects-wrapper"))return t.parent();var a={width:t.outerWidth(!0),height:t.outerHeight(!0),"float":t.css("float")},i=e("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),r={width:t.width(),height:t.height()},s=document.activeElement;try{s.id}catch(n){s=document.body}return t.wrap(i),(t[0]===s||e.contains(t[0],s))&&e(s).focus(),i=t.parent(),"static"===t.css("position")?(i.css({position:"relative"}),t.css({position:"relative"})):(e.extend(a,{position:t.css("position"),zIndex:t.css("z-index")}),e.each(["top","left","bottom","right"],function(e,i){a[i]=t.css(i),isNaN(parseInt(a[i],10))&&(a[i]="auto")}),t.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),t.css(r),i.css(a).show()},removeWrapper:function(t){var a=document.activeElement;return t.parent().is(".ui-effects-wrapper")&&(t.parent().replaceWith(t),(t[0]===a||e.contains(t[0],a))&&e(a).focus()),t},setTransition:function(t,a,i,r){return r=r||{},e.each(a,function(e,a){var s=t.cssUnit(a);s[0]>0&&(r[a]=s[0]*i+s[1])}),r}}),e.fn.extend({effect:function(){function a(t){function a(){e.isFunction(s)&&s.call(r[0]),e.isFunction(t)&&t()}var r=e(this),s=i.complete,o=i.mode;(r.is(":hidden")?"hide"===o:"show"===o)?(r[o](),a()):n.call(r[0],i,a)}var i=t.apply(this,arguments),r=i.mode,s=i.queue,n=e.effects.effect[i.effect];return e.fx.off||!n?r?this[r](i.duration,i.complete):this.each(function(){i.complete&&i.complete.call(this)}):s===!1?this.each(a):this.queue(s||"fx",a)},show:function(e){return function(i){if(a(i))return e.apply(this,arguments);var r=t.apply(this,arguments);return r.mode="show",this.effect.call(this,r)}}(e.fn.show),hide:function(e){return function(i){if(a(i))return e.apply(this,arguments);var r=t.apply(this,arguments);return r.mode="hide",this.effect.call(this,r)}}(e.fn.hide),toggle:function(e){return function(i){if(a(i)||"boolean"==typeof i)return e.apply(this,arguments);var r=t.apply(this,arguments);return r.mode="toggle",this.effect.call(this,r)}}(e.fn.toggle),cssUnit:function(t){var a=this.css(t),i=[];return e.each(["em","px","%","pt"],function(e,t){a.indexOf(t)>0&&(i=[parseFloat(a),t])}),i}})}(),function(){var t={};e.each(["Quad","Cubic","Quart","Quint","Expo"],function(e,a){t[a]=function(t){return Math.pow(t,e+2)}}),e.extend(t,{Sine:function(e){return 1-Math.cos(e*Math.PI/2)},Circ:function(e){return 1-Math.sqrt(1-e*e)},Elastic:function(e){return 0===e||1===e?e:-Math.pow(2,8*(e-1))*Math.sin((80*(e-1)-7.5)*Math.PI/15)},Back:function(e){return e*e*(3*e-2)},Bounce:function(e){for(var t,a=4;((t=Math.pow(2,--a))-1)/11>e;);return 1/Math.pow(4,3-a)-7.5625*Math.pow((3*t-2)/22-e,2)}}),e.each(t,function(t,a){e.easing["easeIn"+t]=a,e.easing["easeOut"+t]=function(e){return 1-a(1-e)},e.easing["easeInOut"+t]=function(e){return.5>e?a(2*e)/2:1-a(-2*e+2)/2}})}(),e.effects,e.effects.effect.blind=function(t,a){var i,r,s,n=e(this),o=/up|down|vertical/,d=/up|left|vertical|horizontal/,u=["position","top","bottom","left","right","height","width"],h=e.effects.setMode(n,t.mode||"hide"),l=t.direction||"up",m=o.test(l),c=m?"height":"width",p=m?"top":"left",f=d.test(l),g={},y="show"===h;
+n.parent().is(".ui-effects-wrapper")?e.effects.save(n.parent(),u):e.effects.save(n,u),n.show(),i=e.effects.createWrapper(n).css({overflow:"hidden"}),r=i[c](),s=parseFloat(i.css(p))||0,g[c]=y?r:0,f||(n.css(m?"bottom":"right",0).css(m?"top":"left","auto").css({position:"absolute"}),g[p]=y?s:r+s),y&&(i.css(c,0),f||i.css(p,s+r)),i.animate(g,{duration:t.duration,easing:t.easing,queue:!1,complete:function(){"hide"===h&&n.hide(),e.effects.restore(n,u),e.effects.removeWrapper(n),a()}})},e.effects.effect.bounce=function(t,a){var i,r,s,n=e(this),o=["position","top","bottom","left","right","height","width"],d=e.effects.setMode(n,t.mode||"effect"),u="hide"===d,h="show"===d,l=t.direction||"up",m=t.distance,c=t.times||5,p=2*c+(h||u?1:0),f=t.duration/p,g=t.easing,y="up"===l||"down"===l?"top":"left",x="up"===l||"left"===l,v=n.queue(),T=v.length;for((h||u)&&o.push("opacity"),e.effects.save(n,o),n.show(),e.effects.createWrapper(n),m||(m=n["top"===y?"outerHeight":"outerWidth"]()/3),h&&(s={opacity:1},s[y]=0,n.css("opacity",0).css(y,x?2*-m:2*m).animate(s,f,g)),u&&(m/=Math.pow(2,c-1)),s={},s[y]=0,i=0;c>i;i++)r={},r[y]=(x?"-=":"+=")+m,n.animate(r,f,g).animate(s,f,g),m=u?2*m:m/2;u&&(r={opacity:0},r[y]=(x?"-=":"+=")+m,n.animate(r,f,g)),n.queue(function(){u&&n.hide(),e.effects.restore(n,o),e.effects.removeWrapper(n),a()}),T>1&&v.splice.apply(v,[1,0].concat(v.splice(T,p+1))),n.dequeue()},e.effects.effect.clip=function(t,a){var i,r,s,n=e(this),o=["position","top","bottom","left","right","height","width"],d=e.effects.setMode(n,t.mode||"hide"),u="show"===d,h=t.direction||"vertical",l="vertical"===h,m=l?"height":"width",c=l?"top":"left",p={};e.effects.save(n,o),n.show(),i=e.effects.createWrapper(n).css({overflow:"hidden"}),r="IMG"===n[0].tagName?i:n,s=r[m](),u&&(r.css(m,0),r.css(c,s/2)),p[m]=u?s:0,p[c]=u?0:s/2,r.animate(p,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){u||n.hide(),e.effects.restore(n,o),e.effects.removeWrapper(n),a()}})},e.effects.effect.drop=function(t,a){var i,r=e(this),s=["position","top","bottom","left","right","opacity","height","width"],n=e.effects.setMode(r,t.mode||"hide"),o="show"===n,d=t.direction||"left",u="up"===d||"down"===d?"top":"left",h="up"===d||"left"===d?"pos":"neg",l={opacity:o?1:0};e.effects.save(r,s),r.show(),e.effects.createWrapper(r),i=t.distance||r["top"===u?"outerHeight":"outerWidth"](!0)/2,o&&r.css("opacity",0).css(u,"pos"===h?-i:i),l[u]=(o?"pos"===h?"+=":"-=":"pos"===h?"-=":"+=")+i,r.animate(l,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===n&&r.hide(),e.effects.restore(r,s),e.effects.removeWrapper(r),a()}})},e.effects.effect.explode=function(t,a){function i(){v.push(this),v.length===l*m&&r()}function r(){c.css({visibility:"visible"}),e(v).remove(),f||c.hide(),a()}var s,n,o,d,u,h,l=t.pieces?Math.round(Math.sqrt(t.pieces)):3,m=l,c=e(this),p=e.effects.setMode(c,t.mode||"hide"),f="show"===p,g=c.show().css("visibility","hidden").offset(),y=Math.ceil(c.outerWidth()/m),x=Math.ceil(c.outerHeight()/l),v=[];for(s=0;l>s;s++)for(d=g.top+s*x,h=s-(l-1)/2,n=0;m>n;n++)o=g.left+n*y,u=n-(m-1)/2,c.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-n*y,top:-s*x}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:y,height:x,left:o+(f?u*y:0),top:d+(f?h*x:0),opacity:f?0:1}).animate({left:o+(f?0:u*y),top:d+(f?0:h*x),opacity:f?1:0},t.duration||500,t.easing,i)},e.effects.effect.fade=function(t,a){var i=e(this),r=e.effects.setMode(i,t.mode||"toggle");i.animate({opacity:r},{queue:!1,duration:t.duration,easing:t.easing,complete:a})},e.effects.effect.fold=function(t,a){var i,r,s=e(this),n=["position","top","bottom","left","right","height","width"],o=e.effects.setMode(s,t.mode||"hide"),d="show"===o,u="hide"===o,h=t.size||15,l=/([0-9]+)%/.exec(h),m=!!t.horizFirst,c=d!==m,p=c?["width","height"]:["height","width"],f=t.duration/2,g={},y={};e.effects.save(s,n),s.show(),i=e.effects.createWrapper(s).css({overflow:"hidden"}),r=c?[i.width(),i.height()]:[i.height(),i.width()],l&&(h=parseInt(l[1],10)/100*r[u?0:1]),d&&i.css(m?{height:0,width:h}:{height:h,width:0}),g[p[0]]=d?r[0]:h,y[p[1]]=d?r[1]:0,i.animate(g,f,t.easing).animate(y,f,t.easing,function(){u&&s.hide(),e.effects.restore(s,n),e.effects.removeWrapper(s),a()})},e.effects.effect.highlight=function(t,a){var i=e(this),r=["backgroundImage","backgroundColor","opacity"],s=e.effects.setMode(i,t.mode||"show"),n={backgroundColor:i.css("backgroundColor")};"hide"===s&&(n.opacity=0),e.effects.save(i,r),i.show().css({backgroundImage:"none",backgroundColor:t.color||"#ffff99"}).animate(n,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===s&&i.hide(),e.effects.restore(i,r),a()}})},e.effects.effect.size=function(t,a){var i,r,s,n=e(this),o=["position","top","bottom","left","right","width","height","overflow","opacity"],d=["position","top","bottom","left","right","overflow","opacity"],u=["width","height","overflow"],h=["fontSize"],l=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],m=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],c=e.effects.setMode(n,t.mode||"effect"),p=t.restore||"effect"!==c,f=t.scale||"both",g=t.origin||["middle","center"],y=n.css("position"),x=p?o:d,v={height:0,width:0,outerHeight:0,outerWidth:0};"show"===c&&n.show(),i={height:n.height(),width:n.width(),outerHeight:n.outerHeight(),outerWidth:n.outerWidth()},"toggle"===t.mode&&"show"===c?(n.from=t.to||v,n.to=t.from||i):(n.from=t.from||("show"===c?v:i),n.to=t.to||("hide"===c?v:i)),s={from:{y:n.from.height/i.height,x:n.from.width/i.width},to:{y:n.to.height/i.height,x:n.to.width/i.width}},("box"===f||"both"===f)&&(s.from.y!==s.to.y&&(x=x.concat(l),n.from=e.effects.setTransition(n,l,s.from.y,n.from),n.to=e.effects.setTransition(n,l,s.to.y,n.to)),s.from.x!==s.to.x&&(x=x.concat(m),n.from=e.effects.setTransition(n,m,s.from.x,n.from),n.to=e.effects.setTransition(n,m,s.to.x,n.to))),("content"===f||"both"===f)&&s.from.y!==s.to.y&&(x=x.concat(h).concat(u),n.from=e.effects.setTransition(n,h,s.from.y,n.from),n.to=e.effects.setTransition(n,h,s.to.y,n.to)),e.effects.save(n,x),n.show(),e.effects.createWrapper(n),n.css("overflow","hidden").css(n.from),g&&(r=e.effects.getBaseline(g,i),n.from.top=(i.outerHeight-n.outerHeight())*r.y,n.from.left=(i.outerWidth-n.outerWidth())*r.x,n.to.top=(i.outerHeight-n.to.outerHeight)*r.y,n.to.left=(i.outerWidth-n.to.outerWidth)*r.x),n.css(n.from),("content"===f||"both"===f)&&(l=l.concat(["marginTop","marginBottom"]).concat(h),m=m.concat(["marginLeft","marginRight"]),u=o.concat(l).concat(m),n.find("*[width]").each(function(){var a=e(this),i={height:a.height(),width:a.width(),outerHeight:a.outerHeight(),outerWidth:a.outerWidth()};p&&e.effects.save(a,u),a.from={height:i.height*s.from.y,width:i.width*s.from.x,outerHeight:i.outerHeight*s.from.y,outerWidth:i.outerWidth*s.from.x},a.to={height:i.height*s.to.y,width:i.width*s.to.x,outerHeight:i.height*s.to.y,outerWidth:i.width*s.to.x},s.from.y!==s.to.y&&(a.from=e.effects.setTransition(a,l,s.from.y,a.from),a.to=e.effects.setTransition(a,l,s.to.y,a.to)),s.from.x!==s.to.x&&(a.from=e.effects.setTransition(a,m,s.from.x,a.from),a.to=e.effects.setTransition(a,m,s.to.x,a.to)),a.css(a.from),a.animate(a.to,t.duration,t.easing,function(){p&&e.effects.restore(a,u)})})),n.animate(n.to,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){0===n.to.opacity&&n.css("opacity",n.from.opacity),"hide"===c&&n.hide(),e.effects.restore(n,x),p||("static"===y?n.css({position:"relative",top:n.to.top,left:n.to.left}):e.each(["top","left"],function(e,t){n.css(t,function(t,a){var i=parseInt(a,10),r=e?n.to.left:n.to.top;return"auto"===a?r+"px":i+r+"px"})})),e.effects.removeWrapper(n),a()}})},e.effects.effect.scale=function(t,a){var i=e(this),r=e.extend(!0,{},t),s=e.effects.setMode(i,t.mode||"effect"),n=parseInt(t.percent,10)||(0===parseInt(t.percent,10)?0:"hide"===s?0:100),o=t.direction||"both",d=t.origin,u={height:i.height(),width:i.width(),outerHeight:i.outerHeight(),outerWidth:i.outerWidth()},h={y:"horizontal"!==o?n/100:1,x:"vertical"!==o?n/100:1};r.effect="size",r.queue=!1,r.complete=a,"effect"!==s&&(r.origin=d||["middle","center"],r.restore=!0),r.from=t.from||("show"===s?{height:0,width:0,outerHeight:0,outerWidth:0}:u),r.to={height:u.height*h.y,width:u.width*h.x,outerHeight:u.outerHeight*h.y,outerWidth:u.outerWidth*h.x},r.fade&&("show"===s&&(r.from.opacity=0,r.to.opacity=1),"hide"===s&&(r.from.opacity=1,r.to.opacity=0)),i.effect(r)},e.effects.effect.puff=function(t,a){var i=e(this),r=e.effects.setMode(i,t.mode||"hide"),s="hide"===r,n=parseInt(t.percent,10)||150,o=n/100,d={height:i.height(),width:i.width(),outerHeight:i.outerHeight(),outerWidth:i.outerWidth()};e.extend(t,{effect:"scale",queue:!1,fade:!0,mode:r,complete:a,percent:s?n:100,from:s?d:{height:d.height*o,width:d.width*o,outerHeight:d.outerHeight*o,outerWidth:d.outerWidth*o}}),i.effect(t)},e.effects.effect.pulsate=function(t,a){var i,r=e(this),s=e.effects.setMode(r,t.mode||"show"),n="show"===s,o="hide"===s,d=n||"hide"===s,u=2*(t.times||5)+(d?1:0),h=t.duration/u,l=0,m=r.queue(),c=m.length;for((n||!r.is(":visible"))&&(r.css("opacity",0).show(),l=1),i=1;u>i;i++)r.animate({opacity:l},h,t.easing),l=1-l;r.animate({opacity:l},h,t.easing),r.queue(function(){o&&r.hide(),a()}),c>1&&m.splice.apply(m,[1,0].concat(m.splice(c,u+1))),r.dequeue()},e.effects.effect.shake=function(t,a){var i,r=e(this),s=["position","top","bottom","left","right","height","width"],n=e.effects.setMode(r,t.mode||"effect"),o=t.direction||"left",d=t.distance||20,u=t.times||3,h=2*u+1,l=Math.round(t.duration/h),m="up"===o||"down"===o?"top":"left",c="up"===o||"left"===o,p={},f={},g={},y=r.queue(),x=y.length;for(e.effects.save(r,s),r.show(),e.effects.createWrapper(r),p[m]=(c?"-=":"+=")+d,f[m]=(c?"+=":"-=")+2*d,g[m]=(c?"-=":"+=")+2*d,r.animate(p,l,t.easing),i=1;u>i;i++)r.animate(f,l,t.easing).animate(g,l,t.easing);r.animate(f,l,t.easing).animate(p,l/2,t.easing).queue(function(){"hide"===n&&r.hide(),e.effects.restore(r,s),e.effects.removeWrapper(r),a()}),x>1&&y.splice.apply(y,[1,0].concat(y.splice(x,h+1))),r.dequeue()},e.effects.effect.slide=function(t,a){var i,r=e(this),s=["position","top","bottom","left","right","width","height"],n=e.effects.setMode(r,t.mode||"show"),o="show"===n,d=t.direction||"left",u="up"===d||"down"===d?"top":"left",h="up"===d||"left"===d,l={};e.effects.save(r,s),r.show(),i=t.distance||r["top"===u?"outerHeight":"outerWidth"](!0),e.effects.createWrapper(r).css({overflow:"hidden"}),o&&r.css(u,h?isNaN(i)?"-"+i:-i:i),l[u]=(o?h?"+=":"-=":h?"-=":"+=")+i,r.animate(l,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===n&&r.hide(),e.effects.restore(r,s),e.effects.removeWrapper(r),a()}})},e.effects.effect.transfer=function(t,a){var i=e(this),r=e(t.to),s="fixed"===r.css("position"),n=e("body"),o=s?n.scrollTop():0,d=s?n.scrollLeft():0,u=r.offset(),h={top:u.top-o,left:u.left-d,height:r.innerHeight(),width:r.innerWidth()},l=i.offset(),m=e("<div class='ui-effects-transfer'></div>").appendTo(document.body).addClass(t.className).css({top:l.top-o,left:l.left-d,height:i.innerHeight(),width:i.innerWidth(),position:s?"fixed":"absolute"}).animate(h,t.duration,t.easing,function(){m.remove(),a()})}});
\ No newline at end of file
--- /dev/null
+//constructs a table of mount points via a json response
+//Loads the table into the given dom.
+var loadMountList = function( dom ) {
+ dom.empty();
+ dom.append( "<p>Loading. Please wait...</p>" );
+ $.ajax( {
+ url: "/apidoc/apis/mounts",
+ datatype: 'jsonp',
+ success: function( strData ){
+ var myData = strData;
+ var list = $( "<table></table>" );
+ for( var key in myData )
+ {
+ list.append( "<tr><td><a href=\"#\" onclick=\"loadMount(" +
+ myData[key].id + ", '" + myData[key].instance + "')\">" +
+ myData[key].instance + "</a></td></tr>");
+ }
+ dom.empty();
+ dom.append( list );
+ }
+ } );
+}
\ No newline at end of file
--- /dev/null
+
+//There can only be a single swagger UI in a dom tree
+//at a time - this is a limitation of the swagger API
+//This method takes a URL which is the root of the swagger
+//documentation, as well as the ID of the dom where we should
+//load the swagger UI.
+//See the swagger documentation for more information.
+var loadSwagger = function(url, dom_id) {
+ $("#" + dom_id).empty();
+ $("#" + dom_id).append("Loading " + url);
+ window.swaggerUi = new SwaggerUi({
+ url : url,
+ dom_id : dom_id,
+ supportedSubmitMethods : [ 'get', 'post', 'put', 'delete' ],
+ onComplete : function(swaggerApi, swaggerUi) {
+ if (console) {
+ console.log("Loaded SwaggerUI")
+ }
+ $('pre code').each(function(i, e) {
+ hljs.highlightBlock(e)
+ });
+ },
+ onFailure : function(data) {
+ if (console) {
+ console.log("Unable to Load SwaggerUI");
+ console.log(data);
+ }
+ },
+ docExpansion : "none"
+ });
+
+ $('#input_apiKey').change(
+ function() {
+ var key = $('#input_apiKey')[0].value;
+ console.log("key: " + key);
+ if (key && key.trim() != "") {
+ console.log("added key " + key);
+ window.authorizations
+ .add("key", new ApiKeyAuthorization("api_key",
+ key, "query"));
+ }
+ });
+ window.swaggerUi.load();
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.sal.rest.doc.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.ws.rs.core.UriInfo;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+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.Resource;
+import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+import com.google.common.base.Preconditions;
+
+/**
+ *
+ */
+public class ApiDocGeneratorTest {
+
+ public static final String HTTP_HOST = "http://host";
+ private ApiDocGenerator generator;
+ private DocGenTestHelper helper;
+
+ @Before
+ public void setUp() throws Exception {
+ generator = new ApiDocGenerator();
+ helper = new DocGenTestHelper();
+ helper.setUp();
+ }
+
+ @After
+ public void after() throws Exception {
+ }
+
+ /**
+ * Method: getApiDeclaration(String module, String revision, UriInfo
+ * uriInfo)
+ */
+ @Test
+ public void testGetModuleDoc() throws Exception {
+ Preconditions.checkArgument(helper.getModules() != null, "No modules found");
+
+ for (Entry<File, Module> m : helper.getModules().entrySet()) {
+ if (m.getKey().getAbsolutePath().endsWith("toaster_short.yang")) {
+ ApiDeclaration doc = generator.getSwaggerDocSpec(m.getValue(),
+ "http://localhost:8080/restconf", "");
+ validateToaster(doc);
+ Assert.assertNotNull(doc);
+ }
+ }
+ }
+
+ private void validateToaster(ApiDeclaration doc) throws Exception {
+ Set<String> 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<String> actualUrls = new TreeSet<>();
+
+ Api configApi = null;
+ for (Api api : doc.getApis()) {
+ actualUrls.add(api.getPath());
+ if (api.getPath().contains("/config/toaster2:toaster/")) {
+ configApi = api;
+ }
+ }
+
+ boolean containsAll = actualUrls.containsAll(expectedUrls);
+ if (!containsAll) {
+ expectedUrls.removeAll(actualUrls);
+ fail("Missing expected urls: " + expectedUrls);
+ }
+
+ Set<String> expectedConfigMethods = new TreeSet<>(Arrays.asList(new String[] { "GET",
+ "PUT", "DELETE" }));
+ Set<String> actualConfigMethods = new TreeSet<>();
+ for (Operation oper : configApi.getOperations()) {
+ actualConfigMethods.add(oper.getMethod());
+ }
+
+ containsAll = actualConfigMethods.containsAll(expectedConfigMethods);
+ if (!containsAll) {
+ expectedConfigMethods.removeAll(actualConfigMethods);
+ fail("Missing expected method on config API: " + expectedConfigMethods);
+ }
+ // TODO: we should really do some more validation of the
+ // documentation...
+ /**
+ * Missing validation: Explicit validation of URLs, and their methods
+ * Input / output models.
+ */
+ }
+
+ @Test
+ public void testGetResourceListing() throws Exception {
+ UriInfo info = helper.createMockUriInfo(HTTP_HOST);
+ SchemaService mockSchemaService = helper.createMockSchemaService();
+
+ generator.setSchemaService(mockSchemaService);
+
+ ResourceList resourceListing = generator.getResourceListing(info);
+
+ Resource toaster = null;
+ Resource toaster2 = null;
+ for (Resource r : resourceListing.getApis()) {
+ String path = r.getPath();
+ if (path.contains("toaster2")) {
+ toaster2 = r;
+ } else if (path.contains("toaster")) {
+ toaster = r;
+ }
+ }
+
+ assertNotNull(toaster2);
+ assertNotNull(toaster);
+
+ assertEquals(HTTP_HOST + "/toaster(2009-11-20)", toaster.getPath());
+ assertEquals(HTTP_HOST + "/toaster2(2009-11-20)", toaster2.getPath());
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.rest.doc.impl;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
+
+public class DocGenTestHelper {
+
+ private Map<File, Module> modules;
+ private ObjectMapper mapper;
+
+ public Map<File, Module> loadModules(String resourceDirectory) throws FileNotFoundException,
+ URISyntaxException {
+
+ URI resourceDirUri = getClass().getResource(resourceDirectory).toURI();
+ final YangModelParser parser = new YangParserImpl();
+ final File testDir = new File(resourceDirUri);
+ final String[] fileList = testDir.list();
+ final List<File> testFiles = new ArrayList<>();
+ if (fileList == null) {
+ throw new FileNotFoundException(resourceDirectory.toString());
+ }
+ for (String fileName : fileList) {
+
+ testFiles.add(new File(testDir, fileName));
+ }
+ return parser.parseYangModelsMapped(testFiles);
+ }
+
+ public Map<File, Module> getModules() {
+ return modules;
+ }
+
+ public void setUp() throws Exception {
+ modules = loadModules("/yang");
+ mapper = new ObjectMapper();
+ mapper.registerModule(new JsonOrgModule());
+ mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
+ }
+
+ public SchemaService createMockSchemaService() {
+ return createMockSchemaService(null);
+ }
+
+ public SchemaService createMockSchemaService(SchemaContext mockContext) {
+ if (mockContext == null) {
+ mockContext = createMockSchemaContext();
+ }
+
+ SchemaService mockSchemaService = mock(SchemaService.class);
+ when(mockSchemaService.getGlobalContext()).thenReturn(mockContext);
+ return mockSchemaService;
+ }
+
+ public SchemaContext createMockSchemaContext() {
+ SchemaContext mockContext = mock(SchemaContext.class);
+ when(mockContext.getModules()).thenReturn(new HashSet<Module>(modules.values()));
+
+ final ArgumentCaptor<String> moduleCapture = ArgumentCaptor.forClass(String.class);
+ final ArgumentCaptor<Date> dateCapture = ArgumentCaptor.forClass(Date.class);
+ when(mockContext.findModuleByName(moduleCapture.capture(), dateCapture.capture())).then(
+ new Answer<Module>() {
+ @Override
+ public Module answer(InvocationOnMock invocation) throws Throwable {
+ String module = moduleCapture.getValue();
+ Date date = dateCapture.getValue();
+ for (Module m : modules.values()) {
+ if (m.getName().equals(module) && m.getRevision().equals(date)) {
+ return m;
+ }
+ }
+ return null;
+ }
+ });
+ return mockContext;
+ }
+
+ public UriInfo createMockUriInfo(String urlPrefix) throws URISyntaxException {
+ final URI uri = new URI(urlPrefix);
+
+ UriBuilder mockBuilder = mock(UriBuilder.class);
+
+ final ArgumentCaptor<String> subStringCapture = ArgumentCaptor.forClass(String.class);
+ when(mockBuilder.path(subStringCapture.capture())).thenReturn(mockBuilder);
+ when(mockBuilder.build()).then(new Answer<URI>() {
+ @Override
+ public URI answer(InvocationOnMock invocation) throws Throwable {
+ return URI.create(uri + "/" + subStringCapture.getValue());
+ }
+ });
+
+ UriInfo info = mock(UriInfo.class);
+
+ when(info.getRequestUriBuilder()).thenReturn(mockBuilder);
+ when(info.getBaseUri()).thenReturn(uri);
+ return info;
+ }
+
+}
+++ /dev/null
-package org.opendaylight.controller.sal.rest.doc.impl;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
-import com.google.common.base.Preconditions;
-import junit.framework.Assert;
-import org.junit.Test;
-import org.junit.Before;
-import org.junit.After;
-import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/**
- *
- */
-public class DocGeneratorTest {
-
- private Set<Module> modules;
- private ObjectMapper mapper;
-
- public Set<Module> loadModules(String resourceDirectory) throws FileNotFoundException, URISyntaxException {
-
- URI resourceDirUri = getClass().getResource(resourceDirectory).toURI();
- final YangModelParser parser = new YangParserImpl();
- final File testDir = new File(resourceDirUri);
- final String[] fileList = testDir.list();
- final List<File> testFiles = new ArrayList<>();
- if (fileList == null) {
- throw new FileNotFoundException(resourceDirectory.toString());
- }
- for (String fileName : fileList) {
-
- testFiles.add(new File(testDir, fileName));
- }
- return parser.parseYangModels(testFiles);
- }
-
- @Before
- public void before() throws Exception {
- modules = loadModules("/yang");
- mapper = new ObjectMapper();
- mapper.registerModule(new JsonOrgModule());
- }
-
- @After
- public void after() throws Exception {
- }
-
- /**
- * Method: getApiDeclaration(String module, String revision, UriInfo uriInfo)
- */
- @Test
- public void testGetModuleDoc() throws Exception {
- Preconditions.checkArgument(modules != null, "No modules found");
-
- for (Module m : modules){
- ApiDeclaration doc = ApiDocGenerator.getInstance().getSwaggerDocSpec(m, "http://localhost:8080/restconf");
- Assert.assertNotNull(doc);
- }
- }
-
-}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.rest.doc.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.ws.rs.core.UriInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.controller.sal.rest.doc.mountpoints.MountPointSwagger;
+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.Resource;
+import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class MountPointSwaggerTest {
+
+ private static final String HTTP_URL = "http://localhost/path";
+ private static final InstanceIdentifier instanceId = InstanceIdentifier.builder()
+ .node(QName.create("nodes"))
+ .nodeWithKey(QName.create("node"), QName.create("id"), "123").build();
+ private static final String INSTANCE_URL = "nodes/node/123/";
+ private MountPointSwagger swagger;
+ private DocGenTestHelper helper;
+
+ @Before
+ public void setUp() throws Exception {
+ swagger = new MountPointSwagger();
+ helper = new DocGenTestHelper();
+ helper.setUp();
+ }
+
+ @Test()
+ public void testGetResourceListBadIid() throws Exception {
+ UriInfo mockInfo = helper.createMockUriInfo(HTTP_URL);
+
+ assertEquals(null, swagger.getResourceList(mockInfo, 1L));
+ }
+
+ @Test()
+ public void getInstanceIdentifiers() throws Exception {
+ UriInfo mockInfo = setUpSwaggerForDocGeneration();
+
+ assertEquals(0, swagger.getInstanceIdentifiers().size());
+ swagger.onMountPointCreated(instanceId); // add this ID into the list of
+ // mount points
+ assertEquals(1, swagger.getInstanceIdentifiers().size());
+ assertEquals((Long) 1L, swagger.getInstanceIdentifiers().entrySet().iterator().next()
+ .getValue());
+ assertEquals(INSTANCE_URL, swagger.getInstanceIdentifiers().entrySet().iterator().next()
+ .getKey());
+ swagger.onMountPointRemoved(instanceId); // remove ID from list of mount
+ // points
+ assertEquals(0, swagger.getInstanceIdentifiers().size());
+ }
+
+ @Test
+ public void testGetResourceListGoodId() throws Exception {
+ UriInfo mockInfo = setUpSwaggerForDocGeneration();
+ swagger.onMountPointCreated(instanceId); // add this ID into the list of
+ // mount points
+ ResourceList resourceList = swagger.getResourceList(mockInfo, 1L);
+
+ Resource dataStoreResource = null;
+ for (Resource r : resourceList.getApis()) {
+ if (r.getPath().endsWith("/Datastores(-)")) {
+ dataStoreResource = r;
+ }
+ }
+ assertNotNull("Failed to find data store resource", dataStoreResource);
+ }
+
+ @Test
+ public void testGetDataStoreApi() throws Exception {
+ UriInfo mockInfo = setUpSwaggerForDocGeneration();
+ swagger.onMountPointCreated(instanceId); // add this ID into the list of
+ // mount points
+ ApiDeclaration mountPointApi = swagger.getMountPointApi(mockInfo, 1L, "Datastores", "-");
+ assertNotNull("failed to find Datastore API", mountPointApi);
+ List<Api> apis = mountPointApi.getApis();
+ assertEquals("Unexpected api list size", 3, apis.size());
+
+ Set<String> actualApis = new TreeSet<>();
+ for (Api api : apis) {
+ actualApis.add(api.getPath());
+ List<Operation> operations = api.getOperations();
+ assertEquals("unexpected operation size on " + api.getPath(), 1, operations.size());
+ assertEquals("unexpected operation method " + api.getPath(), "GET", operations.get(0)
+ .getMethod());
+ assertNotNull("expected non-null desc on " + api.getPath(), operations.get(0)
+ .getNotes());
+ }
+ Set<String> expectedApis = new TreeSet<>(Arrays.asList(new String[] {
+ "/config/" + INSTANCE_URL + "yang-ext:mount/",
+ "/operational/" + INSTANCE_URL + "yang-ext:mount/",
+ "/operations/" + INSTANCE_URL + "yang-ext:mount/", }));
+ assertEquals(expectedApis, actualApis);
+ }
+
+ protected UriInfo setUpSwaggerForDocGeneration() throws URISyntaxException {
+ UriInfo mockInfo = helper.createMockUriInfo(HTTP_URL);
+ // We are sharing the global schema service and the mount schema service
+ // in our test.
+ // OK for testing - real thing would have seperate instances.
+ SchemaContext context = helper.createMockSchemaContext();
+ SchemaService schemaService = helper.createMockSchemaService(context);
+
+ MountProvisionInstance mountPoint = mock(MountProvisionInstance.class);
+ when(mountPoint.getSchemaContext()).thenReturn(context);
+
+ MountProvisionService service = mock(MountProvisionService.class);
+ when(service.getMountPoint(instanceId)).thenReturn(mountPoint);
+ swagger.setMountService(service);
+ swagger.setGlobalSchema(schemaService);
+
+ return mockInfo;
+ }
+
+}
--- /dev/null
+ module toaster2 {
+
+ yang-version 1;
+
+ namespace
+ "http://netconfcentral.org/ns/toaster";
+
+ prefix toast;
+
+ organization "Netconf Central";
+
+ contact
+ "Andy Bierman <andy@netconfcentral.org>";
+
+ description
+ "YANG version of the TOASTER-MIB.";
+
+ revision "2009-11-20" {
+ description
+ "Toaster module in progress.";
+ }
+
+
+ identity toast-type {
+ description
+ "Base for all bread types supported by the toaster.
+ New bread types not listed here nay be added in the
+ future.";
+ }
+
+ identity white-bread {
+ base toast:toast-type;
+ description "White bread.";
+ }
+
+ identity wheat-bread {
+ base toast-type;
+ description "Wheat bread.";
+ }
+
+ identity wonder-bread {
+ base toast-type;
+ description "Wonder bread.";
+ }
+
+ identity frozen-waffle {
+ base toast-type;
+ description "Frozen waffle.";
+ }
+
+ identity frozen-bagel {
+ base toast-type;
+ description "Frozen bagel.";
+ }
+
+ identity hash-brown {
+ base toast-type;
+ description "Hash browned potatos.";
+ }
+
+ typedef DisplayString {
+ type string {
+ length "0 .. 255";
+ }
+ description
+ "YANG version of the SMIv2 DisplayString TEXTUAL-CONVENTION.";
+ reference
+ "RFC 2579, section 2.";
+
+ }
+
+ container toaster {
+ presence
+ "Indicates the toaster service is available";
+ description
+ "Top-level container for all toaster database objects.";
+ leaf toasterManufacturer {
+ type DisplayString;
+ config false;
+ mandatory true;
+ description
+ "The name of the toaster's manufacturer. For instance,
+ Microsoft Toaster.";
+ }
+
+ leaf toasterModelNumber {
+ type DisplayString;
+ config false;
+ mandatory true;
+ description
+ "The name of the toaster's model. For instance,
+ Radiant Automatic.";
+ }
+
+ leaf toasterStatus {
+ type enumeration {
+ enum "up" {
+ value 1;
+ description
+ "The toaster knob position is up.
+ No toast is being made now.";
+ }
+ enum "down" {
+ value 2;
+ description
+ "The toaster knob position is down.
+ Toast is being made now.";
+ }
+ }
+ config false;
+ mandatory true;
+ description
+ "This variable indicates the current state of
+ the toaster.";
+ }
+
+ leaf darknessFactor {
+ type uint32;
+ config true;
+ default 1000;
+ description
+ "The darkness factor. Basically, the number of ms to multiple the doneness value by.";
+ }
+ } // container toaster
+
+ rpc make-toast {
+ description
+ "Make some toast.
+ The toastDone notification will be sent when
+ the toast is finished.
+ An 'in-use' error will be returned if toast
+ is already being made.
+ A 'resource-denied' error will be returned
+ if the toaster service is disabled.";
+ input {
+ leaf toasterDoneness {
+ type uint32 {
+ range "1 .. 10";
+ }
+ default '5';
+ description
+ "This variable controls how well-done is the
+ ensuing toast. It should be on a scale of 1 to 10.
+ Toast made at 10 generally is considered unfit
+ for human consumption; toast made at 1 is warmed
+ lightly.";
+ }
+
+ leaf toasterToastType {
+ type identityref {
+ base toast:toast-type;
+ }
+ default 'wheat-bread';
+ description
+ "This variable informs the toaster of the type of
+ material that is being toasted. The toaster
+ uses this information, combined with
+ toasterDoneness, to compute for how
+ long the material must be toasted to achieve
+ the required doneness.";
+ }
+ }
+ } // rpc make-toast
+
+ rpc cancel-toast {
+ description
+ "Stop making toast, if any is being made.
+ A 'resource-denied' error will be returned
+ if the toaster service is disabled.";
+ } // rpc cancel-toast
+
+ rpc restock-toaster {
+ description
+ "Restocks the toaster with the amount of bread specified.";
+
+ input {
+ leaf amountOfBreadToStock {
+ type uint32;
+ description
+ "Indicates the amount of bread to re-stock";
+ }
+ }
+ }
+
+ notification toasterOutOfBread {
+ description
+ "Indicates that the toaster has run of out bread.";
+ } // notification toasterOutOfStock
+
+ notification toasterRestocked {
+ description
+ "Indicates that the toaster has run of out bread.";
+ leaf amountOfBread {
+ type uint32;
+ description
+ "Indicates the amount of bread that was re-stocked";
+ }
+ } // notification toasterOutOfStock
+
+ } // module toaster
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
<artifactId>model-topology</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.samples.l2switch.md</groupId>
<dependency>
<groupId>org.opendaylight.controller.samples</groupId>
<artifactId>sample-toaster</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.samples</groupId>
<artifactId>sample-toaster-consumer</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.samples</groupId>
<artifactId>sample-toaster-provider</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.openexi</groupId>
<dependency>
<groupId>equinoxSDK381</groupId>
<artifactId>org.eclipse.osgi</artifactId>
- <version>3.8.1.v20120830-144521</version>
<scope>test</scope>
</dependency>
<dependency>
<plugin>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>maven-paxexam-plugin</artifactId>
- <version>1.2.4</version>
<executions>
<execution>
<id>generate-config</id>
<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+
<persisted-snapshots>
<snapshots>
<snapshot>
- <required-capabilities>
- <!-- <capability>urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27</capability>-->
- <capability>
- urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
- </capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28</capability>
- <capability>
- urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
- </capability>
- <capability>urn:ietf:params:netconf:capability:candidate:1.0</capability>
- <capability>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04</capability>
- <capability>urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
- </capability>
- <capability>
- urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
- </capability>
- <capability>urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
- </capability>
- <capability>urn:ietf:params:netconf:capability:rollback-on-error:1.0</capability>
- <capability>urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
- </capability>
- <capability>
- urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
- </capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16</capability>
- <!-- <capability>urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09</capability>-->
- <capability>
- urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
- </capability>
- <capability>http://netconfcentral.org/ns/toaster?module=toaster&revision=2009-11-20</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl?module=kitchen-service-impl&revision=2014-01-31</capability>
- <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl?module=toaster-provider-impl&revision=2014-01-31</capability>
-
- </required-capabilities>
<configuration>
-
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
+ <name>yang-schema-service</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
+ <name>runtime-mapping-singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
+ <name>binding-notification-broker</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
+ <name>binding-broker-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+ <name>binding-notification-broker</name>
+ </notification-service>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+ <name>binding-data-broker</name>
+ </data-broker>
+ </module>
+
+ <!--
+ Tree-based in-memory data store. This is the data store which is currently
+ recommended for single-node deployments.
+ -->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-inmemory-data-broker</type>
+ <name>inmemory-data-broker</name>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
+ <name>inmemory-dom-broker</name>
+ <async-data-broker>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <name>inmemory-data-broker</name>
+ </async-data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-compatible-broker</type>
+ <name>inmemory-binding-data-broker</name>
+ <dom-async-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>dom-broker</name>
+ </dom-async-broker>
+ <binding-mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </binding-mapping-service>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-forwarded-data-broker</type>
+ <name>binding-async-data-broker</name>
+ <binding-forwarded-data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <dom-async-broker>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>dom-broker</name>
+ </dom-async-broker>
+ <binding-mapping-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </binding-mapping-service>
+ </binding-forwarded-data-broker>
+ </module>
+
+ <!-- Toaster Congiguration -->
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
prefix:toaster-provider-impl
<data-broker>
<type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
- <name>ref_binding-data-broker</name>
+ <name>binding-data-broker</name>
</data-broker>
<notification-service>
<type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
binding:binding-notification-service
</type>
- <name>ref_binding-notification-broker</name>
+ <name>binding-notification-broker</name>
</notification-service>
</module>
+ <!-- Kitchen Service -->
<module>
<type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
prefix:kitchen-service-impl
<type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
binding:binding-notification-service
</type>
- <name>ref_binding-notification-broker</name>
- </notification-service>
- </module>
-
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
- prefix:schema-service-singleton
- </type>
- <name>yang-schema-service</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
- prefix:hash-map-data-store
- </type>
- <name>hash-map-data-store</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
- prefix:dom-broker-impl
- </type>
- <name>dom-broker</name>
- <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
- dom:dom-data-store
- </type>
- <name>ref_hash-map-data-store</name>
- </data-store>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- prefix:binding-broker-impl
- </type>
- <name>binding-broker-impl</name>
- <notification-service
- xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
- binding:binding-notification-service
- </type>
- <name>ref_binding-notification-broker</name>
+ <name>binding-notification-broker</name>
</notification-service>
- <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
- binding:binding-data-broker
- </type>
- <name>ref_binding-data-broker</name>
- </data-broker>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- prefix:runtime-generated-mapping
- </type>
- <name>runtime-mapping-singleton</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- prefix:binding-notification-broker
- </type>
- <name>binding-notification-broker</name>
- </module>
- <module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- prefix:binding-data-broker
- </type>
- <name>binding-data-broker</name>
- <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
- dom:dom-broker-osgi-registry
- </type>
- <name>ref_dom-broker</name>
- </dom-broker>
- <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- binding:binding-dom-mapping-service
- </type>
- <name>ref_runtime-mapping-singleton</name>
- </mapping-service>
</module>
</modules>
-
<services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
<service>
- <type xmlns:kitchen="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
- kitchen:kitchen-service
- </type>
- <instance>
- <name>kitchen-service</name>
- <provider>/modules/module[type='kitchen-service-impl'][name='kitchen-service-impl']</provider>
- </instance>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <instance>
+ <name>yang-schema-service</name>
+ <provider>/modules/module[type='schema-service-singleton'][name='yang-schema-service']</provider>
+ </instance>
</service>
<service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
- dom:schema-service
- </type>
+ <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
<instance>
- <name>ref_yang-schema-service</name>
- <provider>
- /config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']
- </provider>
+ <name>runtime-mapping-singleton</name>
+ <provider>/modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton']</provider>
</instance>
</service>
<service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
- binding:binding-notification-service
- </type>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
<instance>
- <name>ref_binding-notification-broker</name>
- <provider>
- /config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']
- </provider>
+ <name>binding-notification-broker</name>
+ <provider>/modules/module[type='binding-notification-broker'][name='binding-notification-broker']</provider>
</instance>
</service>
<service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
- dom:dom-data-store
- </type>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
<instance>
- <name>ref_hash-map-data-store</name>
- <provider>
- /config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']
- </provider>
+ <name>binding-osgi-broker</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
</instance>
</service>
<service>
<provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
</instance>
</service>
+
<service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
- binding:binding-broker-osgi-registry
- </type>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
<instance>
- <name>ref_binding-broker-impl</name>
- <provider>
- /config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']
- </provider>
+ <name>dom-broker</name>
+ <provider>/modules/module[type='dom-broker-impl'][name='inmemory-dom-broker']</provider>
</instance>
</service>
+
<service>
- <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
- binding-impl:binding-dom-mapping-service
- </type>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
<instance>
- <name>ref_runtime-mapping-singleton</name>
- <provider>
- /config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']
- </provider>
+ <name>binding-data-broker</name>
+ <provider>/modules/module[type='binding-data-compatible-broker'][name='inmemory-binding-data-broker']</provider>
</instance>
</service>
+
<service>
- <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
- dom:dom-broker-osgi-registry
- </type>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
<instance>
- <name>ref_dom-broker</name>
- <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']
- </provider>
+ <name>binding-data-broker</name>
+ <provider>/modules/module[type='binding-forwarded-data-broker'][name='binding-async-data-broker']</provider>
</instance>
</service>
+
<service>
- <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
- binding:binding-data-broker
- </type>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
<instance>
- <name>ref_binding-data-broker</name>
- <provider>
- /config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']
- </provider>
+ <name>inmemory-data-broker</name>
+ <provider>/modules/module[type='dom-inmemory-data-broker'][name='inmemory-data-broker']</provider>
</instance>
</service>
+
+ <!-- Toaster samples -->
+ <service>
+ <type xmlns:kitchen="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
+ kitchen:kitchen-service
+ </type>
+ <instance>
+ <name>kitchen-service</name>
+ <provider>/modules/module[type='kitchen-service-impl'][name='kitchen-service-impl']</provider>
+ </instance>
+ </service>
</services>
</data>
-
</configuration>
- </snapshot>
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28</capability>
+ <!-- Toaster capabilities -->
+ <capability>http://netconfcentral.org/ns/toaster?module=toaster&revision=2009-11-20</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl?module=kitchen-service-impl&revision=2014-01-31</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl?module=toaster-provider-impl&revision=2014-01-31</capability>
+
+ </required-capabilities>
+ </snapshot>
</snapshots>
</persisted-snapshots>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
- <version>2.4</version>
</dependency>
<dependency>
<groupId>equinoxSDK381</groupId>
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
<artifactId>model-topology</artifactId>
- <version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml;
import com.google.common.base.Preconditions;
+
import java.util.List;
import java.util.Map;
+
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectNameAttributeMappingStrategy;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
public class ObjectNameAttributeReadingStrategy extends AbstractAttributeReadingStrategy {
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectNameAttributeMappingStrategy;
import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
+
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+
import javax.management.ObjectName;
+
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditStrategyType;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+
import javax.management.ObjectName;
import javax.management.openmbean.OpenType;
+
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeReadingStrategy;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.ObjectXmlReader;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditStrategyType;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config;
import com.google.common.base.Optional;
+
import java.util.Date;
import java.util.Map;
+
import javax.management.ObjectName;
+
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditStrategyType;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
+
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.ObjectNameAttributeReadingStrategy;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.management.ObjectName;
+
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
+
import java.util.Collection;
import java.util.Map;
import java.util.Set;
+
import javax.management.ObjectName;
+
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleConfig;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.opendaylight.controller.config.api.jmx.CommitStatus;
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
package org.opendaylight.controller.netconf.confignetconfconnector.operations;
import com.google.common.base.Optional;
+
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
package org.opendaylight.controller.netconf.confignetconfconnector.operations;
import com.google.common.base.Optional;
+
import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
+
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
+
import javax.management.InstanceNotFoundException;
import javax.management.ObjectName;
+
import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.config.util.ConfigTransactionClient;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfigElementResolved;
import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.collect.Multimap;
+
import java.util.Arrays;
import java.util.Map;
+
import org.opendaylight.controller.config.api.ServiceReferenceReadableRegistry;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleElementDefinition;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleElementResolved;
import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
package org.opendaylight.controller.netconf.confignetconfconnector.operations.get;
import com.google.common.collect.Maps;
+
import java.util.List;
import java.util.Map;
import java.util.Set;
+
import javax.management.ObjectName;
+
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleConfig;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime.InstanceRuntime;
import org.opendaylight.controller.netconf.util.exception.UnexpectedElementException;
import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
xml.checkName(XmlNetconfConstants.GET);
xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
- // Filter option - unsupported
- if (xml.getChildElements(XmlNetconfConstants.FILTER).size() != 0){
- throw new UnsupportedOperationException("Unsupported option " + XmlNetconfConstants.FILTER + " for " + XmlNetconfConstants.GET);
- }
+ // Filter option: ignore for now, TODO only load modules specified by the filter
}
@Override
package org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig;
import com.google.common.base.Optional;
+import java.util.Set;
+import javax.management.ObjectName;
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.config.util.ConfigTransactionClient;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ServiceRegistryWrapper;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
import org.opendaylight.controller.netconf.util.exception.UnexpectedElementException;
import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-import javax.management.ObjectName;
-import java.util.Set;
-
public class GetConfig extends AbstractConfigNetconfOperation {
public static final String GET_CONFIG = "get-config";
logger.debug("Setting source datastore to '{}'", sourceParsed);
Datastore sourceDatastore = Datastore.valueOf(sourceParsed);
- // Filter option - unsupported
- if (xml.getChildElements(XmlNetconfConstants.FILTER).size() != 0){
- throw new UnsupportedOperationException("Unsupported option " + XmlNetconfConstants.FILTER + " for "
- + GET_CONFIG);
- }
+ // Filter option: ignore for now, TODO only load modules specified by the filter
return sourceDatastore;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
+
import org.opendaylight.controller.config.util.ConfigRegistryClient;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
import org.opendaylight.controller.config.yangjmxgenerator.attribute.VoidAttribute;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.AttributeMappingStrategy;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectMapper;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.ObjectName;
import javax.management.openmbean.OpenType;
+
import java.util.Map;
public class RuntimeRpc extends AbstractConfigNetconfOperation {
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
+
import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import javax.management.ObjectName;
+
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.ObjectName;
import javax.xml.parsers.ParserConfigurationException;
+
import org.custommonkey.xmlunit.AbstractNodeTester;
import org.custommonkey.xmlunit.NodeTest;
import org.custommonkey.xmlunit.NodeTestException;
import org.opendaylight.controller.config.yang.test.impl.TestImplModuleFactory;
import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges;
import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.test.types.rev131127.TestIdentity1;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.test.types.rev131127.TestIdentity2;
import org.junit.Test;
import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Element;
import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.mapping.api.Capability;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory;
import org.opendaylight.controller.netconf.util.NetconfUtil;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>ietf-netconf-monitoring-extension</artifactId>
- <version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<instructions>
<Private-Package></Private-Package>
<Import-Package>javax.management,
+ javax.xml.parsers,
org.opendaylight.controller.config.api,
org.opendaylight.controller.config.api.jmx,
org.opendaylight.protocol.framework,
com.google.common.base,</Import-Package>
<Export-Package>org.opendaylight.controller.netconf.api,
org.opendaylight.controller.netconf.api.jmx,
+ org.opendaylight.controller.netconf.api.xml,
org.opendaylight.controller.netconf.api.monitoring,</Export-Package>
</instructions>
</configuration>
package org.opendaylight.controller.netconf.api;
-import org.opendaylight.controller.config.api.ConflictingVersionException;
-import org.opendaylight.controller.config.api.ValidationException;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.ERROR_INFO;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.ERROR_MESSAGE;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.ERROR_SEVERITY;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.ERROR_TAG;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.ERROR_TYPE;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.RPC_ERROR;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
/**
* Checked exception to communicate an error that needs to be sent to the
private static final long serialVersionUID = 1L;
+ private final static Logger LOG = LoggerFactory.getLogger( NetconfDocumentedException.class );
+ private static final DocumentBuilderFactory BUILDER_FACTORY;
+
+ static {
+ BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
+ BUILDER_FACTORY.setNamespaceAware(true);
+ BUILDER_FACTORY.setCoalescing(true);
+ BUILDER_FACTORY.setIgnoringElementContentWhitespace(true);
+ BUILDER_FACTORY.setIgnoringComments(true);
+ }
public enum ErrorType {
transport, rpc, protocol, application;
public String getTagValue() {
return name();
}
+
+ public static ErrorType from( String text ) {
+ try {
+ return valueOf( text );
+ }
+ catch( Exception e ) {
+ return application;
+ }
+ }
}
public enum ErrorTag {
- missing_attribute("missing-attribute"), unknown_element("unknown-element"), operation_not_supported(
- "operation-not-supported"), bad_attribute("bad-attribute"), data_missing("data-missing"), operation_failed(
- "operation-failed"), invalid_value("invalid-value"), malformed_message("malformed-message");
+ access_denied("access-denied"),
+ bad_attribute("bad-attribute"),
+ bad_element("bad-element"),
+ data_exists("data-exists"),
+ data_missing("data-missing"),
+ in_use("in-use"),
+ invalid_value("invalid-value"),
+ lock_denied("lock-denied"),
+ malformed_message("malformed-message"),
+ missing_attribute("missing-attribute"),
+ missing_element("missing-element"),
+ operation_failed("operation-failed"),
+ operation_not_supported("operation-not-supported"),
+ resource_denied("resource-denied"),
+ rollback_failed("rollback-failed"),
+ too_big("too-big"),
+ unknown_attribute("unknown-attribute"),
+ unknown_element("unknown-element"),
+ unknown_namespace("unknown-namespace");
private final String tagValue;
public String getTagValue() {
return this.tagValue;
}
+
+ public static ErrorTag from( String text ) {
+ for( ErrorTag e: values() )
+ {
+ if( e.getTagValue().equals( text ) ) {
+ return e;
+ }
+ }
+
+ return operation_failed;
+ }
}
public enum ErrorSeverity {
public String getTagValue() {
return name();
}
+
+ public static ErrorSeverity from( String text ) {
+ try {
+ return valueOf( text );
+ }
+ catch( Exception e ) {
+ return error;
+ }
+ }
}
private final ErrorType errorType;
ErrorSeverity.error, errorInfo);
}
+ public static NetconfDocumentedException fromXMLDocument( Document fromDoc ) {
+
+ ErrorType errorType = ErrorType.application;
+ ErrorTag errorTag = ErrorTag.operation_failed;
+ ErrorSeverity errorSeverity = ErrorSeverity.error;
+ Map<String, String> errorInfo = null;
+ String errorMessage = "";
+
+ Node rpcReply = fromDoc.getDocumentElement();
+
+ // FIXME: BUG? - we only handle one rpc-error.
+
+ NodeList replyChildren = rpcReply.getChildNodes();
+ for( int i = 0; i < replyChildren.getLength(); i++ ) {
+ Node replyChild = replyChildren.item( i );
+ if( RPC_ERROR.equals( replyChild.getNodeName() ) )
+ {
+ NodeList rpcErrorChildren = replyChild.getChildNodes();
+ for( int j = 0; j < rpcErrorChildren.getLength(); j++ )
+ {
+ Node rpcErrorChild = rpcErrorChildren.item( j );
+ if( ERROR_TYPE.equals( rpcErrorChild.getNodeName() ) ) {
+ errorType = ErrorType.from( rpcErrorChild.getTextContent() );
+ }
+ else if( ERROR_TAG.equals( rpcErrorChild.getNodeName() ) ) {
+ errorTag = ErrorTag.from( rpcErrorChild.getTextContent() );
+ }
+ else if( ERROR_SEVERITY.equals( rpcErrorChild.getNodeName() ) ) {
+ errorSeverity = ErrorSeverity.from( rpcErrorChild.getTextContent() );
+ }
+ else if( ERROR_MESSAGE.equals( rpcErrorChild.getNodeName() ) ) {
+ errorMessage = rpcErrorChild.getTextContent();
+ }
+ else if( ERROR_INFO.equals( rpcErrorChild.getNodeName() ) ) {
+ errorInfo = parseErrorInfo( rpcErrorChild );
+ }
+ }
+
+ break;
+ }
+ }
+
+ return new NetconfDocumentedException( errorMessage, errorType, errorTag, errorSeverity, errorInfo );
+ }
+
+ private static Map<String, String> parseErrorInfo( Node node ) {
+ Map<String, String> infoMap = new HashMap<>();
+ NodeList children = node.getChildNodes();
+ for( int i = 0; i < children.getLength(); i++ ) {
+ Node child = children.item( i );
+ if( child.getNodeType() == Node.ELEMENT_NODE ) {
+ infoMap.put( child.getNodeName(), child.getTextContent() );
+ }
+ }
+
+ return infoMap;
+ }
+
public ErrorType getErrorType() {
return this.errorType;
}
return this.errorInfo;
}
+ public Document toXMLDocument() {
+ Document doc = null;
+ try {
+ doc = BUILDER_FACTORY.newDocumentBuilder().newDocument();
+
+ Node rpcReply = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY );
+ doc.appendChild( rpcReply );
+
+ Node rpcError = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_ERROR );
+ rpcReply.appendChild( rpcError );
+
+ rpcError.appendChild( createTextNode( doc, ERROR_TYPE, getErrorType().getTagValue() ) );
+ rpcError.appendChild( createTextNode( doc, ERROR_TAG, getErrorTag().getTagValue() ) );
+ rpcError.appendChild( createTextNode( doc, ERROR_SEVERITY, getErrorSeverity().getTagValue() ) );
+ rpcError.appendChild( createTextNode( doc, ERROR_MESSAGE, getLocalizedMessage() ) );
+
+ Map<String, String> errorInfoMap = getErrorInfo();
+ if( errorInfoMap != null && !errorInfoMap.isEmpty() ) {
+ /*
+ * <error-info>
+ * <bad-attribute>message-id</bad-attribute>
+ * <bad-element>rpc</bad-element>
+ * </error-info>
+ */
+
+ Node errorInfoNode = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, ERROR_INFO );
+ errorInfoNode.setPrefix( rpcReply.getPrefix() );
+ rpcError.appendChild( errorInfoNode );
+
+ for ( Entry<String, String> entry : errorInfoMap.entrySet() ) {
+ errorInfoNode.appendChild( createTextNode( doc, entry.getKey(), entry.getValue() ) );
+ }
+ }
+ }
+ catch( ParserConfigurationException e ) {
+ LOG.error( "Error outputting to XML document", e ); // this shouldn't happen
+ }
+
+ return doc;
+ }
+
+ private Node createTextNode( Document doc, String tag, String textContent ) {
+ Node node = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, tag );
+ node.setTextContent( textContent );
+ return node;
+ }
+
@Override
public String toString() {
return "NetconfDocumentedException{" + "message=" + getMessage() + ", errorType=" + this.errorType
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.netconf.util.xml;
+package org.opendaylight.controller.netconf.api.xml;
public final class XmlNetconfConstants {
public static final String RPC_KEY = "rpc";
public static final String RPC_REPLY_KEY = "rpc-reply";
public static final String RPC_ERROR = "rpc-error";
+ public static final String ERROR_TYPE = "error-type";
+ public static final String ERROR_TAG = "error-tag";
+ public static final String ERROR_SEVERITY = "error-severity";
+ public static final String ERROR_APP_TAG = "error-app-tag";
+ public static final String ERROR_PATH = "error-path";
+ public static final String ERROR_MESSAGE = "error-message";
+ public static final String ERROR_INFO = "error-info";
public static final String NAME_KEY = "name";
public static final String NOTIFICATION_ELEMENT_NAME = "notification";
// TODO where to store namespace of config ?
public static final String URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG = "urn:opendaylight:params:xml:ns:yang:controller:config";
public static final String GET = "get";
+ public static final String GET_CONFIG = "get-config";
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import com.google.common.collect.ImmutableMap;
+
+
+/**
+ * Unit tests for NetconfDocumentedException.
+ *
+ * @author Thomas Pantelis
+ */
+public class NetconfDocumentedExceptionTest {
+
+ private XPath xpath;
+
+ @Before
+ public void setUp() throws Exception {
+ XPathFactory xPathfactory = XPathFactory.newInstance();
+ xpath = xPathfactory.newXPath();
+ xpath.setNamespaceContext( new NamespaceContext() {
+ @Override
+ public Iterator<?> getPrefixes( String namespaceURI ) {
+ return Collections.singletonList( "netconf" ).iterator();
+ }
+
+ @Override
+ public String getPrefix( String namespaceURI ) {
+ return "netconf";
+ }
+
+ @Override
+ public String getNamespaceURI( String prefix ) {
+ return XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
+ }
+ } );
+ }
+
+ @Test
+ public void testToAndFromXMLDocument() throws XPathExpressionException {
+ String errorMessage = "mock error message";
+ NetconfDocumentedException ex = new NetconfDocumentedException( errorMessage, null,
+ ErrorType.protocol,
+ ErrorTag.data_exists,
+ ErrorSeverity.warning,
+ ImmutableMap.of( "foo", "bar" ) );
+
+ Document doc = ex.toXMLDocument();
+ assertNotNull( "Document is null", doc );
+
+ Node rootNode = doc.getDocumentElement();
+
+ assertEquals( "getNamespaceURI", "urn:ietf:params:xml:ns:netconf:base:1.0", rootNode.getNamespaceURI() );
+ assertEquals( "getLocalName", "rpc-reply", rootNode.getLocalName() );
+
+ Node rpcErrorNode = getNode( "/netconf:rpc-reply/netconf:rpc-error", rootNode );
+ assertNotNull( "rpc-error not found", rpcErrorNode );
+
+ Node errorTypeNode = getNode( "netconf:error-type", rpcErrorNode );
+ assertNotNull( "error-type not found", errorTypeNode );
+ assertEquals( "error-type", ErrorType.protocol.getTagValue(),
+ errorTypeNode.getTextContent() );
+
+ Node errorTagNode = getNode( "netconf:error-tag", rpcErrorNode );
+ assertNotNull( "error-tag not found", errorTagNode );
+ assertEquals( "error-tag", ErrorTag.data_exists.getTagValue(),
+ errorTagNode.getTextContent() );
+
+ Node errorSeverityNode = getNode( "netconf:error-severity", rpcErrorNode );
+ assertNotNull( "error-severity not found", errorSeverityNode );
+ assertEquals( "error-severity", ErrorSeverity.warning.getTagValue(),
+ errorSeverityNode.getTextContent() );
+
+ Node errorInfoNode = getNode( "netconf:error-info/netconf:foo", rpcErrorNode );
+ assertNotNull( "foo not found", errorInfoNode );
+ assertEquals( "foo", "bar", errorInfoNode.getTextContent() );
+
+ Node errorMsgNode = getNode( "netconf:error-message", rpcErrorNode );
+ assertNotNull( "error-message not found", errorMsgNode );
+ assertEquals( "error-message", errorMessage, errorMsgNode.getTextContent() );
+
+ // Test fromXMLDocument
+
+ ex = NetconfDocumentedException.fromXMLDocument( doc );
+
+ assertNotNull( "NetconfDocumentedException is null", ex );
+ assertEquals( "getErrorSeverity", ErrorSeverity.warning, ex.getErrorSeverity() );
+ assertEquals( "getErrorTag", ErrorTag.data_exists, ex.getErrorTag() );
+ assertEquals( "getErrorType", ErrorType.protocol, ex.getErrorType() );
+ assertEquals( "getLocalizedMessage", errorMessage, ex.getLocalizedMessage() );
+ assertEquals( "getErrorInfo", ImmutableMap.of( "foo", "bar" ), ex.getErrorInfo() );
+ }
+
+ @SuppressWarnings("unchecked")
+ <T> T getNode( String xpathExp, Node node ) throws XPathExpressionException {
+ return (T)xpath.compile( xpathExp ).evaluate( node, XPathConstants.NODE );
+ }
+}
+
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>2.4</version>
<executions>
<execution>
<goals>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public final class NetconfClientSession extends AbstractNetconfSession<NetconfClientSession, NetconfClientSessionListener> {
+public class NetconfClientSession extends AbstractNetconfSession<NetconfClientSession, NetconfClientSessionListener> {
private static final Logger logger = LoggerFactory.getLogger(NetconfClientSession.class);
private final Collection<String> capabilities;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.Timer;
import io.netty.util.concurrent.Promise;
+
import java.util.Collection;
+
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
+
import org.opendaylight.controller.netconf.api.NetconfClientSessionPreferences;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.nettyutil.AbstractChannelInitializer;
import org.opendaylight.controller.netconf.nettyutil.AbstractNetconfSessionNegotiator;
import org.opendaylight.controller.netconf.nettyutil.handler.exi.NetconfStartExiMessage;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
+
import io.netty.channel.Channel;
import io.netty.util.Timer;
import io.netty.util.concurrent.Promise;
+
import org.opendaylight.controller.netconf.api.NetconfClientSessionPreferences;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.nettyutil.handler.exi.NetconfStartExiMessage;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.protocol.framework.SessionListenerFactory;
import org.opendaylight.protocol.framework.SessionNegotiator;
import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>ietf-netconf-monitoring-extension</artifactId>
- <version>${project.version}</version>
</dependency>
<!-- compile dependencies -->
<dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netconf-client</artifactId>
- <version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>2.3.7</version>
<configuration>
<instructions>
<Bundle-Activator>org.opendaylight.controller.netconf.impl.osgi.NetconfImplActivator</Bundle-Activator>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>2.4</version>
<executions>
<execution>
<goals>
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
+
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
import org.opendaylight.controller.netconf.api.NetconfSessionListener;
import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
session.onIncommingRpcFail();
throw new IllegalStateException("Unable to process incoming message " + netconfMessage, e);
} catch (NetconfDocumentedException e) {
- logger.trace("Error occured while processing mesage {}",e);
+ logger.trace("Error occurred while processing message",e);
session.onOutgoingRpcError();
session.onIncommingRpcFail();
SendErrorExceptionUtil.sendErrorMessage(session, e, netconfMessage);
logger.info("Session {} closed successfully", session.getSessionId());
}
+
+
private NetconfMessage processDocument(final NetconfMessage netconfMessage, NetconfServerSession session)
throws NetconfDocumentedException {
Document rpcReply = operationRouter.onNetconfMessage(incomingDocument, session);
+ rpcReply = SubtreeFilter.applySubtreeFilter(incomingDocument, rpcReply);
+
session.onIncommingRpcSuccess();
responseDocument.appendChild(responseDocument.importNode(rpcReply.getDocumentElement(), true));
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
+
import java.util.Set;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.protocol.framework.SessionListenerFactory;
import org.opendaylight.protocol.framework.SessionNegotiator;
import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
import io.netty.channel.Channel;
import io.netty.util.Timer;
import io.netty.util.concurrent.Promise;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.impl;
+
+import com.google.common.base.Optional;
+import java.io.IOException;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation.OperationNameAndNamespace;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+/**
+ * See <a href="http://tools.ietf.org/html/rfc6241#section-6">rfc6241</a> for details.
+ */
+public class SubtreeFilter {
+ private static final Logger logger = LoggerFactory.getLogger(SubtreeFilter.class);
+
+ static Document applySubtreeFilter(Document requestDocument, Document rpcReply) throws NetconfDocumentedException {
+ // FIXME: rpcReply document must be reread otherwise some nodes do not inherit namespaces. (services/service)
+ try {
+ rpcReply = XmlUtil.readXmlToDocument(XmlUtil.toString(rpcReply, true));
+ } catch (SAXException | IOException e) {
+ logger.error("Cannot transform document", e);
+ throw new NetconfDocumentedException("Cannot transform document");
+ }
+
+ OperationNameAndNamespace operationNameAndNamespace = new OperationNameAndNamespace(requestDocument);
+ if (XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0.equals(operationNameAndNamespace.getNamespace()) &&
+ XmlNetconfConstants.GET.equals(operationNameAndNamespace.getOperationName()) ||
+ XmlNetconfConstants.GET_CONFIG.equals(operationNameAndNamespace.getOperationName())) {
+ // process subtree filtering here, in case registered netconf operations do
+ // not implement filtering.
+ Optional<XmlElement> maybeFilter = operationNameAndNamespace.getOperationElement().getOnlyChildElementOptionally(
+ XmlNetconfConstants.FILTER, XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
+ if (maybeFilter.isPresent() && (
+ "subtree".equals(maybeFilter.get().getAttribute("type"))||
+ "subtree".equals(maybeFilter.get().getAttribute("type", XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0))
+ )) {
+
+
+ // do
+ return filtered(maybeFilter.get(), rpcReply);
+ }
+ }
+ return rpcReply; // return identical document
+ }
+
+ private static Document filtered(XmlElement filter, Document originalReplyDocument) throws NetconfDocumentedException {
+ Document result = XmlUtil.newDocument();
+ // even if filter is empty, copy /rpc/data
+ Element rpcReply = originalReplyDocument.getDocumentElement();
+ Node rpcReplyDst = result.importNode(rpcReply, false);
+ result.appendChild(rpcReplyDst);
+ XmlElement dataSrc = XmlElement.fromDomElement(rpcReply).getOnlyChildElement("data", XmlNetconfConstants.RFC4741_TARGET_NAMESPACE);
+ Element dataDst = (Element) result.importNode(dataSrc.getDomElement(), false);
+ rpcReplyDst.appendChild(dataDst);
+ addSubtree(filter, dataSrc, XmlElement.fromDomElement(dataDst));
+
+ return result;
+ }
+
+ private static void addSubtree(XmlElement filter, XmlElement src, XmlElement dst) {
+ for (XmlElement srcChild : src.getChildElements()) {
+ for (XmlElement filterChild : filter.getChildElements()) {
+ addSubtree2(filterChild, srcChild, dst);
+ }
+ }
+ }
+
+ private static MatchingResult addSubtree2(XmlElement filter, XmlElement src, XmlElement dstParent) {
+ Document document = dstParent.getDomElement().getOwnerDocument();
+ MatchingResult matches = matches(src, filter);
+ if (matches != MatchingResult.NO_MATCH && matches != MatchingResult.CONTENT_MISMATCH) {
+ // copy srcChild to dst
+ boolean filterHasChildren = filter.getChildElements().isEmpty() == false;
+ // copy to depth if this is leaf of filter tree
+ Element copied = (Element) document.importNode(src.getDomElement(), filterHasChildren == false);
+ boolean shouldAppend = filterHasChildren == false;
+ if (filterHasChildren) { // this implies TAG_MATCH
+ // do the same recursively
+ int numberOfTextMatchingChildren = 0;
+ for (XmlElement srcChild : src.getChildElements()) {
+ for (XmlElement filterChild : filter.getChildElements()) {
+ MatchingResult childMatch = addSubtree2(filterChild, srcChild, XmlElement.fromDomElement(copied));
+ if (childMatch == MatchingResult.CONTENT_MISMATCH) {
+ return MatchingResult.NO_MATCH;
+ }
+ if (childMatch == MatchingResult.CONTENT_MATCH) {
+ numberOfTextMatchingChildren++;
+ }
+ shouldAppend |= childMatch != MatchingResult.NO_MATCH;
+ }
+ }
+ // if only text matching child filters are specified..
+ if (numberOfTextMatchingChildren == filter.getChildElements().size()) {
+ // force all children to be added (to depth). This is done by copying parent node to depth.
+ // implies shouldAppend == true
+ copied = (Element) document.importNode(src.getDomElement(), true);
+ }
+ }
+ if (shouldAppend) {
+ dstParent.getDomElement().appendChild(copied);
+ }
+ }
+ return matches;
+ }
+
+ /**
+ * Shallow compare src node to filter: tag name and namespace must match.
+ * If filter node has no children and has text content, it also must match.
+ */
+ private static MatchingResult matches(XmlElement src, XmlElement filter) {
+ boolean tagMatch = src.getName().equals(filter.getName()) &&
+ src.getNamespaceOptionally().equals(filter.getNamespaceOptionally());
+ MatchingResult result = null;
+ if (tagMatch) {
+ // match text content
+ Optional<String> maybeText = filter.getOnlyTextContentOptionally();
+ if (maybeText.isPresent()) {
+ if (maybeText.equals(src.getOnlyTextContentOptionally())) {
+ result = MatchingResult.CONTENT_MATCH;
+ } else {
+ result = MatchingResult.CONTENT_MISMATCH;
+ }
+ }
+ // match attributes, combination of content and tag is not supported
+ if (result == null) {
+ for (Attr attr : filter.getAttributes().values()) {
+ // ignore namespace declarations
+ if (XmlUtil.XMLNS_URI.equals(attr.getNamespaceURI()) == false ) {
+ // find attr with matching localName(), namespaceURI(), == value() in src
+ String found = src.getAttribute(attr.getLocalName(), attr.getNamespaceURI());
+ if (attr.getValue().equals(found) && result != MatchingResult.NO_MATCH) {
+ result = MatchingResult.TAG_MATCH;
+ } else {
+ result = MatchingResult.NO_MATCH;
+ }
+ }
+ }
+ }
+ if (result == null) {
+ result = MatchingResult.TAG_MATCH;
+ }
+ }
+ if (result == null) {
+ result = MatchingResult.NO_MATCH;
+ }
+ logger.debug("Matching {} to {} resulted in {}", src, filter, tagMatch);
+ return result;
+ }
+
+ enum MatchingResult {
+ NO_MATCH, TAG_MATCH, CONTENT_MATCH, CONTENT_MISMATCH
+ }
+}
package org.opendaylight.controller.netconf.impl.mapping.operations;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
package org.opendaylight.controller.netconf.impl.mapping.operations;
import com.google.common.base.Preconditions;
+
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter;
import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
+
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;\r
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;\r
import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;\r
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;\r
import org.opendaylight.controller.netconf.api.NetconfMessage;\r
import org.opendaylight.controller.netconf.impl.NetconfServerSession;\r
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;\r
import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation;\r
import org.opendaylight.controller.netconf.util.xml.XmlElement;\r
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;\r
import org.opendaylight.controller.netconf.util.xml.XmlUtil;\r
import org.slf4j.Logger;\r
import org.slf4j.LoggerFactory;\r
package org.opendaylight.controller.netconf.impl.mapping.operations;\r
\r
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;\r
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;\r
import org.opendaylight.controller.netconf.impl.NetconfServerSession;\r
import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation;\r
import org.opendaylight.controller.netconf.util.xml.XmlElement;\r
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;\r
import org.opendaylight.controller.netconf.util.xml.XmlUtil;\r
import org.slf4j.Logger;\r
import org.slf4j.LoggerFactory;\r
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
import io.netty.util.concurrent.GlobalEventExecutor;
+
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
+
import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.runners.Parameterized;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
import org.opendaylight.controller.netconf.client.SimpleNetconfClientSessionListener;
import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader;
import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.protocol.framework.NeverReconnectStrategy;
import org.slf4j.Logger;
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.impl;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+@RunWith(value = Parameterized.class)
+public class SubtreeFilterTest {
+ private static final Logger logger = LoggerFactory.getLogger(SubtreeFilterTest.class);
+
+ private final int directoryIndex;
+
+ @Parameters
+ public static Collection<Object[]> data() {
+ List<Object[]> result = new ArrayList<>();
+ for (int i = 0; i <= 8; i++) {
+ result.add(new Object[]{i});
+ }
+ return result;
+ }
+
+ public SubtreeFilterTest(int directoryIndex) {
+ this.directoryIndex = directoryIndex;
+ }
+
+ @Before
+ public void setUp(){
+ XMLUnit.setIgnoreWhitespace(true);
+ }
+
+ @Test
+ public void test() throws Exception {
+ Document requestDocument = getDocument("request.xml");
+ Document preFilterDocument = getDocument("pre-filter.xml");
+ Document postFilterDocument = getDocument("post-filter.xml");
+ Document actualPostFilterDocument = SubtreeFilter.applySubtreeFilter(requestDocument, preFilterDocument);
+ logger.info("Actual document: {}", XmlUtil.toString(actualPostFilterDocument));
+ Diff diff = XMLUnit.compareXML(postFilterDocument, actualPostFilterDocument);
+ assertTrue(diff.toString(), diff.similar());
+
+ }
+
+ public Document getDocument(String fileName) throws SAXException, IOException {
+ return XmlUtil.readXmlToDocument(getClass().getResourceAsStream("/subtree/" + directoryIndex + "/" +
+ fileName));
+ }
+}
--- /dev/null
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="m-2">
+ <data>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
+ prefix:toaster-provider-impl
+ </type>
+ <name>toaster-provider-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-notification-service
+ </type>
+ <name>binding-notification-broker</name>
+ </notification-service>
+ <rpc-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-rpc-registry
+ </type>
+ <name>binding-rpc-broker</name>
+ </rpc-registry>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-data-broker
+ </type>
+ <name>binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ prefix:sal-netconf-connector
+ </type>
+ <name>controller-config</name>
+ <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">1830</port>
+ <connection-timeout-millis xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ 20000
+ </connection-timeout-millis>
+ <between-attempts-timeout-millis
+ xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">2000
+ </between-attempts-timeout-millis>
+ <sleep-factor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">1.5</sleep-factor>
+ <password xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">admin</password>
+ <dom-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ prefix:dom-broker-osgi-registry
+ </type>
+ <name>dom-broker</name>
+ </dom-registry>
+ <client-dispatcher xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">
+ prefix:netconf-client-dispatcher
+ </type>
+ <name>global-netconf-dispatcher</name>
+ </client-dispatcher>
+ <username xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">admin</username>
+ <address xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">127.0.0.1</address>
+ <processing-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
+ <name>global-netconf-processing-executor</name>
+ </processing-executor>
+ <tcp-only xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">false</tcp-only>
+ <binding-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-broker-osgi-registry
+ </type>
+ <name>binding-osgi-broker</name>
+ </binding-registry>
+ <max-connection-attempts xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">0
+ </max-connection-attempts>
+ <event-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
+ <name>global-event-executor</name>
+ </event-executor>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
+ prefix:netconf-client-dispatcher
+ </type>
+ <name>global-netconf-dispatcher</name>
+ <worker-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <name>global-worker-group</name>
+ </worker-thread-group>
+ <timer xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-timer</type>
+ <name>global-timer</name>
+ </timer>
+ <boss-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <name>global-boss-group</name>
+ </boss-thread-group>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:logback:config">prefix:logback</type>
+ <name>singleton</name>
+ <console-appenders xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <threshold-filter>ERROR</threshold-filter>
+ <name>STDOUT</name>
+ <encoder-pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n</encoder-pattern>
+ </console-appenders>
+ <file-appenders xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <append>true</append>
+ <file-name>logs/audit.log</file-name>
+ <name>audit-file</name>
+ <encoder-pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} %msg %n</encoder-pattern>
+ </file-appenders>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>WARN</level>
+ <logger-name>org.opendaylight.controller.logging.bridge</logger-name>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>INFO</level>
+ <logger-name>audit</logger-name>
+ <appenders>audit-file</appenders>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>ERROR</level>
+ <logger-name>ROOT</logger-name>
+ <appenders>STDOUT</appenders>
+ <appenders>opendaylight.log</appenders>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>INFO</level>
+ <logger-name>org.opendaylight</logger-name>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>INFO</level>
+ <logger-name>org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort</logger-name>
+ <appenders>opendaylight.log</appenders>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>TRACE</level>
+ <logger-name>org.opendaylight.controller.netconf</logger-name>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>WARN</level>
+ <logger-name>io.netty</logger-name>
+ </loggers>
+ <rolling-appenders xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <append>true</append>
+ <max-file-size>10MB</max-file-size>
+ <file-name>logs/opendaylight.log</file-name>
+ <name>opendaylight.log</name>
+ <file-name-pattern>logs/opendaylight.%d.log.zip</file-name-pattern>
+ <encoder-pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{35} - %msg%n</encoder-pattern>
+ <clean-history-on-start>false</clean-history-on-start>
+ <max-history>1</max-history>
+ <rolling-policy-type>TimeBasedRollingPolicy</rolling-policy-type>
+ </rolling-appenders>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:shutdown:impl">prefix:shutdown</type>
+ <name>shutdown</name>
+ <secret xmlns="urn:opendaylight:params:xml:ns:yang:controller:shutdown:impl"/>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty:timer">
+ prefix:netty-hashed-wheel-timer
+ </type>
+ <name>global-timer</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup">
+ prefix:netty-threadgroup-fixed
+ </type>
+ <name>global-boss-group</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup">
+ prefix:netty-threadgroup-fixed
+ </type>
+ <name>global-worker-group</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ prefix:schema-service-singleton
+ </type>
+ <name>yang-schema-service</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl
+ </type>
+ <name>inmemory-dom-broker</name>
+ <async-data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-async-data-broker
+ </type>
+ <name>inmemory-data-broker</name>
+ </async-data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ prefix:dom-inmemory-data-broker
+ </type>
+ <name>inmemory-data-broker</name>
+ <schema-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">
+ prefix:threadpool-flexible
+ </type>
+ <name>global-netconf-processing-executor</name>
+ <threadFactory xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
+ <name>global-netconf-processing-executor-threadfactory</name>
+ </threadFactory>
+ <minThreadCount xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">1
+ </minThreadCount>
+ <max-thread-count xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">4
+ </max-thread-count>
+ <keepAliveMillis xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">600000
+ </keepAliveMillis>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor">
+ prefix:netty-global-event-executor
+ </type>
+ <name>singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:binding-broker-impl
+ </type>
+ <name>binding-broker-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-notification-service
+ </type>
+ <name>binding-notification-broker</name>
+ </notification-service>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-data-broker
+ </type>
+ <name>binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:runtime-generated-mapping
+ </type>
+ <name>runtime-mapping-singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:binding-notification-broker
+ </type>
+ <name>binding-notification-broker</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:binding-data-compatible-broker
+ </type>
+ <name>inmemory-binding-data-broker</name>
+ <dom-async-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ prefix:dom-broker-osgi-registry
+ </type>
+ <name>dom-broker</name>
+ </dom-async-broker>
+ <binding-mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:binding-dom-mapping-service
+ </type>
+ <name>runtime-mapping-singleton</name>
+ </binding-mapping-service>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ prefix:threadfactory-naming
+ </type>
+ <name>global-netconf-processing-executor-threadfactory</name>
+ <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">
+ remote-connector-processing-executor
+ </name-prefix>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
+ prefix:kitchen-service-impl
+ </type>
+ <name>kitchen-service-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-notification-service
+ </type>
+ <name>binding-notification-broker</name>
+ </notification-service>
+ <rpc-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-rpc-registry
+ </type>
+ <name>binding-rpc-broker</name>
+ </rpc-registry>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">
+ prefix:remote-zeromq-rpc-server
+ </type>
+ <name>remoter</name>
+ <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">5666</port>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ prefix:dom-broker-osgi-registry
+ </type>
+ <name>dom-broker</name>
+ </dom-broker>
+ </module>
+ </modules>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:schema-service</type>
+ <instance>
+ <name>yang-schema-service</name>
+ <provider>/modules/module[type='schema-service-singleton'][name='yang-schema-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry
+ </type>
+ <instance>
+ <name>dom-broker</name>
+ <provider>/modules/module[type='dom-broker-impl'][name='inmemory-dom-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-async-data-broker
+ </type>
+ <instance>
+ <name>inmemory-data-broker</name>
+ <provider>/modules/module[type='dom-inmemory-data-broker'][name='inmemory-data-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
+ <instance>
+ <name>global-netconf-processing-executor</name>
+ <provider>/modules/module[type='threadpool-flexible'][name='global-netconf-processing-executor']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
+ <instance>
+ <name>global-netconf-processing-executor-threadfactory</name>
+ <provider>
+ /modules/module[type='threadfactory-naming'][name='global-netconf-processing-executor-threadfactory']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ prefix:binding-dom-mapping-service
+ </type>
+ <instance>
+ <name>runtime-mapping-singleton</name>
+ <provider>/modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-timer</type>
+ <instance>
+ <name>global-timer</name>
+ <provider>/modules/module[type='netty-hashed-wheel-timer'][name='global-timer']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <instance>
+ <name>global-boss-group</name>
+ <provider>/modules/module[type='netty-threadgroup-fixed'][name='global-boss-group']</provider>
+ </instance>
+ <instance>
+ <name>global-worker-group</name>
+ <provider>/modules/module[type='netty-threadgroup-fixed'][name='global-worker-group']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
+ <instance>
+ <name>global-event-executor</name>
+ <provider>/modules/module[type='netty-global-event-executor'][name='singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-rpc-registry
+ </type>
+ <instance>
+ <name>binding-rpc-broker</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-notification-service
+ </type>
+ <instance>
+ <name>binding-notification-broker</name>
+ <provider>/modules/module[type='binding-notification-broker'][name='binding-notification-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-broker-osgi-registry
+ </type>
+ <instance>
+ <name>binding-osgi-broker</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-data-broker
+ </type>
+ <instance>
+ <name>binding-data-broker</name>
+ <provider>/modules/module[type='binding-data-compatible-broker'][name='inmemory-binding-data-broker']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
+ prefix:kitchen-service
+ </type>
+ <instance>
+ <name>kitchen-service</name>
+ <provider>/modules/module[type='kitchen-service-impl'][name='kitchen-service-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">
+ prefix:netconf-client-dispatcher
+ </type>
+ <instance>
+ <name>global-netconf-dispatcher</name>
+ <provider>/modules/module[type='netconf-client-dispatcher'][name='global-netconf-dispatcher']</provider>
+ </instance>
+ </service>
+ </services>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<configuration scan="true">
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <root level="error">
+ <appender-ref ref="STDOUT" />
+ </root>
+ <logger name="org.opendaylight.controller.netconf" level="TRACE"/>
+</configuration>
--- /dev/null
+<!--
+ ~ Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+
+<rpc-reply message-id="0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>Charlie Root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ <user>
+ <name>barney</name>
+ <type>admin</type>
+ <full-name>Barney Rubble</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>3</id>
+ </company-info>
+ </user>
+ </users>
+ <groups>
+ <group>
+ <name>admin</name>
+ </group>
+ </groups>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc-reply message-id="0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>Charlie Root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ <user>
+ <name>barney</name>
+ <type>admin</type>
+ <full-name>Barney Rubble</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>3</id>
+ </company-info>
+ </user>
+ </users>
+ <groups>
+ <group>
+ <name>admin</name>
+ </group>
+ </groups>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc message-id="0"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get>
+ </get>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc-reply message-id="1"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ </data>
+</rpc-reply>
--- /dev/null
+<rpc-reply message-id="1" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>Charlie Root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ <user>
+ <name>barney</name>
+ <type>admin</type>
+ <full-name>Barney Rubble</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>3</id>
+ </company-info>
+ </user>
+ </users>
+ <groups>
+ <group>
+ <name>admin</name>
+ </group>
+ </groups>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc message-id="1"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get>
+ <filter type="subtree">
+ </filter>
+ </get>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc-reply message-id="2" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>Charlie Root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ <user>
+ <name>barney</name>
+ <type>admin</type>
+ <full-name>Barney Rubble</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>3</id>
+ </company-info>
+ </user>
+ </users>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+
+<rpc-reply message-id="2" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>Charlie Root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ <user>
+ <name>barney</name>
+ <type>admin</type>
+ <full-name>Barney Rubble</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>3</id>
+ </company-info>
+ </user>
+ </users>
+ <groups>
+ <group>
+ <name>admin</name>
+ </group>
+ </groups>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc message-id="2"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get-config>
+ <source>
+ <running/>
+ </source>
+ <filter type="subtree">
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users/>
+ </top>
+ </filter>
+ </get-config>
+</rpc>
--- /dev/null
+<!--
+ ~ Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+
+<rpc-reply message-id="3"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ </user>
+ <user>
+ <name>fred</name>
+ </user>
+ <user>
+ <name>barney</name>
+ </user>
+ </users>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+
+<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>Charlie Root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ <user>
+ <name>barney</name>
+ <type>admin</type>
+ <full-name>Barney Rubble</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>3</id>
+ </company-info>
+ </user>
+ </users>
+ <groups>
+ <group>
+ <name>admin</name>
+ </group>
+ </groups>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+
+<rpc message-id="3"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get-config>
+ <source>
+ <running/>
+ </source>
+ <filter type="subtree">
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name/>
+ </user>
+ </users>
+ </top>
+ </filter>
+ </get-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc-reply message-id="4"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ </users>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<!--
+ ~ Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+
+<rpc-reply message-id="4" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>Charlie Root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ <user>
+ <name>barney</name>
+ <type>admin</type>
+ <full-name>Barney Rubble</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>3</id>
+ </company-info>
+ </user>
+ </users>
+ <groups>
+ <group>
+ <name>admin</name>
+ </group>
+ </groups>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc message-id="4"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get-config>
+ <source>
+ <running/>
+ </source>
+ <filter type="subtree">
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>fred</name>
+ </user>
+ </users>
+ </top>
+ </filter>
+ </get-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc-reply message-id="5"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ </user>
+ </users>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc-reply message-id="5" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>Charlie Root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ <user>
+ <name>barney</name>
+ <type>admin</type>
+ <full-name>Barney Rubble</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>3</id>
+ </company-info>
+ </user>
+ </users>
+ <groups>
+ <group>
+ <name>admin</name>
+ </group>
+ </groups>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc message-id="5"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get-config>
+ <source>
+ <running/>
+ </source>
+ <filter type="subtree">
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>fred</name>
+ <type/>
+ <full-name/>
+ </user>
+ </users>
+ </top>
+ </filter>
+ </get-config>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc-reply message-id="6"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>fred</name>
+ <company-info>
+ <id>2</id>
+ </company-info>
+ </user>
+ </users>
+ </top>
+ </data>
+</rpc-reply>
--- /dev/null
+<!--
+ ~ Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ ~
+ ~ This program and the accompanying materials are made available under the
+ ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+ -->
+
+<rpc-reply message-id="6" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>Charlie Root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ <user>
+ <name>barney</name>
+ <type>admin</type>
+ <full-name>Barney Rubble</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>3</id>
+ </company-info>
+ </user>
+ </users>
+ <groups>
+ <group>
+ <name>admin</name>
+ </group>
+ </groups>
+ </top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc message-id="6"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get-config>
+ <source>
+ <running/>
+ </source>
+ <filter type="subtree">
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <company-info/>
+ </user>
+ <user>
+ <name>fred</name>
+ <company-info>
+ <id/>
+ </company-info>
+ </user>
+ <user>
+ <name>barney</name>
+ <type>superuser</type>
+ <company-info>
+ <dept/>
+ </company-info>
+ </user>
+ </users>
+ </top>
+ </filter>
+ </get-config>
+</rpc>
+
--- /dev/null
+<rpc-reply message-id="7"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <t:top xmlns:t="http://example.com/schema/1.2/stats">
+ <t:interfaces>
+ <t:interface t:ifName="eth0">
+ <t:ifInOctets>45621</t:ifInOctets>
+ <t:ifOutOctets>774344</t:ifOutOctets>
+ </t:interface>
+ </t:interfaces>
+ </t:top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc-reply message-id="7" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <data>
+ <top xmlns="http://example.com/schema/1.2/config">
+ <users>
+ <user>
+ <name>root</name>
+ <type>superuser</type>
+ <full-name>Charlie Root</full-name>
+ <company-info>
+ <dept>1</dept>
+ <id>1</id>
+ </company-info>
+ </user>
+ <user>
+ <name>fred</name>
+ <type>admin</type>
+ <full-name>Fred Flintstone</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>2</id>
+ </company-info>
+ </user>
+ <user>
+ <name>barney</name>
+ <type>admin</type>
+ <full-name>Barney Rubble</full-name>
+ <company-info>
+ <dept>2</dept>
+ <id>3</id>
+ </company-info>
+ </user>
+ </users>
+ <groups>
+ <group>
+ <name>admin</name>
+ </group>
+ </groups>
+ </top>
+ <prefix:top xmlns:prefix="http://example.com/schema/1.2/stats">
+ <prefix:interfaces>
+ <prefix:interface prefix:ifName="eth0">
+ <prefix:ifInOctets>45621</prefix:ifInOctets>
+ <prefix:ifOutOctets>774344</prefix:ifOutOctets>
+ </prefix:interface>
+ <prefix:interface prefix:ifName="eth1">
+ <prefix:ifInOctets>1</prefix:ifInOctets>
+ <prefix:ifOutOctets>1</prefix:ifOutOctets>
+ </prefix:interface>
+ </prefix:interfaces>
+ </prefix:top>
+ </data>
+</rpc-reply>
\ No newline at end of file
--- /dev/null
+<rpc message-id="7"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <get>
+ <filter type="subtree">
+ <t:top xmlns:t="http://example.com/schema/1.2/stats">
+ <t:interfaces>
+ <t:interface t:ifName="eth0"/>
+ </t:interfaces>
+ </t:top>
+ </filter>
+ </get>
+</rpc>
\ No newline at end of file
--- /dev/null
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="m-6">
+ <data>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:schema-service</type>
+ <instance>
+ <name>yang-schema-service</name>
+ <provider>/modules/module[type='schema-service-singleton'][name='yang-schema-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
+ <instance>
+ <name>dom-broker</name>
+ <provider>/modules/module[type='dom-broker-impl'][name='inmemory-dom-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-async-data-broker</type>
+ <instance>
+ <name>inmemory-data-broker</name>
+ <provider>/modules/module[type='dom-inmemory-data-broker'][name='inmemory-data-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
+ <instance>
+ <name>global-netconf-processing-executor</name>
+ <provider>/modules/module[type='threadpool-flexible'][name='global-netconf-processing-executor']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
+ <instance>
+ <name>global-netconf-processing-executor-threadfactory</name>
+ <provider>/modules/module[type='threadfactory-naming'][name='global-netconf-processing-executor-threadfactory']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-dom-mapping-service</type>
+ <instance>
+ <name>runtime-mapping-singleton</name>
+ <provider>/modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-timer</type>
+ <instance>
+ <name>global-timer</name>
+ <provider>/modules/module[type='netty-hashed-wheel-timer'][name='global-timer']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <instance>
+ <name>global-boss-group</name>
+ <provider>/modules/module[type='netty-threadgroup-fixed'][name='global-boss-group']</provider>
+ </instance>
+ <instance>
+ <name>global-worker-group</name>
+ <provider>/modules/module[type='netty-threadgroup-fixed'][name='global-worker-group']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
+ <instance>
+ <name>global-event-executor</name>
+ <provider>/modules/module[type='netty-global-event-executor'][name='singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-rpc-registry</type>
+ <instance>
+ <name>binding-rpc-broker</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-notification-service</type>
+ <instance>
+ <name>binding-notification-broker</name>
+ <provider>/modules/module[type='binding-notification-broker'][name='binding-notification-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>
+ <instance>
+ <name>binding-osgi-broker</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-data-broker</type>
+ <instance>
+ <name>binding-data-broker</name>
+ <provider>/modules/module[type='binding-data-compatible-broker'][name='inmemory-binding-data-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">prefix:kitchen-service</type>
+ <instance>
+ <name>kitchen-service</name>
+ <provider>/modules/module[type='kitchen-service-impl'][name='kitchen-service-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
+ <instance>
+ <name>global-netconf-dispatcher</name>
+ <provider>/modules/module[type='netconf-client-dispatcher'][name='global-netconf-dispatcher']</provider>
+ </instance>
+ </service>
+ </services>
+ </data>
+</rpc-reply>
--- /dev/null
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="m-6">
+ <data>
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">prefix:toaster-provider-impl</type>
+ <name>toaster-provider-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-notification-service</type>
+ <name>binding-notification-broker</name>
+ </notification-service>
+ <rpc-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-rpc-registry</type>
+ <name>binding-rpc-broker</name>
+ </rpc-registry>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-data-broker</type>
+ <name>binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">prefix:sal-netconf-connector</type>
+ <name>controller-config</name>
+ <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">1830</port>
+ <connection-timeout-millis xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">20000</connection-timeout-millis>
+ <between-attempts-timeout-millis xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">2000</between-attempts-timeout-millis>
+ <sleep-factor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">1.5</sleep-factor>
+ <password xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">admin</password>
+ <dom-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
+ <name>dom-broker</name>
+ </dom-registry>
+ <client-dispatcher xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
+ <name>global-netconf-dispatcher</name>
+ </client-dispatcher>
+ <username xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">admin</username>
+ <address xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">127.0.0.1</address>
+ <processing-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
+ <name>global-netconf-processing-executor</name>
+ </processing-executor>
+ <tcp-only xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">false</tcp-only>
+ <binding-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>
+ <name>binding-osgi-broker</name>
+ </binding-registry>
+ <max-connection-attempts xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">0</max-connection-attempts>
+ <event-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
+ <name>global-event-executor</name>
+ </event-executor>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">prefix:netconf-client-dispatcher</type>
+ <name>global-netconf-dispatcher</name>
+ <worker-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <name>global-worker-group</name>
+ </worker-thread-group>
+ <timer xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-timer</type>
+ <name>global-timer</name>
+ </timer>
+ <boss-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <name>global-boss-group</name>
+ </boss-thread-group>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:logback:config">prefix:logback</type>
+ <name>singleton</name>
+ <console-appenders xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <threshold-filter>ERROR</threshold-filter>
+ <name>STDOUT</name>
+ <encoder-pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n</encoder-pattern>
+ </console-appenders>
+ <file-appenders xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <append>true</append>
+ <file-name>logs/audit.log</file-name>
+ <name>audit-file</name>
+ <encoder-pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} %msg %n</encoder-pattern>
+ </file-appenders>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>WARN</level>
+ <logger-name>org.opendaylight.controller.logging.bridge</logger-name>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>INFO</level>
+ <logger-name>audit</logger-name>
+ <appenders>audit-file</appenders>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>ERROR</level>
+ <logger-name>ROOT</logger-name>
+ <appenders>STDOUT</appenders>
+ <appenders>opendaylight.log</appenders>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>INFO</level>
+ <logger-name>org.opendaylight</logger-name>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>INFO</level>
+ <logger-name>org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort</logger-name>
+ <appenders>opendaylight.log</appenders>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>TRACE</level>
+ <logger-name>org.opendaylight.controller.netconf</logger-name>
+ </loggers>
+ <loggers xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <level>WARN</level>
+ <logger-name>io.netty</logger-name>
+ </loggers>
+ <rolling-appenders xmlns="urn:opendaylight:params:xml:ns:yang:controller:logback:config">
+ <append>true</append>
+ <max-file-size>10MB</max-file-size>
+ <file-name>logs/opendaylight.log</file-name>
+ <name>opendaylight.log</name>
+ <file-name-pattern>logs/opendaylight.%d.log.zip</file-name-pattern>
+ <encoder-pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{35} - %msg%n</encoder-pattern>
+ <clean-history-on-start>false</clean-history-on-start>
+ <max-history>1</max-history>
+ <rolling-policy-type>TimeBasedRollingPolicy</rolling-policy-type>
+ </rolling-appenders>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:shutdown:impl">prefix:shutdown</type>
+ <name>shutdown</name>
+ <secret xmlns="urn:opendaylight:params:xml:ns:yang:controller:shutdown:impl"/>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty:timer">prefix:netty-hashed-wheel-timer</type>
+ <name>global-timer</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup">prefix:netty-threadgroup-fixed</type>
+ <name>global-boss-group</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup">prefix:netty-threadgroup-fixed</type>
+ <name>global-worker-group</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
+ <name>yang-schema-service</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
+ <name>inmemory-dom-broker</name>
+ <async-data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-async-data-broker</type>
+ <name>inmemory-data-broker</name>
+ </async-data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-inmemory-data-broker</type>
+ <name>inmemory-data-broker</name>
+ <schema-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">prefix:threadpool-flexible</type>
+ <name>global-netconf-processing-executor</name>
+ <threadFactory xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
+ <name>global-netconf-processing-executor-threadfactory</name>
+ </threadFactory>
+ <minThreadCount xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">1</minThreadCount>
+ <max-thread-count xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">4</max-thread-count>
+ <keepAliveMillis xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">600000</keepAliveMillis>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor">prefix:netty-global-event-executor</type>
+ <name>singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
+ <name>binding-broker-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-notification-service</type>
+ <name>binding-notification-broker</name>
+ </notification-service>
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-data-broker</type>
+ <name>binding-data-broker</name>
+ </data-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
+ <name>runtime-mapping-singleton</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
+ <name>binding-notification-broker</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-compatible-broker</type>
+ <name>inmemory-binding-data-broker</name>
+ <dom-async-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
+ <name>dom-broker</name>
+ </dom-async-broker>
+ <binding-mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </binding-mapping-service>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">prefix:threadfactory-naming</type>
+ <name>global-netconf-processing-executor-threadfactory</name>
+ <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">remote-connector-processing-executor</name-prefix>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">prefix:kitchen-service-impl</type>
+ <name>kitchen-service-impl</name>
+ <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-notification-service</type>
+ <name>binding-notification-broker</name>
+ </notification-service>
+ <rpc-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-rpc-registry</type>
+ <name>binding-rpc-broker</name>
+ </rpc-registry>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">prefix:remote-zeromq-rpc-server</type>
+ <name>remoter</name>
+ <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">5666</port>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote:rpc">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
+ <name>dom-broker</name>
+ </dom-broker>
+ </module>
+ </modules>
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:schema-service</type>
+ <instance>
+ <name>yang-schema-service</name>
+ <provider>/modules/module[type='schema-service-singleton'][name='yang-schema-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
+ <instance>
+ <name>dom-broker</name>
+ <provider>/modules/module[type='dom-broker-impl'][name='inmemory-dom-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-async-data-broker</type>
+ <instance>
+ <name>inmemory-data-broker</name>
+ <provider>/modules/module[type='dom-inmemory-data-broker'][name='inmemory-data-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
+ <instance>
+ <name>global-netconf-processing-executor</name>
+ <provider>/modules/module[type='threadpool-flexible'][name='global-netconf-processing-executor']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
+ <instance>
+ <name>global-netconf-processing-executor-threadfactory</name>
+ <provider>/modules/module[type='threadfactory-naming'][name='global-netconf-processing-executor-threadfactory']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-dom-mapping-service</type>
+ <instance>
+ <name>runtime-mapping-singleton</name>
+ <provider>/modules/module[type='runtime-generated-mapping'][name='runtime-mapping-singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-timer</type>
+ <instance>
+ <name>global-timer</name>
+ <provider>/modules/module[type='netty-hashed-wheel-timer'][name='global-timer']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-threadgroup</type>
+ <instance>
+ <name>global-boss-group</name>
+ <provider>/modules/module[type='netty-threadgroup-fixed'][name='global-boss-group']</provider>
+ </instance>
+ <instance>
+ <name>global-worker-group</name>
+ <provider>/modules/module[type='netty-threadgroup-fixed'][name='global-worker-group']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
+ <instance>
+ <name>global-event-executor</name>
+ <provider>/modules/module[type='netty-global-event-executor'][name='singleton']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-rpc-registry</type>
+ <instance>
+ <name>binding-rpc-broker</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-notification-service</type>
+ <instance>
+ <name>binding-notification-broker</name>
+ <provider>/modules/module[type='binding-notification-broker'][name='binding-notification-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>
+ <instance>
+ <name>binding-osgi-broker</name>
+ <provider>/modules/module[type='binding-broker-impl'][name='binding-broker-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-data-broker</type>
+ <instance>
+ <name>binding-data-broker</name>
+ <provider>/modules/module[type='binding-data-compatible-broker'][name='inmemory-binding-data-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:kitchen-service:impl">prefix:kitchen-service</type>
+ <instance>
+ <name>kitchen-service</name>
+ <provider>/modules/module[type='kitchen-service-impl'][name='kitchen-service-impl']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
+ <instance>
+ <name>global-netconf-dispatcher</name>
+ <provider>/modules/module[type='netconf-client-dispatcher'][name='global-netconf-dispatcher']</provider>
+ </instance>
+ </service>
+ </services>
+ </data>
+</rpc-reply>
--- /dev/null
+<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="m-10">
+ <get-config>
+ <filter xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:type="subtree">
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service/>
+ </services>
+ </filter>
+ <source>
+ <running/>
+ </source>
+ </get-config>
+</rpc>
<artifactId>netconf-it</artifactId>
<name>${project.artifactId}</name>
- <properties>
- <tinybundles.version>2.0.0</tinybundles.version>
- </properties>
<dependencies>
<dependency>
<plugin>
<groupId>org.ops4j.pax.exam</groupId>
<artifactId>maven-paxexam-plugin</artifactId>
- <version>1.2.4</version>
<executions>
<execution>
<id>generate-config</id>
package org.opendaylight.controller.netconf.monitoring;
import com.google.common.collect.Maps;
+
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.monitoring.NetconfMonitoringService;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.controller.netconf.monitoring.xml.JaxBSerializer;
import org.opendaylight.controller.netconf.monitoring.xml.model.NetconfState;
import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NetconfMonitoringOperationService implements NetconfOperationService {
- public static final Set<Capability> CAPABILITIES = Sets.<Capability>newHashSet(new Capability() {
+ private static final Set<Capability> CAPABILITIES = Sets.<Capability>newHashSet(new Capability() {
@Override
public String getCapabilityUri() {
import java.util.List;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.openexi.proc.common.EXIOptions;
import org.w3c.dom.Document;
synchronized(lock) {
this.bb.discardReadBytes();
this.bb.writeBytes((ByteBuf) o);
+ ((ByteBuf) o).release();
lock.notifyAll();
}
}
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>2.3.7</version>
<configuration>
<instructions>
<Bundle-Activator>org.opendaylight.controller.netconf.ssh.osgi.NetconfSSHActivator</Bundle-Activator>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>2.4</version>
<executions>
<execution>
<goals>
AuthProvider authProvider = mock(AuthProvider.class);
doReturn(PEMGenerator.generate().toCharArray()).when(authProvider).getPEMAsCharArray();
doReturn(true).when(authProvider).authenticated(anyString(), anyString());
- NetconfSSHServer thread = NetconfSSHServer.start(1831, NetconfConfigUtil.getNetconfLocalAddress(), authProvider, new NioEventLoopGroup());
+ NetconfSSHServer thread = NetconfSSHServer.start(10831, NetconfConfigUtil.getNetconfLocalAddress(), authProvider, new NioEventLoopGroup());
Thread.sleep(2000);
logger.info("Closing socket");
thread.close();
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>2.3.7</version>
<configuration>
<instructions>
<Bundle-Activator>org.opendaylight.controller.netconf.tcp.osgi.NetconfTCPActivator</Bundle-Activator>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
- <version>2.3.7</version>
<configuration>
<instructions>
<Import-Package>com.google.common.base, com.google.common.collect, io.netty.channel,
io.netty.util.concurrent, javax.annotation, javax.xml.namespace, javax.xml.parsers, javax.xml.transform,
javax.xml.transform.dom, javax.xml.transform.stream, javax.xml.validation, javax.xml.xpath,
- org.opendaylight.controller.netconf.api, org.opendaylight.controller.netconf.mapping.api,
+ org.opendaylight.controller.netconf.api, org.opendaylight.controller.netconf.api.xml,
+ org.opendaylight.controller.netconf.mapping.api,
org.osgi.framework, org.slf4j, org.w3c.dom, org.xml.sax,io.netty.channel.local</Import-Package>
<Export-Package>org.opendaylight.controller.netconf.util.*</Export-Package>
</instructions>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>2.4</version>
<executions>
<execution>
<goals>
package org.opendaylight.controller.netconf.util;
import com.google.common.base.Preconditions;
+
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public static final class OperationNameAndNamespace {
private final String operationName, namespace;
+ private final XmlElement operationElement;
public OperationNameAndNamespace(Document message) throws NetconfDocumentedException {
XmlElement requestElement = null;
requestElement = getRequestElementWithCheck(message);
-
- XmlElement operationElement = requestElement.getOnlyChildElement();
+ operationElement = requestElement.getOnlyChildElement();
operationName = operationElement.getName();
namespace = operationElement.getNamespace();
}
public String getNamespace() {
return namespace;
}
+
+ public XmlElement getOperationElement() {
+ return operationElement;
+ }
}
protected static XmlElement getRequestElementWithCheck(Document message) throws NetconfDocumentedException {
import java.util.Set;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
+
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import javax.annotation.Nullable;
+
import java.util.Collection;
import java.util.List;
package org.opendaylight.controller.netconf.util.messages;
import com.google.common.base.Preconditions;
+
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
+
import org.opendaylight.controller.netconf.api.NetconfSession;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-
-import javax.xml.xpath.XPathConstants;
-import javax.xml.xpath.XPathExpression;
-import java.io.InputStream;
-import java.util.Map.Entry;
public final class SendErrorExceptionUtil {
private static final Logger logger = LoggerFactory.getLogger(SendErrorExceptionUtil.class);
}
}
- private static XPathExpression rpcErrorExpression = XMLNetconfUtil
- .compileXPath("/netconf:rpc-reply/netconf:rpc-error");
- private static XPathExpression errorTypeExpression = XMLNetconfUtil.compileXPath("netconf:error-type");
- private static XPathExpression errorTagExpression = XMLNetconfUtil.compileXPath("netconf:error-tag");
- private static XPathExpression errorSeverityExpression = XMLNetconfUtil.compileXPath("netconf:error-severity");
-
private static Document createDocument(final NetconfDocumentedException sendErrorException) {
-
- final InputStream errIS = SendErrorExceptionUtil.class.getResourceAsStream("server_error.xml");
- Document originalErrorDocument;
- try {
- originalErrorDocument = XmlUtil.readXmlToDocument(errIS);
- } catch (final Exception e) {
- throw new IllegalStateException(e);
- }
-
- final Document errorDocument = XmlUtil.createDocumentCopy(originalErrorDocument);
- final Node rootNode = errorDocument.getFirstChild();
-
- final Node rpcErrorNode = (Node) XmlUtil.evaluateXPath(rpcErrorExpression, rootNode, XPathConstants.NODE);
-
- final Node errorTypeNode = (Node) XmlUtil.evaluateXPath(errorTypeExpression, rpcErrorNode, XPathConstants.NODE);
- errorTypeNode.setTextContent(sendErrorException.getErrorType().getTagValue());
-
- final Node errorTagNode = (Node) XmlUtil.evaluateXPath(errorTagExpression, rpcErrorNode, XPathConstants.NODE);
- errorTagNode.setTextContent(sendErrorException.getErrorTag().getTagValue());
-
- final Node errorSeverityNode = (Node) XmlUtil.evaluateXPath(errorSeverityExpression, rpcErrorNode,
- XPathConstants.NODE);
- errorSeverityNode.setTextContent(sendErrorException.getErrorSeverity().getTagValue());
-
- if (sendErrorException.getErrorInfo() != null && !sendErrorException.getErrorInfo().isEmpty()) {
- /*
- * <error-info> <bad-attribute>message-id</bad-attribute>
- * <bad-element>rpc</bad-element> </error-info>
- */
- final Node errorInfoNode = errorDocument.createElementNS(
- XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, "error-info");
-
- errorInfoNode.setPrefix(rootNode.getPrefix());
- rpcErrorNode.appendChild(errorInfoNode);
- for (final Entry<String, String> errorInfoEntry : sendErrorException.getErrorInfo().entrySet()) {
- final Node node = errorDocument.createElementNS(
- XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, errorInfoEntry.getKey());
- node.setTextContent(errorInfoEntry.getValue());
- errorInfoNode.appendChild(node);
- }
-
- }
- return errorDocument;
+ return sendErrorException.toXMLDocument();
}
/**
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+
public final class XMLNetconfUtil {
private XMLNetconfUtil() {}
);
}
+ public Optional<String> getOnlyTextContentOptionally() {
+ // only return text content if this node has exactly one Text child node
+ if (element.getChildNodes().getLength() == 1) {
+ Node item = element.getChildNodes().item(0);
+ if (item instanceof Text) {
+ return Optional.of(((Text) item).getWholeText());
+ }
+ }
+ return Optional.absent();
+ }
+
public String getNamespaceAttribute() throws MissingNameSpaceException {
String attribute = element.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY);
if (attribute == null || attribute.equals("")){
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
+
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
+
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.validation.SchemaFactory;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
+
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+
+ <artifactId>swagger-ui</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <properties>
+ <api.dir>${resource.dir}/apis</api.dir>
+ <resource.dir>${project.build.directory}/classes</resource.dir>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>equinoxSDK381</groupId>
+ <artifactId>javax.servlet</artifactId>
+ </dependency>
+ <!-- add dependency on all northbound bundles -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>connectionmanager.northbound</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>controllermanager.northbound</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>flowprogrammer.northbound</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>forwarding.staticrouting.northbound</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>hosttracker.northbound</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>networkconfig.bridgedomain.northbound</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>statistics.northbound</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>subnets.northbound</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>switchmanager.northbound</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>topology.northbound</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>usermanager.northbound</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Export-Package></Export-Package>
+ <Import-Package>org.slf4j,
+ javax.annotation,
+ javax.naming,
+ javax.servlet,
+ javax.servlet.annotation,
+ javax.servlet.http,
+ com.google.gson,</Import-Package>
+ <Export-Package></Export-Package>
+ <Include-Resource>apis=target/classes/apis,
+ index.html=target/classes/index.html,
+ apilist.json=target/classes/apilist.json,
+ css=target/classes/css,
+ lib=target/classes/lib,
+ swagger-ui.min.js=target/classes/swagger-ui.min.js,
+ swagger-ui.js=target/classes/swagger-ui.js,
+ images=target/classes/images,
+ WEB-INF/web.xml=target/classes/WEB-INF/web.xml</Include-Resource>
+ <Web-ContextPath>/swagger</Web-ContextPath>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.7</version>
+ <dependencies>
+ <dependency>
+ <groupId>ant-contrib</groupId>
+ <artifactId>ant-contrib</artifactId>
+ <version>1.0b3</version>
+ <exclusions>
+ <exclusion>
+ <groupId>ant</groupId>
+ <artifactId>ant</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+ <executions>
+ <execution>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <phase>generate-sources</phase>
+ <configuration>
+ <target>
+ <taskdef classpathref="maven.plugin.classpath" resource="net/sf/antcontrib/antlib.xml"></taskdef>
+ <patternset id="rest.paths">
+ <include name="**/enunciate/generate/swagger/ui/*.json"></include>
+ <exclude name="**/java-client/**"></exclude>
+ <exclude name="**/swagger-ui/**"></exclude>
+ </patternset>
+
+ <echo message="======== Assembling swagger docs ========"></echo>
+ <!-- make api directory -->
+ <mkdir dir="${api.dir}"></mkdir>
+ <!-- copy swagger libs -->
+ <copy todir="${resource.dir}">
+ <fileset dir="../subnets/target/enunciate/generate/swagger/ui">
+ <exclude name="**/*.json"></exclude>
+ <exclude name="**/*.png"></exclude>
+ <exclude name="**/index.html"></exclude>
+ </fileset>
+ </copy>
+ <!-- Copy NorthBound json files into ui directory-->
+ <copy todir="${api.dir}">
+ <fileset dir="${basedir}/../../..">
+ <patternset refid="rest.paths"></patternset>
+ </fileset>
+ <mapper>
+ <regexpmapper from="^(.*)/([^/]+)/*/target/enunciate/generate/swagger/ui/(.*Northbound).*$$" to="\3"></regexpmapper>
+ <regexpmapper from="^(.*)/([^/]+)/*/target/enunciate/generate/swagger/ui/(.*resource-list.json)$$" to="\2-\3"></regexpmapper>
+ </mapper>
+ </copy>
+
+ <!-- Correct base path -->
+ <replaceregexp match="/full" replace="">
+ <fileset dir="${api.dir}">
+ <include name="**/*Northbound"></include>
+ </fileset>
+ </replaceregexp>
+ <!-- Merge Resource list -->
+ <echo append="false" file="${resource.dir}/apilist.json">{
+ "swaggerVersion": "1.1",
+ "basePath": "http://localhost:8080/swagger/apis",
+ "apis": [</echo>
+ <for param="file">
+ <path>
+ <fileset dir="${api.dir}">
+ <include name="**/*resource-list.json"></include>
+ <exclude name="**/neutron*"></exclude>
+ </fileset>
+ </path>
+ <sequential>
+ <echo message="Processing json resource @{file}"></echo>
+ <loadfile property="jsoncontent" srcfile="@{file}">
+ <filterchain>
+ <headfilter lines="10"></headfilter>
+ <tailfilter lines="6"></tailfilter>
+ </filterchain>
+ </loadfile>
+ <echo append="true" file="${resource.dir}/apilist.json" message="${jsoncontent},"></echo>
+ <var name="jsoncontent" unset="true"></var>
+ </sequential>
+ </for>
+ <sequential>
+ <loadfile property="jsoncontent" srcfile="${api.dir}/neutron-resource-list.json">
+ <filterchain>
+ <headfilter lines="46"></headfilter>
+ <tailfilter lines="42"></tailfilter>
+ </filterchain>
+ </loadfile>
+ <echo append="true" file="${resource.dir}/apilist.json" message="${jsoncontent},"></echo>
+ <var name="jsoncontent" unset="true"></var>
+ </sequential>
+ <echo append="true" file="${resource.dir}/apilist.json">{ }
+ ]
+}</echo>
+
+ <!-- Remove .json from api paths -->
+ <replaceregexp file="${resource.dir}/apilist.json" flags="g" match=".json" replace=""></replaceregexp>
+ <replaceregexp file="${resource.dir}/apilist.json" flags="g" match="JAXRS" replace=""></replaceregexp>
+
+ <!-- cleanup resource files as we don't need them -->
+ <delete>
+ <fileset dir="${api.dir}" includes="**/*resource-list.json"></fileset>
+ </delete>
+ <echo message="======== Build successful ========"></echo>
+ </target>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+ </scm>
+</project>
--- /dev/null
+package org.opendaylight.controller.swaggerui;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * Servlet implementation class BasePathModifierServlet
+ */
+public class BasePathModifierServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+ private static final Logger logger = LoggerFactory
+ .getLogger(BasePathModifierServlet.class);
+
+ private static final String API_BASE_PATH_SUFFIX = "/swagger/apis";
+ private static final String BASE_PATH_KEY = "basePath";
+
+ /**
+ * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
+ * response)
+ */
+ @Override
+ protected void doGet(HttpServletRequest request,
+ HttpServletResponse response) throws ServletException, IOException {
+ String resourcePath = request.getRequestURI().substring(
+ request.getContextPath().length());
+ logger.debug("Locating resource : {}.", resourcePath);
+ JsonObject jsonObject = null;
+ try {
+
+ InputStream stream = this.getServletContext().getResourceAsStream(
+ resourcePath);
+ if (stream == null) {
+ response.sendError(HttpServletResponse.SC_NOT_FOUND,
+ "API / API Listing not found");
+ return;
+ }
+ BufferedReader streamReader = new BufferedReader(
+ new InputStreamReader(stream, "UTF-8"));
+ StringBuilder responseStrBuilder = new StringBuilder();
+
+ String inputStr;
+ while ((inputStr = streamReader.readLine()) != null)
+ responseStrBuilder.append(inputStr);
+
+ JsonElement jelement = new JsonParser().parse(responseStrBuilder
+ .toString());
+ jsonObject = jelement.getAsJsonObject();
+
+ String basePath = jsonObject.get(BASE_PATH_KEY).getAsString();
+
+ // construct base path
+ StringBuilder requestURL = new StringBuilder();
+
+ requestURL.append(request.isSecure() ? "https://" : "http://")
+ .append(request.getServerName()).append(":")
+ .append(request.getServerPort());
+ if (!basePath.contains(requestURL)) {
+ String endPath = "";
+ if (basePath.contains(API_BASE_PATH_SUFFIX)) {
+ endPath = basePath.substring(basePath
+ .indexOf(API_BASE_PATH_SUFFIX));
+ }
+ basePath = requestURL + endPath;
+ logger.debug("Modified Base Path is {}", basePath);
+ jsonObject.addProperty(BASE_PATH_KEY, basePath);
+ }
+ } catch (Exception ex) {
+ logger.error("Error processing JSON data", ex);
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ "Could not read API Listing or APIs");
+ return;
+ }
+
+ try {
+ response.setContentType("application/json");
+ PrintWriter out = response.getWriter();
+ out.print(jsonObject);
+ out.flush();
+ } catch (Exception ex) {
+ logger.error("Error while writing response", ex);
+ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ "Internal Error while writing resposne");
+ }
+ }
+
+}
--- /dev/null
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+ <servlet>
+ <servlet-name>BasePathModifierServlet</servlet-name>
+ <servlet-class>org.opendaylight.controller.swaggerui.BasePathModifierServlet</servlet-class>
+ </servlet>
+ <!-- Mapping for all northbound json files aka apis-->
+ <servlet-mapping>
+ <servlet-name>BasePathModifierServlet</servlet-name>
+ <url-pattern>/apis/*</url-pattern>
+ </servlet-mapping>
+ <!-- Mapping to get list of available NB apis -->
+ <servlet-mapping>
+ <servlet-name>BasePathModifierServlet</servlet-name>
+ <url-pattern>/apilist.json</url-pattern>
+ </servlet-mapping>
+ <welcome-file-list>
+ <welcome-file>index.html</welcome-file>
+ </welcome-file-list>
+</web-app>
--- /dev/null
+body #header a#logo {
+ font-size: 1.5em;
+ font-weight: bold;
+ text-decoration: none;
+ background: transparent url(../images/logo.png) no-repeat left center;
+ padding: 20px 0 20px 70px;
+ color: white;
+}
+
+div.footer {
+ display: none;
+}
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Swagger UI</title>
+ <link href='//fonts.googleapis.com/css?family=Droid+Sans:400,700' rel='stylesheet' type='text/css'/>
+ <link href='css/highlight.default.css' media='screen' rel='stylesheet' type='text/css'/>
+ <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
+ <link href='css/custom.css' media='screen' rel='stylesheet' type='text/css'/>
+ <script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
+ <script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
+ <script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
+ <script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
+ <script src='lib/handlebars-1.0.rc.1.js' type='text/javascript'></script>
+ <script src='lib/underscore-min.js' type='text/javascript'></script>
+ <script src='lib/backbone-min.js' type='text/javascript'></script>
+ <script src='lib/swagger.js' type='text/javascript'></script>
+ <script src='swagger-ui.js' type='text/javascript'></script>
+ <script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
+
+ <script type="text/javascript">
+ $(function () {
+ window.swaggerUi = new SwaggerUi({
+ discoveryUrl:"./apilist.json",
+ apiKey:"",
+ dom_id:"swagger-ui-container",
+ supportHeaderParams: false,
+ supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
+ onComplete: function(swaggerApi, swaggerUi){
+ if(console) {
+ console.log("Loaded SwaggerUI")
+ console.log(swaggerApi);
+ console.log(swaggerUi);
+ }
+ $('pre code').each(function(i, e) {hljs.highlightBlock(e)});
+ },
+ onFailure: function(data) {
+ if(console) {
+ console.log("Unable to Load SwaggerUI");
+ console.log(data);
+ }
+ },
+ docExpansion: "none"
+ });
+
+ window.swaggerUi.load();
+ });
+
+ </script>
+</head>
+
+<body>
+ <div id='header'>
+ <div class="swagger-ui-wrap">
+ <a id="logo" href="http://www.opendaylight.org/">ODL NB API</a>
+ <form id='api_selector'>
+ <div class='input'>
+ <input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/>
+ </div>
+ <div class='input'>
+ <input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/>
+ </div>
+ <div class='input'><a id="explore" href="#">Explore</a></div>
+ </form>
+ </div>
+ </div>
+
+ <div id="message-bar" class="swagger-ui-wrap">
+
+ </div>
+
+ <div id="swagger-ui-container" class="swagger-ui-wrap">
+
+ </div>
+
+</body>
+
+</html>
@Override
public Node connect (String type, String connectionIdentifier, Map<ConnectionConstants, String> params) {
IPluginInConnectionService s = pluginService.get(type);
- if (s != null) return s.connect(connectionIdentifier, params);
+ if (s != null) {
+ return s.connect(connectionIdentifier, params);
+ }
return null;
}
for (String pluginType : this.pluginService.keySet()) {
IPluginInConnectionService s = pluginService.get(pluginType);
Node node = s.connect(connectionIdentifier, params);
- if (node != null) return node;
+ if (node != null) {
+ return node;
+ }
}
}
return null;
<module>opendaylight/hosttracker_new/implementation</module>
<module>opendaylight/containermanager/api</module>
<module>opendaylight/containermanager/implementation</module>
+ <module>opendaylight/containermanager/shell</module>
<module>opendaylight/appauth</module>
<module>opendaylight/switchmanager/api</module>
<module>opendaylight/switchmanager/implementation</module>
<!-- Karaf Distribution -->
<module>features/base</module>
+ <module>features/controller</module>
<module>opendaylight/dummy-console</module>
<module>opendaylight/karaf-branding</module>
<module>opendaylight/distribution/opendaylight-karaf</module>
</activation>
<modules>
<module>opendaylight/northbound/java-client</module>
+ <module>opendaylight/northbound/swagger-ui</module>
</modules>
</profile>
</profiles>