Merge "Add message to update the schema context of the InMemoryDOMDataStore"
authorEd Warnicke <eaw@cisco.com>
Mon, 23 Jun 2014 11:19:19 +0000 (11:19 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 23 Jun 2014 11:19:19 +0000 (11:19 +0000)
167 files changed:
opendaylight/commons/opendaylight/pom.xml
opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java
opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java
opendaylight/distribution/opendaylight/pom.xml
opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/FlowProgrammerAdapter.java
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java
opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ToSalConversionsUtils.java
opendaylight/md-sal/compatibility/sal-compatibility/src/test/java/org/opendaylight/controller/sal/compatibility/test/NodeMappingTest.java
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/pom.xml
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModuleFactory.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingBrokerImplModule.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBackwardsCompatibleDataBroker.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootBindingAwareBroker.java
opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java [new file with mode: 0644]
opendaylight/md-sal/sal-binding-config/src/main/yang/opendaylight-md-sal-binding.yang
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonNotExistingLeafTypeTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MediaTypesTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java
opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/nested-module.yang [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/pom.xml
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/DocProvider.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/api/ApiDocService.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocGenerator.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocServiceImpl.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/BaseYangSwaggerGenerator.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ModelGenerator.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/jaxrs/ApiDocApplication.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/jaxrs/JaxbContextResolver.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/model/builder/OperationBuilder.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/mountpoints/MountPointSwagger.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Api.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ApiDeclaration.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Operation.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Parameter.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Resource.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ResourceList.java
opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ResponseMessage.java
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/animated-overlay.gif [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_222222_256x240.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_228ef1_256x240.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ef8c08_256x240.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffd27a_256x240.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffffff_256x240.png [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/jquery-ui-1.10.4.custom.css [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/jquery-ui-1.10.4.custom.min.css [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/images/logo_small.png
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/index.html
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/lib/jquery-ui-1.11.0.min.js [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/lib/odl/list_mounts.js [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/lib/odl/swagger.js [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocGeneratorTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/DocGenTestHelper.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/DocGeneratorTest.java [deleted file]
opendaylight/md-sal/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/MountPointSwaggerTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-docgen/src/test/resources/yang/toaster_short.yang [new file with mode: 0644]
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectNameAttributeReadingStrategy.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectNameAttributeWritingStrategy.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Config.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleConfig.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/Runtime.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Commit.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Validate.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigXmlParser.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpc.java
opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpcElementResolved.java
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java
opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/ValidateTest.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java
opendaylight/netconf/netconf-api/pom.xml
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDocumentedException.java
opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/xml/XmlNetconfConstants.java [moved from opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java with 84% similarity]
opendaylight/netconf/netconf-api/src/test/java/org/opendaylight/controller/netconf/api/NetconfDocumentedExceptionTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/SubtreeFilter.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStartExi.java
opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultStopExi.java
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java
opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/SubtreeFilterTest.java [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/getConfig_reply_unfiltered.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/logback-test.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/0/post-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/0/pre-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/0/request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/1/post-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/1/pre-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/1/request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/2/post-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/2/pre-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/2/request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/3/post-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/3/pre-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/3/request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/4/post-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/4/pre-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/4/request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/5/post-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/5/pre-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/5/request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/6/post-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/6/pre-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/6/request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/7/post-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/7/pre-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/7/request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/8/post-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/8/pre-filter.xml [new file with mode: 0644]
opendaylight/netconf/netconf-impl/src/test/resources/subtree/8/request.xml [new file with mode: 0644]
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/Get.java
opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringOperationService.java
opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/exi/NetconfStartExiMessage.java
opendaylight/netconf/netconf-ssh/src/test/java/org/opendaylight/controller/netconf/netty/SSHTest.java
opendaylight/netconf/netconf-util/pom.xml
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/NetconfUtil.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/mapping/AbstractNetconfOperation.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfHelloMessage.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageUtil.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XMLNetconfUtil.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlElement.java
opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlUtil.java
opendaylight/northbound/swagger-ui/pom.xml [new file with mode: 0644]
opendaylight/northbound/swagger-ui/src/main/java/org/opendaylight/controller/swaggerui/BasePathModifierServlet.java [new file with mode: 0644]
opendaylight/northbound/swagger-ui/src/main/resources/WEB-INF/web.xml [new file with mode: 0644]
opendaylight/northbound/swagger-ui/src/main/resources/css/custom.css [new file with mode: 0644]
opendaylight/northbound/swagger-ui/src/main/resources/images/logo.png [new file with mode: 0644]
opendaylight/northbound/swagger-ui/src/main/resources/index.html [new file with mode: 0644]
opendaylight/sal/connection/implementation/src/main/java/org/opendaylight/controller/sal/connection/implementation/internal/ConnectionService.java
pom.xml

index 73ae6de..9902764 100644 (file)
     <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>
-    <commons.checkstyle.version>0.0.3-SNAPSHOT</commons.checkstyle.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.jasper>7.0.32.v201211201952</commons.jasper>
+    <commons.juli.version>7.0.32.v201211081135</commons.juli.version>
     <commons.lang.version>3.1</commons.lang.version>
     <commons.logback_settings.version>0.0.2-SNAPSHOT</commons.logback_settings.version>
     <commons.net.version>3.0.1</commons.net.version>
@@ -49,6 +56,8 @@
     <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>
       <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>
index 5b0196c..fac4d57 100644 (file)
@@ -185,7 +185,9 @@ public class JavaAttribute extends AbstractAttribute implements TypedAttribute {
             OpenType<?> innerCompositeType;
 
             if(isDerivedType(innerTypeBaseType, innerType)) {
-                innerCompositeType = getCompositeType(innerTypeBaseType, baseInnerTypeDefinition);
+                innerCompositeType = baseInnerTypeDefinition instanceof UnionTypeDefinition ?
+                        getCompositeTypeForUnion(baseInnerTypeDefinition) :
+                        getCompositeType(innerTypeBaseType, baseInnerTypeDefinition);
             } else {
                 innerCompositeType = SimpleTypeResolver.getSimpleType(innerType);
             }
index ebc5692..d76c094 100644 (file)
@@ -261,7 +261,7 @@ public class ConnectionManager implements IConnectionManager,
             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);
index 3916e05..7160acb 100644 (file)
         </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>
index d872bfd..8b07ce3 100644 (file)
                         <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>
index e5a9d3e..ba7377e 100644 (file)
@@ -163,6 +163,7 @@ public class FlowProgrammerAdapter implements IPluginInFlowProgrammerService, Sa
 
     @Override
     public void onFlowRemoved(final FlowRemoved notification) {
+        // notified upon remove flow rpc successfully invoked
         if (notification == null) {
             return;
         }
@@ -190,7 +191,25 @@ public class FlowProgrammerAdapter implements IPluginInFlowProgrammerService, Sa
 
     @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
index ba86ad9..f5701bf 100644 (file)
@@ -124,6 +124,14 @@ public final class NodeMapping {
         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);
     }
index da3477e..97a25bf 100644 (file)
@@ -55,6 +55,7 @@ import org.opendaylight.controller.sal.core.Node;
 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;
@@ -94,8 +95,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.acti
 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;
@@ -114,18 +118,53 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026
 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());
@@ -140,16 +179,7 @@ public class ToSalConversionsUtils {
         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(
@@ -356,7 +386,7 @@ public class ToSalConversionsUtils {
         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());
@@ -364,11 +394,30 @@ public class ToSalConversionsUtils {
             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();
index 041924a..b418e6d 100644 (file)
@@ -11,6 +11,7 @@ import org.junit.Assert;
 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;
 
 /**
@@ -55,4 +56,19 @@ public class NodeMappingTest {
         }
     }
 
+    /**
+     * 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());
+    }
+
 }
index c6a9efe..7eee5c8 100644 (file)
@@ -13,7 +13,7 @@ import org.opendaylight.yangtools.concepts.ListenerRegistration;
 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();
 
diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingService.java
new file mode 100644 (file)
index 0000000..ccce73c
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 {
+
+}
index 2470ba5..7fc4676 100644 (file)
                             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>
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java
new file mode 100644 (file)
index 0000000..17cd67a
--- /dev/null
@@ -0,0 +1,75 @@
+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
+    }
+
+}
diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModuleFactory.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModuleFactory.java
new file mode 100644 (file)
index 0000000..763e6ad
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+* 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;
+    }
+}
index 44a508c..188272f 100644 (file)
@@ -31,14 +31,14 @@ public final class BindingBrokerImplModule extends
 \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
@@ -63,23 +63,24 @@ public final class BindingBrokerImplModule extends
     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
@@ -89,7 +90,7 @@ public final class BindingBrokerImplModule extends
         return bundleContext;\r
     }\r
 \r
-    public void setBundleContext(BundleContext bundleContext) {\r
+    public void setBundleContext(final BundleContext bundleContext) {\r
         this.bundleContext = bundleContext;\r
     }\r
 }\r
index 647ca85..7467e54 100644 (file)
@@ -59,10 +59,9 @@ public final class ForwardedCompatibleDataBrokerImplModule extends
         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);
index 2a866a9..b09cd1c 100644 (file)
@@ -23,6 +23,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
 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;
@@ -33,6 +34,7 @@ import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMapping
 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -40,7 +42,7 @@ import com.google.common.base.Objects;
 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
@@ -53,12 +55,14 @@ public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBr
     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() {
@@ -286,4 +290,9 @@ public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBr
         // NOOP
     }
 
+    @Override
+    public void close() throws Exception {
+        this.schemaListenerRegistration.close();
+    }
+
 }
index e08e9a4..bf18454 100644 (file)
@@ -35,6 +35,7 @@ import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
 import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
 import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
 import org.opendaylight.yangtools.concepts.Delegator;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
@@ -60,12 +61,11 @@ public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDat
     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.");
     }
@@ -129,12 +129,6 @@ public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDat
         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<>();
index fb06b13..5ab088e 100644 (file)
@@ -18,6 +18,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 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;
@@ -41,8 +42,8 @@ import com.google.common.util.concurrent.ListenableFuture;
  */
 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
index 3fef544..00c9f1e 100644 (file)
@@ -16,6 +16,7 @@ import org.opendaylight.yangtools.yang.binding.util.BindingReflections
 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 {
 
@@ -28,7 +29,7 @@ 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) [
@@ -53,7 +54,7 @@ class RuntimeCodeGenerator extends AbstractRuntimeCodeGenerator {
                     '''
                 ]
             ]
-            return createdCls.toClass(iface.classLoader).newInstance
+            return createdCls.toClass(iface.classLoader).newInstance as RpcService
         ]
     }
 
@@ -63,7 +64,7 @@ class RuntimeCodeGenerator extends AbstractRuntimeCodeGenerator {
             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) [
@@ -106,7 +107,7 @@ class RuntimeCodeGenerator extends AbstractRuntimeCodeGenerator {
                     '''
                 ]
             ]
-            return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance
+            return  targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as RpcService
         ];
     }
 
index 75d44db..8acad1b 100644 (file)
@@ -7,7 +7,9 @@
  */
 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;
@@ -32,13 +34,13 @@ import org.opendaylight.yangtools.yang.binding.RpcService;
 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);
 
@@ -50,7 +52,9 @@ public class RootBindingAwareBroker implements //
 
     private NotificationProviderService notificationBroker;
 
-    private DataProviderService dataBroker;
+    private DataProviderService legacyDataBroker;
+
+    private BindingDataBroker dataBroker;
 
     private MountPointManagerImpl mountManager;
 
@@ -58,7 +62,7 @@ public class RootBindingAwareBroker implements //
         return mountManager;
     }
 
-    public void setMountManager(MountPointManagerImpl mountManager) {
+    public void setMountManager(final MountPointManagerImpl mountManager) {
         this.mountManager = mountManager;
     }
 
@@ -66,11 +70,12 @@ public class RootBindingAwareBroker implements //
 
     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;
     }
@@ -80,7 +85,7 @@ public class RootBindingAwareBroker implements //
     }
 
     public DataProviderService getDataBroker() {
-        return this.dataBroker;
+        return this.legacyDataBroker;
     }
 
     public NotificationProviderService getNotificationBroker() {
@@ -95,16 +100,16 @@ public class RootBindingAwareBroker implements //
         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() {
@@ -113,29 +118,30 @@ public class RootBindingAwareBroker implements //
 
         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);
     }
@@ -146,34 +152,38 @@ public class RootBindingAwareBroker implements //
     }
 
     @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;
+    }
 }
index 428025a..4456dea 100644 (file)
@@ -42,6 +42,12 @@ module opendaylight-sal-binding-broker-impl {
         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;
@@ -62,6 +68,26 @@ module opendaylight-sal-binding-broker-impl {
         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'";
@@ -123,22 +149,15 @@ module opendaylight-sal-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;
             }
         }
     }
index ae65c88..623b2fd 100644 (file)
@@ -48,7 +48,6 @@ import org.opendaylight.controller.sal.dom.broker.impl.DataStoreStatsWrapper;
 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;
@@ -57,7 +56,6 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 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;
@@ -70,7 +68,7 @@ import com.google.common.collect.ImmutableClassToInstanceMap;
 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();
@@ -101,22 +99,19 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
 
     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;
@@ -128,6 +123,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
         this.startWithSchema = startWithSchema;
     }
 
+    @Deprecated
     public void startDomDataStore() {
         checkState(dataStore == null, "DataStore already started.");
         checkState(biDataImpl != null, "Dom Data Broker not present");
@@ -140,7 +136,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
         } else {
             dataStore = schemaAwareDataStore;
         }
-
+        mockSchemaService.registerSchemaServiceListener(schemaAwareDataStore);
         biDataImpl.registerConfigurationReader(TREE_ROOT, dataStore);
         biDataImpl.registerOperationalReader(TREE_ROOT, dataStore);
         biDataImpl.registerCommitHandler(TREE_ROOT, dataStore);
@@ -166,9 +162,9 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
 
         biCompatibleBroker = new BackwardsCompatibleDataBroker(newDOMDataBroker);
 
-        schemaListeners.add(configStore);
-        schemaListeners.add(operStore);
-        schemaListeners.add(biCompatibleBroker);
+        mockSchemaService.registerSchemaServiceListener(configStore);
+        mockSchemaService.registerSchemaServiceListener(operStore);
+        mockSchemaService.registerSchemaServiceListener(biCompatibleBroker);
         biDataLegacyBroker = biCompatibleBroker;
     }
 
@@ -188,7 +184,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
         baBrokerImpl.getMountManager().setDataCommitExecutor(executor);
         baBrokerImpl.getMountManager().setNotificationExecutor(executor);
         baBrokerImpl.setRpcBroker(new RpcProviderRegistryImpl("test"));
-        baBrokerImpl.setDataBroker(baData);
+        baBrokerImpl.setLegacyDataBroker(baData);
         baBrokerImpl.setNotificationBroker(baNotifyImpl);
         baBrokerImpl.start();
     }
@@ -268,20 +264,11 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
     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() {
@@ -340,8 +327,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
     }
 
     public void startNewBindingDataBroker() {
-        ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl, executor);
-        schemaListeners.add(forwarded);
+        ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl,mockSchemaService, executor);
         baData = forwarded;
     }
 
@@ -353,7 +339,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
     private void startDomBroker() {
         checkState(executor != null);
         biBrokerImpl = new BrokerImpl();
-        biBrokerImpl.setRouter(new SchemaAwareRpcBroker("/", this));
+        biBrokerImpl.setRouter(new SchemaAwareRpcBroker("/", mockSchemaService));
 
     }
 
@@ -429,4 +415,6 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
     public MountProvisionService getDomMountProviderService() {
         return biMountImpl;
     }
+
+
 }
diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java
new file mode 100644 (file)
index 0000000..c8acbcd
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.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
index 38f0da3..4a2ec8a 100644 (file)
@@ -22,6 +22,11 @@ module opendaylight-md-sal-binding {
         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";
index e78f2b3..4da727f 100644 (file)
@@ -10,17 +10,20 @@ package org.opendaylight.controller.sal.connect.netconf.listener;
 import java.util.ArrayDeque;
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Queue;
+import 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;
@@ -35,6 +38,8 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 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;
 
@@ -45,12 +50,9 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
 
     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) {
@@ -62,14 +64,21 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
     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,
@@ -77,37 +86,75 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
         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
@@ -123,73 +170,109 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
         }
     }
 
-    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 );
                 }
             }
         });
@@ -215,12 +298,11 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener,
     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;
         }
     }
 }
index 1284d6d..08a5822 100644 (file)
@@ -12,14 +12,17 @@ import java.util.ArrayList;
 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;
@@ -36,6 +39,7 @@ import org.w3c.dom.Element;
 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;
 
@@ -99,20 +103,68 @@ public class NetconfMessageTransformUtil {
         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;
         }
     }
 
index 5ac32b5..c1b9f7b 100644 (file)
@@ -26,7 +26,7 @@ import java.util.concurrent.Executors;
 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;
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java
new file mode 100644 (file)
index 0000000..391bf9c
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * 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;
+    }
+}
index 4d9b198..056be72 100644 (file)
@@ -107,13 +107,15 @@ public interface RestconfService {
     @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:.+}")
index c0ce90e..9700d48 100644 (file)
@@ -8,6 +8,13 @@
  */
 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;
@@ -20,12 +27,10 @@ import java.util.HashMap;
 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;
@@ -49,6 +54,7 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdent
 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;
@@ -67,13 +73,6 @@ import org.opendaylight.yangtools.yang.model.util.EmptyType;
 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();
 
@@ -252,7 +251,8 @@ public class RestconfImpl implements RestconfService {
                 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);
 
@@ -566,7 +566,7 @@ public class RestconfImpl implements RestconfService {
     }
 
     @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();
@@ -577,11 +577,57 @@ public class RestconfImpl implements RestconfService {
             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();
@@ -592,6 +638,7 @@ public class RestconfImpl implements RestconfService {
             data = broker.readOperationalData(iiWithData.getInstanceIdentifier());
         }
 
+        data = pruneDataAtDepth( data, parseDepthParameter( info ) );
         return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint);
     }
 
index 42e1e3f..24dba17 100644 (file)
@@ -12,15 +12,14 @@ import static org.junit.Assert.assertTrue;
 
 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;
@@ -28,6 +27,7 @@ import org.opendaylight.yangtools.yang.data.api.MutableSimpleNode;
 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;
@@ -65,9 +65,9 @@ public class CnSnToJsonNotExistingLeafTypeTest extends YangAndXmlAndDataSchemaLo
 
     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);
 
index 2037fd4..319603d 100644 (file)
@@ -23,6 +23,7 @@ import java.io.UnsupportedEncodingException;
 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;
@@ -96,21 +97,21 @@ public class MediaTypesTest extends 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
@@ -118,21 +119,21 @@ public class MediaTypesTest extends JerseyTest {
         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
index 893622f..f0a232f 100644 (file)
@@ -7,15 +7,17 @@
  */
 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;
@@ -24,17 +26,23 @@ import java.text.SimpleDateFormat;
 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;
@@ -45,6 +53,7 @@ import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
 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;
@@ -52,10 +61,28 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 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;
@@ -588,4 +615,314 @@ public class RestGetOperationTest extends JerseyTest {
         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 );
+    }
 }
index 3f984c2..e146cf8 100644 (file)
@@ -34,6 +34,7 @@ import javax.ws.rs.core.Application;
 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;
@@ -220,7 +221,7 @@ public class RestconfDocumentedExceptionMapperTest extends JerseyTest {
 
     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,
@@ -776,8 +777,8 @@ public class RestconfDocumentedExceptionMapperTest extends JerseyTest {
 
         // 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();
 
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/nested-module.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/nested-module.yang
new file mode 100644 (file)
index 0000000..590743c
--- /dev/null
@@ -0,0 +1,47 @@
+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
index 8954c05..79a3434 100644 (file)
       <artifactId>guava</artifactId>
     </dependency>
 
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>3.3.2</version>
+      <!--$NO-MVN-MAN-VER$ -->
+    </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>
index 934412d..31f4253 100644 (file)
@@ -9,11 +9,17 @@ package org.opendaylight.controller.sal.rest.doc;
 
 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;
@@ -22,24 +28,37 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer;
 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");
     }
 
@@ -49,42 +68,45 @@ public class DocProvider implements BundleActivator, ServiceTrackerCustomizer<Br
     }
 
     @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);
     }
 }
index 3542d6a..2646a6a 100644 (file)
@@ -16,47 +16,83 @@ import javax.ws.rs.core.MediaType;
 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);
+
 }
index bcd11bc..82409d2 100644 (file)
@@ -7,76 +7,44 @@
  */
 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() {
@@ -87,240 +55,7 @@ public class ApiDocGenerator {
      *
      * @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();
-    }
-
 }
index 241c8b8..c95f41c 100644 (file)
  */
 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();
-  }
 }
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/BaseYangSwaggerGenerator.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/BaseYangSwaggerGenerator.java
new file mode 100644 (file)
index 0000000..68d31de
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * 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;
+    }
+}
index 719dd78..0e929af 100644 (file)
@@ -18,6 +18,7 @@ import java.util.Set;
 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;
@@ -58,7 +59,7 @@ import org.slf4j.LoggerFactory;
  */
 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";
@@ -88,109 +89,123 @@ public class ModelGenerator {
     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();
@@ -201,49 +216,62 @@ public class ModelGenerator {
         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;
     }
@@ -253,7 +281,7 @@ public class ModelGenerator {
      * @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);
 
@@ -274,12 +302,13 @@ public class ModelGenerator {
      * @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();
@@ -295,23 +324,23 @@ public class ModelGenerator {
         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);
         }
     }
@@ -319,9 +348,9 @@ public class ModelGenerator {
     /**
      * Parses a ListSchema node.
      *
-     * Due to a limitation of the RAML--->JAX-RS tool, sub-properties
-     * must be in a separate JSON schema file. Hence, we have to write
-     * some properties to a new file, while continuing to process the rest.
+     * 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
@@ -329,7 +358,8 @@ public class ModelGenerator {
      * @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();
@@ -340,20 +370,19 @@ public class ModelGenerator {
         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;
@@ -366,7 +395,7 @@ public class ModelGenerator {
      * @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();
@@ -384,7 +413,7 @@ public class ModelGenerator {
      * @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();
@@ -399,27 +428,28 @@ public class ModelGenerator {
      * @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);
@@ -432,29 +462,32 @@ public class ModelGenerator {
      * @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);
@@ -467,10 +500,11 @@ public class ModelGenerator {
      * @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));
@@ -482,14 +516,15 @@ public class ModelGenerator {
      * @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();
@@ -497,27 +532,26 @@ public class ModelGenerator {
         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
      */
index bec34b2..e833c61 100644 (file)
@@ -7,18 +7,19 @@
  */
 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;
+    }
 }
index 5508507..66fc80f 100644 (file)
@@ -7,37 +7,39 @@
  */
 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
+    }
 }
index fc890d5..9a33ee3 100644 (file)
  */
 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;
+        }
     }
-  }
 }
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/mountpoints/MountPointSwagger.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/mountpoints/MountPointSwagger.java
new file mode 100644 (file)
index 0000000..b996bf1
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * 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;
+    }
+
+}
index a2be741..cc45875 100644 (file)
@@ -10,28 +10,29 @@ package org.opendaylight.controller.sal.rest.doc.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;
+    }
 }
index e89bdaf..4ba0ab2 100644 (file)
@@ -7,85 +7,86 @@
  */
 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);
+    }
 }
index d1150fd..ba0c796 100644 (file)
@@ -10,81 +10,82 @@ package org.opendaylight.controller.sal.rest.doc.swagger;
 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;
+    }
 }
index dd56b9e..41cec4d 100644 (file)
@@ -8,54 +8,55 @@
 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;
+    }
 }
index c5c3979..cb5b200 100644 (file)
@@ -8,27 +8,28 @@
 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;
+    }
 }
index 9b1a106..48362dc 100644 (file)
@@ -10,36 +10,37 @@ package org.opendaylight.controller.sal.rest.doc.swagger;
 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;
+    }
 }
index 6c23099..d82e1f4 100644 (file)
@@ -8,27 +8,28 @@
 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;
+    }
 }
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/animated-overlay.gif b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/animated-overlay.gif
new file mode 100644 (file)
index 0000000..d441f75
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/animated-overlay.gif differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png
new file mode 100644 (file)
index 0000000..a28d678
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png
new file mode 100644 (file)
index 0000000..ab561d2
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png
new file mode 100644 (file)
index 0000000..be5c4c6
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png
new file mode 100644 (file)
index 0000000..c955a2c
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png
new file mode 100644 (file)
index 0000000..de7c46d
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png
new file mode 100644 (file)
index 0000000..6cc858e
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png
new file mode 100644 (file)
index 0000000..653f494
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png
new file mode 100644 (file)
index 0000000..663b112
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png
new file mode 100644 (file)
index 0000000..ee995d9
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_222222_256x240.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_222222_256x240.png
new file mode 100644 (file)
index 0000000..c1cb117
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_222222_256x240.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_228ef1_256x240.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_228ef1_256x240.png
new file mode 100644 (file)
index 0000000..3a0140c
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_228ef1_256x240.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ef8c08_256x240.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ef8c08_256x240.png
new file mode 100644 (file)
index 0000000..036ee07
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ef8c08_256x240.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffd27a_256x240.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffd27a_256x240.png
new file mode 100644 (file)
index 0000000..8b6c058
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffd27a_256x240.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffffff_256x240.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffffff_256x240.png
new file mode 100644 (file)
index 0000000..4f624bb
Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffffff_256x240.png differ
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/jquery-ui-1.10.4.custom.css b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/jquery-ui-1.10.4.custom.css
new file mode 100644 (file)
index 0000000..a9aa9c4
--- /dev/null
@@ -0,0 +1,1178 @@
+/*! jQuery UI - v1.10.4 - 2014-06-03
+* http://jqueryui.com
+*