Merge "Bug 7172 - Correct error-info for missing-attribute errors"
authorTomas Cere <tcere@cisco.com>
Tue, 29 Nov 2016 13:08:31 +0000 (13:08 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 29 Nov 2016 13:08:31 +0000 (13:08 +0000)
184 files changed:
features/netconf-connector/pom.xml
features/netconf-connector/src/main/features/features.xml
features/netconf/pom.xml
features/netconf/src/main/features/features.xml
netconf/mdsal-netconf-impl/src/main/resources/org/opendaylight/blueprint/mdsal-netconf-impl.xml
netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/monitoring/MdsalMonitoringMapperFactory.java [moved from netconf/mdsal-netconf-monitoring/src/main/java/org/opendaylight/controller/config/yang/netconf/mdsal/monitoring/MdSalMonitoringMapperFactory.java with 96% similarity]
netconf/mdsal-netconf-monitoring/src/main/resources/org/opendaylight/blueprint/mdsal-netconf-monitoring.xml
netconf/mdsal-netconf-tcp/pom.xml [new file with mode: 0644]
netconf/mdsal-netconf-tcp/src/main/resources/org/opendaylight/blueprint/netconf-tcp.xml [new file with mode: 0755]
netconf/mdsal-netconf-yang-library/src/main/java/org/opendaylight/netconf/mdsal/yang/library/SchemaServiceToMdsalWriter.java
netconf/models/ietf-netconf-monitoring/src/main/java/org/opendaylight/yang/gen/v1/urn/ietf/params/xml/ns/yang/ietf/netconf/monitoring/rev101004/netconf/state/schemas/SchemaLocationBuilder.java
netconf/netconf-artifacts/pom.xml
netconf/netconf-client/src/main/java/org/opendaylight/controller/config/yang/config/netconf/client/dispatcher/NetconfClientDispatcherModule.java [deleted file]
netconf/netconf-client/src/main/java/org/opendaylight/controller/config/yang/config/netconf/client/dispatcher/NetconfClientDispatcherModuleFactory.java [deleted file]
netconf/netconf-client/src/main/yang/odl-netconfig-client-cfg.yang [deleted file]
netconf/netconf-config-dispatcher/pom.xml [deleted file]
netconf/netconf-config-dispatcher/src/main/yang/odl-netconf-cfg.yang [deleted file]
netconf/netconf-config-dispatcher/src/main/yang/odl-netconfig-client-cfg.yang [deleted file]
netconf/netconf-config/pom.xml
netconf/netconf-config/src/main/resources/initial/01-netconf.xml [deleted file]
netconf/netconf-config/src/main/resources/org/opendaylight/blueprint/netconf-config.xml [new file with mode: 0644]
netconf/netconf-connector-config/pom.xml
netconf/netconf-connector-config/src/main/resources/initial/99-netconf-connector.xml [deleted file]
netconf/netconf-connector-config/src/main/resources/org/opendaylight/blueprint/netconf-connector.xml [new file with mode: 0644]
netconf/netconf-impl/pom.xml
netconf/netconf-impl/src/main/java/org/opendaylight/netconf/impl/osgi/NetconfCapabilityMonitoringService.java
netconf/netconf-impl/src/main/java/org/opendaylight/netconf/impl/osgi/NetconfImplActivator.java
netconf/netconf-impl/src/test/java/org/opendaylight/netconf/impl/NetconfServerSessionListenerTest.java
netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/osgi/NetconfSSHActivator.java
netconf/netconf-ssh/src/test/java/org/opendaylight/netconf/netty/EchoServer.java
netconf/netconf-ssh/src/test/java/org/opendaylight/netconf/netty/ProxyServer.java
netconf/netconf-ssh/src/test/java/org/opendaylight/netconf/netty/SSHTest.java
netconf/netconf-ssh/src/test/java/org/opendaylight/netconf/ssh/authentication/SSHServerTest.java
netconf/netconf-tcp/src/main/java/org/opendaylight/controller/config/yang/netconf/northbound/tcp/NetconfNorthboundTcpModule.java [deleted file]
netconf/netconf-tcp/src/main/java/org/opendaylight/controller/config/yang/netconf/northbound/tcp/NetconfNorthboundTcpModuleFactory.java [deleted file]
netconf/netconf-tcp/src/main/java/org/opendaylight/netconf/tcp/NetconfNorthboundTcpServer.java [new file with mode: 0644]
netconf/netconf-tcp/src/main/java/org/opendaylight/netconf/tcp/osgi/NetconfTCPActivator.java
netconf/netconf-tcp/src/main/yang/netconf-northbound-tcp.yang [deleted file]
netconf/netconf-topology-config/pom.xml
netconf/netconf-topology-config/src/main/resources/initial/02-netconf-topology.xml [deleted file]
netconf/netconf-topology-config/src/main/resources/org/opendaylight/blueprint/netconf-topology.xml [new file with mode: 0755]
netconf/netconf-topology-singleton/src/main/resources/org/opendaylight/blueprint/netconf-topology-singleton.xml
netconf/netconf-topology/pom.xml
netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/NetconfTopologyModule.java [deleted file]
netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/NetconfTopologyModuleFactory.java [deleted file]
netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/shared/schema/repository/SchemaRepositoryImplModule.java [deleted file]
netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/shared/schema/repository/SchemaRepositoryImplModuleFactory.java [deleted file]
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/NetconfConnectorFactory.java [new file with mode: 0644]
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/NetconfTopology.java [moved from netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/NetconfTopology.java with 94% similarity]
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/SchemaRepositoryProvider.java [moved from netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/SchemaRepositoryProvider.java with 91% similarity]
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfConnectorFactoryImpl.java [new file with mode: 0644]
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java
netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/SchemaRepositoryProviderImpl.java [new file with mode: 0644]
netconf/netconf-topology/src/main/yang/netconf-topology.yang [deleted file]
netconf/netconf-topology/src/main/yang/shared-schema-repository.yang [deleted file]
netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java
netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/osgi/NetconfConfigUtil.java
netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/osgi/NetconfConfiguration.java
netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/osgi/NetconfConfigurationActivator.java
netconf/netconf-util/src/main/java/org/opendaylight/netconf/util/osgi/NetconfConfigurationHolder.java
netconf/netconf-util/src/test/java/org/opendaylight/netconf/util/osgi/NetconfConfigUtilTest.java
netconf/netconf-util/src/test/java/org/opendaylight/netconf/util/osgi/NetconfConfigurationActivatorTest.java [new file with mode: 0644]
netconf/netconf-util/src/test/java/org/opendaylight/netconf/util/osgi/NetconfConfigurationTest.java [new file with mode: 0644]
netconf/pom.xml
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceDataBroker.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/AbstractWriteTx.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/TxChain.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/TxListener.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/TxChainTest.java [new file with mode: 0644]
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/TesttoolParameters.java
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/Rpc.java [new file with mode: 0644]
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/RpcMapping.java [new file with mode: 0644]
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/Rpcs.java [new file with mode: 0644]
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/SettableOperationProvider.java [new file with mode: 0644]
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/SettableRpc.java [new file with mode: 0644]
netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/XmlData.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/RestconfWrapperProviders.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaExportContentYangBodyWriter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaExportContentYinBodyWriter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/api/Draft02.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/DepthAwareNormalizedNodeWriter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeJsonBodyWriter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/NormalizedNodeXmlBodyWriter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/PATCHJsonBodyWriter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/PATCHXmlBodyWriter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfCompositeWrapper.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/api/JSONRestconfService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/BrokerFacade.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/JSONRestconfServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/NormalizedNodeContext.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/QueryParametersParser.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/StatisticsRestconfServiceWrapper.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/restconf/impl/WriterParameters.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/Notificator.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/Draft18.java [moved from restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/Draft17.java with 94% similarity]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/RestConnectorProvider.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/RestconfApplication.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/common/references/SchemaContextRef.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/common/wrapper/services/ServicesWrapperImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/handlers/NotificationServiceHandler.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/handlers/RpcServiceHandler.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/JsonNormalizedNodeBodyReader.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/JsonToPATCHBodyReader.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/NormalizedNodeJsonBodyWriter.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/NormalizedNodeXmlBodyWriter.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriter.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/XmlNormalizedNodeBodyReader.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/XmlToPATCHBodyReader.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/parser/builder/YangInstanceIdentifierDeserializer.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/services/api/RestconfModulesService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/services/api/RestconfOperationsService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/services/api/RestconfSchemaService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/services/api/RestconfStreamsService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/services/impl/RestconfModulesServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/rest/services/impl/RestconfStreamsServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/api/RestconfDataService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/api/RestconfInvokeOperationsService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/api/RestconfStreamsSubscriptionService.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfInvokeOperationsServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/CreateStreamUtil.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ParametersUtil.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PostDataTransactionUtil.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtil.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ReadDataTransactionUtil.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfDataServiceConstant.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfStreamsConstants.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserFieldsParameter.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserIdentifier.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtil.java
restconf/sal-rest-connector/src/main/resources/WEB-INF/web.xml
restconf/sal-rest-connector/src/main/yang/sal-remote-augment.yang
restconf/sal-rest-connector/src/main/yang/subscribe-to-notification.yang [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/input/to/cnsn/test/RestPutListDataTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/JSONRestconfServiceImplTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MediaTypesTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestDeleteOperationTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPostOperationTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutConfigTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestPutOperationTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/URIParametersParsing.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/streams/listeners/NotificationListenerTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/netconf/sal/rest/impl/DepthAwareNormalizedNodeWriterTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriterDepthTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriterFieldsTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriterParametersTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/services/impl/RestconfModulesServiceTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/services/impl/RestconfModulesServiceTestUtils.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/services/impl/RestconfStreamsServiceTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImplTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfInvokeOperationsServiceImplTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImplTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/CreateStreamUtilTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/ParametersUtilTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/PostDataTransactionUtilTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtilTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/ReadDataTransactionUtilTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtilTest.java
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/parser/ParserFieldsParameterTest.java [new file with mode: 0644]
restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtilTest.java
restconf/sal-rest-connector/src/test/resources/datastore-and-scope-specification/sal-remote-augment.yang
restconf/sal-rest-connector/src/test/resources/full-versions/yangs/subscribe-to-notification.yang [new file with mode: 0644]
restconf/sal-rest-connector/src/test/resources/jukebox/augmented-jukebox@2016-05-05.yang [new file with mode: 0644]
restconf/sal-rest-connector/src/test/resources/notifications/notifi-module.yang [new file with mode: 0644]
restconf/sal-rest-connector/src/test/resources/notifications/subscribe-to-notification.yang [new file with mode: 0644]
restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_DOWN.xml [new file with mode: 0644]
restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_NUMBER.xml [new file with mode: 0644]
restconf/sal-rest-connector/src/test/resources/ordered/by/user/ordered-by-user-example.yang [new file with mode: 0644]

index 681047f6600ddbfa5eb2f20758e33866150603f9..0750c52c2d0ec28f0c2ee5db90cc6aae50d9c320 100644 (file)
     <netconf.version>1.2.0-SNAPSHOT</netconf.version>
     <netconf.connector.version>1.5.0-SNAPSHOT</netconf.connector.version>
     <yangtools.version>1.1.0-SNAPSHOT</yangtools.version>
-
-    <features.file>features.xml</features.file>
-    <config.configfile.directory>etc/opendaylight/karaf</config.configfile.directory>
-    <config.netconf.client.configfile>01-netconf.xml</config.netconf.client.configfile>
-    <config.netconf.topology.configfile>02-netconf-topology.xml</config.netconf.topology.configfile>
-    <config.netconf.connector.configfile>99-netconf-connector.xml</config.netconf.connector.configfile>
   </properties>
 
   <dependencyManagement>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>netconf-topology-config</artifactId>
-      <classifier>config</classifier>
-      <type>xml</type>
     </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <groupId>${project.groupId}</groupId>
       <artifactId>netconf-topology-singleton</artifactId>
     </dependency>
-
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>netconf-connector-config</artifactId>
-      <version>${netconf.version}</version>
-      <type>xml</type>
-      <classifier>config</classifier>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>netconf-config</artifactId>
     </dependency>
   </dependencies>
 
index 10fbb4f03a59a5d19b5363259a70ae8acc77ca3c..c6a4263fbfc9f3f647a39f7b04145b02bf7706de 100644 (file)
         <bundle>mvn:org.opendaylight.controller.model/model-inventory/{{VERSION}}</bundle>
         <bundle>mvn:org.opendaylight.netconf/netconf-topology/{{VERSION}}</bundle>
         <bundle>mvn:org.opendaylight.netconf/sal-netconf-connector/{{VERSION}}</bundle>
-        <configfile finalname='${config.configfile.directory}/${config.netconf.client.configfile}'>mvn:org.opendaylight.netconf/netconf-config/{{VERSION}}/xml/config</configfile>
+        <bundle>mvn:org.opendaylight.netconf/netconf-config/{{VERSION}}</bundle>
     </feature>
 
     <feature name='odl-netconf-connector-ssh' version='${project.version}' description="OpenDaylight :: Netconf Connector :: Netconf Connector + Netconf SSH Server + loopback connection configuration">
         <feature version='${project.version}'>odl-netconf-topology</feature>
-        <configfile finalname="${config.configfile.directory}/${config.netconf.connector.configfile}">mvn:org.opendaylight.netconf/netconf-connector-config/{{VERSION}}/xml/config</configfile>
+        <bundle>mvn:org.opendaylight.netconf/netconf-connector-config/{{VERSION}}</bundle>
     </feature>
 
     <feature name='odl-netconf-topology' version='${project.version}' description="OpenDaylight :: Netconf Topology :: Netconf Connector + Netconf SSH Server + Netconf configuration via config topology datastore">
         <feature version='${project.version}'>odl-netconf-ssh</feature>
         <feature version='${project.version}'>odl-netconf-connector</feature>
-        <configfile finalname='${config.configfile.directory}/${config.netconf.topology.configfile}'>mvn:org.opendaylight.netconf/netconf-topology-config/{{VERSION}}/xml/config</configfile>
+        <bundle>mvn:org.opendaylight.netconf/netconf-topology-config/{{VERSION}}</bundle>
     </feature>
 
     <feature name='odl-netconf-clustered-topology' version='${project.version}' description="OpenDaylight :: Clustered Netconf Topology :: Netconf Connector + Netconf SSH Server">
index 6656a4871f1a47c68fa6346687bde63d9d6c4057..0eb098842bb705930d4dbc274a2ae8ab1573fb19 100644 (file)
@@ -31,9 +31,6 @@
     <protocol-framework.version>0.9.0-SNAPSHOT</protocol-framework.version>
     <yangtools.version>1.1.0-SNAPSHOT</yangtools.version>
     <sshd-core.version>0.14.0</sshd-core.version>
-
-    <config.configfile.directory>etc/opendaylight/karaf</config.configfile.directory>
-    <config.netconf.client.configfile>01-netconf.xml</config.netconf.client.configfile>
   </properties>
 
   <dependencyManagement>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>netconf-config</artifactId>
-      <type>xml</type>
-      <classifier>config</classifier>
-    </dependency>
-    <dependency>
-      <groupId>${project.groupId}</groupId>
-      <artifactId>netconf-connector-config</artifactId>
-      <type>xml</type>
-      <classifier>config</classifier>
     </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <groupId>org.opendaylight.netconf</groupId>
       <artifactId>mdsal-netconf-ssh</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.netconf</groupId>
+      <artifactId>mdsal-netconf-tcp</artifactId>
+    </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>mdsal-netconf-monitoring</artifactId>
index 175f417dd06e9da0761d7d1560617e0011038416..cdf0a6362a1b670b8005a0678e85061a04475b88 100644 (file)
@@ -92,7 +92,7 @@
     <feature version='${config.version}'>odl-config-netty</feature>
     <feature version='${project.version}'>odl-netconf-netty-util</feature>
     <bundle>mvn:org.opendaylight.netconf/netconf-client/{{VERSION}}</bundle>
-    <configfile finalname='${config.configfile.directory}/${config.netconf.client.configfile}'>mvn:org.opendaylight.netconf/netconf-config/{{VERSION}}/xml/config</configfile>
+    <bundle>mvn:org.opendaylight.netconf/netconf-config/{{VERSION}}</bundle>
   </feature>
 
   <feature name='odl-netconf-monitoring' version='${project.version}' description="OpenDaylight :: Netconf :: Monitoring">
     <feature version='${project.version}'>odl-netconf-client</feature>
     <feature version='${controller.mdsal.version}'>odl-mdsal-broker</feature>
     <bundle>mvn:org.opendaylight.netconf/mdsal-netconf-ssh/{{VERSION}}</bundle>
+    <bundle>mvn:org.opendaylight.netconf/mdsal-netconf-tcp/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.netconf/mdsal-netconf-notification/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.netconf/mdsal-netconf-connector/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.netconf/mdsal-netconf-monitoring/{{VERSION}}</bundle>
index f75383c90f7b2384527b8283bd114383a6d7e7ba..9d785eedde83ba4c01ba9e24b6bf8d15ffb9f071 100755 (executable)
 
     <!--This is the MD-SAL netconf server implementation blueprint xml file-->
 
-    <reference id="globalBossGroup" interface="io.netty.channel.EventLoopGroup" odl:type="global-boss-group"/>
-    <reference id="globalWorkerGroup" interface="io.netty.channel.EventLoopGroup" odl:type="global-worker-group"/>
-    <reference id="global-timer" interface="io.netty.util.Timer" odl:type="global-timer"/>
-    <reference id="scheduledThreadPool" interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"/>
+    <reference id="globalBossGroup"
+               interface="io.netty.channel.EventLoopGroup"
+               odl:type="global-boss-group"/>
+    <reference id="globalWorkerGroup"
+               interface="io.netty.channel.EventLoopGroup"
+               odl:type="global-worker-group"/>
+    <reference id="global-timer"
+               interface="io.netty.util.Timer"
+               odl:type="global-timer"/>
+    <reference id="scheduledThreadPool"
+               interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"
+               odl:type="global-netconf-ssh-scheduled-executor"/>
 
     <cm:property-placeholder persistent-id="org.opendaylight.netconf.impl" update-strategy="none">
         <cm:default-properties>
@@ -81,7 +89,8 @@
     <!--NetconfServerMonitoring -->
 
     <bean id="netconfMonitoringService"
-          class="org.opendaylight.netconf.impl.osgi.NetconfMonitoringServiceImpl">
+          class="org.opendaylight.netconf.impl.osgi.NetconfMonitoringServiceImpl"
+          destroy-method="close">
         <argument ref="aggregatedNetconfOperationServiceFactory"/>
         <argument ref="scheduledThreadPool"/>
         <argument value="${monitoring-update-interval}"/>
@@ -20,7 +20,7 @@ import org.opendaylight.netconf.monitoring.GetSchema;
 import java.util.Collections;
 import java.util.Set;
 
-public class MdSalMonitoringMapperFactory implements NetconfOperationServiceFactory, AutoCloseable {
+public class MdsalMonitoringMapperFactory implements NetconfOperationServiceFactory, AutoCloseable {
 
     private final NetconfOperationService operationService;
     private final MonitoringToMdsalWriter monitoringToMdsalWriter;
@@ -28,7 +28,7 @@ public class MdSalMonitoringMapperFactory implements NetconfOperationServiceFact
 
     private static final Set<Capability> CAPABILITIES = Collections.emptySet();
 
-    public MdSalMonitoringMapperFactory(final NetconfOperationServiceFactoryListener netconfOperationServiceFactoryListener,
+    public MdsalMonitoringMapperFactory(final NetconfOperationServiceFactoryListener netconfOperationServiceFactoryListener,
                                         final NetconfMonitoringService netconfMonitoringService,
                                         final MonitoringToMdsalWriter monitoringToMdsalWriter) {
 
index 13b263303fc3726e6fdfa57af19155d2c554ed2a..c1c8c8d0afb6d0b5cf1137cb8082be1d360f8a8a 100644 (file)
@@ -29,7 +29,7 @@
     </bean>
 
     <bean id="mdsalMonitoringMapperFactory"
-          class="org.opendaylight.controller.config.yang.netconf.mdsal.monitoring.MdSalMonitoringMapperFactory"
+          class="org.opendaylight.controller.config.yang.netconf.mdsal.monitoring.MdsalMonitoringMapperFactory"
           destroy-method="close">
         <argument ref="netconfOperationServiceFactoryListener"/>
         <argument ref="netconfMonitoringService"/>
diff --git a/netconf/mdsal-netconf-tcp/pom.xml b/netconf/mdsal-netconf-tcp/pom.xml
new file mode 100644 (file)
index 0000000..fb528d7
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Inocybe Technologies and others.  All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.opendaylight.odlparent</groupId>
+    <artifactId>bundle-parent</artifactId>
+    <version>1.8.0-SNAPSHOT</version>
+    <relativePath/>
+  </parent>
+
+  <groupId>org.opendaylight.netconf</groupId>
+  <artifactId>mdsal-netconf-tcp</artifactId>
+  <version>1.2.0-SNAPSHOT</version>
+  <name>${project.artifactId}</name>
+  <packaging>bundle</packaging>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.opendaylight.netconf</groupId>
+        <artifactId>netconf-subsystem</artifactId>
+        <version>${project.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>netconf-tcp</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+            <Embed-Dependency>netconf-tcp</Embed-Dependency>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/netconf/mdsal-netconf-tcp/src/main/resources/org/opendaylight/blueprint/netconf-tcp.xml b/netconf/mdsal-netconf-tcp/src/main/resources/org/opendaylight/blueprint/netconf-tcp.xml
new file mode 100755 (executable)
index 0000000..b41b38b
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Inocybe Technologies 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
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.3.0"
+           odl:restart-dependents-on-updates="true">
+
+    <reference id="netconfServerDispatcher"
+               interface="org.opendaylight.netconf.api.NetconfServerDispatcher"
+               odl:type="netconf-server-dispatcher"/>
+
+    <!-- NETCONF TCP server for MD-SAL (listening by default on port 2831)-->
+
+    <cm:property-placeholder persistent-id="org.opendaylight.netconf.tcp" update-strategy="none">
+        <cm:default-properties>
+            <cm:property name="bindingAddress" value="0.0.0.0"/>
+            <cm:property name="portNumber" value="2831"/>
+        </cm:default-properties>
+    </cm:property-placeholder>
+
+    <!-- If you need/want to use a TCP NETCONF server to interact with MD-SAL, uncomment bellow bean -->
+
+    <!--
+    <bean id="netconfMdsalTcpServer"
+          class="org.opendaylight.netconf.tcp.NetconfNorthboundTcpServer"
+          destroy-method="close">
+        <argument ref="netconfServerDispatcher"/>
+        <argument value="${bindingAddress}"/>
+        <argument value="${portNumber}"/>
+    </bean>
+    -->
+
+</blueprint>
\ No newline at end of file
index 2360369e6af354a1a934dfde79f3867a927c0957..953d878d1eb0c785dedc7b61a5150b2d33f70de4 100644 (file)
@@ -44,6 +44,7 @@ import org.slf4j.LoggerFactory;
  * Listens for updates on global schema context, transforms context to ietf-yang-library:modules-state and
  * writes this state to operational data store
  */
+// TODO Implement also yang-library-change notfication
 public class SchemaServiceToMdsalWriter implements SchemaContextListener, AutoCloseable {
 
     private static final Logger LOG = LoggerFactory.getLogger(SchemaServiceToMdsalWriter.class);
index a3ceeb3cea7f313808c6d409ee3b6a44830765bb..2007d4b1a08df5ffd512c2ba03367d5072fe07d1 100644 (file)
@@ -19,7 +19,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.mon
 public class SchemaLocationBuilder {
 
     public static Location getDefaultInstance(final String defaultValue) {
-        throw new java.lang.UnsupportedOperationException("Not yet implemented");
+        return LocationBuilder.getDefaultInstance(defaultValue);
     }
 
 }
index 0057b35c53806961809097dd0cdf277b5dc61a34..17db1dafb2ea0e974e1e4dee67aa7b01533409a4 100644 (file)
                 <groupId>${project.groupId}</groupId>
                 <artifactId>netconf-config</artifactId>
                 <version>${project.version}</version>
-                <type>xml</type>
-                <classifier>config</classifier>
             </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>netconf-connector-config</artifactId>
                 <version>${project.version}</version>
-                <type>xml</type>
-                <classifier>config</classifier>
             </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>mdsal-netconf-ssh</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>${project.groupId}</groupId>
+                <artifactId>mdsal-netconf-tcp</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>netconf-topology</artifactId>
                 <groupId>${project.groupId}</groupId>
                 <artifactId>netconf-topology-config</artifactId>
                 <version>${project.version}</version>
-                <classifier>config</classifier>
-                <type>xml</type>
             </dependency>
             <dependency>
                 <groupId>${project.groupId}</groupId>
diff --git a/netconf/netconf-client/src/main/java/org/opendaylight/controller/config/yang/config/netconf/client/dispatcher/NetconfClientDispatcherModule.java b/netconf/netconf-client/src/main/java/org/opendaylight/controller/config/yang/config/netconf/client/dispatcher/NetconfClientDispatcherModule.java
deleted file mode 100644 (file)
index 6bde635..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.config.yang.config.netconf.client.dispatcher;
-
-import com.google.common.reflect.AbstractInvocationHandler;
-import com.google.common.reflect.Reflection;
-import java.lang.reflect.Method;
-import org.opendaylight.controller.config.api.osgi.WaitingServiceTracker;
-import org.opendaylight.netconf.client.NetconfClientDispatcher;
-import org.osgi.framework.BundleContext;
-
-/**
- * @deprecated Replaced by blueprint wiring
- */
-@Deprecated
-public final class NetconfClientDispatcherModule extends org.opendaylight.controller.config.yang.config.netconf.client.dispatcher.AbstractNetconfClientDispatcherModule {
-
-    private BundleContext bundleContext;
-
-    public NetconfClientDispatcherModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
-        super(identifier, dependencyResolver);
-    }
-
-    public NetconfClientDispatcherModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
-            NetconfClientDispatcherModule oldModule, java.lang.AutoCloseable oldInstance) {
-
-        super(identifier, dependencyResolver, oldModule, oldInstance);
-    }
-
-    @Override
-    protected void customValidation(){
-    }
-
-    @Override
-    public java.lang.AutoCloseable createInstance() {
-        final WaitingServiceTracker<NetconfClientDispatcher> tracker =
-                WaitingServiceTracker.create(NetconfClientDispatcher.class, bundleContext, "(type=netconf-client-dispatcher)");
-        final NetconfClientDispatcher service = tracker.waitForService(WaitingServiceTracker.FIVE_MINUTES);
-
-        return Reflection.newProxy(AutoCloseableNetconfClientDispatcher.class, new AbstractInvocationHandler() {
-            @Override
-            protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
-                if (method.getName().equals("close")) {
-                    tracker.close();
-                    return null;
-                } else {
-                    return method.invoke(service, args);
-                }
-            }
-        });
-    }
-
-    void setBundleContext(BundleContext bundleContext) {
-        this.bundleContext = bundleContext;
-    }
-
-    private static interface AutoCloseableNetconfClientDispatcher extends NetconfClientDispatcher, AutoCloseable {
-    }
-}
diff --git a/netconf/netconf-client/src/main/java/org/opendaylight/controller/config/yang/config/netconf/client/dispatcher/NetconfClientDispatcherModuleFactory.java b/netconf/netconf-client/src/main/java/org/opendaylight/controller/config/yang/config/netconf/client/dispatcher/NetconfClientDispatcherModuleFactory.java
deleted file mode 100644 (file)
index f361ed2..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.controller.config.yang.config.netconf.client.dispatcher;
-
-import org.opendaylight.controller.config.api.DependencyResolver;
-import org.osgi.framework.BundleContext;
-
-/**
- * @deprecated Replaced by blueprint wiring
- */
-@Deprecated
-public class NetconfClientDispatcherModuleFactory extends AbstractNetconfClientDispatcherModuleFactory {
-    @Override
-    public NetconfClientDispatcherModule instantiateModule(String instanceName, DependencyResolver dependencyResolver,
-                                                           NetconfClientDispatcherModule oldModule, AutoCloseable oldInstance, BundleContext bundleContext) {
-        NetconfClientDispatcherModule module = super.instantiateModule(instanceName, dependencyResolver, oldModule,
-                oldInstance, bundleContext);
-        module.setBundleContext(bundleContext);
-        return module;
-    }
-
-    @Override
-    public NetconfClientDispatcherModule instantiateModule(String instanceName, DependencyResolver dependencyResolver,
-                                                      BundleContext bundleContext) {
-        NetconfClientDispatcherModule module = super.instantiateModule(instanceName, dependencyResolver, bundleContext);
-        module.setBundleContext(bundleContext);
-        return module;
-    }
-}
diff --git a/netconf/netconf-client/src/main/yang/odl-netconfig-client-cfg.yang b/netconf/netconf-client/src/main/yang/odl-netconfig-client-cfg.yang
deleted file mode 100644 (file)
index 64343a0..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-// vi: set smarttab et sw=4 tabstop=4:
-module odl-netconfig-client-cfg {
-
-    yang-version 1;
-    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher";
-    prefix "cfg-net-client";
-
-    import config { prefix config; revision-date 2013-04-05; }
-    import odl-netconf-cfg { prefix cfg-net; revision-date 2014-04-08; }
-    import netty {prefix netty; }
-
-    description
-        "This module contains the base YANG definitions for
-        netconf-client-dispatcher implementation.
-
-        Copyright (c)2013 Cisco Systems, Inc. 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";
-
-    revision "2014-04-08" {
-        description
-            "Initial revision.";
-    }
-
-    identity netconf-client-dispatcher {
-            base config:module-type;
-            config:provided-service cfg-net:netconf-client-dispatcher;
-            config:java-name-prefix NetconfClientDispatcher;
-    }
-
-    augment "/config:modules/config:module/config:configuration" {
-        case netconf-client-dispatcher {
-            when "/config:modules/config:module/config:type = 'netconf-client-dispatcher'";
-        }
-    }
-
-}
\ No newline at end of file
diff --git a/netconf/netconf-config-dispatcher/pom.xml b/netconf/netconf-config-dispatcher/pom.xml
deleted file mode 100644 (file)
index f9e2732..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
-
- This program and the accompanying materials are made available under the
- terms of the Eclipse Public License v1.0 which accompanies this distribution,
- and is available at http://www.eclipse.org/legal/epl-v10.html
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <groupId>org.opendaylight.mdsal</groupId>
-        <artifactId>binding-parent</artifactId>
-        <version>0.10.0-SNAPSHOT</version>
-        <relativePath/>
-    </parent>
-
-  <!--TODO Delete this bundle as soon as https://git.opendaylight.org/gerrit/#/c/46621/ is merged.-->
-
-  <groupId>org.opendaylight.netconf</groupId>
-  <artifactId>netconf-config-dispatcher</artifactId>
-  <version>1.2.0-SNAPSHOT</version>
-  <packaging>bundle</packaging>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>org.opendaylight.netconf</groupId>
-                <artifactId>netconf-subsystem</artifactId>
-                <version>${project.version}</version>
-               <type>pom</type>
-               <scope>import</scope>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
-
-  <dependencies>
-      <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-api</artifactId>
-      </dependency>
-      <dependency>
-          <groupId>${project.groupId}</groupId>
-          <artifactId>netconf-client</artifactId>
-      </dependency>
-
-      <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-manager</artifactId>
-          <type>test-jar</type>
-          <scope>test</scope>
-      </dependency>
-      <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-manager</artifactId>
-          <scope>test</scope>
-      </dependency>
-      <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>config-util</artifactId>
-          <scope>test</scope>
-      </dependency>
-      <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>netty-threadgroup-config</artifactId>
-          <scope>test</scope>
-      </dependency>
-      <dependency>
-          <groupId>org.opendaylight.controller</groupId>
-          <artifactId>netty-timer-config</artifactId>
-          <scope>test</scope>
-      </dependency>
-      <dependency>
-          <groupId>org.opendaylight.yangtools</groupId>
-          <artifactId>mockito-configuration</artifactId>
-          <scope>test</scope>
-      </dependency>
-  </dependencies>
-</project>
diff --git a/netconf/netconf-config-dispatcher/src/main/yang/odl-netconf-cfg.yang b/netconf/netconf-config-dispatcher/src/main/yang/odl-netconf-cfg.yang
deleted file mode 100644 (file)
index be1f08c..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-// vi: set smarttab et sw=4 tabstop=4:
-module odl-netconf-cfg {
-
-/**
-* TODO Delete this YANG file as soon as https://git.opendaylight.org/gerrit/#/c/46621/ is merged.
-*/
-
-    yang-version 1;
-    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:netconf";
-    prefix "cfg-net";
-
-    import config { prefix config; revision-date 2013-04-05; }
-
-    description
-        "This module contains the base YANG definitions for
-        netconf related services.
-
-        Copyright (c)2013 Cisco Systems, Inc. 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";
-
-    revision "2014-04-08" {
-        description
-            "Initial revision.";
-    }
-
-    identity netconf-client-dispatcher {
-        base "config:service-type";
-        config:java-class "org.opendaylight.netconf.client.NetconfClientDispatcher";
-    }
-}
\ No newline at end of file
diff --git a/netconf/netconf-config-dispatcher/src/main/yang/odl-netconfig-client-cfg.yang b/netconf/netconf-config-dispatcher/src/main/yang/odl-netconfig-client-cfg.yang
deleted file mode 100644 (file)
index caa400a..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-// vi: set smarttab et sw=4 tabstop=4:
-module odl-netconfig-client-cfg {
-
-/**
-* TODO Delete this YANG file as soon as https://git.opendaylight.org/gerrit/#/c/46621/ is merged.
-*/
-
-    yang-version 1;
-    namespace "urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher";
-    prefix "cfg-net-client";
-
-    import config { prefix config; revision-date 2013-04-05; }
-    import odl-netconf-cfg { prefix cfg-net; revision-date 2014-04-08; }
-    import netty {prefix netty; }
-
-    description
-        "This module contains the base YANG definitions for
-        netconf-client-dispatcher implementation.
-
-        Copyright (c)2013 Cisco Systems, Inc. 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";
-
-    revision "2014-04-08" {
-        description
-            "Initial revision.";
-    }
-
-    identity netconf-client-dispatcher {
-            base config:module-type;
-            config:provided-service cfg-net:netconf-client-dispatcher;
-            config:java-name-prefix NetconfClientDispatcher;
-    }
-
-    augment "/config:modules/config:module/config:configuration" {
-        case netconf-client-dispatcher {
-            when "/config:modules/config:module/config:type = 'netconf-client-dispatcher'";
-        }
-    }
-
-}
\ No newline at end of file
index b44c4c164de0e0f8b043af237bf41d36514d409b..1e047798a6835579f3fd172044fc74dc9c49f8bf 100644 (file)
@@ -11,7 +11,7 @@
   <modelVersion>4.0.0</modelVersion>
   <parent>
     <groupId>org.opendaylight.odlparent</groupId>
-    <artifactId>odlparent</artifactId>
+    <artifactId>bundle-parent</artifactId>
     <version>1.8.0-SNAPSHOT</version>
     <relativePath/>
   </parent>
   <artifactId>netconf-config</artifactId>
   <version>1.2.0-SNAPSHOT</version>
   <description>Configuration files for netconf</description>
-  <packaging>jar</packaging>
+  <packaging>bundle</packaging>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>config-artifacts</artifactId>
+        <version>0.6.0-SNAPSHOT</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>threadpool-config-impl</artifactId>
+    </dependency>
+  </dependencies>
 
   <build>
     <plugins>
-        <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>build-helper-maven-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>attach-artifacts</id>
-            <goals>
-              <goal>attach-artifact</goal>
-            </goals>
-            <phase>package</phase>
-            <configuration>
-              <artifacts>
-                <artifact>
-                  <file>${project.build.directory}/classes/initial/01-netconf.xml</file>
-                  <type>xml</type>
-                  <classifier>config</classifier>
-                </artifact>
-              </artifacts>
-            </configuration>
-          </execution>
-        </executions>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+            <Embed-Dependency>threadpool-config-impl</Embed-Dependency>
+          </instructions>
+        </configuration>
       </plugin>
     </plugins>
   </build>
diff --git a/netconf/netconf-config/src/main/resources/initial/01-netconf.xml b/netconf/netconf-config/src/main/resources/initial/01-netconf.xml
deleted file mode 100644 (file)
index e641ac5..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- vi: set et smarttab sw=4 tabstop=4: -->
-<!--
- Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
-
- This program and the accompanying materials are made available under the
- terms of the Eclipse Public License v1.0 which accompanies this distribution,
- and is available at http://www.eclipse.org/legal/epl-v10.html
--->
-<snapshot>
-  <configuration>
-    <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-      <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-
-        <!-- Netconf dispatcher to be used by all netconf-connectors -->
-        <module>
-          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher">prefix:netconf-client-dispatcher</type>
-          <name>global-netconf-dispatcher</name>
-        </module>
-
-        <!-- Thread factory to be used by all threadpools in netconf-connectors -->
-        <module>
-          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">prefix:threadfactory-naming</type>
-          <name>global-netconf-processing-executor-threadfactory</name>
-          <name-prefix xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">remote-connector-processing-executor</name-prefix>
-        </module>
-        <!-- flexible threadpool for all netconf connectors, Max thread count is set to 4.  -->
-        <module>
-          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">prefix:threadpool-flexible</type>
-          <name>global-netconf-processing-executor</name>
-          <minThreadCount xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">1</minThreadCount>
-          <max-thread-count xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">4</max-thread-count>
-          <keepAliveMillis xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">600000</keepAliveMillis>
-
-          <threadFactory xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible">
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
-            <name>global-netconf-processing-executor-threadfactory</name>
-          </threadFactory>
-        </module>
-
-        <module>
-          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled">prefix:threadpool-scheduled</type>
-          <name>global-netconf-ssh-scheduled-executor</name>
-          <max-thread-count xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled">8</max-thread-count>
-
-          <threadFactory xmlns="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled">
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
-            <name>global-netconf-processing-executor-threadfactory</name>
-          </threadFactory>
-        </module>
-      </modules>
-
-      <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-        <service>
-          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
-          <instance>
-            <name>global-netconf-dispatcher</name>
-            <provider>/modules/module[type='netconf-client-dispatcher'][name='global-netconf-dispatcher']</provider>
-          </instance>
-        </service>
-        <service>
-          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadfactory</type>
-          <instance>
-            <name>global-netconf-processing-executor-threadfactory</name>
-            <provider>/modules/module[type='threadfactory-naming'][name='global-netconf-processing-executor-threadfactory']</provider>
-          </instance>
-        </service>
-        <service>
-          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
-          <instance>
-            <name>global-netconf-processing-executor</name>
-            <provider>/modules/module[type='threadpool-flexible'][name='global-netconf-processing-executor']</provider>
-          </instance>
-            <instance>
-            <name>global-netconf-ssh-scheduled-executor</name>
-            <provider>/modules/module[type='threadpool-scheduled'][name='global-netconf-ssh-scheduled-executor']</provider>
-          </instance>
-        </service>
-          <service>
-              <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:scheduled-threadpool</type>
-              <instance>
-                  <name>global-netconf-ssh-scheduled-executor</name>
-                  <provider>/modules/module[type='threadpool-scheduled'][name='global-netconf-ssh-scheduled-executor']</provider>
-              </instance>
-          </service>
-      </services>
-
-    </data>
-  </configuration>
-  <required-capabilities>
-      <capability>urn:opendaylight:params:xml:ns:yang:controller:config:netconf:client:dispatcher?module=odl-netconfig-client-cfg&amp;revision=2014-04-08</capability>
-      <capability>urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup?module=threadgroup&amp;revision=2013-11-07</capability>
-      <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&amp;revision=2013-04-05</capability>
-      <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible?module=threadpool-impl-flexible&amp;revision=2013-12-01</capability>
-      <capability>urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled?module=threadpool-impl-scheduled&amp;revision=2013-12-01</capability>
-  </required-capabilities>
-</snapshot>
diff --git a/netconf/netconf-config/src/main/resources/org/opendaylight/blueprint/netconf-config.xml b/netconf/netconf-config/src/main/resources/org/opendaylight/blueprint/netconf-config.xml
new file mode 100644 (file)
index 0000000..5c01b16
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Inocybe Technologies 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
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.3.0"
+           odl:restart-dependents-on-updates="true">
+
+    <!-- Default configuration for thread pools -->
+
+    <cm:property-placeholder persistent-id="org.opendaylight.netconf.config"
+                             update-strategy="none">
+        <cm:default-properties>
+            <cm:property name="name-prefix" value="remote-connector-processing-executor"/>
+            <cm:property name="min-thread-count-flexible-thread-pool" value="1"/>
+            <cm:property name="max-thread-count-flexible-thread-pool" value="4"/>
+            <cm:property name="keep-alive-millis-flexible-thread-pool" value="600000"/>
+            <cm:property name="max-thread-count-scheduled-thread-pool" value="8"/>
+        </cm:default-properties>
+    </cm:property-placeholder>
+
+    <!-- Naming Thread Factory -->
+
+    <bean id="namingThreadPoolFactory"
+          class="org.opendaylight.controller.config.threadpool.util.NamingThreadPoolFactory">
+        <argument value="${name-prefix}"/>
+    </bean>
+
+    <!-- Flexible Thread Pool -->
+
+    <bean id="timeUnitMilli" class="java.util.concurrent.TimeUnit"
+          factory-method="valueOf">
+        <argument value="MILLISECONDS"/>
+    </bean>
+
+    <bean id="flexibleThreadPool"
+          class="org.opendaylight.controller.config.threadpool.util.FlexibleThreadPoolWrapper">
+        <argument value="${min-thread-count-flexible-thread-pool}"/>
+        <argument value="${max-thread-count-flexible-thread-pool}"/>
+        <argument value="${keep-alive-millis-flexible-thread-pool}"/>
+        <argument ref="timeUnitMilli"/>
+        <argument ref="namingThreadPoolFactory"/>
+    </bean>
+    <service ref="flexibleThreadPool"
+             interface="org.opendaylight.controller.config.threadpool.ThreadPool"
+             odl:type="global-netconf-processing-executor"/>
+
+    <!-- Scheduled Thread Pool -->
+
+    <bean id="scheduleThreadPool"
+          class="org.opendaylight.controller.config.threadpool.util.ScheduledThreadPoolWrapper">
+        <argument value="${max-thread-count-scheduled-thread-pool}"/>
+        <argument ref="namingThreadPoolFactory"/>
+    </bean>
+    <service ref="scheduleThreadPool"
+             interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"
+             odl:type="global-netconf-ssh-scheduled-executor"/>
+
+</blueprint>
index ff6e0ec1776b4c5b5bc9525ccdef9c37c6b5ed64..771a3bea453fabd1ad1b11a95569bc25f2d5b876 100644 (file)
@@ -6,46 +6,55 @@
  terms of the Eclipse Public License v1.0 which accompanies this distribution,
  and is available at http://www.eclipse.org/legal/epl-v10.html
 -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
-    <groupId>org.opendaylight.odlparent</groupId>
-    <artifactId>odlparent-lite</artifactId>
-    <version>1.8.0-SNAPSHOT</version>
-    <relativePath/>
-  </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.opendaylight.odlparent</groupId>
+        <artifactId>bundle-parent</artifactId>
+        <version>1.8.0-SNAPSHOT</version>
+        <relativePath/>
+    </parent>
 
-  <groupId>org.opendaylight.netconf</groupId>
-  <artifactId>netconf-connector-config</artifactId>
-  <description>Configuration files for netconf-connector</description>
-  <version>1.2.0-SNAPSHOT</version>
-  <packaging>jar</packaging>
+    <groupId>org.opendaylight.netconf</groupId>
+    <artifactId>netconf-connector-config</artifactId>
+    <description>Configuration files for netconf-connector</description>
+    <version>1.2.0-SNAPSHOT</version>
+    <packaging>bundle</packaging>
 
-  <build>
-    <plugins>
-        <plugin>
-        <groupId>org.codehaus.mojo</groupId>
-        <artifactId>build-helper-maven-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>attach-artifacts</id>
-            <goals>
-              <goal>attach-artifact</goal>
-            </goals>
-            <phase>package</phase>
-            <configuration>
-              <artifacts>
-                <artifact>
-                  <file>${project.build.directory}/classes/initial/99-netconf-connector.xml</file>
-                  <type>xml</type>
-                  <classifier>config</classifier>
-                </artifact>
-              </artifacts>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.netconf</groupId>
+                <artifactId>netconf-subsystem</artifactId>
+                <version>${project.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-topology</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                        <Embed-Dependency>netconf-topology</Embed-Dependency>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/netconf/netconf-connector-config/src/main/resources/initial/99-netconf-connector.xml b/netconf/netconf-connector-config/src/main/resources/initial/99-netconf-connector.xml
deleted file mode 100644 (file)
index 62b2c8d..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- vi: set et smarttab sw=4 tabstop=4: -->
-<!--
- Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
-
- This program and the accompanying materials are made available under the
- terms of the Eclipse Public License v1.0 which accompanies this distribution,
- and is available at http://www.eclipse.org/legal/epl-v10.html
--->
-<snapshot>
-  <configuration>
-    <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-      <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-        <!-- Loopback connection to netconf server in controller using netconf-connector -->
-        <module>
-          <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">prefix:sal-netconf-connector</type>
-          <name>controller-config</name>
-          <address xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">127.0.0.1</address>
-          <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">1830</port>
-          <username xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">admin</username>
-          <password xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">admin</password>
-          <tcp-only xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">false</tcp-only>
-          <reconnect-on-changed-schema xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">true</reconnect-on-changed-schema>
-          <event-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
-            <name>global-event-executor</name>
-          </event-executor>
-          <binding-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>
-            <name>binding-osgi-broker</name>
-          </binding-registry>
-          <dom-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
-            <name>dom-broker</name>
-          </dom-registry>
-          <client-dispatcher xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
-            <name>global-netconf-dispatcher</name>
-          </client-dispatcher>
-          <processing-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
-            <name>global-netconf-processing-executor</name>
-          </processing-executor>
-          <keepalive-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf">
-            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:scheduled-threadpool</type>
-            <name>global-netconf-ssh-scheduled-executor</name>
-          </keepalive-executor>
-        </module>
-        </modules>
-    </data>
-  </configuration>
-  <required-capabilities>
-      <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:connector:netconf?module=odl-sal-netconf-connector-cfg&amp;revision=2015-08-03</capability>
-  </required-capabilities>
-</snapshot>
diff --git a/netconf/netconf-connector-config/src/main/resources/org/opendaylight/blueprint/netconf-connector.xml b/netconf/netconf-connector-config/src/main/resources/org/opendaylight/blueprint/netconf-connector.xml
new file mode 100644 (file)
index 0000000..7312471
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Inocybe Technologies 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
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.3.0"
+           odl:use-default-for-reference-types="true">
+
+    <reference id="dataBroker" interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"/>
+    <odl:static-reference id="netconfConnectorFactory" interface="org.opendaylight.netconf.topology.api.NetconfConnectorFactory"/>
+
+    <cm:property-placeholder persistent-id="org.opendaylight.netconf.sal.connect"
+                             update-strategy="none">
+        <cm:default-properties>
+            <cm:property name="name" value="controller-config"/>
+            <cm:property name="address" value="127.0.0.1"/>
+            <cm:property name="port" value="1830"/>
+            <cm:property name="username" value="admin"/>
+            <cm:property name="password" value="admin"/>
+            <cm:property name="tcp-only" value="false"/>
+            <cm:property name="reconnect-on-schema-change" value="true"/>
+        </cm:default-properties>
+    </cm:property-placeholder>
+
+    <bean id="controllerConfig" factory-ref="netconfConnectorFactory" factory-method="newInstance">
+        <argument ref="dataBroker"/>
+        <argument type="java.lang.String" value="${name}"/>
+        <argument type="java.lang.String" value="${address}"/>
+        <argument type="java.lang.Integer" value="${port}"/>
+        <argument type="java.lang.String" value="${username}"/>
+        <argument type="java.lang.String" value="${password}"/>
+        <argument type="java.lang.Boolean" value="${tcp-only}"/>
+        <argument type="java.lang.Boolean" value="${reconnect-on-schema-change}"/>
+    </bean>
+
+</blueprint>
\ No newline at end of file
index a4ff368ca7215bacbed181196842c6859890167a..c78eb147a937f2a6aa6eae4c7eb8f57196e6de27 100644 (file)
       <artifactId>xmlunit</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+    </dependency>
   </dependencies>
 
   <build>
index edd3c36bde39ae97b33b5fa1daa8befb6ce97892..c31531d052fee92b71120122046c749e209a20dc 100644 (file)
@@ -16,7 +16,6 @@ import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
-import io.netty.util.internal.ConcurrentSet;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -24,11 +23,9 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 import org.opendaylight.controller.config.util.capability.BasicCapability;
 import org.opendaylight.controller.config.util.capability.Capability;
 import org.opendaylight.netconf.api.monitoring.CapabilityListener;
-import org.opendaylight.netconf.api.monitoring.NetconfManagementSession;
 import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
 import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
 import org.opendaylight.netconf.notifications.BaseNotificationPublisherRegistration;
@@ -58,13 +55,12 @@ class NetconfCapabilityMonitoringService implements CapabilityListener, AutoClos
         }
     };
 
-    private final Set<NetconfManagementSession> sessions = new ConcurrentSet<>();
     private final NetconfOperationServiceFactory netconfOperationProvider;
-    private final Map<Uri, Capability> capabilities = new ConcurrentHashMap<>();
-    private final Map<String, Map<String, String>> mappedModulesToRevisionToSchema = new ConcurrentHashMap<>();
+    private final Map<Uri, Capability> capabilities = Maps.newHashMap();
+    private final Map<String, Map<String, String>> mappedModulesToRevisionToSchema = Maps.newHashMap();
 
 
-    private final Set<NetconfMonitoringService.CapabilitiesListener> listeners = new ConcurrentSet<>();
+    private final Set<NetconfMonitoringService.CapabilitiesListener> listeners = Sets.newHashSet();
     private volatile BaseNotificationPublisherRegistration notificationPublisher;
 
     NetconfCapabilityMonitoringService(final NetconfOperationServiceFactory netconfOperationProvider) {
@@ -216,7 +212,6 @@ class NetconfCapabilityMonitoringService implements CapabilityListener, AutoClos
     @Override
     public synchronized void close() throws Exception {
         listeners.clear();
-        sessions.clear();
         capabilities.clear();
     }
 
index 9d520bea56b82da45857f9e4029c24882d69762c..8feaef6aedc9366381d35cea1a9cc78a8599edf0 100644 (file)
@@ -24,6 +24,7 @@ import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactoryListen
 import org.opendaylight.netconf.notifications.BaseNotificationPublisherRegistration;
 import org.opendaylight.netconf.notifications.NetconfNotificationCollector;
 import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
@@ -52,7 +53,8 @@ public class NetconfImplActivator implements BundleActivator {
 
             SessionIdProvider idProvider = new SessionIdProvider();
             timer = new HashedWheelTimer();
-            long connectionTimeoutMillis = NetconfConfigUtil.extractTimeoutMillis(context);
+
+            long connectionTimeoutMillis = NetconfConfiguration.DEFAULT_TIMEOUT_MILLIS;
 
             final NetconfMonitoringServiceImpl monitoringService = startMonitoringService(context, factoriesListener);
 
@@ -70,7 +72,7 @@ public class NetconfImplActivator implements BundleActivator {
                     serverNegotiatorFactory);
             NetconfServerDispatcherImpl dispatch = new NetconfServerDispatcherImpl(serverChannelInitializer, eventLoopGroup, eventLoopGroup);
 
-            LocalAddress address = NetconfConfigUtil.getNetconfLocalAddress();
+            LocalAddress address = NetconfConfiguration.NETCONF_LOCAL_ADDRESS;
             LOG.trace("Starting local netconf server at {}", address);
             dispatch.createLocalServer(address);
 
index af514d195c643344bec4149b944f4d8f94480f0f..4b435e8f7685bcb3db39d8b8b0715262cd62f5f4 100644 (file)
@@ -124,9 +124,9 @@ public class NetconfServerSessionListenerTest {
         final Document reply =
                 XmlUtil.readXmlToDocument("<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
                         "<rpc-error>\n" +
-                        "<error-type>PROTOCOL</error-type>\n" +
+                        "<error-type>protocol</error-type>\n" +
                         "<error-tag>unknown-element</error-tag>\n" +
-                        "<error-severity>ERROR</error-severity>\n" +
+                        "<error-severity>error</error-severity>\n" +
                         "<error-message>Unknown tag bad-rpc in message:\n" +
                         "&lt;bad-rpc/&gt;\n" +
                         "</error-message>\n" +
index 14cbae81e1891edb357717c5f0dfc88e88dcd545..537ea993d1b113b5b46d76b06fd777ed13dca151 100644 (file)
@@ -84,7 +84,7 @@ public class NetconfSSHActivator implements BundleActivator {
         final InetSocketAddress sshSocketAddress = netconfConfiguration.getSshServerAddress();
         LOG.info("Starting netconf SSH server at {}", sshSocketAddress);
 
-        final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
+        final LocalAddress localAddress = NetconfConfiguration.NETCONF_LOCAL_ADDRESS;
         authProviderTracker = new AuthProviderTracker(bundleContext);
 
         final String path = netconfConfiguration.getPrivateKeyPath();
index 53a54dc601ef6525c5d3aa6cb638b7a39100352c..a6f9e6cb66595bd0616c35774684571fe683a11b 100644 (file)
@@ -21,7 +21,7 @@ import io.netty.handler.logging.LogLevel;
 import io.netty.handler.logging.LoggingHandler;
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
-import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,7 +49,7 @@ public class EchoServer implements Runnable {
                     });
 
             // Start the server.
-            LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
+            LocalAddress localAddress = NetconfConfiguration.NETCONF_LOCAL_ADDRESS;
             ChannelFuture f = b.bind(localAddress).sync();
 
             // Wait until the server socket is closed.
index 8294a35c954b3326f42c87d27466907f83aec515..2d9b67d42dd7dd2d54759747f2d61a9767f31935 100644 (file)
@@ -22,6 +22,7 @@ import io.netty.handler.logging.LogLevel;
 import io.netty.handler.logging.LoggingHandler;
 import java.net.InetSocketAddress;
 import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
 
 public class ProxyServer implements Runnable {
     private final ProxyHandlerFactory proxyHandlerFactory;
@@ -35,7 +36,7 @@ public class ProxyServer implements Runnable {
         final EventLoopGroup bossGroup = new NioEventLoopGroup();
         EventLoopGroup workerGroup = new NioEventLoopGroup();
         try {
-            final LocalAddress localAddress = NetconfConfigUtil.getNetconfLocalAddress();
+            final LocalAddress localAddress = NetconfConfiguration.NETCONF_LOCAL_ADDRESS;
             ServerBootstrap serverBootstrap = new ServerBootstrap();
             serverBootstrap.group(bossGroup, workerGroup)
                     .channel(NioServerSocketChannel.class)
index b7379808cf3d941374abd6b4f025b59c3243771e..759b96210bdd068c05f1a6309b223c3d026e40ab 100644 (file)
@@ -36,7 +36,7 @@ import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswo
 import org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandler;
 import org.opendaylight.netconf.ssh.SshProxyServer;
 import org.opendaylight.netconf.ssh.SshProxyServerConfigurationBuilder;
-import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -74,7 +74,7 @@ public class SSHTest {
         final InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 10831);
         final SshProxyServer sshProxyServer = new SshProxyServer(minaTimerEx, nettyGroup, nioExec);
         sshProxyServer.bind(
-                new SshProxyServerConfigurationBuilder().setBindingAddress(addr).setLocalAddress(NetconfConfigUtil.getNetconfLocalAddress()).setAuthenticator(new AuthProvider() {
+                new SshProxyServerConfigurationBuilder().setBindingAddress(addr).setLocalAddress(NetconfConfiguration.NETCONF_LOCAL_ADDRESS).setAuthenticator(new AuthProvider() {
                     @Override
                     public boolean authenticated(final String username, final String password) {
                         return true;
index f0350247187a925132de8aae7fab374d863bc0ca..7c7ef6383d9882e1ae5887b73688d00062e6238a 100644 (file)
@@ -34,6 +34,7 @@ import org.opendaylight.netconf.auth.AuthProvider;
 import org.opendaylight.netconf.ssh.SshProxyServer;
 import org.opendaylight.netconf.ssh.SshProxyServerConfigurationBuilder;
 import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
+import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
@@ -73,7 +74,7 @@ public class SSHServerTest {
         final InetSocketAddress addr = InetSocketAddress.createUnresolved(HOST, PORT);
         server = new SshProxyServer(minaTimerEx, clientGroup, nioExec);
         server.bind(
-                new SshProxyServerConfigurationBuilder().setBindingAddress(addr).setLocalAddress(NetconfConfigUtil.getNetconfLocalAddress()).setAuthenticator(new AuthProvider() {
+                new SshProxyServerConfigurationBuilder().setBindingAddress(addr).setLocalAddress(NetconfConfiguration.NETCONF_LOCAL_ADDRESS).setAuthenticator(new AuthProvider() {
                     @Override
                     public boolean authenticated(final String username, final String password) {
                         return true;
diff --git a/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/config/yang/netconf/northbound/tcp/NetconfNorthboundTcpModule.java b/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/config/yang/netconf/northbound/tcp/NetconfNorthboundTcpModule.java
deleted file mode 100644 (file)
index bb33aaa..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.config.yang.netconf.northbound.tcp;
-
-import io.netty.channel.ChannelFuture;
-import io.netty.util.concurrent.GenericFutureListener;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import org.opendaylight.netconf.api.NetconfServerDispatcher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class NetconfNorthboundTcpModule extends org.opendaylight.controller.config.yang.netconf.northbound.tcp.AbstractNetconfNorthboundTcpModule {
-
-    private static final Logger LOG = LoggerFactory.getLogger(NetconfNorthboundTcpModule.class);
-
-
-    public NetconfNorthboundTcpModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
-        super(identifier, dependencyResolver);
-    }
-
-    public NetconfNorthboundTcpModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.netconf.northbound.tcp.NetconfNorthboundTcpModule oldModule, java.lang.AutoCloseable oldInstance) {
-        super(identifier, dependencyResolver, oldModule, oldInstance);
-    }
-
-    @Override
-    public void customValidation() {
-        // add custom validation form module attributes here.
-    }
-
-    @Override
-    public java.lang.AutoCloseable createInstance() {
-        final NetconfServerDispatcher dispatch = getDispatcherDependency();
-        final ChannelFuture tcpServer = dispatch.createServer(getInetAddress());
-
-        tcpServer.addListener(new GenericFutureListener<ChannelFuture>() {
-            @Override
-            public void operationComplete(ChannelFuture future) throws Exception {
-                if (future.isDone() && future.isSuccess()) {
-                    LOG.info("Netconf TCP endpoint started successfully at {}", getInetAddress());
-                } else {
-                    LOG.warn("Unable to start TCP netconf server at {}", getInetAddress(), future.cause());
-                    throw new RuntimeException("Unable to start TCP netconf server", future.cause());
-                }
-            }
-        });
-
-        return new NetconfServerCloseable(tcpServer);
-    }
-
-    private InetSocketAddress getInetAddress() {
-        try {
-            final InetAddress inetAd = InetAddress.getByName(getBindingAddress().getIpv4Address() == null ? getBindingAddress().getIpv6Address().getValue() : getBindingAddress().getIpv4Address().getValue());
-            return new InetSocketAddress(inetAd, getPort().getValue());
-        } catch (final UnknownHostException e) {
-            throw new IllegalArgumentException("Unable to bind netconf endpoint to address " + getBindingAddress(), e);
-        }
-    }
-
-    private static final class NetconfServerCloseable implements AutoCloseable {
-        private final ChannelFuture localServer;
-
-        public NetconfServerCloseable(final ChannelFuture localServer) {
-            this.localServer = localServer;
-        }
-
-        @Override
-        public void close() throws Exception {
-            if(localServer.isDone()) {
-                localServer.channel().close();
-            } else {
-                localServer.cancel(true);
-            }
-        }
-    }
-
-}
diff --git a/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/config/yang/netconf/northbound/tcp/NetconfNorthboundTcpModuleFactory.java b/netconf/netconf-tcp/src/main/java/org/opendaylight/controller/config/yang/netconf/northbound/tcp/NetconfNorthboundTcpModuleFactory.java
deleted file mode 100644 (file)
index 3ed70d9..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-/*
-* Generated file
-*
-* Generated from: yang module name: netconf-northbound-tcp yang module local name: netconf-northbound-tcp
-* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
-* Generated at: Thu Apr 23 16:34:55 CEST 2015
-*
-* Do not modify this file unless it is present under src/main directory
-*/
-package org.opendaylight.controller.config.yang.netconf.northbound.tcp;
-public class NetconfNorthboundTcpModuleFactory extends org.opendaylight.controller.config.yang.netconf.northbound.tcp.AbstractNetconfNorthboundTcpModuleFactory {
-
-}
diff --git a/netconf/netconf-tcp/src/main/java/org/opendaylight/netconf/tcp/NetconfNorthboundTcpServer.java b/netconf/netconf-tcp/src/main/java/org/opendaylight/netconf/tcp/NetconfNorthboundTcpServer.java
new file mode 100644 (file)
index 0000000..5792ebb
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.netconf.tcp;
+
+import io.netty.channel.ChannelFuture;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import org.opendaylight.netconf.api.NetconfServerDispatcher;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Create an MD-SAL NETCONF server using TCP.
+ */
+public class NetconfNorthboundTcpServer implements AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfNorthboundTcpServer.class);
+
+    private final ChannelFuture tcpServer;
+
+    public NetconfNorthboundTcpServer(final NetconfServerDispatcher netconfServerDispatcher,
+                                      final String address,
+                                      final String port) {
+        InetSocketAddress inetAddress = getInetAddress(address, port);
+        tcpServer = netconfServerDispatcher.createServer(inetAddress);
+        tcpServer.addListener(future -> {
+            if (future.isDone() && future.isSuccess()) {
+                LOG.info("Netconf TCP endpoint started successfully at {}", inetAddress);
+            } else {
+                LOG.warn("Unable to start TCP netconf server at {}", inetAddress, future.cause());
+                throw new RuntimeException("Unable to start TCP netconf server", future.cause());
+            }
+        });
+    }
+
+    private InetSocketAddress getInetAddress(final String bindingAddress, final String portNumber) {
+        try {
+            IpAddress ipAddress = IpAddressBuilder.getDefaultInstance(bindingAddress);
+            final InetAddress inetAd = InetAddress.getByName(ipAddress.getIpv4Address() == null ?
+                    ipAddress.getIpv6Address().getValue() : ipAddress.getIpv4Address().getValue());
+            return new InetSocketAddress(inetAd, Integer.valueOf(portNumber));
+        } catch (final UnknownHostException e) {
+            throw new IllegalArgumentException("Unable to bind netconf tcp endpoint to address " + bindingAddress, e);
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (tcpServer.isDone()) {
+            tcpServer.channel().close();
+        } else {
+            tcpServer.cancel(true);
+        }
+    }
+}
index d972a32ba70c9ede29a146cdc572bd610f979862..2d6798cfb75298f8b7c6d77d40c0e8c6274e2ef6 100644 (file)
@@ -11,7 +11,6 @@ package org.opendaylight.netconf.tcp.osgi;
 import java.net.InetSocketAddress;
 import org.opendaylight.netconf.tcp.netty.ProxyServer;
 import org.opendaylight.netconf.util.osgi.NetconfConfigUtil;
-import org.opendaylight.netconf.util.osgi.NetconfConfigUtil.InfixProp;
 import org.opendaylight.netconf.util.osgi.NetconfConfiguration;
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
@@ -33,11 +32,11 @@ public class NetconfTCPActivator implements BundleActivator {
         final InetSocketAddress address = netconfConfiguration.getTcpServerAddress();
 
         if (address.getAddress().isAnyLocalAddress()) {
-            LOG.warn("Unprotected netconf TCP address is configured to ANY local address. This is a security risk. Consider changing {} to 127.0.0.1",
-                    NetconfConfigUtil.getNetconfServerAddressKey(InfixProp.tcp));
+            LOG.warn("Unprotected netconf TCP address is configured to ANY local address. This is a security risk. " +
+                            "Consider changing tcp-address in netconf.cfg to 127.0.0.1");
         }
         LOG.info("Starting TCP netconf server at {}", address);
-        proxyServer = new ProxyServer(address, NetconfConfigUtil.getNetconfLocalAddress());
+        proxyServer = new ProxyServer(address, NetconfConfiguration.NETCONF_LOCAL_ADDRESS);
     }
 
     @Override
diff --git a/netconf/netconf-tcp/src/main/yang/netconf-northbound-tcp.yang b/netconf/netconf-tcp/src/main/yang/netconf-northbound-tcp.yang
deleted file mode 100644 (file)
index 8b0751e..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-module netconf-northbound-tcp {
-    yang-version 1;
-    namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:tcp";
-    prefix "nni";
-
-    import netconf-northbound-mapper { prefix nnm; revision-date 2015-01-14; }
-    import netconf-northbound { prefix nn; revision-date 2015-01-14; }
-    import config { prefix config; revision-date 2013-04-05; }
-    import threadpool {prefix th;}
-    import netty {prefix netty;}
-    import ietf-inet-types { prefix inet; revision-date 2013-07-15; }
-
-    organization "Cisco Systems, Inc.";
-
-    description
-        "This module contains the base YANG definitions for
-         a default implementation of netconf northbound tcp server";
-
-    revision "2015-04-23" {
-        description
-            "Initial revision.";
-    }
-
-    identity netconf-northbound-tcp {
-        base config:module-type;
-        config:java-name-prefix NetconfNorthboundTcp;
-    }
-
-    augment "/config:modules/config:module/config:configuration" {
-        case netconf-northbound-tcp {
-            when "/config:modules/config:module/config:type = 'netconf-northbound-tcp'";
-
-            leaf port {
-                type inet:port-number;
-                default 2831;
-            }
-
-            leaf binding-address {
-                type inet:ip-address;
-                default "0.0.0.0";
-            }
-
-            container dispatcher {
-                uses config:service-ref {
-                    refine type {
-                        config:required-identity nn:netconf-server-dispatcher;
-                    }
-                }
-            }
-        }
-    }
-
-}
index 75113816f39a41908befb264f29907ade9f2b074..2a0e0d982d98afe38dc7cbd09a6789d46c83fcbf 100644 (file)
@@ -13,7 +13,7 @@
 
     <parent>
         <groupId>org.opendaylight.odlparent</groupId>
-        <artifactId>odlparent-lite</artifactId>
+        <artifactId>bundle-parent</artifactId>
         <version>1.8.0-SNAPSHOT</version>
         <relativePath/>
     </parent>
     <artifactId>netconf-topology-config</artifactId>
     <version>1.2.0-SNAPSHOT</version>
     <description>Configuration files for netconf topology</description>
-    <packaging>jar</packaging>
+    <packaging>bundle</packaging>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.opendaylight.netconf</groupId>
+                <artifactId>netconf-subsystem</artifactId>
+                <version>${project.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>netconf-topology</artifactId>
+        </dependency>
+    </dependencies>
 
     <build>
         <plugins>
             <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>build-helper-maven-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>attach-artifacts</id>
-                        <goals>
-                            <goal>attach-artifact</goal>
-                        </goals>
-                        <phase>package</phase>
-                        <configuration>
-                            <artifacts>
-                                <artifact>
-                                    <file>${project.build.directory}/classes/initial/02-netconf-topology.xml</file>
-                                    <type>xml</type>
-                                    <classifier>config</classifier>
-                                </artifact>
-                            </artifacts>
-                        </configuration>
-                    </execution>
-                </executions>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                        <Embed-Dependency>netconf-topology</Embed-Dependency>
+                    </instructions>
+                </configuration>
             </plugin>
         </plugins>
     </build>
-
 </project>
diff --git a/netconf/netconf-topology-config/src/main/resources/initial/02-netconf-topology.xml b/netconf/netconf-topology-config/src/main/resources/initial/02-netconf-topology.xml
deleted file mode 100644 (file)
index 27e0617..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- vi: set et smarttab sw=4 tabstop=4: -->
-<!--
- Copyright (c) 2015 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
--->
-<snapshot>
-    <configuration>
-        <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
-            <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-                <module>
-                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">prefix:netconf-topology-impl</type>
-                    <name>default-netconf-topology</name>
-                    <topology-id xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">topology-netconf</topology-id>
-                    <event-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-event-executor</type>
-                        <name>global-event-executor</name>
-                    </event-executor>
-                    <binding-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">prefix:binding-broker-osgi-registry</type>
-                        <name>binding-osgi-broker</name>
-                    </binding-registry>
-                    <dom-registry xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
-                        <name>dom-broker</name>
-                    </dom-registry>
-                    <client-dispatcher xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf">prefix:netconf-client-dispatcher</type>
-                        <name>global-netconf-dispatcher</name>
-                    </client-dispatcher>
-                    <processing-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:threadpool</type>
-                        <name>global-netconf-processing-executor</name>
-                    </processing-executor>
-                    <keepalive-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">prefix:scheduled-threadpool</type>
-                        <name>global-netconf-ssh-scheduled-executor</name>
-                    </keepalive-executor>
-                    <shared-schema-repository xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">
-                        <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology:shared:schema:repository">prefix:shared-schema-repository</type>
-                        <name>default-shared-schema-repository</name>
-                    </shared-schema-repository>
-                </module>
-            </modules>
-
-            <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
-                <service>
-                    <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:topology">prefix:netconf-topology</type>
-                    <instance>
-                        <name>default-netconf-topology</name>
-                        <provider>/modules/module[type='netconf-topology-impl'][name='default-netconf-topology']</provider>
-                    </instance>
-                </service>
-            </services>
-        </data>
-    </configuration>
-    <required-capabilities>
-        <capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:topology?module=netconf-topology&amp;revision=2015-07-27</capability>
-    </required-capabilities>
-</snapshot>
\ No newline at end of file
diff --git a/netconf/netconf-topology-config/src/main/resources/org/opendaylight/blueprint/netconf-topology.xml b/netconf/netconf-topology-config/src/main/resources/org/opendaylight/blueprint/netconf-topology.xml
new file mode 100755 (executable)
index 0000000..09a5d6f
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Inocybe Technologies 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
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+           odl:use-default-for-reference-types="true">
+
+    <reference id="clientDispatcherDependency"
+               interface="org.opendaylight.netconf.client.NetconfClientDispatcher"
+               odl:type="netconf-client-dispatcher"/>
+    <reference id="bindingAwareBroker"
+               interface="org.opendaylight.controller.sal.binding.api.BindingAwareBroker"/>
+    <reference id="keepAliveExecutor"
+               interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"
+               odl:type="global-netconf-ssh-scheduled-executor"/>
+    <reference id="processingExecutor"
+               interface="org.opendaylight.controller.config.threadpool.ThreadPool"
+               odl:type="global-netconf-processing-executor"/>
+    <reference id="domBroker"
+               interface="org.opendaylight.controller.sal.core.api.Broker"/>
+    <reference id="eventExecutor"
+               interface="io.netty.util.concurrent.EventExecutor"
+               odl:type="global-event-executor"/>
+    <reference id="dataBroker"
+               interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"/>
+    <reference id="domMountPointService"
+               interface="org.opendaylight.controller.md.sal.dom.api.DOMMountPointService"/>
+
+    <bean id="schemaRepository" class="org.opendaylight.netconf.topology.impl.SchemaRepositoryProviderImpl">
+        <argument value="shared-schema-repository-impl"/>
+    </bean>
+
+    <bean id="netconfTopology" class="org.opendaylight.netconf.topology.impl.NetconfTopologyImpl"
+          init-method="init"
+          destroy-method="close">
+        <argument value="topology-netconf"/>
+        <argument ref="clientDispatcherDependency"/>
+        <argument ref="bindingAwareBroker"/>
+        <argument ref="domBroker"/>
+        <argument ref="eventExecutor"/>
+        <argument ref="keepAliveExecutor"/>
+        <argument ref="processingExecutor"/>
+        <argument ref="schemaRepository"/>
+        <argument ref="dataBroker"/>
+        <argument ref="domMountPointService"/>
+    </bean>
+
+    <bean id="netconfConnectorFactory" class="org.opendaylight.netconf.topology.impl.NetconfConnectorFactoryImpl"/>
+    <service ref="netconfConnectorFactory" interface="org.opendaylight.netconf.topology.api.NetconfConnectorFactory"
+             odl:type="default"/>
+
+</blueprint>
\ No newline at end of file
index 8fced1956ea89d516b1ecb6c83ec60a3f3233bb8..ea9ed8131ac4f0bb650d6ec2d1c423175588caaf 100644 (file)
@@ -20,9 +20,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
     <reference id="bindingAwareBroker"
                interface="org.opendaylight.controller.sal.binding.api.BindingAwareBroker"/>
     <reference id="keepAliveExecutor"
-               interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"/>
+               interface="org.opendaylight.controller.config.threadpool.ScheduledThreadPool"
+               odl:type="global-netconf-ssh-scheduled-executor"/>
     <reference id="processingExecutor"
-               interface="org.opendaylight.controller.config.threadpool.ThreadPool"/>
+               interface="org.opendaylight.controller.config.threadpool.ThreadPool"
+               odl:type="global-netconf-processing-executor"/>
     <reference id="domBroker"
                interface="org.opendaylight.controller.sal.core.api.Broker"/>
     <reference id="actorSystemProvider"
@@ -31,7 +33,8 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
                interface="io.netty.util.concurrent.EventExecutor"
                odl:type="global-event-executor"/>
     <reference id="clientDispatcherDependency"
-               interface="org.opendaylight.netconf.client.NetconfClientDispatcher"/>
+               interface="org.opendaylight.netconf.client.NetconfClientDispatcher"
+               odl:type="netconf-client-dispatcher"/>
 
     <bean id="netconfTopologyManager"
           class="org.opendaylight.netconf.topology.singleton.impl.NetconfTopologyManager"
index 5ffd6d8d37aa1c1df9ad5bd54bc24cd0cf3537e5..ae7679f62be5ec733cff35ec327e8be0c7ab6eca 100644 (file)
             <artifactId>yang-model-api</artifactId>
         </dependency>
     </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>build-helper-maven-plugin</artifactId>
-                <version>1.8</version>
-                <executions>
-                    <execution>
-                        <id>add-source</id>
-                        <phase>generate-sources</phase>
-                        <goals>
-                            <goal>add-source</goal>
-                        </goals>
-                        <configuration>
-                            <sources>
-                                <source>${project.build.directory}/generated-sources/config</source>;
-                            </sources>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
-
 </project>
diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/NetconfTopologyModule.java b/netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/NetconfTopologyModule.java
deleted file mode 100644 (file)
index e4321b7..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.config.yang.netconf.topology;
-
-import org.opendaylight.netconf.topology.impl.NetconfTopologyImpl;
-
-public class NetconfTopologyModule extends org.opendaylight.controller.config.yang.netconf.topology.AbstractNetconfTopologyModule {
-    public NetconfTopologyModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
-        super(identifier, dependencyResolver);
-    }
-
-    public NetconfTopologyModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.netconf.topology.NetconfTopologyModule oldModule, AutoCloseable oldInstance) {
-        super(identifier, dependencyResolver, oldModule, oldInstance);
-    }
-
-    @Override
-    public void customValidation() {
-        // add custom validation form module attributes here.
-        this.getClientDispatcherDependency();
-    }
-
-    @Override
-    public AutoCloseable createInstance() {
-        return new NetconfTopologyImpl(getTopologyId(), getClientDispatcherDependency(), getBindingRegistryDependency(),
-                getDomRegistryDependency(), getEventExecutorDependency(), getKeepaliveExecutorDependency(),
-                getProcessingExecutorDependency(), getSharedSchemaRepositoryDependency());
-    }
-
-}
diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/NetconfTopologyModuleFactory.java b/netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/NetconfTopologyModuleFactory.java
deleted file mode 100644 (file)
index f8dd199..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (c) 2015 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
- */
-/*
-* Generated file
-*
-* Generated from: yang module name: netconf-topology yang module local name: netconf-topology-impl
-* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
-* Generated at: Tue Jul 28 15:33:44 CEST 2015
-*
-* Do not modify this file unless it is present under src/main directory
-*/
-package org.opendaylight.controller.config.yang.netconf.topology;
-
-public class NetconfTopologyModuleFactory extends org.opendaylight.controller.config.yang.netconf.topology.AbstractNetconfTopologyModuleFactory {
-
-}
diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/shared/schema/repository/SchemaRepositoryImplModule.java b/netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/shared/schema/repository/SchemaRepositoryImplModule.java
deleted file mode 100644 (file)
index c4f5f98..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository;
-
-import org.opendaylight.netconf.topology.SchemaRepositoryProvider;
-import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
-
-public class SchemaRepositoryImplModule extends org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository.AbstractSchemaRepositoryImplModule {
-    public SchemaRepositoryImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
-        super(identifier, dependencyResolver);
-    }
-
-    public SchemaRepositoryImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository.SchemaRepositoryImplModule oldModule, java.lang.AutoCloseable oldInstance) {
-        super(identifier, dependencyResolver, oldModule, oldInstance);
-    }
-
-    @Override
-    public void customValidation() {
-        // add custom validation form module attributes here.
-    }
-
-    @Override
-    public java.lang.AutoCloseable createInstance() {
-        return new SchemaRepositoryProviderAutoCloseAble(this);
-    }
-
-    private static class SchemaRepositoryProviderAutoCloseAble implements SchemaRepositoryProvider, AutoCloseable {
-
-        private final SharedSchemaRepository schemaRepository;
-
-        public SchemaRepositoryProviderAutoCloseAble(SchemaRepositoryImplModule module) {
-            schemaRepository = new SharedSchemaRepository(module.getIdentifier().getInstanceName());
-        }
-
-        @Override
-        public void close() throws Exception {
-            //NOOP
-        }
-
-        @Override
-        public SharedSchemaRepository getSharedSchemaRepository() {
-            return schemaRepository;
-        }
-    }
-}
diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/shared/schema/repository/SchemaRepositoryImplModuleFactory.java b/netconf/netconf-topology/src/main/java/org/opendaylight/controller/config/yang/netconf/topology/shared/schema/repository/SchemaRepositoryImplModuleFactory.java
deleted file mode 100644 (file)
index 9cf6e1c..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2015 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
- */
-
-/*
-* Generated file
-*
-* Generated from: yang module name: shared-schema-repository yang module local name: shared-schema-repository-impl
-* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
-* Generated at: Tue Sep 08 13:43:39 CEST 2015
-*
-* Do not modify this file unless it is present under src/main directory
-*/
-package org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository;
-
-import com.google.common.collect.Sets;
-import java.util.Set;
-import org.opendaylight.controller.config.api.DependencyResolverFactory;
-import org.opendaylight.controller.config.api.ModuleIdentifier;
-import org.osgi.framework.BundleContext;
-
-public class SchemaRepositoryImplModuleFactory extends org.opendaylight.controller.config.yang.netconf.topology.shared.schema.repository.AbstractSchemaRepositoryImplModuleFactory {
-
-    public static final ModuleIdentifier defaultInstanceId = new ModuleIdentifier(NAME, "default-shared-schema-repository");
-
-    @Override
-    public Set<SchemaRepositoryImplModule> getDefaultModules(DependencyResolverFactory dependencyResolverFactory, BundleContext bundleContext) {
-        SchemaRepositoryImplModule defaultModule = new SchemaRepositoryImplModule(
-                defaultInstanceId, dependencyResolverFactory.createDependencyResolver(defaultInstanceId));
-        return Sets.newHashSet(defaultModule);
-    }
-}
index 7174c53be544c628a4d85eb510c870d7a18ea751..997d6cb0117cb63d6a15305b0ae37110c0004b32 100644 (file)
@@ -20,8 +20,6 @@ import java.io.File;
 import java.math.BigDecimal;
 import java.net.InetSocketAddress;
 import java.net.URL;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -30,11 +28,7 @@ import org.opendaylight.controller.config.threadpool.ThreadPool;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
-import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
 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.netconf.api.NetconfMessage;
 import org.opendaylight.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.netconf.client.NetconfClientSessionListener;
@@ -57,6 +51,8 @@ import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade;
 import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.netconf.topology.api.NetconfTopology;
+import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
 import org.opendaylight.protocol.framework.ReconnectStrategyFactory;
 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
@@ -81,7 +77,7 @@ import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public abstract class AbstractNetconfTopology implements NetconfTopology, BindingAwareProvider, Provider {
+public abstract class AbstractNetconfTopology implements NetconfTopology {
 
     private static final Logger LOG = LoggerFactory.getLogger(AbstractNetconfTopology.class);
 
@@ -164,19 +160,20 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
     protected final ScheduledThreadPool keepaliveExecutor;
     protected final ThreadPool processingExecutor;
     protected final SharedSchemaRepository sharedSchemaRepository;
+    protected final DataBroker dataBroker;
+    protected final DOMMountPointService mountPointService;
 
     protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
     protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
     protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
 
-    protected DOMMountPointService mountPointService = null;
-    protected DataBroker dataBroker = null;
     protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
 
     protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher,
                                       final BindingAwareBroker bindingAwareBroker, final Broker domBroker,
                                       final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
-                                      final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider) {
+                                      final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider,
+                                      final DataBroker dataBroker, final DOMMountPointService mountPointService) {
         this.topologyId = topologyId;
         this.clientDispatcher = clientDispatcher;
         this.bindingAwareBroker = bindingAwareBroker;
@@ -185,11 +182,8 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
         this.keepaliveExecutor = keepaliveExecutor;
         this.processingExecutor = processingExecutor;
         this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository();
-    }
-
-    protected void registerToSal(BindingAwareProvider baProvider, Provider provider) {
-        domBroker.registerProvider(provider);
-        bindingAwareBroker.registerProvider(baProvider);
+        this.dataBroker = dataBroker;
+        this.mountPointService = mountPointService;
     }
 
     public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) {
@@ -200,9 +194,6 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
         this.schemaContextFactory = schemaContextFactory;
     }
 
-    @Override
-    public abstract void onSessionInitiated(ProviderContext session);
-
     @Override
     public ListenableFuture<NetconfDeviceCapabilities> connectNode(NodeId nodeId, Node configNode) {
         LOG.info("Connecting RemoteDevice{{}} , with config {}", nodeId, configNode);
@@ -438,16 +429,6 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin
 
     protected abstract RemoteDeviceHandler<NetconfSessionPreferences> createSalFacade(final RemoteDeviceId id, final Broker domBroker, final BindingAwareBroker bindingBroker);
 
-    @Override
-    public void onSessionInitiated(ProviderSession session) {
-         mountPointService = session.getService(DOMMountPointService.class);
-    }
-
-    @Override
-    public Collection<ProviderFunctionality> getProviderFunctionality() {
-        return Collections.emptySet();
-    }
-
     private InetSocketAddress getSocketAddress(final Host host, int port) {
         if(host.getDomainName() != null) {
             return new InetSocketAddress(host.getDomainName().getValue(), port);
diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/NetconfConnectorFactory.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/NetconfConnectorFactory.java
new file mode 100644 (file)
index 0000000..632c2f9
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.netconf.topology.api;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+
+/**
+ * Created by adetalhouet on 2016-11-03.
+ */
+public interface NetconfConnectorFactory {
+
+    /**
+     * Create a new netconf connector with default values.
+     * <p>
+     * This method will create a {@link Node} and a {@link NetconfNode} that will be added as an augmentation to the
+     * {@link Node}. Afterward, that {@link Node} will be written in the MDSAL datastore under the {@link NetconfTopology}.
+     * Listeners of that subtree located within network-topology bundle will setup the session.
+     *
+     * @param dataBroker Instance of the {@link DataBroker}
+     * @param instanceName The name of the node
+     * @param address The address
+     * @param port The port
+     * @param username The username of the netconf session
+     * @param password The password of the netconf session
+     * @param tcpOnly Whether to create a TCP or SSH session
+     * @param reconnectOnSchemaChange Whether to enable ietf-netconf-monitoring and register the NETCONF stream.
+     * @return The created {@link Node}
+     */
+    Node newInstance(final DataBroker dataBroker,
+                     final String instanceName,
+                     final String address,
+                     final Integer port,
+                     final String username,
+                     final String password,
+                     final Boolean tcpOnly,
+                     final Boolean reconnectOnSchemaChange);
+}
similarity index 94%
rename from netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/NetconfTopology.java
rename to netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/NetconfTopology.java
index b275c7584da82dd8db87cc986d64a15da00ca563..d69e2e272b14ee85f1726030015fcb704598bdbb 100644 (file)
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.netconf.topology;
+package org.opendaylight.netconf.topology.api;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
similarity index 91%
rename from netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/SchemaRepositoryProvider.java
rename to netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/api/SchemaRepositoryProvider.java
index e4664d6f3bf160d631511ef46bd6a744cea3c16b..2ceee2bd072dd7e829d2e671402c3f3c520a449a 100644 (file)
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.netconf.topology;
+package org.opendaylight.netconf.topology.api;
 
 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
 
diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfConnectorFactoryImpl.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfConnectorFactoryImpl.java
new file mode 100644 (file)
index 0000000..dc5649f
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.netconf.topology.impl;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.netconf.topology.api.NetconfConnectorFactory;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.HostBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by adetalhouet on 2016-11-03.
+ */
+public class NetconfConnectorFactoryImpl implements NetconfConnectorFactory {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NetconfConnectorFactoryImpl.class);
+
+    private static final InstanceIdentifier<Topology> TOPOLOGY_PATH = InstanceIdentifier.create(NetworkTopology.class)
+            .child(Topology.class, new TopologyKey(new TopologyId("topology-netconf")));
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Node newInstance(final DataBroker dataBroker,
+                            final String instanceName,
+                            final String address,
+                            final Integer port,
+                            final String username,
+                            final String password,
+                            final Boolean tcpOnly,
+                            final Boolean reconnectOnSchemaChange) {
+
+        final NodeId nodeId = new NodeId(instanceName);
+        final NodeKey nodeKey = new NodeKey(nodeId);
+        final Credentials credentials = new LoginPasswordBuilder()
+                .setUsername(username)
+                .setPassword(password)
+                .build();
+        final Host host = HostBuilder.getDefaultInstance(address);
+        final PortNumber portNumber = new PortNumber(port);
+        final NetconfNode netconfNode = new NetconfNodeBuilder()
+                .setHost(host)
+                .setPort(portNumber)
+                .setCredentials(credentials)
+                .setTcpOnly(tcpOnly)
+                .setReconnectOnChangedSchema(reconnectOnSchemaChange)
+                .build();
+        final Node node =  new NodeBuilder()
+                .setNodeId(nodeId)
+                .setKey(nodeKey)
+                .addAugmentation(NetconfNode.class, netconfNode)
+                .build();
+
+        final InstanceIdentifier<Node> nodePath = TOPOLOGY_PATH.child(Node.class, nodeKey);
+        final WriteTransaction transaction = dataBroker.newWriteOnlyTransaction();
+        transaction.put(LogicalDatastoreType.CONFIGURATION, nodePath, node);
+        final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = transaction.submit();
+        Futures.addCallback(submitFuture, new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(@Nullable final Void result) {
+                LOG.debug("Node {} was successfully added to the topology", instanceName);
+            }
+
+            @Override
+            public void onFailure(final Throwable t) {
+                LOG.error("Node {} creation failed: {}", instanceName, t);
+            }
+        });
+        return node;
+    }
+}
\ No newline at end of file
index 0553a5aa5b4753ccf1af8c94a26c331cefda5177..9b02f0b3d15c82904e4f52c9b0931eabcc1b5b01 100644 (file)
@@ -22,8 +22,8 @@ import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
 import org.opendaylight.controller.sal.core.api.Broker;
 import org.opendaylight.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
@@ -31,7 +31,7 @@ import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPrefe
 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalFacade;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.netconf.topology.AbstractNetconfTopology;
-import org.opendaylight.netconf.topology.SchemaRepositoryProvider;
+import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
@@ -48,16 +48,16 @@ public class NetconfTopologyImpl extends AbstractNetconfTopology implements Data
 
     private static final Logger LOG = LoggerFactory.getLogger(NetconfTopologyImpl.class);
 
-        private ListenerRegistration<NetconfTopologyImpl> datastoreListenerRegistration = null;
+    private ListenerRegistration<NetconfTopologyImpl> datastoreListenerRegistration = null;
 
     public NetconfTopologyImpl(final String topologyId, final NetconfClientDispatcher clientDispatcher,
                                final BindingAwareBroker bindingAwareBroker, final Broker domBroker,
                                final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor,
-                               final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider) {
+                               final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider,
+                               final DataBroker dataBroker, final DOMMountPointService mountPointService) {
         super(topologyId, clientDispatcher,
                 bindingAwareBroker, domBroker, eventExecutor,
-                keepaliveExecutor, processingExecutor, schemaRepositoryProvider);
-        registerToSal(this, this);
+                keepaliveExecutor, processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService);
     }
 
     @Override
@@ -79,10 +79,10 @@ public class NetconfTopologyImpl extends AbstractNetconfTopology implements Data
         return new NetconfDeviceSalFacade(id, domBroker, bindingAwareBroker);
     }
 
-    @Override
-    public void onSessionInitiated(ProviderContext session) {
-        dataBroker = session.getSALService(DataBroker.class);
-
+    /**
+     * Invoke by blueprint
+     */
+    public void init() {
         final WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
         initTopology(wtx, LogicalDatastoreType.CONFIGURATION);
         initTopology(wtx, LogicalDatastoreType.OPERATIONAL);
diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/SchemaRepositoryProviderImpl.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/SchemaRepositoryProviderImpl.java
new file mode 100644 (file)
index 0000000..9fc5bee
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016 Inocybe Technologies 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.netconf.topology.impl;
+
+import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+
+public class SchemaRepositoryProviderImpl implements SchemaRepositoryProvider {
+
+    private final SharedSchemaRepository schemaRepository;
+
+    public SchemaRepositoryProviderImpl(final String moduleName) {
+        schemaRepository = new SharedSchemaRepository(moduleName);
+    }
+
+    @Override
+    public SharedSchemaRepository getSharedSchemaRepository() {
+        return schemaRepository;
+    }
+}
\ No newline at end of file
diff --git a/netconf/netconf-topology/src/main/yang/netconf-topology.yang b/netconf/netconf-topology/src/main/yang/netconf-topology.yang
deleted file mode 100644 (file)
index 8f55e63..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-module netconf-topology {
-
-    yang-version 1;
-    namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:topology";
-    prefix "nt";
-
-    import config { prefix config; revision-date 2013-04-05; }
-    import threadpool {prefix th;}
-    import netty {prefix netty;}
-    import opendaylight-md-sal-dom {prefix dom;}
-    import opendaylight-md-sal-binding {prefix md-sal-binding; revision-date 2013-10-28;}
-    import odl-netconf-cfg { prefix cfg-net; revision-date 2014-04-08; }
-    import shared-schema-repository { prefix sh; revision-date 2015-07-27; }
-
-    description
-            "Module definition for Netconf topolgy. Netconf topology provides a set of common configuration ";
-
-    revision "2015-07-27" {
-        description
-            "Initial revision";
-    }
-
-    identity netconf-topology {
-        base config:service-type;
-        config:java-class "org.opendaylight.netconf.topology.NetconfTopology";
-    }
-
-    identity netconf-topology-impl {
-        base config:module-type;
-        config:java-name-prefix NetconfTopology;
-        config:provided-service netconf-topology;
-    }
-
-    augment "/config:modules/config:module/config:configuration" {
-        case netconf-topology-impl {
-            when "/config:modules/config:module/config:type = 'netconf-topology-impl'";
-
-            leaf topology-id {
-                mandatory true;
-                type string;
-            }
-
-            container dom-registry {
-                uses config:service-ref {
-                    refine type {
-                        mandatory true;
-                        config:required-identity dom:dom-broker-osgi-registry;
-                    }
-                }
-            }
-
-            container binding-registry {
-                uses config:service-ref {
-                    refine type {
-                        mandatory true;
-                        config:required-identity md-sal-binding:binding-broker-osgi-registry;
-                    }
-                }
-            }
-
-            container event-executor {
-                uses config:service-ref {
-                    refine type {
-                        mandatory true;
-                        config:required-identity netty:netty-event-executor;
-                    }
-                }
-            }
-
-            container processing-executor {
-                uses config:service-ref {
-                    refine type {
-                        mandatory true;
-                        config:required-identity th:threadpool;
-                    }
-                }
-
-                description "Makes up for flaws in netty threading design";
-            }
-
-            container client-dispatcher {
-                uses config:service-ref {
-                    refine type {
-                        mandatory false;
-                        config:required-identity cfg-net:netconf-client-dispatcher;
-                    }
-                }
-            }
-
-            container keepalive-executor {
-                uses config:service-ref {
-                    refine type {
-                        mandatory false;
-                        config:required-identity th:scheduled-threadpool;
-                    }
-                }
-
-                description "Dedicated solely to keepalive execution";
-            }
-
-            container shared-schema-repository {
-                uses config:service-ref {
-                    refine type {
-                        mandatory true;
-                        config:required-identity sh:shared-schema-repository;
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/netconf/netconf-topology/src/main/yang/shared-schema-repository.yang b/netconf/netconf-topology/src/main/yang/shared-schema-repository.yang
deleted file mode 100644 (file)
index e5d1f07..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-module shared-schema-repository {
-
-    yang-version 1;
-    namespace "urn:opendaylight:params:xml:ns:yang:controller:netconf:topology:shared:schema:repository";
-    prefix "ssr";
-
-    import config { prefix config; revision-date 2013-04-05; }
-
-    description
-            "Module definition for Shared schema repository.";
-
-    revision "2015-07-27" {
-        description
-            "Initial revision";
-    }
-
-    identity shared-schema-repository {
-        base "config:service-type";
-        config:java-class "org.opendaylight.netconf.topology.SchemaRepositoryProvider";
-    }
-
-    identity shared-schema-repository-impl {
-        base "config:module-type";
-        config:provided-service shared-schema-repository;
-        config:java-name-prefix SchemaRepositoryImpl;
-    }
-
-    augment "/config:modules/config:module/config:configuration" {
-        case shared-schema-repository-impl {
-            when "/config:modules/config:module/config:type = 'shared-schema-repository-impl'";
-
-            container schema-repository-fallbacks {
-                list schema-repository-fallback {
-                    leaf repository-url {
-                        type string;
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
index 2a2f2f4125c6d1fd1e3125972e5de531c199eb80..ceb5cf078453b09293b451d8068da4b5d62fcbac 100644 (file)
@@ -37,12 +37,13 @@ import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
 import org.opendaylight.controller.sal.core.api.Broker;
 import org.opendaylight.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
-import org.opendaylight.netconf.topology.SchemaRepositoryProvider;
+import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
@@ -90,6 +91,11 @@ public class NetconfTopologyImplTest {
     @Mock
     private SchemaRepositoryProvider mockedSchemaRepositoryProvider;
 
+    @Mock
+    private DataBroker dataBroker;
+
+    @Mock
+    private DOMMountPointService domMountPointService;
 
     private TestingNetconfTopologyImpl topology;
     private TestingNetconfTopologyImpl spyTopology;
@@ -104,21 +110,19 @@ public class NetconfTopologyImplTest {
         when(mockedClientDispatcher.createReconnectingClient(any(NetconfReconnectingClientConfiguration.class))).thenReturn(future);
 
         topology = new TestingNetconfTopologyImpl(TOPOLOGY_ID, mockedClientDispatcher, mockedBindingAwareBroker,
-                mockedDataBroker, mockedEventExecutor, mockedKeepaliveExecutor, mockedProcessingExecutor, mockedSchemaRepositoryProvider);
+                mockedDataBroker, mockedEventExecutor, mockedKeepaliveExecutor, mockedProcessingExecutor, mockedSchemaRepositoryProvider,
+                dataBroker, domMountPointService);
 
         spyTopology = spy(topology);
     }
 
     @Test
-    public void testOnSessionInitiated() {
-        BindingAwareBroker.ProviderContext session = mock(BindingAwareBroker.ProviderContext.class);
-        DataBroker dataBroker = mock(DataBroker.class);
-        when(session.getSALService(DataBroker.class)).thenReturn(dataBroker);
+    public void testInit() {
         WriteTransaction wtx = mock(WriteTransaction.class);
         when(dataBroker.newWriteOnlyTransaction()).thenReturn(wtx);
         doNothing().when(wtx).merge(any(LogicalDatastoreType.class), any(InstanceIdentifier.class), any(DataObject.class));
         when(wtx.submit()).thenReturn(Futures.<Void, TransactionCommitFailedException>immediateCheckedFuture(null));
-        topology.onSessionInitiated(session);
+        topology.init();
 
         //verify initialization of topology
         final InstanceIdentifier<NetworkTopology> networkTopologyId = InstanceIdentifier.builder(NetworkTopology.class).build();
@@ -185,8 +189,13 @@ public class NetconfTopologyImplTest {
 
     public static class TestingNetconfTopologyImpl extends NetconfTopologyImpl {
 
-        public TestingNetconfTopologyImpl(String topologyId, NetconfClientDispatcher clientDispatcher, BindingAwareBroker bindingAwareBroker, Broker domBroker, EventExecutor eventExecutor, ScheduledThreadPool keepaliveExecutor, ThreadPool processingExecutor, SchemaRepositoryProvider schemaRepositoryProvider) {
-            super(topologyId, clientDispatcher, bindingAwareBroker, domBroker, eventExecutor, keepaliveExecutor, processingExecutor, schemaRepositoryProvider);
+        public TestingNetconfTopologyImpl(String topologyId, NetconfClientDispatcher clientDispatcher,
+                                          BindingAwareBroker bindingAwareBroker, Broker domBroker,
+                                          EventExecutor eventExecutor, ScheduledThreadPool keepaliveExecutor,
+                                          ThreadPool processingExecutor, SchemaRepositoryProvider schemaRepositoryProvider,
+                                          DataBroker dataBroker, DOMMountPointService domMountPointService) {
+            super(topologyId, clientDispatcher, bindingAwareBroker, domBroker, eventExecutor, keepaliveExecutor,
+                    processingExecutor, schemaRepositoryProvider, dataBroker, domMountPointService);
         }
 
         @Override
index 9d4b5a123839fa39eaa39a119560dc116deb7cb3..63bec7c525362593537c2dd2b48824ec73f15613 100644 (file)
@@ -8,11 +8,8 @@
 
 package org.opendaylight.netconf.util.osgi;
 
-import com.google.common.base.Optional;
-import io.netty.channel.local.LocalAddress;
-import java.net.InetSocketAddress;
 import java.util.Collection;
-import java.util.concurrent.TimeUnit;
+import java.util.Optional;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
@@ -23,111 +20,23 @@ import org.slf4j.LoggerFactory;
 public final class NetconfConfigUtil {
     private static final Logger LOG = LoggerFactory.getLogger(NetconfConfigUtil.class);
 
-    private static final String PREFIX_PROP = "netconf.";
-
     private NetconfConfigUtil() {
     }
 
-    public enum InfixProp {
-        tcp, ssh
-    }
-
-    private static final String PORT_SUFFIX_PROP = ".port";
-    private static final String ADDRESS_SUFFIX_PROP = ".address";
-    private static final String PRIVATE_KEY_PATH_PROP = ".pk.path";
-
-    private static final String CONNECTION_TIMEOUT_MILLIS_PROP = "connectionTimeoutMillis";
-    private static final String LOCAL_HOST = "127.0.0.1";
-    private static final String INADDR_ANY = "0.0.0.0";
-    public static final long DEFAULT_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(30);
-    private static final LocalAddress NETCONF_LOCAL_ADDRESS = new LocalAddress("netconf");
-    public static final String DEFAULT_PRIVATE_KEY_PATH = "./configuration/RSA.pk";
-    public static final InetSocketAddress DEFAULT_TCP_SERVER_ADRESS = new InetSocketAddress(LOCAL_HOST, 8383);
-    public static final InetSocketAddress DEFAULT_SSH_SERVER_ADRESS = new InetSocketAddress(INADDR_ANY, 1830);
-
-    public static LocalAddress getNetconfLocalAddress() {
-        return NETCONF_LOCAL_ADDRESS;
-    }
-
-    public static long extractTimeoutMillis(final BundleContext bundleContext) {
-        final String key = PREFIX_PROP + CONNECTION_TIMEOUT_MILLIS_PROP;
-        final String timeoutString = bundleContext.getProperty(key);
-        if (timeoutString == null || timeoutString.length() == 0) {
-            return DEFAULT_TIMEOUT_MILLIS;
-        }
-        try {
-            return Long.parseLong(timeoutString);
-        } catch (final NumberFormatException e) {
-            LOG.warn("Cannot parse {} property: {}, using defaults", key, timeoutString, e);
-            return DEFAULT_TIMEOUT_MILLIS;
-        }
-    }
-
-    /**
-     * @param context from which properties are being read.
-     * @return value of private key path if value is present, Optional.absent otherwise
-     */
-    public static Optional<String> getPrivateKeyPath(final BundleContext context) {
-        return getProperty(context, getPrivateKeyKey());
-    }
-
-    public static String getPrivateKeyKey() {
-        return PREFIX_PROP + InfixProp.ssh + PRIVATE_KEY_PATH_PROP;
-    }
-
-    public static String getNetconfServerAddressKey(final InfixProp infixProp) {
-        return PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP;
-    }
-
-    /**
-     * @param context   from which properties are being read.
-     * @param infixProp either tcp or ssh
-     * @return value if address and port are present and valid, Optional.absent otherwise.
-     */
-    public static Optional<InetSocketAddress> extractNetconfServerAddress(final BundleContext context,
-                                                                           final InfixProp infixProp) {
-
-        final Optional<String> address = getProperty(context, getNetconfServerAddressKey(infixProp));
-        final Optional<String> port = getProperty(context, PREFIX_PROP + infixProp + PORT_SUFFIX_PROP);
-
-        if (address.isPresent() && port.isPresent()) {
-            try {
-                return Optional.of(parseAddress(address, port));
-            } catch (final IllegalArgumentException | SecurityException e) {
-                LOG.warn("Unable to parse {} netconf address from {}:{}, fallback to default",
-                        infixProp, address, port, e);
-            }
-        }
-        return Optional.absent();
-    }
-
-    private static InetSocketAddress parseAddress(final Optional<String> address, final Optional<String> port) {
-        final int portNumber = Integer.valueOf(port.get());
-        return new InetSocketAddress(address.get(), portNumber);
-    }
-
-    private static Optional<String> getProperty(final BundleContext context, final String propKey) {
-        String value = context.getProperty(propKey);
-        if (value != null && value.isEmpty()) {
-            value = null;
-        }
-        return Optional.fromNullable(value);
-    }
-
-    public static java.util.Optional<NetconfConfiguration> getNetconfConfigurationService(BundleContext bundleContext) {
+    public static Optional<NetconfConfiguration> getNetconfConfigurationService(BundleContext bundleContext) {
         final Collection<ServiceReference<ManagedService>> serviceReferences;
         try {
             serviceReferences = bundleContext.getServiceReferences(ManagedService.class, null);
             for (final ServiceReference<ManagedService> serviceReference : serviceReferences) {
                 ManagedService service = bundleContext.getService(serviceReference);
                 if (service instanceof NetconfConfiguration){
-                    return java.util.Optional.of((NetconfConfiguration) service);
+                    return Optional.of((NetconfConfiguration) service);
                 }
             }
         } catch (InvalidSyntaxException e) {
             LOG.error("Unable to retrieve references for ManagedService: {}", e);
         }
-        LOG.error("Unable to retrieve NetconfConfiguration service. Not found. Bundle netconf-util probably failed.");
-        return java.util.Optional.empty();
+        LOG.error("Unable to retrieve NetconfConfiguration service. Not found. Bundle netconf-util probably failed to load.");
+        return Optional.empty();
     }
 }
index e33f7e2e71fa55c0f0a10515a9c54bea4d610f97..bf935dee867906e0889997a0f7bdea142321d9a2 100644 (file)
@@ -8,8 +8,10 @@
 
 package org.opendaylight.netconf.util.osgi;
 
+import io.netty.channel.local.LocalAddress;
 import java.net.InetSocketAddress;
 import java.util.Dictionary;
+import java.util.concurrent.TimeUnit;
 import org.osgi.service.cm.ManagedService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -17,39 +19,61 @@ import org.slf4j.LoggerFactory;
 public class NetconfConfiguration implements ManagedService {
     private static final Logger LOG = LoggerFactory.getLogger(NetconfConfiguration.class);
 
-    private static final NetconfConfiguration instance = new NetconfConfiguration();
-    private NetconfConfigurationHolder netconfConfiguration;
+    /**
+     * Props to access information within the dictionary.
+     */
+
+    private static final String SSH_ADDRESS_PROP = "ssh-address";
+    private static final String SSH_PORT_PROP = "ssh-port";
+    private static final String TCP_ADDRESS_PROP = "tcp-address";
+    private static final String TCP_PORT_PROP = "tcp-port";
+    private static final String SSH_PK_PATH_PROP = "ssh-pk-path";
+
+    /**
+     * Default values used if no dictionary is provided.
+     */
+
+    public static final LocalAddress NETCONF_LOCAL_ADDRESS = new LocalAddress("netconf");
+    public static final long DEFAULT_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(30);
 
-    public static final String KEY_SSH_ADDRESS = "ssh-address";
-    public static final String KEY_SSH_PORT = "ssh-port";
-    public static final String KEY_TCP_ADDRESS = "tcp-address";
-    public static final String KEY_TCP_PORT = "tcp-port";
-    public static final String KEY_SSH_PK_PATH = "ssh-pk-path";
+    private static final String LOCAL_HOST = "127.0.0.1";
+    private static final String INADDR_ANY = "0.0.0.0";
+    private static final String DEFAULT_PRIVATE_KEY_PATH = "./configuration/RSA.pk";
+    private static final InetSocketAddress DEFAULT_TCP_SERVER_ADRESS = new InetSocketAddress(LOCAL_HOST, 8383);
+    private static final InetSocketAddress DEFAULT_SSH_SERVER_ADRESS = new InetSocketAddress(INADDR_ANY, 1830);
+
+    /**
+     * Singleton instance of this class, registered as a service in the OSGi registry.
+     */
+    private static final NetconfConfiguration INSTANCE = new NetconfConfiguration();
+
+    private NetconfConfigurationHolder netconfConfiguration;
 
     public static NetconfConfiguration getInstance() {
-        return instance;
+        return INSTANCE;
     }
 
     private NetconfConfiguration() {
-        netconfConfiguration = new NetconfConfigurationHolder(NetconfConfigUtil.DEFAULT_TCP_SERVER_ADRESS,
-                NetconfConfigUtil.DEFAULT_SSH_SERVER_ADRESS, NetconfConfigUtil.DEFAULT_PRIVATE_KEY_PATH);
+        netconfConfiguration = new NetconfConfigurationHolder(DEFAULT_TCP_SERVER_ADRESS,
+                DEFAULT_SSH_SERVER_ADRESS, DEFAULT_PRIVATE_KEY_PATH);
     }
 
     @Override
     public void updated(final Dictionary<String, ?> dictionaryConfig) {
         if (dictionaryConfig == null) {
-            LOG.warn("Netconf configuration cannot be updated.");
+            LOG.trace("CSS netconf server configuration cannot be updated as passed dictionary is null");
             return;
         }
-        final InetSocketAddress sshServerAddress = new InetSocketAddress((String) dictionaryConfig.get(KEY_SSH_ADDRESS),
-                Integer.parseInt((String) dictionaryConfig.get(KEY_SSH_PORT)));
-        final InetSocketAddress tcpServerAddress = new InetSocketAddress((String) dictionaryConfig.get(KEY_TCP_ADDRESS),
-                Integer.parseInt((String) dictionaryConfig.get(KEY_TCP_PORT)));
+        final InetSocketAddress sshServerAddress = new InetSocketAddress((String) dictionaryConfig.get(SSH_ADDRESS_PROP),
+                Integer.parseInt((String) dictionaryConfig.get(SSH_PORT_PROP)));
+        final InetSocketAddress tcpServerAddress = new InetSocketAddress((String) dictionaryConfig.get(TCP_ADDRESS_PROP),
+                Integer.parseInt((String) dictionaryConfig.get(TCP_PORT_PROP)));
 
-        netconfConfiguration = new NetconfConfigurationHolder(tcpServerAddress, sshServerAddress,
-                (String) dictionaryConfig.get(KEY_SSH_PK_PATH));
+        netconfConfiguration = new NetconfConfigurationHolder(tcpServerAddress,
+                sshServerAddress,
+                (String) dictionaryConfig.get(SSH_PK_PATH_PROP));
 
-        LOG.info("Netconf configuration was updated: {}", dictionaryConfig.toString());
+        LOG.debug("CSS netconf server configuration was updated: {}", dictionaryConfig.toString());
     }
 
     public InetSocketAddress getSshServerAddress(){
index a752322d5042131834b3f06cade1ad630c7c9f98..99371503b589b53e4394d4bd71bab3f898f9e58a 100644 (file)
@@ -28,12 +28,12 @@ public class NetconfConfigurationActivator implements BundleActivator {
     @Override
     public void stop(BundleContext bundleContext) {
         if (configService != null) {
-          configService.unregister();
-          configService = null;
+            configService.unregister();
+            configService = null;
         }
     }
 
-    private Hashtable<String, String> getNetconfConfigProperties(){
+    private Hashtable<String, String> getNetconfConfigProperties() {
         Hashtable<String, String> properties = new Hashtable<>();
         properties.put(Constants.SERVICE_PID, CONFIG_PID);
         return properties;
index 74b3a089fa7aa506dc7a3bb49cce314a9f1f9a05..9eb88c4cb2b1fe088bf7ac87352ba939fb5500ca 100644 (file)
@@ -16,7 +16,9 @@ final class NetconfConfigurationHolder {
     private final InetSocketAddress sshServerAddress;
     private final String privateKeyPath;
 
-    NetconfConfigurationHolder(InetSocketAddress tcpServerAddress, InetSocketAddress sshServerAddress, String privateKeyPath){
+    NetconfConfigurationHolder(final InetSocketAddress tcpServerAddress,
+                               final InetSocketAddress sshServerAddress,
+                               final String privateKeyPath) {
         this.tcpServerAddress = tcpServerAddress;
         this.sshServerAddress = sshServerAddress;
         this.privateKeyPath = privateKeyPath;
@@ -33,5 +35,4 @@ final class NetconfConfigurationHolder {
     InetSocketAddress getTcpServerAddress() {
         return tcpServerAddress;
     }
-
 }
index 4e363b14b27200ef9a672d4b214d05175e9b0f82..72106403225d806818188ba9677ec77235794a85 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2016 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,
@@ -8,76 +8,73 @@
 
 package org.opendaylight.netconf.util.osgi;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 
-import com.google.common.base.Optional;
-import io.netty.channel.local.LocalAddress;
-import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ManagedService;
 
 public class NetconfConfigUtilTest {
 
-    private BundleContext bundleContext;
+    @Mock
+    private ServiceReference<ManagedService> serviceRef;
 
-    @Before
-    public void setUp() throws Exception {
-        bundleContext = mock(BundleContext.class);
-    }
-
-    @Test
-    public void testNetconfConfigUtil() throws Exception {
-        assertEquals(NetconfConfigUtil.getNetconfLocalAddress(), new LocalAddress("netconf"));
-
-        doReturn("").when(bundleContext).getProperty("netconf.connectionTimeoutMillis");
-        assertEquals(NetconfConfigUtil.extractTimeoutMillis(bundleContext), NetconfConfigUtil.DEFAULT_TIMEOUT_MILLIS);
-
-        doReturn("a").when(bundleContext).getProperty("netconf.connectionTimeoutMillis");
-        assertEquals(NetconfConfigUtil.extractTimeoutMillis(bundleContext), NetconfConfigUtil.DEFAULT_TIMEOUT_MILLIS);
-    }
+    @Mock
+    private ServiceReference<ManagedService> netconfConfigurationRef;
 
-    @Test
-    public void testgetPrivateKeyKey() throws Exception {
-        assertEquals(NetconfConfigUtil.getPrivateKeyKey(), "netconf.ssh.pk.path");
-    }
-
-    @Test
-    public void testgetNetconfServerAddressKey() throws Exception {
-        NetconfConfigUtil.InfixProp prop = NetconfConfigUtil.InfixProp.tcp;
-        assertEquals(NetconfConfigUtil.getNetconfServerAddressKey(prop), "netconf.tcp.address");
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
     }
 
     @Test
-    public void testExtractNetconfServerAddress() throws Exception {
-        NetconfConfigUtil.InfixProp prop = NetconfConfigUtil.InfixProp.tcp;
-        doReturn("").when(bundleContext).getProperty(anyString());
-        assertEquals(NetconfConfigUtil.extractNetconfServerAddress(bundleContext, prop), Optional.absent());
-    }
+    public void testGetNetconfConfigurationService() throws Exception {
+        final Collection<ServiceReference<ManagedService>> services = new ArrayList<>();
+        services.add(serviceRef);
+        services.add(netconfConfigurationRef);
+        final BundleContext context = mock(BundleContext.class);
+        doReturn(services).when(context).getServiceReferences(ManagedService.class, null);
+        final ManagedService service = mock(ManagedService.class);
+        doReturn(service).when(context).getService(serviceRef);
+        doReturn(NetconfConfiguration.getInstance()).when(context).getService(netconfConfigurationRef);
+        final java.util.Optional<NetconfConfiguration> netconfConfigurationOptional =
+                NetconfConfigUtil.getNetconfConfigurationService(context);
+        Assert.assertTrue(netconfConfigurationOptional.isPresent());
+        Assert.assertEquals(NetconfConfiguration.getInstance(), netconfConfigurationOptional.get());
 
-    @Test
-    public void testExtractNetconfServerAddress2() throws Exception {
-        NetconfConfigUtil.InfixProp prop = NetconfConfigUtil.InfixProp.tcp;
-        doReturn("1.1.1.1").when(bundleContext).getProperty("netconf.tcp.address");
-        doReturn("20").when(bundleContext).getProperty("netconf.tcp.port");
-        Optional<InetSocketAddress> inetSocketAddressOptional = NetconfConfigUtil.extractNetconfServerAddress(bundleContext, prop);
-        assertTrue(inetSocketAddressOptional.isPresent());
-        assertEquals(inetSocketAddressOptional.get(), new InetSocketAddress("1.1.1.1", 20));
     }
 
     @Test
-    public void testGetPrivateKeyPath() throws Exception {
-        doReturn("path").when(bundleContext).getProperty("netconf.ssh.pk.path");
-        assertEquals(NetconfConfigUtil.getPrivateKeyPath(bundleContext).get(), "path");
+    public void testGetNetconfConfigurationServiceAbsent() throws Exception {
+        final Collection<ServiceReference<ManagedService>> services = new ArrayList<>();
+        services.add(serviceRef);
+        final BundleContext context = mock(BundleContext.class);
+        doReturn(services).when(context).getServiceReferences(ManagedService.class, null);
+        final ManagedService service = mock(ManagedService.class);
+        doReturn(service).when(context).getService(serviceRef);
+        final java.util.Optional<NetconfConfiguration> netconfConfigurationOptional =
+                NetconfConfigUtil.getNetconfConfigurationService(context);
+        Assert.assertFalse(netconfConfigurationOptional.isPresent());
     }
 
     @Test
-    public void testGetPrivateKeyPathNotPresent() throws Exception {
-        doReturn(null).when(bundleContext).getProperty("netconf.ssh.pk.path");
-        assertEquals(NetconfConfigUtil.getPrivateKeyPath(bundleContext), Optional.absent());
+    public void testGetNetconfConfigurationServiceInvalidSyntax() throws Exception {
+        final BundleContext context = mock(BundleContext.class);
+        final InvalidSyntaxException exception = new InvalidSyntaxException("Invalid syntax", "filter");
+        doThrow(exception).when(context).getServiceReferences(ManagedService.class, null);
+        final java.util.Optional<NetconfConfiguration> netconfConfigurationOptional =
+                NetconfConfigUtil.getNetconfConfigurationService(context);
+        Assert.assertFalse(netconfConfigurationOptional.isPresent());
     }
 }
diff --git a/netconf/netconf-util/src/test/java/org/opendaylight/netconf/util/osgi/NetconfConfigurationActivatorTest.java b/netconf/netconf-util/src/test/java/org/opendaylight/netconf/util/osgi/NetconfConfigurationActivatorTest.java
new file mode 100644 (file)
index 0000000..7ace84b
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 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.netconf.util.osgi;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ManagedService;
+
+public class NetconfConfigurationActivatorTest {
+
+    @Mock
+    private BundleContext context;
+    @Mock
+    private ServiceRegistration registration;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        doReturn(registration).when(context)
+                .registerService(eq(ManagedService.class), any(NetconfConfiguration.class), any());
+        doNothing().when(registration).unregister();
+    }
+
+    @Test
+    public void testStartStop() throws Exception {
+        final NetconfConfigurationActivator activator = new NetconfConfigurationActivator();
+        activator.start(context);
+        final Dictionary<String, String> props = new Hashtable<>();
+        props.put(Constants.SERVICE_PID, "netconf");
+        verify(context).registerService(eq(ManagedService.class), eq(NetconfConfiguration.getInstance()), eq(props));
+        activator.stop(context);
+        verify(registration).unregister();
+    }
+
+}
\ No newline at end of file
diff --git a/netconf/netconf-util/src/test/java/org/opendaylight/netconf/util/osgi/NetconfConfigurationTest.java b/netconf/netconf-util/src/test/java/org/opendaylight/netconf/util/osgi/NetconfConfigurationTest.java
new file mode 100644 (file)
index 0000000..c80f388
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016 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.netconf.util.osgi;
+
+import io.netty.channel.local.LocalAddress;
+import java.net.InetSocketAddress;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NetconfConfigurationTest {
+
+    @Test
+    public void testUpdated() throws Exception {
+        final NetconfConfiguration config = NetconfConfiguration.getInstance();
+        Assert.assertEquals(new InetSocketAddress("0.0.0.0", 1830), config.getSshServerAddress());
+        Assert.assertEquals(new InetSocketAddress("127.0.0.1", 8383), config.getTcpServerAddress());
+        Assert.assertEquals("./configuration/RSA.pk", config.getPrivateKeyPath());
+        final Dictionary<String, String> newValues = new Hashtable<>();
+        final String newSshIp = "192.168.1.1";
+        final String newTcpIp = "192.168.1.2";
+        final int newSshPort = 1234;
+        final int newTcpPort = 4567;
+        final String newSshKeyPath = "./new_folder/configuration/RSA.pk";
+        newValues.put("ssh-address", newSshIp);
+        newValues.put("ssh-port", Integer.toString(newSshPort));
+        newValues.put("tcp-address", newTcpIp);
+        newValues.put("tcp-port", Integer.toString(newTcpPort));
+        newValues.put("ssh-pk-path", newSshKeyPath);
+        config.updated(newValues);
+        Assert.assertEquals(new InetSocketAddress(newSshIp, newSshPort), config.getSshServerAddress());
+        Assert.assertEquals(new InetSocketAddress(newTcpIp, newTcpPort), config.getTcpServerAddress());
+        Assert.assertEquals(newSshKeyPath, config.getPrivateKeyPath());
+    }
+
+    @Test
+    public void testUpdatedNull() throws Exception {
+        final NetconfConfiguration config = NetconfConfiguration.getInstance();
+        Assert.assertEquals(new InetSocketAddress("0.0.0.0", 1830), config.getSshServerAddress());
+        Assert.assertEquals(new InetSocketAddress("127.0.0.1", 8383), config.getTcpServerAddress());
+        Assert.assertEquals("./configuration/RSA.pk", config.getPrivateKeyPath());
+        final Dictionary<String, String> nullDictionary = null;
+        config.updated(nullDictionary);
+        Assert.assertEquals(new InetSocketAddress("0.0.0.0", 1830), config.getSshServerAddress());
+        Assert.assertEquals(new InetSocketAddress("127.0.0.1", 8383), config.getTcpServerAddress());
+        Assert.assertEquals("./configuration/RSA.pk", config.getPrivateKeyPath());
+    }
+}
\ No newline at end of file
index 56887d08f41418af8e9fcefbd7bda2980d5fd49d..f6783c6219a7c659764c127f6b95b18307727a0c 100644 (file)
@@ -29,6 +29,7 @@
     <module>netconf-impl</module>
     <module>config-netconf-connector</module>
     <module>mdsal-netconf-ssh</module>
+    <module>mdsal-netconf-tcp</module>
     <module>mdsal-netconf-connector</module>
     <module>mdsal-netconf-monitoring</module>
     <module>mdsal-netconf-notification</module>
@@ -38,8 +39,6 @@
     <module>netconf-netty-util</module>
     <module>netconf-mapping-api</module>
     <module>netconf-client</module>
-    <!--TODO Delete netconf-config-dispatcher bundle as soon as https://git.opendaylight.org/gerrit/#/c/46621/ is merged.-->
-    <module>netconf-config-dispatcher</module>
     <module>netconf-ssh</module>
     <module>netconf-tcp</module>
     <module>netconf-monitoring</module>
index 421e52da71978af327083e7f39b83aa3262015a7..2a56d01b1a4f4702ba4d95b7560dfb76d92c948f 100644 (file)
@@ -24,6 +24,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
 import org.opendaylight.netconf.sal.connect.netconf.sal.tx.ReadOnlyTx;
 import org.opendaylight.netconf.sal.connect.netconf.sal.tx.ReadWriteTx;
+import org.opendaylight.netconf.sal.connect.netconf.sal.tx.TxChain;
 import org.opendaylight.netconf.sal.connect.netconf.sal.tx.WriteCandidateRunningTx;
 import org.opendaylight.netconf.sal.connect.netconf.sal.tx.WriteCandidateTx;
 import org.opendaylight.netconf.sal.connect.netconf.sal.tx.WriteRunningTx;
@@ -38,8 +39,8 @@ public final class NetconfDeviceDataBroker implements DOMDataBroker {
     private final NetconfBaseOps netconfOps;
 
     private final boolean rollbackSupport;
-    private boolean candidateSupported;
-    private boolean runningWritable;
+    private final boolean candidateSupported;
+    private final boolean runningWritable;
 
     public NetconfDeviceDataBroker(final RemoteDeviceId id, final SchemaContext schemaContext, final DOMRpcService rpc, final NetconfSessionPreferences netconfSessionPreferences) {
         this.id = id;
@@ -83,7 +84,7 @@ public final class NetconfDeviceDataBroker implements DOMDataBroker {
 
     @Override
     public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
-        throw new UnsupportedOperationException(id + ": Transaction chains not supported for netconf mount point");
+        return new TxChain(this, listener);
     }
 
     @Override
index 702657dabaf244138074c2435fcd8fa9dc686636..d0f89bc9b4910520baf01725160fe1965f3b13ec 100644 (file)
@@ -16,14 +16,18 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import javax.annotation.Nullable;
 import org.opendaylight.controller.config.util.xml.DocumentedException;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.netconf.api.NetconfDocumentedException;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
@@ -42,6 +46,7 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
     protected final NetconfBaseOps netOps;
     protected final boolean rollbackSupport;
     protected final List<ListenableFuture<DOMRpcResult>> resultsFutures;
+    private final List<TxListener> listeners = new CopyOnWriteArrayList<>();
     // Allow commit to be called only once
     protected boolean finished = false;
 
@@ -70,7 +75,7 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
         if(isFinished()) {
             return false;
         }
-
+        listeners.forEach(listener -> listener.onTransactionCancelled(this));
         finished = true;
         cleanup();
         return true;
@@ -131,10 +136,27 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
 
     @Override
     public final ListenableFuture<RpcResult<TransactionStatus>> commit() {
+        listeners.forEach(listener -> listener.onTransactionSubmitted(this));
         checkNotFinished();
         finished = true;
+        final ListenableFuture<RpcResult<TransactionStatus>> result = performCommit();
+        Futures.addCallback(result, new FutureCallback<RpcResult<TransactionStatus>>() {
+            @Override
+            public void onSuccess(@Nullable final RpcResult<TransactionStatus> result) {
+                if (result != null && result.isSuccessful()) {
+                    listeners.forEach(txListener -> txListener.onTransactionSuccessful(AbstractWriteTx.this));
+                } else {
+                    final TransactionCommitFailedException cause = new TransactionCommitFailedException("Transaction failed", result.getErrors().toArray(new RpcError[result.getErrors().size()]));
+                    listeners.forEach(listener -> listener.onTransactionFailed(AbstractWriteTx.this, cause));
+                }
+            }
 
-        return performCommit();
+            @Override
+            public void onFailure(final Throwable t) {
+                listeners.forEach(listener -> listener.onTransactionFailed(AbstractWriteTx.this, t));
+            }
+        });
+        return result;
     }
 
     protected abstract ListenableFuture<RpcResult<TransactionStatus>> performCommit();
@@ -172,15 +194,20 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction {
             public void onFailure(final Throwable throwable) {
                 final NetconfDocumentedException exception =
                         new NetconfDocumentedException(
-                                new DocumentedException(id + ":RPC during tx returned an exception",
-                                        new Exception(throwable),
-                                        DocumentedException.ErrorType.APPLICATION,
-                                        DocumentedException.ErrorTag.OPERATION_FAILED,
-                                        DocumentedException.ErrorSeverity.ERROR));
+                                id + ":RPC during tx returned an exception",
+                                new Exception(throwable),
+                                DocumentedException.ErrorType.APPLICATION,
+                                DocumentedException.ErrorTag.OPERATION_FAILED,
+                                DocumentedException.ErrorSeverity.ERROR);
                 transformed.setException(exception);
             }
         });
 
         return transformed;
     }
+
+    AutoCloseable addListener(final TxListener listener) {
+        listeners.add(listener);
+        return () -> listeners.remove(listener);
+    }
 }
diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/TxChain.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/TxChain.java
new file mode 100644 (file)
index 0000000..7dbc835
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2016 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.netconf.sal.connect.netconf.sal.tx;
+
+import com.google.common.base.Preconditions;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainClosedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link DOMTransactionChain} implementation for Netconf connector.
+ */
+public class TxChain implements DOMTransactionChain, TxListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TxChain.class);
+
+    private final DOMDataBroker dataBroker;
+    private final TransactionChainListener listener;
+    /**
+     * Submitted transactions that haven't completed yet.
+     */
+    private final Map<DOMDataWriteTransaction, AutoCloseable> pendingTransactions = new HashMap<>();
+
+    /**
+     * Transaction created by this chain that hasn't been submitted or cancelled yet.
+     */
+    private AbstractWriteTx currentTransaction = null;
+    private boolean closed = false;
+    private boolean successful = true;
+
+    public TxChain(final DOMDataBroker dataBroker, final TransactionChainListener listener) {
+        this.dataBroker = dataBroker;
+        this.listener = listener;
+    }
+
+    @Override
+    public synchronized DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+        checkOperationPermitted();
+        return dataBroker.newReadOnlyTransaction();
+    }
+
+    @Override
+    public synchronized AbstractWriteTx newWriteOnlyTransaction() {
+        checkOperationPermitted();
+        final DOMDataWriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        Preconditions.checkState(writeTransaction instanceof AbstractWriteTx);
+        final AbstractWriteTx pendingWriteTx = (AbstractWriteTx) writeTransaction;
+        pendingTransactions.put(pendingWriteTx, pendingWriteTx.addListener(this));
+        currentTransaction = pendingWriteTx;
+        return pendingWriteTx;
+    }
+
+    @Override
+    public synchronized DOMDataReadWriteTransaction newReadWriteTransaction() {
+        return new ReadWriteTx(dataBroker.newReadOnlyTransaction(), newWriteOnlyTransaction());
+    }
+
+    @Override
+    public synchronized void close() {
+        if (!closed) {
+            closed = true;
+            notifyChainListenerSuccess();
+        }
+    }
+
+    @Override
+    public synchronized void onTransactionSuccessful(final AbstractWriteTx transaction) {
+        removePendingTx(transaction);
+        notifyChainListenerSuccess();
+    }
+
+    @Override
+    public synchronized void onTransactionFailed(final AbstractWriteTx transaction, final Throwable cause) {
+        removePendingTx(transaction);
+        successful = false;
+        if (currentTransaction != null) {
+            currentTransaction.cancel();
+        }
+        listener.onTransactionChainFailed(this, transaction, cause);
+    }
+
+    @Override
+    public synchronized void onTransactionSubmitted(final AbstractWriteTx transaction) {
+        currentTransaction = null;
+    }
+
+    @Override
+    public synchronized void onTransactionCancelled(final AbstractWriteTx transaction) {
+        removePendingTx(transaction);
+        currentTransaction = null;
+    }
+
+    private void removePendingTx(final AbstractWriteTx transaction) {
+        try {
+            pendingTransactions.remove(transaction).close();
+        } catch (final Exception e) {
+            LOG.error("Can't remove transaction listener registration", e);
+        }
+    }
+
+    /**
+     * Checks, if chain isn't closed and if there is no not submitted write transaction waiting.
+     */
+    private void checkOperationPermitted() {
+        if (closed) {
+            throw new TransactionChainClosedException("Transaction chain was closed");
+        }
+        Preconditions.checkState(currentTransaction == null, "Last write transaction has not finished yet");
+    }
+
+    private void notifyChainListenerSuccess() {
+        if (closed && pendingTransactions.isEmpty() && successful) {
+            listener.onTransactionChainSuccessful(this);
+        }
+    }
+
+}
diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/TxListener.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/TxListener.java
new file mode 100644 (file)
index 0000000..1c97cab
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016 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.netconf.sal.connect.netconf.sal.tx;
+
+interface TxListener {
+
+    /**
+     * Invoked, when transaction completes successfully.
+     * @param transaction transaction
+     */
+    void onTransactionSuccessful(AbstractWriteTx transaction);
+
+    /**
+     * Invoked, when transaction fails.
+     *
+     * @param transaction transaction
+     * @param cause cause
+     */
+    void onTransactionFailed(AbstractWriteTx transaction, Throwable cause);
+
+    /**
+     * Invoked, when transaction is cancelled.
+     * @param transaction transaction
+     */
+    void onTransactionCancelled(AbstractWriteTx transaction);
+
+    /**
+     * Invoked, when transaction is submitted.
+     * @param transaction transaction
+     */
+    void onTransactionSubmitted(AbstractWriteTx transaction);
+
+
+}
index 3efa83719f3caa3ec787c3ee76f1303669259e28..7d4dd0abaecdecf03333cf144653ea68b62599c2 100644 (file)
@@ -15,6 +15,7 @@ import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME;
 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_FILTER_QNAME;
@@ -24,8 +25,10 @@ import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTr
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import java.net.InetSocketAddress;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -140,4 +143,51 @@ public class NetconfDeviceWriteOnlyTxTest {
         inOrder.verify(rpc).invokeRpc(toPath(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME), NetconfBaseOps.getUnLockContent(NETCONF_RUNNING_QNAME));
     }
 
+    @Test
+    public void testListenerSuccess() throws Exception {
+        doReturn(Futures.immediateCheckedFuture(new DefaultDOMRpcResult((NormalizedNode<?, ?>) null)))
+                .when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
+        final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, BaseSchema.BASE_NETCONF_CTX.getSchemaContext()), false);
+        final TxListener listener = mock(TxListener.class);
+        tx.addListener(listener);
+        tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
+        tx.submit();
+        verify(listener).onTransactionSubmitted(tx);
+        verify(listener).onTransactionSuccessful(tx);
+        verify(listener, never()).onTransactionFailed(eq(tx), any());
+        verify(listener, never()).onTransactionCancelled(tx);
+    }
+
+    @Test
+    public void testListenerCancellation() throws Exception {
+        doReturn(Futures.immediateCheckedFuture(new DefaultDOMRpcResult((NormalizedNode<?, ?>) null)))
+                .when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
+        final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, BaseSchema.BASE_NETCONF_CTX.getSchemaContext()), false);
+        final TxListener listener = mock(TxListener.class);
+        tx.addListener(listener);
+        tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
+        tx.cancel();
+        verify(listener).onTransactionCancelled(tx);
+        verify(listener, never()).onTransactionSubmitted(tx);
+        verify(listener, never()).onTransactionSuccessful(tx);
+        verify(listener, never()).onTransactionFailed(eq(tx), any());
+    }
+
+    @Test
+    public void testListenerFailure() throws Exception {
+        final IllegalStateException cause = new IllegalStateException("Failed tx");
+        doReturn(Futures.immediateFailedCheckedFuture(cause))
+                .when(rpc).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
+        final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, BaseSchema.BASE_NETCONF_CTX.getSchemaContext()), false);
+        final TxListener listener = mock(TxListener.class);
+        tx.addListener(listener);
+        tx.delete(LogicalDatastoreType.CONFIGURATION, yangIId);
+        tx.submit();
+        final ArgumentCaptor<Exception> excCaptor = ArgumentCaptor.forClass(Exception.class);
+        verify(listener).onTransactionSubmitted(tx);
+        verify(listener).onTransactionFailed(eq(tx), excCaptor.capture());
+        Assert.assertEquals(cause, excCaptor.getValue().getCause().getCause());
+        verify(listener, never()).onTransactionSuccessful(tx);
+        verify(listener, never()).onTransactionCancelled(tx);
+    }
 }
diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/TxChainTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/TxChainTest.java
new file mode 100644 (file)
index 0000000..e56b02d
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2016 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.netconf.sal.connect.netconf.sal.tx;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainClosedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+
+public class TxChainTest {
+
+    @Mock
+    private DOMDataBroker broker;
+    @Mock
+    private TransactionChainListener listener;
+    @Mock
+    private DOMDataReadOnlyTransaction readOnlyTx;
+    @Mock
+    private AbstractWriteTx writeOnlyTx1;
+    @Mock
+    private AbstractWriteTx writeOnlyTx2;
+    @Mock
+    private AbstractWriteTx writeOnlyTx3;
+    @Mock
+    private AutoCloseable registration1;
+    @Mock
+    private AutoCloseable registration2;
+    @Mock
+    private AutoCloseable registration3;
+    private final ArgumentCaptor<TxListener> captor = ArgumentCaptor.forClass(TxListener.class);
+    private TxChain chain;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(broker.newReadOnlyTransaction()).thenReturn(readOnlyTx);
+        when(broker.newWriteOnlyTransaction()).thenReturn(writeOnlyTx1).thenReturn(writeOnlyTx2).thenReturn(writeOnlyTx3);
+        when(writeOnlyTx1.addListener(any())).thenReturn(registration1);
+        when(writeOnlyTx2.addListener(any())).thenReturn(registration2);
+        when(writeOnlyTx3.addListener(any())).thenReturn(registration3);
+        chain = new TxChain(broker, listener);
+    }
+
+    @Test()
+    public void testNewReadOnlyTransactionPrevSubmitted() throws Exception {
+        chain.newWriteOnlyTransaction();
+        verify(writeOnlyTx1).addListener(captor.capture());
+        captor.getValue().onTransactionSubmitted(writeOnlyTx1);
+        chain.newReadOnlyTransaction();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testNewReadOnlyTransactionPrevNotSubmitted() throws Exception {
+        chain.newWriteOnlyTransaction();
+        chain.newReadOnlyTransaction();
+    }
+
+    @Test
+    public void testNewReadWriteTransactionPrevSubmitted() throws Exception {
+        chain.newReadWriteTransaction();
+        verify(writeOnlyTx1).addListener(captor.capture());
+        captor.getValue().onTransactionSubmitted(writeOnlyTx1);
+        chain.newReadWriteTransaction();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testNewReadWriteTransactionPrevNotSubmitted() throws Exception {
+        chain.newReadWriteTransaction();
+        chain.newReadWriteTransaction();
+    }
+
+    @Test
+    public void testNewWriteOnlyTransactionPrevSubmitted() throws Exception {
+        chain.newWriteOnlyTransaction();
+        verify(writeOnlyTx1).addListener(captor.capture());
+        captor.getValue().onTransactionSubmitted(writeOnlyTx1);
+        chain.newWriteOnlyTransaction();
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testNewWriteOnlyTransactionPrevNotSubmitted() throws Exception {
+        chain.newWriteOnlyTransaction();
+        chain.newWriteOnlyTransaction();
+    }
+
+    @Test(expected = TransactionChainClosedException.class)
+    public void testCloseAfterFinished() throws Exception {
+        chain.close();
+        verify(listener).onTransactionChainSuccessful(chain);
+        chain.newReadOnlyTransaction();
+    }
+
+    @Test
+    public void testChainFail() throws Exception {
+        final AbstractWriteTx writeTx = chain.newWriteOnlyTransaction();
+        final ArgumentCaptor<TxListener> captor = ArgumentCaptor.forClass(TxListener.class);
+        verify(writeOnlyTx1).addListener(captor.capture());
+        writeTx.submit();
+        final TransactionCommitFailedException cause = new TransactionCommitFailedException("fail");
+        captor.getValue().onTransactionFailed(writeOnlyTx1, cause);
+        verify(registration1).close();
+        verify(listener).onTransactionChainFailed(chain, writeOnlyTx1, cause);
+    }
+
+    @Test
+    public void testChainSuccess() throws Exception {
+        final AbstractWriteTx writeTx = chain.newWriteOnlyTransaction();
+        chain.close();
+        verify(writeOnlyTx1).addListener(captor.capture());
+        writeTx.submit();
+        captor.getValue().onTransactionSuccessful(writeOnlyTx1);
+        verify(registration1).close();
+        verify(listener).onTransactionChainSuccessful(chain);
+    }
+
+    @Test
+    public void testCancel() throws Exception {
+        final AbstractWriteTx writeTx = chain.newWriteOnlyTransaction();
+        verify(writeOnlyTx1).addListener(captor.capture());
+        writeTx.cancel();
+        captor.getValue().onTransactionCancelled(writeOnlyTx1);
+        chain.newWriteOnlyTransaction();
+    }
+
+    @Test
+    public void testMultiplePendingTransactions() throws Exception {
+        //create 1st tx
+        final AbstractWriteTx writeTx1 = chain.newWriteOnlyTransaction();
+        final ArgumentCaptor<TxListener> captor1 = ArgumentCaptor.forClass(TxListener.class);
+        verify(writeOnlyTx1).addListener(captor1.capture());
+        //submit 1st tx
+        writeTx1.submit();
+        captor1.getValue().onTransactionSubmitted(writeOnlyTx1);
+
+        //create 2nd tx
+        final AbstractWriteTx writeTx2 = chain.newWriteOnlyTransaction();
+        final ArgumentCaptor<TxListener> captor2 = ArgumentCaptor.forClass(TxListener.class);
+        verify(writeTx2).addListener(captor2.capture());
+        //submit 2nd tx
+        writeTx2.submit();
+        captor2.getValue().onTransactionSubmitted(writeOnlyTx2);
+
+        //create 3rd tx
+        final AbstractWriteTx writeTx3 = chain.newWriteOnlyTransaction();
+        final ArgumentCaptor<TxListener> captor3 = ArgumentCaptor.forClass(TxListener.class);
+        verify(writeTx3).addListener(captor3.capture());
+        //cancel 3rd tx
+        writeTx3.cancel();
+        captor3.getValue().onTransactionCancelled(writeOnlyTx3);
+
+        //close chain
+        chain.close();
+
+        //complete first two transactions successfully
+        captor1.getValue().onTransactionSuccessful(writeOnlyTx1);
+        captor2.getValue().onTransactionSuccessful(writeOnlyTx2);
+
+        verify(registration1).close();
+        verify(registration2).close();
+        verify(registration3).close();
+        verify(listener).onTransactionChainSuccessful(chain);
+    }
+
+    @Test
+    public void testMultiplePendingTransactionsFail() throws Exception {
+        //create 1st tx
+        final AbstractWriteTx writeTx1 = chain.newWriteOnlyTransaction();
+        final ArgumentCaptor<TxListener> captor1 = ArgumentCaptor.forClass(TxListener.class);
+        verify(writeOnlyTx1).addListener(captor1.capture());
+        //submit 1st tx
+        writeTx1.submit();
+        captor1.getValue().onTransactionSubmitted(writeOnlyTx1);
+
+        //create 2nd tx
+        final AbstractWriteTx writeTx2 = chain.newWriteOnlyTransaction();
+        final ArgumentCaptor<TxListener> captor2 = ArgumentCaptor.forClass(TxListener.class);
+        verify(writeTx2).addListener(captor2.capture());
+        //submit 2nd tx
+        writeTx2.submit();
+        captor2.getValue().onTransactionSubmitted(writeOnlyTx2);
+
+        //create 3rd tx
+        final AbstractWriteTx writeTx3 = chain.newWriteOnlyTransaction();
+        final ArgumentCaptor<TxListener> captor3 = ArgumentCaptor.forClass(TxListener.class);
+        verify(writeTx3).addListener(captor3.capture());
+
+        chain.close();
+
+        //fail 1st transaction
+        final Exception cause1 = new Exception("fail");
+        captor1.getValue().onTransactionFailed(writeOnlyTx1, cause1);
+        //current unsubmitted transaction should be cancelled
+        verify(writeTx3).cancel();
+        captor3.getValue().onTransactionCancelled(writeTx3);
+        //2nd transaction success
+        captor2.getValue().onTransactionSuccessful(writeOnlyTx2);
+
+        verify(registration1).close();
+        verify(registration2).close();
+        verify(registration3).close();
+        verify(listener).onTransactionChainFailed(chain, writeOnlyTx1, cause1);
+        // 1 transaction failed, onTransactionChainSuccessful must not be called
+        verify(listener, never()).onTransactionChainSuccessful(chain);
+    }
+}
\ No newline at end of file
index 165decc6d4d6a8a41a05722026753ee375a696ea..3ade59f8a5822b4aa209ec2cc8c0ea8d1f5192c3 100644 (file)
@@ -23,7 +23,6 @@ import io.netty.channel.local.LocalAddress;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.util.HashedWheelTimer;
 import java.io.Closeable;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.BindException;
@@ -56,6 +55,7 @@ import org.opendaylight.netconf.monitoring.osgi.NetconfMonitoringOperationServic
 import org.opendaylight.netconf.ssh.SshProxyServer;
 import org.opendaylight.netconf.ssh.SshProxyServerConfiguration;
 import org.opendaylight.netconf.ssh.SshProxyServerConfigurationBuilder;
+import org.opendaylight.netconf.test.tool.customrpc.SettableOperationProvider;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -102,9 +102,9 @@ public class NetconfDeviceSimulator implements Closeable {
         this.nioExecutor = nioExecutor;
     }
 
-    private NetconfServerDispatcherImpl createDispatcher(final Set<Capability> capabilities, final boolean exi, final int generateConfigsTimeout,
-                                                         final Optional<File> notificationsFile, final boolean mdSal, final Optional<File> initialConfigXMLFile,
-                                                         final SchemaSourceProvider<YangTextSchemaSource> sourceProvider) {
+    private NetconfServerDispatcherImpl createDispatcher(final Set<Capability> capabilities,
+                                                         final SchemaSourceProvider<YangTextSchemaSource> sourceProvider,
+                                                         final TesttoolParameters params) {
 
         final Set<Capability> transformedCapabilities = Sets.newHashSet(Collections2.transform(capabilities, new Function<Capability, Capability>() {
             @Override
@@ -117,49 +117,66 @@ public class NetconfDeviceSimulator implements Closeable {
                 }
             }
         }));
-
-        final SessionIdProvider idProvider = new SessionIdProvider();
-
-        final AggregatedNetconfOperationServiceFactory aggregatedNetconfOperationServiceFactory = new AggregatedNetconfOperationServiceFactory();
-        final NetconfOperationServiceFactory operationProvider = mdSal ? new MdsalOperationProvider(idProvider, transformedCapabilities, schemaContext, sourceProvider) :
-                new SimulatedOperationProvider(idProvider, transformedCapabilities, notificationsFile, initialConfigXMLFile);
-
         transformedCapabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0"));
-
         final NetconfMonitoringService monitoringService1 = new DummyMonitoringService(transformedCapabilities);
+        final SessionIdProvider idProvider = new SessionIdProvider();
 
-        final NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory monitoringService =
-                new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
-                        new NetconfMonitoringOperationService(monitoringService1));
-        aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(operationProvider);
-        aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(monitoringService);
+        final NetconfOperationServiceFactory aggregatedNetconfOperationServiceFactory = createOperationServiceFactory(sourceProvider, params, transformedCapabilities, monitoringService1, idProvider);
 
-        final Set<String> serverCapabilities = exi
+        final Set<String> serverCapabilities = params.exi
                 ? NetconfServerSessionNegotiatorFactory.DEFAULT_BASE_CAPABILITIES
                 : Sets.newHashSet(XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0, XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1);
 
         final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new TesttoolNegotiationFactory(
-                hashedWheelTimer, aggregatedNetconfOperationServiceFactory, idProvider, generateConfigsTimeout, monitoringService1, serverCapabilities);
+                hashedWheelTimer, aggregatedNetconfOperationServiceFactory, idProvider, params.generateConfigsTimeout, monitoringService1, serverCapabilities);
 
         final NetconfServerDispatcherImpl.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcherImpl.ServerChannelInitializer(
                 serverNegotiatorFactory);
         return new NetconfServerDispatcherImpl(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
     }
 
+    private NetconfOperationServiceFactory createOperationServiceFactory(final SchemaSourceProvider<YangTextSchemaSource> sourceProvider,
+                                                                         final TesttoolParameters params,
+                                                                         final Set<Capability> transformedCapabilities,
+                                                                         final NetconfMonitoringService monitoringService1,
+                                                                         final SessionIdProvider idProvider) {
+        final AggregatedNetconfOperationServiceFactory aggregatedNetconfOperationServiceFactory = new AggregatedNetconfOperationServiceFactory();
+
+        final NetconfOperationServiceFactory operationProvider;
+        if (params.mdSal) {
+            operationProvider = new MdsalOperationProvider(idProvider, transformedCapabilities, schemaContext, sourceProvider);
+        } else {
+            operationProvider = new SimulatedOperationProvider(idProvider, transformedCapabilities,
+                    Optional.fromNullable(params.notificationFile),
+                    Optional.fromNullable(params.initialConfigXMLFile));
+        }
+
+
+        final NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory monitoringService =
+                new NetconfMonitoringActivator.NetconfMonitoringOperationServiceFactory(
+                        new NetconfMonitoringOperationService(monitoringService1));
+        aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(operationProvider);
+        aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(monitoringService);
+        if (params.rpcConfig != null) {
+            final SettableOperationProvider settableService = new SettableOperationProvider(params.rpcConfig);
+            aggregatedNetconfOperationServiceFactory.onAddNetconfOperationServiceFactory(settableService);
+        }
+        return aggregatedNetconfOperationServiceFactory;
+    }
+
     public List<Integer> start(final TesttoolParameters params) {
         LOG.info("Starting {}, {} simulated devices starting on port {}", params.deviceCount, params.ssh ? "SSH" : "TCP", params.startingPort);
 
         final SharedSchemaRepository schemaRepo = new SharedSchemaRepository("netconf-simulator");
         final Set<Capability> capabilities = parseSchemasToModuleCapabilities(params, schemaRepo);
 
-        final NetconfServerDispatcherImpl dispatcher = createDispatcher(capabilities, params.exi, params.generateConfigsTimeout,
-                Optional.fromNullable(params.notificationFile), params.mdSal, Optional.fromNullable(params.initialConfigXMLFile),
+        final NetconfServerDispatcherImpl dispatcher = createDispatcher(capabilities,
                 new SchemaSourceProvider<YangTextSchemaSource>() {
                     @Override
                     public CheckedFuture<? extends YangTextSchemaSource, SchemaSourceException> getSource(final SourceIdentifier sourceIdentifier) {
                         return schemaRepo.getSchemaSource(sourceIdentifier, YangTextSchemaSource.class);
                     }
-                });
+                }, params);
 
         int currentPort = params.startingPort;
 
index 8218d73a15acc669599b0d03790b978ee9060076..f08daee52008fcd3e8ba6e0d8113cbffd8f4fef1 100644 (file)
@@ -94,6 +94,8 @@ public class TesttoolParameters {
 
     @Arg(dest = "thread-pool-size")
     public int threadPoolSize;
+    @Arg(dest = "rpc-config")
+    public File rpcConfig;
 
     static ArgumentParser getParser() {
         final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf testtool");
@@ -234,6 +236,11 @@ public class TesttoolParameters {
                 .setDefault(8)
                 .help("The number of threads to keep in the pool, when creating a device simulator. Even if they are idle.")
                 .dest("thread-pool-size");
+        parser.addArgument("--rpc-config")
+                .type(File.class)
+                .help("Rpc config file. It can be used to define custom rpc behavior, or override the default one." +
+                        "Usable for testing buggy device behavior.")
+                .dest("rpc-config");
 
         return parser;
     }
@@ -323,6 +330,11 @@ public class TesttoolParameters {
                 }
             }
         }
+        if (rpcConfig != null) {
+            checkArgument(rpcConfig.exists(), "Rpc config file has to exist");
+            checkArgument(!rpcConfig.isDirectory(), "Rpc config file can't be a directory");
+            checkArgument(rpcConfig.canRead(), "Rpc config file to be readable");
+        }
     }
 
     public ArrayList<ArrayList<Execution.DestToPayload>> getThreadsPayloads(final List<Integer> openDevices) {
diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/Rpc.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/Rpc.java
new file mode 100644 (file)
index 0000000..c232aa9
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2016 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.netconf.test.tool.customrpc;
+
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+
+class Rpc {
+
+    @XmlElement(name = "input")
+    private XmlData input;
+
+    @XmlElement(name = "output")
+    private List<XmlData> output;
+
+    XmlData getInput() {
+        return input;
+    }
+
+    List<XmlData> getOutput() {
+        return output;
+    }
+}
diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/RpcMapping.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/RpcMapping.java
new file mode 100644 (file)
index 0000000..5d3f7eb
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2016 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.netconf.test.tool.customrpc;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.xml.bind.JAXB;
+import org.opendaylight.controller.config.util.xml.DocumentedException;
+import org.opendaylight.controller.config.util.xml.XmlElement;
+import org.opendaylight.controller.config.util.xml.XmlUtil;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+
+/**
+ * Mapping between RPCs and responses.
+ */
+class RpcMapping {
+
+    private final Multimap<Request, Document> requestToResponseMap = ArrayListMultimap.create();
+
+    /**
+     * Creates new mapping from file.
+     *
+     * @param config config file
+     */
+    RpcMapping(final File config) {
+        final Rpcs rpcs = JAXB.unmarshal(config, Rpcs.class);
+        for (final Rpc rpc : rpcs.getRpcs()) {
+            final Stream<Document> stream = rpc.getOutput().stream()
+                    .map(XmlData::getData);
+            final XmlElement element = XmlElement.fromDomDocument(rpc.getInput().getData());
+            requestToResponseMap.putAll(new Request(element), stream::iterator);
+        }
+    }
+
+    /**
+     * Returns response matching given input. If multiple responses are configured for the same
+     * request, every invocation of this method returns next response as they are defined in the config file.
+     * Last configured response is used, when number of invocations is higher than number of configured responses
+     * for rpc.
+     *
+     * @param input request
+     * @return response document, or absent if mapping is not defined
+     */
+    Optional<Document> getResponse(final XmlElement input) {
+        final Collection<Document> responses = requestToResponseMap.get(new Request(input));
+        final Iterator<Document> iterator = responses.iterator();
+        if (iterator.hasNext()) {
+            final Document response = iterator.next();
+            if (iterator.hasNext()) {
+                iterator.remove();
+            }
+            return Optional.of(response);
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * Rpc input wrapper. Needed because of custom {@link Request#equals(Object)}
+     * and {@link Request#hashCode()} implementations.
+     */
+    private static class Request {
+        private final XmlElement xmlElement;
+        private final int hashCode;
+
+        private Request(final XmlElement element) {
+            this.xmlElement = element;
+            hashCode = XmlUtil.toString(element)
+                    .replaceAll("message-id=.*(>| )", "") //message id is variable, remove it
+                    .replaceAll("\\s+", "") //remove whitespaces
+                    .hashCode();
+        }
+
+        @Override
+        public boolean equals(final Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            final Request request = (Request) o;
+            return documentEquals(this.xmlElement, request.xmlElement);
+        }
+
+        @Override
+        public int hashCode() {
+            return hashCode;
+        }
+
+        private static boolean documentEquals(final XmlElement e1, final XmlElement e2) {
+            if (!e1.getNamespaceOptionally().equals(e2.getNamespaceOptionally())) {
+                return false;
+            }
+            if (!e1.getName().equals(e2.getName())) {
+                return false;
+            }
+            if (attributesNotEquals(e1, e2)) {
+                return false;
+            }
+
+            final List<XmlElement> e1Children = e1.getChildElements();
+            final List<XmlElement> e2Children = e2.getChildElements();
+            if (e1Children.size() != e2Children.size()) {
+                return false;
+            }
+            final Iterator<XmlElement> e1Iterator = e1Children.iterator();
+            final Iterator<XmlElement> e2Iterator = e2Children.iterator();
+            while (e1Iterator.hasNext() && e2Iterator.hasNext()) {
+                if (!documentEquals(e1Iterator.next(), e2Iterator.next())) {
+                    return false;
+                }
+            }
+
+            if (e1Children.isEmpty() && e1Children.isEmpty()) {
+                try {
+                    return e1.getTextContent().equals(e2.getTextContent());
+                } catch (final DocumentedException e) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private static boolean attributesNotEquals(final XmlElement e1, final XmlElement e2) {
+            final Map<String, Attr> e1Attrs = e1.getAttributes();
+            final Map<String, Attr> e2Attrs = e2.getAttributes();
+            final Iterator<Map.Entry<String, Attr>> e1AttrIt = e1Attrs.entrySet().iterator();
+            final Iterator<Map.Entry<String, Attr>> e2AttrIt = e2Attrs.entrySet().iterator();
+            while (e1AttrIt.hasNext() && e2AttrIt.hasNext()) {
+                final Map.Entry<String, Attr> e1Next = e1AttrIt.next();
+                final Map.Entry<String, Attr> e2Next = e2AttrIt.next();
+                if (e1Next.getKey().equals("message-id") && e2Next.getKey().equals("message-id")) {
+                    continue;
+                }
+                if (e1Next.getKey().equals("xmlns") && e2Next.getKey().equals("xmlns")) {
+                    continue;
+                }
+                if (!e1Next.getKey().equals(e2Next.getKey())) {
+                    return true;
+                }
+                if (!e1Next.getValue().getValue().equals(e2Next.getValue().getValue())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+    }
+}
diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/Rpcs.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/Rpcs.java
new file mode 100644 (file)
index 0000000..9a2ad36
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2016 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.netconf.test.tool.customrpc;
+
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "rpcs")
+@XmlAccessorType(XmlAccessType.FIELD)
+class Rpcs {
+
+    @XmlElement(name = "rpc")
+    private List<Rpc> rpcs;
+
+    List<Rpc> getRpcs() {
+        return rpcs;
+    }
+
+}
diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/SettableOperationProvider.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/SettableOperationProvider.java
new file mode 100644 (file)
index 0000000..d811fe7
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 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.netconf.test.tool.customrpc;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Set;
+import org.opendaylight.controller.config.util.capability.Capability;
+import org.opendaylight.netconf.api.monitoring.CapabilityListener;
+import org.opendaylight.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
+
+public class SettableOperationProvider implements NetconfOperationServiceFactory {
+
+    private final File rpcConfig;
+
+    public SettableOperationProvider(final File rpcConfig) {
+        this.rpcConfig = rpcConfig;
+    }
+
+    @Override
+    public Set<Capability> getCapabilities() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public AutoCloseable registerCapabilityListener(final CapabilityListener listener) {
+        return () -> {
+            //no op
+        };
+    }
+
+    @Override
+    public NetconfOperationService createService(final String netconfSessionIdForReporting) {
+        return new SettableOperationService(rpcConfig);
+    }
+
+    private static class SettableOperationService implements NetconfOperationService {
+
+        private final SettableRpc rpc;
+
+        private SettableOperationService(final File rpcConfig) {
+            this.rpc = new SettableRpc(rpcConfig);
+        }
+
+        @Override
+        public Set<NetconfOperation> getNetconfOperations() {
+            return Collections.singleton(rpc);
+        }
+
+        @Override
+        public void close() {
+            // no op
+        }
+    }
+}
diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/SettableRpc.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/SettableRpc.java
new file mode 100644 (file)
index 0000000..fddc0be
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016 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.netconf.test.tool.customrpc;
+
+import java.io.File;
+import java.util.Optional;
+import org.opendaylight.controller.config.util.xml.DocumentedException;
+import org.opendaylight.controller.config.util.xml.XmlElement;
+import org.opendaylight.controller.config.util.xml.XmlUtil;
+import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.netconf.mapping.api.HandlingPriority;
+import org.opendaylight.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.netconf.mapping.api.NetconfOperationChainedExecution;
+import org.w3c.dom.Document;
+
+/**
+ * {@link NetconfOperation} implementation. It can be configured to intercept rpcs with defined input
+ * and reply with defined output. If input isn't defined, rpc handling is delegated to the subsequent
+ * {@link NetconfOperation} which is able to handle it.
+ */
+class SettableRpc implements NetconfOperation {
+
+    private final RpcMapping mapping;
+
+    SettableRpc(final File rpcConfig) {
+        mapping = new RpcMapping(rpcConfig);
+    }
+
+    @Override
+    public HandlingPriority canHandle(final Document message) throws DocumentedException {
+        return HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY.increasePriority(1000);
+    }
+
+    @Override
+    public Document handle(final Document requestMessage, final NetconfOperationChainedExecution subsequentOperation)
+            throws DocumentedException {
+        final XmlElement requestElement = XmlElement.fromDomDocument(requestMessage);
+        final XmlElement rpcElement = requestElement.getOnlyChildElement();
+        final String msgId = requestElement.getAttribute(XmlNetconfConstants.MESSAGE_ID);
+        final Optional<Document> response = mapping.getResponse(rpcElement);
+        if (response.isPresent()) {
+            final Document document = response.get();
+            checkForError(document);
+            document.getDocumentElement().setAttribute(XmlNetconfConstants.MESSAGE_ID, msgId);
+            return document;
+        } else if (subsequentOperation.isExecutionTermination()) {
+            throw new DocumentedException("Mapping not found " + XmlUtil.toString(requestMessage),
+                    DocumentedException.ErrorType.APPLICATION, DocumentedException.ErrorTag.OPERATION_NOT_SUPPORTED,
+                    DocumentedException.ErrorSeverity.ERROR);
+        } else {
+            return subsequentOperation.execute(requestMessage);
+        }
+    }
+
+    private void checkForError(final Document document) throws DocumentedException {
+        final XmlElement rpcReply = XmlElement.fromDomDocument(document);
+        if (rpcReply.getOnlyChildElementOptionally("rpc-error").isPresent()) {
+            throw DocumentedException.fromXMLDocument(document);
+        }
+    }
+
+}
diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/XmlData.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/customrpc/XmlData.java
new file mode 100644 (file)
index 0000000..69fb660
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2016 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.netconf.test.tool.customrpc;
+
+import javax.xml.bind.annotation.XmlAnyElement;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+class XmlData {
+    @XmlAnyElement
+    private Element data;
+
+    Document getData() {
+        return data.getOwnerDocument();
+    }
+
+}
index 48b8670c8f39eeb89e6abfaff8ce39cbb337723a..ec634bcd9b44b3e765dfe9cb329b85c358b03081 100644 (file)
@@ -16,21 +16,21 @@ import org.opendaylight.restconf.RestConnectorProvider;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
 
 /**
- * Wrapping providers from restconf draft02 and draft17.
+ * Wrapping providers from restconf draft02 and draft18.
  *
  */
 public class RestconfWrapperProviders implements AutoCloseable, RestConnector {
 
     // DRAFT02
     private final RestconfProviderImpl providerDraft02;
-    // DRAFT17
-    private final RestConnectorProvider providerDraft17;
+    // DRAFT18
+    private final RestConnectorProvider providerDraft18;
 
     /**
      * Init both providers:
      * <ul>
      * <li>draft02 - {@link RestconfProviderImpl}
-     * <li>draft17 - {@link RestConnectorProvider}
+     * <li>draft18 - {@link RestConnectorProvider}
      * </ul>
      *
      * @param port
@@ -41,14 +41,14 @@ public class RestconfWrapperProviders implements AutoCloseable, RestConnector {
         this.providerDraft02 = new RestconfProviderImpl();
         this.providerDraft02.setWebsocketPort(port);
 
-        this.providerDraft17 = new RestConnectorProvider();
+        this.providerDraft18 = new RestConnectorProvider();
     }
 
     /**
      * Register both providers, which will use the SAL layer:
      * <ul>
      * <li>draft02 - {@link RestconfProviderImpl}
-     * <li>draft17 - {@link RestConnectorProvider}
+     * <li>draft18 - {@link RestConnectorProvider}
      * </ul>
      *
      * @param broker
@@ -58,8 +58,8 @@ public class RestconfWrapperProviders implements AutoCloseable, RestConnector {
         // Register draft02 provider
         broker.registerProvider(this.providerDraft02);
 
-        // Register draft17 provider
-        broker.registerProvider(this.providerDraft17);
+        // Register draft18 provider
+        broker.registerProvider(this.providerDraft18);
     }
 
     /**
@@ -77,7 +77,7 @@ public class RestconfWrapperProviders implements AutoCloseable, RestConnector {
     @Override
     public void close() throws Exception {
         this.providerDraft02.close();
-        this.providerDraft17.close();
+        this.providerDraft18.close();
     }
 
 }
index 9516a3954e2759a400fb2e8b7e10e99ea45d2a16..8d7632733bb2f2674b72a31e1b1ca6aa02306803 100644 (file)
@@ -18,10 +18,10 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyWriter;
 import javax.ws.rs.ext.Provider;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 
 @Provider
-@Produces({ SchemaRetrievalService.YANG_MEDIA_TYPE, Draft17.MediaTypes.YANG })
+@Produces({ SchemaRetrievalService.YANG_MEDIA_TYPE, Draft18.MediaTypes.YANG })
 public class SchemaExportContentYangBodyWriter implements MessageBodyWriter<SchemaExportContext> {
 
     @Override
index 724e593604521085e6d55da36c2d0d825b6b6b95..76a9c4a0ddc7a89cdc46bd5fbf2e99e34b771b48 100644 (file)
@@ -18,12 +18,12 @@ import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyWriter;
 import javax.ws.rs.ext.Provider;
 import javax.xml.stream.XMLStreamException;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.model.export.YinExportUtils;
 
 @Provider
-@Produces({ SchemaRetrievalService.YIN_MEDIA_TYPE, Draft17.MediaTypes.YIN + RestconfConstants.XML })
+@Produces({ SchemaRetrievalService.YIN_MEDIA_TYPE, Draft18.MediaTypes.YIN + RestconfConstants.XML })
 public class SchemaExportContentYinBodyWriter implements MessageBodyWriter<SchemaExportContext> {
 
     @Override
index 0675f89ac6043937432cb69f315724abc013ca86..0b1d28772727ac5408916dff0498158cbbed2c8b 100644 (file)
@@ -7,12 +7,12 @@
  */
 package org.opendaylight.netconf.sal.rest.api;
 
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.yangtools.yang.common.QName;
 
 /**
  * @deprecated Do not use old implementation of restconf draft. It will be
- *             replaced by {@link Draft17}.
+ *             replaced by {@link Draft18}.
  *
  */
 @Deprecated
index ade5a41d5878729fa9a238ec1989aeb0fed81de7..4419105d1de537f7b1ae06f395c26bb69bcb9d13 100644 (file)
@@ -33,23 +33,24 @@ import org.opendaylight.restconf.restful.services.api.RestconfDataService;
 import org.opendaylight.restconf.restful.services.api.RestconfInvokeOperationsService;
 
 /**
- * The URI hierarchy for the RESTCONF resources consists of an entry point container, 4 top-level resources, and 1
- * field.
+ * The URI hierarchy for the RESTCONF resources consists of an entry point
+ * container, 4 top-level resources, and 1 field.
  * <ul>
  * <li><b>/restconf</b> - {@link #getRoot()}
  * <ul>
- *      <li><b>/config</b> - {@link #readConfigurationData(String, UriInfo)}
- *                              {@link #updateConfigurationData(String, NormalizedNodeContext)}
- *                              {@link #createConfigurationData(NormalizedNodeContext, UriInfo)}
- *                              {@link #createConfigurationData(String, NormalizedNodeContext, UriInfo)}
+ * <li><b>/config</b> - {@link #readConfigurationData(String, UriInfo)}
+ * {@link #updateConfigurationData(String, NormalizedNodeContext, UriInfo)}
+ * {@link #createConfigurationData(NormalizedNodeContext, UriInfo)}
+ * {@link #createConfigurationData(String, NormalizedNodeContext, UriInfo)}
  * {@link #deleteConfigurationData(String)}
  * <li><b>/operational</b> - {@link #readOperationalData(String, UriInfo)}
  * <li>/modules - {@link #getModules(UriInfo)}
  * <ul>
  * <li>/module
  * </ul>
- *      <li><b>/operations</b> - {@link #invokeRpc(String, NormalizedNodeContext, UriInfo)}
- *                               {@link #invokeRpc(String, NormalizedNodeContext, UriInfo)}
+ * <li><b>/operations</b> -
+ * {@link #invokeRpc(String, NormalizedNodeContext, UriInfo)}
+ * {@link #invokeRpc(String, NormalizedNodeContext, UriInfo)}
  * <li>/version (field)
  * </ul>
  * </ul>
@@ -169,14 +170,15 @@ public interface RestconfService {
 
     /**
      * @deprecated do not use this method. It will be replaced by
-     *             {@link RestconfDataService#putData(String, NormalizedNodeContext)}
+     *             {@link RestconfDataService#putData(String, NormalizedNodeContext, UriInfo)}
      */
     @Deprecated
     @PUT
     @Path("/config/{identifier:.+}")
     @Consumes({ Draft02.MediaTypes.DATA + JSON, Draft02.MediaTypes.DATA + XML, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
-    public Response updateConfigurationData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload);
+    public Response updateConfigurationData(@Encoded @PathParam("identifier") String identifier,
+            NormalizedNodeContext payload, @Context UriInfo uriInfo);
 
     /**
      * @deprecated do not use this method. It will be replaced by
@@ -212,7 +214,8 @@ public interface RestconfService {
 
     @GET
     @Path("/streams/stream/{identifier:.+}")
-    public Response subscribeToStream(@Encoded @PathParam("identifier") String identifier, @Context UriInfo uriInfo);
+    public NormalizedNodeContext subscribeToStream(@Encoded @PathParam("identifier") String identifier,
+            @Context UriInfo uriInfo);
 
     /**
      * @deprecated do not use this method. It will be replaced by
index 23872ce0c737c1ec0e6440d5e6575d1a2de8a206..240839b61022c8204633a585863a7e74ef988e38 100644 (file)
@@ -8,7 +8,7 @@
 package org.opendaylight.netconf.sal.rest.impl;
 
 import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
-import com.google.common.annotations.Beta;
+
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
@@ -44,8 +44,11 @@ import org.slf4j.LoggerFactory;
  * the opposite of a {@link javax.xml.stream.XMLStreamReader} -- unlike instantiating an iterator over
  * the backing data, this encapsulates a {@link NormalizedNodeStreamWriter} and allows
  * us to write multiple nodes.
+ *
+ * @deprecated This class will be replaced by
+ * {@link org.opendaylight.restconf.jersey.providers.ParameterAwareNormalizedNodeWriter}
  */
-@Beta
+@Deprecated
 public class DepthAwareNormalizedNodeWriter implements RestconfNormalizedNodeWriter {
     private final NormalizedNodeStreamWriter writer;
     protected int currentDepth = 0;
index 179d2ad59c5ed20a6a6821efb83949d77dbd6729..305e0067408dc208dc0f0b25d13f2db8ec8f3c80 100644 (file)
@@ -30,7 +30,7 @@ import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
@@ -53,7 +53,7 @@ import org.slf4j.LoggerFactory;
 
 @Provider
 @Consumes({ Draft02.MediaTypes.DATA + RestconfService.JSON, Draft02.MediaTypes.OPERATION + RestconfService.JSON,
-        Draft17.MediaTypes.DATA + RestconfConstants.JSON, MediaType.APPLICATION_JSON })
+        Draft18.MediaTypes.DATA + RestconfConstants.JSON, MediaType.APPLICATION_JSON })
 public class JsonNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader<NormalizedNodeContext> {
 
     private final static Logger LOG = LoggerFactory.getLogger(JsonNormalizedNodeBodyReader.class);
index 96389809db7994c1143a80eb69d9e4048ff3307d..462c1001cd9dff2a162e5f46d9590e6099f42dd0 100644 (file)
@@ -16,6 +16,7 @@ import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.net.URI;
 import java.nio.charset.StandardCharsets;
+import java.util.Map.Entry;
 import javax.ws.rs.Produces;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
@@ -27,8 +28,6 @@ import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter;
 import org.opendaylight.netconf.sal.rest.api.RestconfService;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.Draft17;
-import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
@@ -45,47 +44,57 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
+/**
+ * @deprecated This class will be replaced by
+ * {@link org.opendaylight.restconf.jersey.providers.NormalizedNodeJsonBodyWriter}
+ */
+@Deprecated
 @Provider
 @Produces({ Draft02.MediaTypes.API + RestconfService.JSON, Draft02.MediaTypes.DATA + RestconfService.JSON,
-        Draft02.MediaTypes.OPERATION + RestconfService.JSON, Draft17.MediaTypes.DATA + RestconfConstants.JSON,
-        MediaType.APPLICATION_JSON })
+        Draft02.MediaTypes.OPERATION + RestconfService.JSON, MediaType.APPLICATION_JSON })
 public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<NormalizedNodeContext> {
 
     private static final int DEFAULT_INDENT_SPACES_NUM = 2;
 
     @Override
-    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
+    public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+            final MediaType mediaType) {
         return type.equals(NormalizedNodeContext.class);
     }
 
     @Override
-    public long getSize(final NormalizedNodeContext t, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
+    public long getSize(final NormalizedNodeContext t, final Class<?> type, final Type genericType,
+            final Annotation[] annotations, final MediaType mediaType) {
         return -1;
     }
 
     @Override
-    public void writeTo(final NormalizedNodeContext t, final Class<?> type, final Type genericType, final Annotation[] annotations,
-            final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream)
-                    throws IOException, WebApplicationException {
+    public void writeTo(final NormalizedNodeContext t, final Class<?> type, final Type genericType,
+            final Annotation[] annotations, final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders,
+            final OutputStream entityStream) throws IOException, WebApplicationException {
+        for (final Entry<String, Object> entry : t.getNewHeaders().entrySet()) {
+            httpHeaders.add(entry.getKey(), entry.getValue());
+        }
         final NormalizedNode<?, ?> data = t.getData();
         if (data == null) {
             return;
         }
 
         @SuppressWarnings("unchecked")
-        final InstanceIdentifierContext<SchemaNode> context = (InstanceIdentifierContext<SchemaNode>) t.getInstanceIdentifierContext();
+        final InstanceIdentifierContext<SchemaNode> context =
+                (InstanceIdentifierContext<SchemaNode>) t.getInstanceIdentifierContext();
 
         final SchemaPath path = context.getSchemaNode().getPath();
         final JsonWriter jsonWriter = createJsonWriter(entityStream, t.getWriterParameters().isPrettyPrint());
         jsonWriter.beginObject();
-        writeNormalizedNode(jsonWriter,path,context,data, t.getWriterParameters().getDepth());
+        writeNormalizedNode(jsonWriter,path,context,data, Optional.fromNullable(t.getWriterParameters().getDepth()));
         jsonWriter.endObject();
         jsonWriter.flush();
     }
 
     private void writeNormalizedNode(final JsonWriter jsonWriter, SchemaPath path,
-            final InstanceIdentifierContext<SchemaNode> context, NormalizedNode<?, ?> data, final Optional<Integer> depth) throws
-            IOException {
+            final InstanceIdentifierContext<SchemaNode> context, NormalizedNode<?, ?> data,
+            final Optional<Integer> depth) throws IOException {
         final RestconfNormalizedNodeWriter nnWriter;
         if (SchemaPath.ROOT.equals(path)) {
             /*
@@ -139,7 +148,8 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<Normalize
         } else {
             initialNs = null;
         }
-        final NormalizedNodeStreamWriter streamWriter = JSONNormalizedNodeStreamWriter.createNestedWriter(codecs,path,initialNs,jsonWriter);
+        final NormalizedNodeStreamWriter streamWriter =
+                JSONNormalizedNodeStreamWriter.createNestedWriter(codecs, path, initialNs, jsonWriter);
         if (depth.isPresent()) {
             return DepthAwareNormalizedNodeWriter.forStreamWriter(streamWriter, depth.get());
         } else {
index 0a0435c09000630b8ad05aef4b3154bb404b080a..3d78e952fd55dd17ed12466843e3bbeb6c6acca0 100644 (file)
@@ -13,6 +13,8 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
+import java.util.Map.Entry;
+import javanet.staxutils.IndentingXMLStreamWriter;
 import javax.ws.rs.Produces;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
@@ -29,8 +31,6 @@ import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter;
 import org.opendaylight.netconf.sal.rest.api.RestconfService;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.Draft17;
-import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
@@ -41,12 +41,15 @@ import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
-import javanet.staxutils.IndentingXMLStreamWriter;
 
+/**
+ * @deprecated This class will be replaced by
+ * {@link org.opendaylight.restconf.jersey.providers.NormalizedNodeXmlBodyWriter}
+ */
+@Deprecated
 @Provider
 @Produces({ Draft02.MediaTypes.API + RestconfService.XML, Draft02.MediaTypes.DATA + RestconfService.XML,
-        Draft02.MediaTypes.OPERATION + RestconfService.XML, Draft17.MediaTypes.DATA + RestconfConstants.XML,
-        MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+        Draft02.MediaTypes.OPERATION + RestconfService.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
 public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<NormalizedNodeContext> {
 
     private static final XMLOutputFactory XML_FACTORY;
@@ -73,6 +76,9 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
             final Annotation[] annotations, final MediaType mediaType,
             final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException,
             WebApplicationException {
+        for (final Entry<String, Object> entry : t.getNewHeaders().entrySet()) {
+            httpHeaders.add(entry.getKey(), entry.getValue());
+        }
         final InstanceIdentifierContext<?> pathContext = t.getInstanceIdentifierContext();
         if (t.getData() == null) {
             return;
@@ -92,14 +98,12 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
         final NormalizedNode<?, ?> data = t.getData();
         final SchemaPath schemaPath = pathContext.getSchemaNode().getPath();
 
-
-
-        writeNormalizedNode(xmlWriter, schemaPath, pathContext, data, t.getWriterParameters().getDepth());
-
+        writeNormalizedNode(xmlWriter, schemaPath, pathContext, data, Optional.fromNullable(t.getWriterParameters().getDepth()));
     }
 
-    private void writeNormalizedNode(final XMLStreamWriter xmlWriter, final SchemaPath schemaPath, final InstanceIdentifierContext<?>
-            pathContext, NormalizedNode<?, ?> data, final Optional<Integer> depth) throws IOException {
+    private void writeNormalizedNode(final XMLStreamWriter xmlWriter, final SchemaPath schemaPath,
+            final InstanceIdentifierContext<?> pathContext, NormalizedNode<?, ?> data, final Optional<Integer> depth)
+            throws IOException {
         final RestconfNormalizedNodeWriter nnWriter;
         final SchemaContext schemaCtx = pathContext.getSchemaContext();
         if (SchemaPath.ROOT.equals(schemaPath)) {
@@ -122,8 +126,9 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
     }
 
     private RestconfNormalizedNodeWriter createNormalizedNodeWriter(final XMLStreamWriter xmlWriter,
-                                                                        final SchemaContext schemaContext, final SchemaPath schemaPath, final Optional<Integer> depth) {
-        final NormalizedNodeStreamWriter xmlStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, schemaContext, schemaPath);
+            final SchemaContext schemaContext, final SchemaPath schemaPath, final Optional<Integer> depth) {
+        final NormalizedNodeStreamWriter xmlStreamWriter =
+                XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, schemaContext, schemaPath);
         if (depth.isPresent()) {
             return DepthAwareNormalizedNodeWriter.forStreamWriter(xmlStreamWriter, depth.get());
         } else {
@@ -136,7 +141,8 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
             throws IOException {
         try {
             final QName name = data.getNodeType();
-            xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, name.getLocalName(), name.getNamespace().toString());
+            xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, name.getLocalName(),
+                    name.getNamespace().toString());
             xmlWriter.writeDefaultNamespace(name.getNamespace().toString());
             for(final NormalizedNode<?,?> child : data.getValue()) {
                 nnWriter.write(child);
index 1283d362cd82ef5fd4bd69ec75b7b7f7c1fb1077..8165704c9ceb02c4d59327c51b78c591fa2f536c 100644 (file)
@@ -27,14 +27,14 @@ import org.opendaylight.netconf.sal.rest.api.RestconfService;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusEntity;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
 
 
 @Provider
 @Produces({Draft02.MediaTypes.PATCH_STATUS + RestconfService.JSON,
-        Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.JSON})
+        Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.JSON})
 public class PATCHJsonBodyWriter implements MessageBodyWriter<PATCHStatusContext> {
 
     @Override
index 747e273453f4236108f91eb56cbfe6cecf2628b2..3a92515bcbcfe4977a2e80d06e293a44a3eb8892 100644 (file)
@@ -28,12 +28,12 @@ import org.opendaylight.netconf.sal.rest.api.RestconfService;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusEntity;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 
 @Provider
 @Produces({Draft02.MediaTypes.PATCH_STATUS + RestconfService.XML,
-        Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.XML})
+        Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.XML})
 public class PATCHXmlBodyWriter implements MessageBodyWriter<PATCHStatusContext> {
 
     private static final XMLOutputFactory XML_FACTORY;
index df56bc4c0e70a7ada81e6ab0d38169a6d60eed57..dffcf5b813367b535870ca3312aa9f930d4218e7 100644 (file)
@@ -9,7 +9,6 @@
 package org.opendaylight.netconf.sal.rest.impl;
 
 import com.google.common.base.Preconditions;
-import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContext;
@@ -31,97 +30,101 @@ public class RestconfCompositeWrapper implements RestconfService, SchemaRetrieva
 
     @Override
     public Object getRoot() {
-        return restconf.getRoot();
+        return this.restconf.getRoot();
     }
 
     @Override
     public NormalizedNodeContext getModules(final UriInfo uriInfo) {
-        return restconf.getModules(uriInfo);
+        return this.restconf.getModules(uriInfo);
     }
 
     @Override
     public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
-        return restconf.getModules(identifier, uriInfo);
+        return this.restconf.getModules(identifier, uriInfo);
     }
 
     @Override
     public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
-        return restconf.getModule(identifier, uriInfo);
+        return this.restconf.getModule(identifier, uriInfo);
     }
 
     @Override
     public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
-        return restconf.getOperations(uriInfo);
+        return this.restconf.getOperations(uriInfo);
     }
 
     @Override
     public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
-        return restconf.getOperations(identifier, uriInfo);
+        return this.restconf.getOperations(identifier, uriInfo);
     }
 
     @Override
-    public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
-        return restconf.invokeRpc(identifier, payload, uriInfo);
+    public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
+            final UriInfo uriInfo) {
+        return this.restconf.invokeRpc(identifier, payload, uriInfo);
     }
 
     @Override
     @Deprecated
     public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
-        return restconf.invokeRpc(identifier, noPayload, uriInfo);
+        return this.restconf.invokeRpc(identifier, noPayload, uriInfo);
     }
 
     @Override
     public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
-        return restconf.readConfigurationData(identifier, uriInfo);
+        return this.restconf.readConfigurationData(identifier, uriInfo);
     }
 
     @Override
     public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) {
-        return restconf.readOperationalData(identifier, uriInfo);
+        return this.restconf.readOperationalData(identifier, uriInfo);
     }
 
     @Override
-    public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
-        return restconf.updateConfigurationData(identifier, payload);
+    public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload,
+            final UriInfo uriInfo) {
+        return this.restconf.updateConfigurationData(identifier, payload, uriInfo);
     }
 
     @Override
-    public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
-        return restconf.createConfigurationData(identifier, payload, uriInfo);
+    public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload,
+            final UriInfo uriInfo) {
+        return this.restconf.createConfigurationData(identifier, payload, uriInfo);
     }
 
     @Override
     public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
-        return restconf.createConfigurationData(payload, uriInfo);
+        return this.restconf.createConfigurationData(payload, uriInfo);
     }
 
     @Override
     public Response deleteConfigurationData(final String identifier) {
-        return restconf.deleteConfigurationData(identifier);
+        return this.restconf.deleteConfigurationData(identifier);
     }
 
     @Override
-    public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
-        return restconf.subscribeToStream(identifier, uriInfo);
+    public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
+        return this.restconf.subscribeToStream(identifier, uriInfo);
     }
 
     @Override
     public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
-        return restconf.getAvailableStreams(uriInfo);
+        return this.restconf.getAvailableStreams(uriInfo);
     }
 
     @Override
-    public PATCHStatusContext patchConfigurationData(final String identifier, PATCHContext payload, UriInfo uriInfo) {
-        return restconf.patchConfigurationData(identifier, payload, uriInfo);
+    public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext payload,
+            final UriInfo uriInfo) {
+        return this.restconf.patchConfigurationData(identifier, payload, uriInfo);
     }
 
     @Override
     public PATCHStatusContext patchConfigurationData(final PATCHContext context, final UriInfo uriInfo) {
-        return restconf.patchConfigurationData(context, uriInfo);
+        return this.restconf.patchConfigurationData(context, uriInfo);
     }
 
     @Override
     public SchemaExportContext getSchema(final String mountId) {
-        return schema.getSchema(mountId);
+        return this.schema.getSchema(mountId);
     }
 }
index 6a16a5f0366b21d78656f8b8e98b1fc5b3497698..d3f918741901564814cc8b176aca17ff4234df78 100644 (file)
@@ -9,12 +9,14 @@ package org.opendaylight.netconf.sal.restconf.api;
 
 import com.google.common.base.Optional;
 import javax.annotation.Nonnull;
+import javax.ws.rs.core.UriInfo;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.yangtools.yang.common.OperationFailedException;
 
 /**
  * @author Thomas Pantelis
  */
+@Deprecated
 public interface JSONRestconfService {
     /**
      * The data tree root path.
@@ -29,7 +31,7 @@ public interface JSONRestconfService {
      * @param payload the payload data in JSON format.
      * @throws OperationFailedException if the request fails.
      */
-    void put(String uriPath, @Nonnull String payload) throws OperationFailedException;
+    void put(String uriPath, @Nonnull String payload, UriInfo uriInfo) throws OperationFailedException;
 
     /**
      * Issues a restconf POST request to the configuration data store.
@@ -39,7 +41,7 @@ public interface JSONRestconfService {
      * @param payload the payload data in JSON format.
      * @throws OperationFailedException if the request fails.
      */
-    void post(String uriPath, @Nonnull String payload) throws OperationFailedException;
+    void post(String uriPath, @Nonnull String payload, UriInfo uriInfo) throws OperationFailedException;
 
     /**
      * Issues a restconf DELETE request to the configuration data store.
@@ -59,7 +61,8 @@ public interface JSONRestconfService {
      * @return an Optional containing the data in JSON format if present.
      * @throws OperationFailedException if the request fails.
      */
-    Optional<String> get(String uriPath, LogicalDatastoreType datastoreType) throws OperationFailedException;
+    Optional<String> get(String uriPath, LogicalDatastoreType datastoreType, UriInfo uriInfo)
+            throws OperationFailedException;
 
     /**
      * Invokes a yang-defined RPC.
index 60347f98f665f286032047bb181164df60755702..940932025103c1880167d5cc1bcd923f3d9e2382 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.netconf.sal.restconf.impl;
 
 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
-
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -44,12 +43,33 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
@@ -88,28 +108,89 @@ public class BrokerFacade {
         }
     }
 
-    // READ configuration
+    /**
+     * Read config data by path
+     *
+     * @param path
+     *            - path of data
+     * @return read date
+     */
     public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
+        return readConfigurationData(path, null);
+    }
+
+    /**
+     * Read config data by path
+     *
+     * @param path
+     *            - path of data
+     * @param withDefa
+     *            - value of with-defaults parameter
+     * @return read date
+     */
+    public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path, final String withDefa) {
         checkPreconditions();
-        return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
+        return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path, withDefa);
     }
 
-    public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
+    /**
+     * Read config data from mount point by path.
+     *
+     * @param mountPoint
+     *            - mount point for reading data
+     * @param path
+     *            - path of data
+     * @return read data
+     */
+    public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint,
+            final YangInstanceIdentifier path) {
+        return readConfigurationData(mountPoint, path, null);
+    }
+
+    /**
+     * Read config data from mount point by path.
+     *
+     * @param mountPoint
+     *            - mount point for reading data
+     * @param path
+     *            - path of data
+     * @param withDefa
+     *            - value of with-defaults parameter
+     * @return read data
+     */
+    public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path,
+            final String withDefa) {
         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
         if (domDataBrokerService.isPresent()) {
-            return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path);
+            return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path,
+                    withDefa);
         }
         final String errMsg = "DOM data broker service isn't available for mount point " + path;
         LOG.warn(errMsg);
         throw new RestconfDocumentedException(errMsg);
     }
 
-    // READ operational
+    /**
+     * Read operational data by path.
+     *
+     * @param path
+     *            - path of data
+     * @return read data
+     */
     public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
         checkPreconditions();
         return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
     }
 
+    /**
+     * Read operational data from mount point by path.
+     *
+     * @param mountPoint
+     *            - mount point for reading data
+     * @param path
+     *            - path of data
+     * @return read data
+     */
     public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
         if (domDataBrokerService.isPresent()) {
@@ -132,10 +213,13 @@ public class BrokerFacade {
      *            - path of node
      * @param payload
      *            - input data
+     * @param point
+     * @param insert
      * @return wrapper of status and future of PUT
      */
     public PutResult commitConfigurationDataPut(
-            final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+            final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final String insert, final String point) {
         Preconditions.checkNotNull(globalSchema);
         Preconditions.checkNotNull(path);
         Preconditions.checkNotNull(payload);
@@ -146,7 +230,7 @@ public class BrokerFacade {
         final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
                 : Status.CREATED;
         final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
-                newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema);
+                newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
         return new PutResult(status, future);
     }
 
@@ -163,10 +247,13 @@ public class BrokerFacade {
      *            - path of node
      * @param payload
      *            - input data
+     * @param point
+     * @param insert
      * @return wrapper of status and future of PUT
      */
     public PutResult commitMountPointDataPut(
-            final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+            final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final String insert, final String point) {
         Preconditions.checkNotNull(mountPoint);
         Preconditions.checkNotNull(path);
         Preconditions.checkNotNull(payload);
@@ -177,8 +264,8 @@ public class BrokerFacade {
             final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
                     ? Status.OK : Status.CREATED;
             final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
-                    newReadWriteTransaction, CONFIGURATION, path,
-                    payload, mountPoint.getSchemaContext());
+                    newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
+                    point);
             return new PutResult(status, future);
         }
         final String errMsg = "DOM data broker service isn't available for mount point " + path;
@@ -361,17 +448,20 @@ public class BrokerFacade {
 
     // POST configuration
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
-            final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+            final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final String insert, final String point) {
         checkPreconditions();
-        return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema);
+        return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
+                globalSchema, insert, point);
     }
 
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
-            final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+            final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final String insert, final String point) {
         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
         if (domDataBrokerService.isPresent()) {
             return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
-                    payload, mountPoint.getSchemaContext());
+                    payload, mountPoint.getSchemaContext(), insert, point);
         }
         final String errMsg = "DOM data broker service isn't available for mount point " + path;
         LOG.warn(errMsg);
@@ -423,6 +513,11 @@ public class BrokerFacade {
 
     private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
             final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
+        return readDataViaTransaction(transaction, datastore, path, null);
+    }
+
+    private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final String withDefa) {
         LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
         final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
         final ReadDataResult<NormalizedNode<?, ?>> readData = new ReadDataResult<>();
@@ -450,18 +545,146 @@ public class BrokerFacade {
             LOG.warn(msg);
             throw new RestconfDocumentedException(msg, e);
         }
-        return readData.getResult();
+        if (withDefa == null) {
+            return readData.getResult();
+        } else {
+            return prepareDataByParamWithDef(readData.getResult(), path, withDefa);
+        }
+
+    }
+
+    private NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
+            final YangInstanceIdentifier path, final String withDefa) {
+        boolean trim;
+        switch (withDefa) {
+            case "trim":
+                trim = true;
+                break;
+            case "explicit":
+                trim = false;
+                break;
+            default:
+                throw new RestconfDocumentedException("Bad value used with with-defaults parameter : " + withDefa);
+        }
+
+        final SchemaContext ctx = ControllerContext.getInstance().getGlobalSchema();
+        final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
+        final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+        if (result instanceof ContainerNode) {
+            final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
+                    Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
+            buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
+            return builder.build();
+        } else {
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
+                    Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
+            buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
+                    ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
+            return builder.build();
+        }
+    }
+
+    private void buildMapEntryBuilder(final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
+            final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
+            final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
+        for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
+            final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
+            final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+            if (child instanceof ContainerNode) {
+                final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
+                        Builders.containerBuilder((ContainerSchemaNode) childSchema);
+                buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
+                builder.withChild(childBuilder.build());
+            } else if (child instanceof MapNode) {
+                final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
+                        Builders.mapBuilder((ListSchemaNode) childSchema);
+                buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
+                        ((ListSchemaNode) childSchema).getKeyDefinition());
+                builder.withChild(childBuilder.build());
+            } else if (child instanceof LeafNode) {
+                final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
+                final String nodeVal = ((LeafNode<String>) child).getValue();
+                final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
+                        Builders.leafBuilder((LeafSchemaNode) childSchema);
+                if (keys.contains(child.getNodeType())) {
+                    leafBuilder.withValue(((LeafNode) child).getValue());
+                    builder.withChild(leafBuilder.build());
+                } else {
+                    if (trim) {
+                        if ((defaultVal == null) || !defaultVal.equals(nodeVal)) {
+                            leafBuilder.withValue(((LeafNode) child).getValue());
+                            builder.withChild(leafBuilder.build());
+                        }
+                    } else {
+                        if ((defaultVal != null) && defaultVal.equals(nodeVal)) {
+                            leafBuilder.withValue(((LeafNode) child).getValue());
+                            builder.withChild(leafBuilder.build());
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
+            final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
+            final List<QName> keys) {
+        for (final MapEntryNode mapEntryNode : result.getValue()) {
+            final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
+            final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
+                    Builders.mapEntryBuilder((ListSchemaNode) childSchema);
+            buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
+            builder.withChild(mapEntryBuilder.build());
+        }
+    }
+
+    private void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
+            final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
+            final YangInstanceIdentifier actualPath, final boolean trim) {
+        for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
+            final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
+            final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+            if(child instanceof ContainerNode){
+                final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
+                        Builders.containerBuilder((ContainerSchemaNode) childSchema);
+                buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
+                builder.withChild(builderChild.build());
+            } else if (child instanceof MapNode) {
+                final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
+                        Builders.mapBuilder((ListSchemaNode) childSchema);
+                buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
+                        ((ListSchemaNode) childSchema).getKeyDefinition());
+                builder.withChild(childBuilder.build());
+            } else if (child instanceof LeafNode) {
+                final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
+                final String nodeVal = ((LeafNode<String>) child).getValue();
+                final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
+                        Builders.leafBuilder((LeafSchemaNode) childSchema);
+                if (trim) {
+                    if ((defaultVal == null) || !defaultVal.equals(nodeVal)) {
+                        leafBuilder.withValue(((LeafNode) child).getValue());
+                        builder.withChild(leafBuilder.build());
+                    }
+                } else {
+                    if ((defaultVal != null) && defaultVal.equals(nodeVal)) {
+                        leafBuilder.withValue(((LeafNode) child).getValue());
+                        builder.withChild(leafBuilder.build());
+                    }
+                }
+            }
+        }
     }
 
     /**
      * POST data and submit transaction {@link DOMDataReadWriteTransaction}
-     * @return
      */
     private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
             final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
-            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+            final String insert, final String point) {
         LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
-        postData(rWTransaction, datastore, path, payload, schemaContext);
+        postData(rWTransaction, datastore, path, payload, schemaContext, insert, point);
         return rWTransaction.submit();
     }
 
@@ -472,13 +695,188 @@ public class BrokerFacade {
             final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
         LOG.trace("POST {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
-        postData(rWTransaction, datastore, path, payload, schemaContext);
+        postData(rWTransaction, datastore, path, payload, schemaContext, null, null);
     }
 
-    // FIXME: This is doing correct post for container and list children, not sure if this will work for choice case
     private void postData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
                           final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
-                          final SchemaContext schemaContext) {
+            final SchemaContext schemaContext, final String insert, final String point) {
+        if (insert == null) {
+            makeNormalPost(rWTransaction, datastore, path, payload, schemaContext);
+        } else {
+            final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
+            checkItemDoesNotExists(rWTransaction, datastore, path);
+            switch (insert) {
+                case "first":
+                    if(schemaNode instanceof ListSchemaNode){
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            rWTransaction.delete(datastore, path.getParent().getParent());
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                            makeNormalPost(rWTransaction, datastore, path.getParent().getParent(), readList,
+                                    schemaContext);
+                        }
+                    } else {
+                        final OrderedLeafSetNode readLeafList =
+                                (OrderedLeafSetNode) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            rWTransaction.delete(datastore, path.getParent());
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                            makeNormalPost(rWTransaction, datastore, path.getParent().getParent(), readLeafList,
+                                    schemaContext);
+                        }
+                    }
+                    break;
+                case "last":
+                    simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                    break;
+                case "before":
+                    if(schemaNode instanceof ListSchemaNode){
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            insertWithPointListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readList,
+                                    true);
+                        }
+                    } else {
+                        final OrderedLeafSetNode<?> readLeafList =
+                                (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            insertWithPointLeafListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readLeafList, true);
+                        }
+                    }
+                    break;
+                case "after":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            insertWithPointListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readList,
+                                    false);
+                        }
+                    } else {
+                        final OrderedLeafSetNode<?> readLeafList =
+                                (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            insertWithPointLeafListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readLeafList, false);
+                        }
+                    }
+                    break;
+                default:
+                    throw new RestconfDocumentedException(
+                            "Used bad value of insert parameter. Possible values are first, last, before or after, "
+                                    + "but was: " + insert);
+            }
+        }
+    }
+
+    private void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
+            final boolean before) {
+        rWTransaction.delete(datastore, path.getParent().getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree =
+                ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (h == p) {
+                checkItemDoesNotExists(rWTransaction, datastore, path);
+                simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
+            checkItemDoesNotExists(rWTransaction, datastore, childPath);
+            rWTransaction.put(datastore, childPath, nodeChild);
+            h++;
+        }
+    }
+
+    private void insertWithPointListPost(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+            final String point, final MapNode readList, final boolean before) {
+        rWTransaction.delete(datastore, path.getParent().getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (mapEntryNode.getIdentifier()
+                    .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree =
+                ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (h == p) {
+                checkItemDoesNotExists(rWTransaction, datastore, path);
+                simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
+            checkItemDoesNotExists(rWTransaction, datastore, childPath);
+            rWTransaction.put(datastore, childPath, mapEntryNode);
+            h++;
+        }
+    }
+
+    private DataSchemaNode checkListAndOrderedType(final SchemaContext ctx,
+            final YangInstanceIdentifier path) {
+        final YangInstanceIdentifier parent = path.getParent();
+        final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
+        final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
+
+        if (dataSchemaNode instanceof ListSchemaNode) {
+            if(!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
+                throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
+            }
+            return dataSchemaNode;
+        }
+        if (dataSchemaNode instanceof LeafListSchemaNode) {
+            if(!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
+                throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user leaf-list.");
+            }
+            return dataSchemaNode;
+        }
+        throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
+    }
+
+    private void makeNormalPost(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
         if (payload instanceof MapNode) {
             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
             rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
@@ -488,13 +886,27 @@ public class BrokerFacade {
                 checkItemDoesNotExists(rWTransaction, datastore, childPath);
                 rWTransaction.put(datastore, childPath, child);
             }
-        } else {
-            checkItemDoesNotExists(rWTransaction, datastore, path);
+        } else if (payload instanceof LeafSetNode) {
+            final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+            rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
             ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
-            rWTransaction.put(datastore, path, payload);
+            for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) payload).getValue()) {
+                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+                checkItemDoesNotExists(rWTransaction, datastore, childPath);
+                rWTransaction.put(datastore, childPath, child);
+            }
+        } else {
+            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
         }
     }
 
+    private void simplePostPut(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+        checkItemDoesNotExists(rWTransaction, datastore, path);
+        ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
+        rWTransaction.put(datastore, path, payload);
+    }
+
     /**
      * Check if item already exists. Throws error if it does NOT already exist.
      * @param rWTransaction Current transaction
@@ -583,27 +995,182 @@ public class BrokerFacade {
 
     /**
      * PUT data and submit {@link DOMDataReadWriteTransaction}
+     *
+     * @param point
+     * @param insert
      */
     private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
             final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
-            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+            final String insert, final String point) {
         LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
-        putData(readWriteTransaction, datastore, path, payload, schemaContext);
+        putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
         return readWriteTransaction.submit();
     }
 
     /**
      * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}
+     *
+     * @param insert
+     * @param point
      */
     private void putDataWithinTransaction(
             final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
         LOG.trace("Put {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
-        putData(writeTransaction, datastore, path, payload, schemaContext);
+        putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
     }
 
     // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
-    private void putData(final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+    private void putData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+            final String insert, final String point) {
+        if (insert == null) {
+            makePut(rWTransaction, datastore, path, payload, schemaContext);
+        } else {
+            final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
+            checkItemDoesNotExists(rWTransaction, datastore, path);
+            switch (insert) {
+                case "first":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            rWTransaction.delete(datastore, path.getParent());
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                            makePut(rWTransaction, datastore, path.getParent(), readList, schemaContext);
+                        }
+                    } else {
+                        final OrderedLeafSetNode<?> readLeafList =
+                                (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            rWTransaction.delete(datastore, path.getParent());
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                            makePut(rWTransaction, datastore, path.getParent(), readLeafList,
+                                    schemaContext);
+                        }
+                    }
+                    break;
+                case "last":
+                    simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                    break;
+                case "before":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            insertWithPointListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readList, true);
+                        }
+                    } else {
+                        final OrderedLeafSetNode<?> readLeafList =
+                                (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            insertWithPointLeafListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readLeafList, true);
+                        }
+                    }
+                    break;
+                case "after":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            insertWithPointListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readList, false);
+                        }
+                    } else {
+                        final OrderedLeafSetNode<?> readLeafList =
+                                (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            insertWithPointLeafListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readLeafList, false);
+                        }
+                    }
+                    break;
+                default:
+                    throw new RestconfDocumentedException(
+                            "Used bad value of insert parameter. Possible values are first, last, before or after, "
+                                    + "but was: " + insert);
+            }
+        }
+    }
+
+    private void insertWithPointLeafListPut(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
+            final boolean before) {
+        rWTransaction.delete(datastore, path.getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree =
+                ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (h == p) {
+                simplePut(datastore, path, rWTransaction, schemaContext, payload);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
+            rWTransaction.put(datastore, childPath, nodeChild);
+            h++;
+        }
+    }
+
+    private void insertWithPointListPut(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final SchemaContext schemaContext, final String point, final OrderedMapNode readList,
+            final boolean before) {
+        rWTransaction.delete(datastore, path.getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree =
+                ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (h == p) {
+                simplePut(datastore, path, rWTransaction, schemaContext, payload);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
+            rWTransaction.put(datastore, childPath, mapEntryNode);
+            h++;
+        }
+    }
+
+    private void makePut(final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
         if (payload instanceof MapNode) {
             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
@@ -614,11 +1181,17 @@ public class BrokerFacade {
                 writeTransaction.put(datastore, childPath, child);
             }
         } else {
-            ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
-            writeTransaction.put(datastore, path, payload);
+            simplePut(datastore, path, writeTransaction, schemaContext, payload);
         }
     }
 
+    private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+            final DOMDataReadWriteTransaction writeTransaction, final SchemaContext schemaContext,
+            final NormalizedNode<?, ?> payload) {
+        ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
+        writeTransaction.put(datastore, path, payload);
+    }
+
     private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
             final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
             final YangInstanceIdentifier path) {
index 14f8c22d5325021effe16852a5c057d3b781195d..9c9084fc2f2cfb4cbbdd8f5b314e4b53f7c16a95 100644 (file)
@@ -17,6 +17,7 @@ import java.lang.annotation.Annotation;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter;
@@ -34,78 +35,81 @@ import org.slf4j.LoggerFactory;
  *
  * @author Thomas Pantelis
  */
+@Deprecated
 public class JSONRestconfServiceImpl implements JSONRestconfService, AutoCloseable {
     private final static Logger LOG = LoggerFactory.getLogger(JSONRestconfServiceImpl.class);
 
     private static final Annotation[] EMPTY_ANNOTATIONS = new Annotation[0];
 
     @Override
-    public void put(String uriPath, String payload) throws OperationFailedException {
+    public void put(final String uriPath, final String payload, final UriInfo uriInfo) throws OperationFailedException {
         Preconditions.checkNotNull(payload, "payload can't be null");
 
         LOG.debug("put: uriPath: {}, payload: {}", uriPath, payload);
 
-        InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
-        NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, false);
+        final InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
+        final NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, false);
 
         LOG.debug("Parsed YangInstanceIdentifier: {}", context.getInstanceIdentifierContext().getInstanceIdentifier());
         LOG.debug("Parsed NormalizedNode: {}", context.getData());
 
         try {
-            RestconfImpl.getInstance().updateConfigurationData(uriPath, context);
-        } catch (Exception e) {
+            RestconfImpl.getInstance().updateConfigurationData(uriPath, context, uriInfo);
+        } catch (final Exception e) {
             propagateExceptionAs(uriPath, e, "PUT");
         }
     }
 
     @Override
-    public void post(String uriPath, String payload) throws OperationFailedException {
+    public void post(final String uriPath, final String payload, final UriInfo uriInfo)
+            throws OperationFailedException {
         Preconditions.checkNotNull(payload, "payload can't be null");
 
         LOG.debug("post: uriPath: {}, payload: {}", uriPath, payload);
 
-        InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
-        NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true);
+        final InputStream entityStream = new ByteArrayInputStream(payload.getBytes(StandardCharsets.UTF_8));
+        final NormalizedNodeContext context = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true);
 
         LOG.debug("Parsed YangInstanceIdentifier: {}", context.getInstanceIdentifierContext().getInstanceIdentifier());
         LOG.debug("Parsed NormalizedNode: {}", context.getData());
 
         try {
-            RestconfImpl.getInstance().createConfigurationData(uriPath, context, null);
-        } catch (Exception e) {
+            RestconfImpl.getInstance().createConfigurationData(uriPath, context, uriInfo);
+        } catch (final Exception e) {
             propagateExceptionAs(uriPath, e, "POST");
         }
     }
 
     @Override
-    public void delete(String uriPath) throws OperationFailedException {
+    public void delete(final String uriPath) throws OperationFailedException {
         LOG.debug("delete: uriPath: {}", uriPath);
 
         try {
             RestconfImpl.getInstance().deleteConfigurationData(uriPath);
-        } catch (Exception e) {
+        } catch (final Exception e) {
             propagateExceptionAs(uriPath, e, "DELETE");
         }
     }
 
     @Override
-    public Optional<String> get(String uriPath, LogicalDatastoreType datastoreType) throws OperationFailedException {
+    public Optional<String> get(final String uriPath, final LogicalDatastoreType datastoreType, final UriInfo uriInfo)
+            throws OperationFailedException {
         LOG.debug("get: uriPath: {}", uriPath);
 
         try {
             NormalizedNodeContext readData;
             if(datastoreType == LogicalDatastoreType.CONFIGURATION) {
-                readData = RestconfImpl.getInstance().readConfigurationData(uriPath, null);
+                readData = RestconfImpl.getInstance().readConfigurationData(uriPath, uriInfo);
             } else {
-                readData = RestconfImpl.getInstance().readOperationalData(uriPath, null);
+                readData = RestconfImpl.getInstance().readOperationalData(uriPath, uriInfo);
             }
 
-            Optional<String> result = Optional.of(toJson(readData));
+            final Optional<String> result = Optional.of(toJson(readData));
 
             LOG.debug("get returning: {}", result.get());
 
             return result;
-        } catch (Exception e) {
+        } catch (final Exception e) {
             if(!isDataMissing(e)) {
                 propagateExceptionAs(uriPath, e, "GET");
             }
@@ -116,10 +120,10 @@ public class JSONRestconfServiceImpl implements JSONRestconfService, AutoCloseab
     }
 
     @Override
-    public Optional<String> invokeRpc(String uriPath, Optional<String> input) throws OperationFailedException {
+    public Optional<String> invokeRpc(final String uriPath, final Optional<String> input) throws OperationFailedException {
         Preconditions.checkNotNull(uriPath, "uriPath can't be null");
 
-        String actualInput = input.isPresent() ? input.get() : null;
+        final String actualInput = input.isPresent() ? input.get() : null;
 
         LOG.debug("invokeRpc: uriPath: {}, input: {}", uriPath, actualInput);
 
@@ -127,8 +131,8 @@ public class JSONRestconfServiceImpl implements JSONRestconfService, AutoCloseab
         try {
             NormalizedNodeContext outputContext;
             if(actualInput != null) {
-                InputStream entityStream = new ByteArrayInputStream(actualInput.getBytes(StandardCharsets.UTF_8));
-                NormalizedNodeContext inputContext = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true);
+                final InputStream entityStream = new ByteArrayInputStream(actualInput.getBytes(StandardCharsets.UTF_8));
+                final NormalizedNodeContext inputContext = JsonNormalizedNodeBodyReader.readFrom(uriPath, entityStream, true);
 
                 LOG.debug("Parsed YangInstanceIdentifier: {}", inputContext.getInstanceIdentifierContext()
                         .getInstanceIdentifier());
@@ -142,7 +146,7 @@ public class JSONRestconfServiceImpl implements JSONRestconfService, AutoCloseab
             if(outputContext.getData() != null) {
                 output = toJson(outputContext);
             }
-        } catch (Exception e) {
+        } catch (final Exception e) {
             propagateExceptionAs(uriPath, e, "RPC");
         }
 
@@ -153,18 +157,18 @@ public class JSONRestconfServiceImpl implements JSONRestconfService, AutoCloseab
     public void close() {
     }
 
-    private String toJson(NormalizedNodeContext readData) throws IOException {
-        NormalizedNodeJsonBodyWriter writer = new NormalizedNodeJsonBodyWriter();
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+    private String toJson(final NormalizedNodeContext readData) throws IOException {
+        final NormalizedNodeJsonBodyWriter writer = new NormalizedNodeJsonBodyWriter();
+        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         writer.writeTo(readData, NormalizedNodeContext.class, null, EMPTY_ANNOTATIONS,
                 MediaType.APPLICATION_JSON_TYPE, null, outputStream );
         return outputStream.toString(StandardCharsets.UTF_8.name());
     }
 
-    private boolean isDataMissing(Exception e) {
+    private boolean isDataMissing(final Exception e) {
         boolean dataMissing = false;
         if(e instanceof RestconfDocumentedException) {
-            RestconfDocumentedException rde = (RestconfDocumentedException)e;
+            final RestconfDocumentedException rde = (RestconfDocumentedException)e;
             if(!rde.getErrors().isEmpty()) {
                 if(rde.getErrors().get(0).getErrorTag() == ErrorTag.DATA_MISSING) {
                     dataMissing = true;
@@ -175,7 +179,7 @@ public class JSONRestconfServiceImpl implements JSONRestconfService, AutoCloseab
         return dataMissing;
     }
 
-    private static void propagateExceptionAs(String uriPath, Exception e, String operation) throws OperationFailedException {
+    private static void propagateExceptionAs(final String uriPath, final Exception e, final String operation) throws OperationFailedException {
         LOG.debug("Error for uriPath: {}", uriPath, e);
 
         if(e instanceof RestconfDocumentedException) {
@@ -186,10 +190,10 @@ public class JSONRestconfServiceImpl implements JSONRestconfService, AutoCloseab
         throw new OperationFailedException(String.format("%s failed for URI %s", operation, uriPath), e);
     }
 
-    private static RpcError[] toRpcErrors(List<RestconfError> from) {
-        RpcError[] to = new RpcError[from.size()];
+    private static RpcError[] toRpcErrors(final List<RestconfError> from) {
+        final RpcError[] to = new RpcError[from.size()];
         int i = 0;
-        for(RestconfError e: from) {
+        for(final RestconfError e: from) {
             to[i++] = RpcResultBuilder.newError(toRpcErrorType(e.getErrorType()), e.getErrorTag().getTagValue(),
                     e.getErrorMessage());
         }
@@ -197,7 +201,7 @@ public class JSONRestconfServiceImpl implements JSONRestconfService, AutoCloseab
         return to;
     }
 
-    private static ErrorType toRpcErrorType(RestconfError.ErrorType errorType) {
+    private static ErrorType toRpcErrorType(final RestconfError.ErrorType errorType) {
         switch(errorType) {
             case TRANSPORT: {
                 return ErrorType.TRANSPORT;
index 3bed8d1ad03d74790a13260dbee0a556724398b5..6fc830cd839f562b57b30c7aad6d56052cb90cf3 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.netconf.sal.restconf.impl;
 
+import java.util.HashMap;
+import java.util.Map;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 
@@ -15,14 +17,24 @@ public class NormalizedNodeContext {
     private final InstanceIdentifierContext<? extends SchemaNode> context;
     private final NormalizedNode<?,?> data;
     private final WriterParameters writerParameters;
+    private Map<String, Object> headers = new HashMap<>();
 
     public NormalizedNodeContext(final InstanceIdentifierContext<? extends SchemaNode> context,
-                                 final NormalizedNode<?, ?> data, WriterParameters writerParameters) {
+                                 final NormalizedNode<?, ?> data, final WriterParameters writerParameters) {
         this.context = context;
         this.data = data;
         this.writerParameters = writerParameters;
     }
 
+    public NormalizedNodeContext(final InstanceIdentifierContext<? extends SchemaNode> context,
+            final NormalizedNode<?, ?> data, final WriterParameters writerParameters,
+            final Map<String, Object> headers) {
+        this.context = context;
+        this.data = data;
+        this.writerParameters = writerParameters;
+        this.headers = headers;
+    }
+
     public NormalizedNodeContext(final InstanceIdentifierContext<? extends SchemaNode> context,
                                  final NormalizedNode<?, ?> data) {
         this.context = context;
@@ -31,15 +43,32 @@ public class NormalizedNodeContext {
         this.writerParameters = new WriterParameters.WriterParametersBuilder().build();
     }
 
+    public NormalizedNodeContext(final InstanceIdentifierContext<? extends SchemaNode> context,
+            final NormalizedNode<?, ?> data, final Map<String, Object> headers) {
+        this.context = context;
+        this.data = data;
+        // default writer parameters
+        this.writerParameters = new WriterParameters.WriterParametersBuilder().build();
+        this.headers = headers;
+    }
+
+
     public InstanceIdentifierContext<? extends SchemaNode> getInstanceIdentifierContext() {
-        return context;
+        return this.context;
     }
 
     public NormalizedNode<?, ?> getData() {
-        return data;
+        return this.data;
     }
 
     public WriterParameters getWriterParameters() {
-        return writerParameters;
+        return this.writerParameters;
+    }
+
+    /**
+     * @return
+     */
+    public Map<String, Object> getNewHeaders() {
+        return this.headers;
     }
 }
index 082ba18e81e183b4db3d40c80b83fddd2e7a58f2..70d9280c4207c09c2cb7857cd8cfe319bb98d1c5 100644 (file)
@@ -24,12 +24,17 @@ public class QueryParametersParser {
 
         @Override
         public String toString() {
-            return uriParameterName;
+            return this.uriParameterName;
         }
     }
 
     public static WriterParameters parseWriterParameters(final UriInfo info) {
-        WriterParameters.WriterParametersBuilder wpBuilder = new WriterParameters.WriterParametersBuilder();
+        return parseParams(info, false);
+    }
+
+    private static WriterParameters parseParams(final UriInfo info, final boolean tagged) {
+        final WriterParameters.WriterParametersBuilder wpBuilder = new WriterParameters.WriterParametersBuilder();
+        wpBuilder.setTagged(tagged);
         if(info == null) {
             return wpBuilder.build();
         }
@@ -55,4 +60,8 @@ public class QueryParametersParser {
         return wpBuilder.build();
     }
 
+    public static WriterParameters parseWriterParameters(final UriInfo uriInfo, final boolean tagged) {
+        return parseParams(uriInfo, tagged);
+    }
+
 }
index 669028f38e7142dc668f2516015dc895df0f6a93..edb32d48c6a863f44724de664ff2ba968dd911c4 100644 (file)
@@ -22,6 +22,7 @@ import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -33,6 +34,7 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CountDownLatch;
@@ -62,6 +64,8 @@ import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.Notificator;
 import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -82,7 +86,9 @@ import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@@ -131,6 +137,8 @@ public class RestconfImpl implements RestconfService {
 
     private static final String SCOPE_PARAM_NAME = "scope";
 
+    private static final String OUTPUT_TYPE_PARAM_NAME = "notification-output-type";
+
     private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
 
     private static final String NETCONF_BASE_PAYLOAD_NAME = "data";
@@ -150,11 +158,14 @@ public class RestconfImpl implements RestconfService {
     static {
         try {
             final Date eventSubscriptionAugRevision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
-            NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME );
+            NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null),
+                    NETCONF_BASE_PAYLOAD_NAME);
             SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
                     eventSubscriptionAugRevision);
-            SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"),
-                    QName.create(SAL_REMOTE_AUGMENT, "datastore")));
+            SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(
+                    QName.create(SAL_REMOTE_AUGMENT, "scope"),
+                    QName.create(SAL_REMOTE_AUGMENT, "datastore"),
+                    QName.create(SAL_REMOTE_AUGMENT, "notification-output-type")));
         } catch (final ParseException e) {
             final String errMsg = "It wasn't possible to convert revision date of sal-remote-augment to date";
             LOG.debug(errMsg);
@@ -214,7 +225,8 @@ public class RestconfImpl implements RestconfService {
             throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
         }
 
-        final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
+        final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext
+                .toMountPointIdentifier(identifier);
         final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
         final Set<Module> modules = this.controllerContext.getAllModules(mountPoint);
         final MapNode mountPointModulesMap = makeModuleMapNode(modules);
@@ -241,7 +253,8 @@ public class RestconfImpl implements RestconfService {
         DOMMountPoint mountPoint = null;
         final SchemaContext schemaContext;
         if (identifier.contains(ControllerContext.MOUNT)) {
-            final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
+            final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext
+                    .toMountPointIdentifier(identifier);
             mountPoint = mountPointIdentifier.getMountPoint();
             module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
             schemaContext = mountPoint.getSchemaContext();
@@ -274,8 +287,8 @@ public class RestconfImpl implements RestconfService {
         final SchemaContext schemaContext = this.controllerContext.getGlobalSchema();
         final Set<String> availableStreams = Notificator.getStreamNames();
         final Module restconfModule = getRestconfModule();
-        final DataSchemaNode streamSchemaNode = this.controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
-                Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
+        final DataSchemaNode streamSchemaNode = this.controllerContext
+                .getRestconfModuleRestConfSchemaNode(restconfModule, Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
         Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
 
         final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamsBuilder = Builders
@@ -309,14 +322,17 @@ public class RestconfImpl implements RestconfService {
         Set<Module> modules = null;
         DOMMountPoint mountPoint = null;
         if (identifier.contains(ControllerContext.MOUNT)) {
-            final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier);
+            final InstanceIdentifierContext<?> mountPointIdentifier = this.controllerContext
+                    .toMountPointIdentifier(identifier);
             mountPoint = mountPointIdentifier.getMountPoint();
             modules = this.controllerContext.getAllModules(mountPoint);
 
         } else {
-            final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
+            final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to "
+                    + "end with ";
             LOG.debug(errMsg + ControllerContext.MOUNT + " for " + identifier);
-            throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+            throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL,
+                    ErrorTag.INVALID_VALUE);
         }
 
         return operationsFromModulesToNormalizedContext(modules, mountPoint);
@@ -361,8 +377,8 @@ public class RestconfImpl implements RestconfService {
         final Set<Module> fakeModules = new HashSet<>();
         fakeModules.add(fakeModule);
         final SchemaContext fakeSchemaCtx = EffectiveSchemaContext.resolveSchemaContext(fakeModules);
-        final InstanceIdentifierContext<ContainerSchemaNode> instanceIdentifierContext = new InstanceIdentifierContext<>(
-                null, fakeContSchNode, mountPoint, fakeSchemaCtx);
+        final InstanceIdentifierContext<ContainerSchemaNode> instanceIdentifierContext =
+                new InstanceIdentifierContext<>(null, fakeContSchNode, mountPoint, fakeSchemaCtx);
         return new NormalizedNodeContext(instanceIdentifierContext, containerBuilder.build());
     }
 
@@ -414,12 +430,14 @@ public class RestconfImpl implements RestconfService {
     }
 
     @Override
-    public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+    public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
+            final UriInfo uriInfo) {
         final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
         final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace();
         final CheckedFuture<DOMRpcResult, DOMRpcException> response;
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
         final SchemaContext schemaContext;
+
         if (mountPoint != null) {
             final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
             if ( ! mountRpcServices.isPresent()) {
@@ -485,11 +503,14 @@ public class RestconfImpl implements RestconfService {
                     throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
                             ErrorTag.INVALID_VALUE);
                 } else if (cause instanceof DOMRpcImplementationNotAvailableException) {
-                    throw new RestconfDocumentedException(cause.getMessage(), ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
+                    throw new RestconfDocumentedException(cause.getMessage(), ErrorType.APPLICATION,
+                            ErrorTag.OPERATION_NOT_SUPPORTED);
                 }
-                throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",cause);
+                throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
+                        cause);
             } else {
-                throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",e);
+                throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
+                        e);
             }
         } catch (final CancellationException e) {
             final String errMsg = "The operation was cancelled while executing.";
@@ -508,7 +529,8 @@ public class RestconfImpl implements RestconfService {
         }
     }
 
-    private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
+    private CheckedFuture<DOMRpcResult, DOMRpcException>
+            invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
         final ContainerNode value = (ContainerNode) payload.getData();
         final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
         final Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(new NodeIdentifier(
@@ -523,16 +545,22 @@ public class RestconfImpl implements RestconfService {
 
         final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
         String streamName = (String) CREATE_DATA_SUBSCR;
+        NotificationOutputType outputType = null;
         if (!pathIdentifier.isEmpty()) {
             final String fullRestconfIdentifier = DATA_SUBSCR
                     + this.controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
 
-            LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME);
+            LogicalDatastoreType datastore =
+                    parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME);
             datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
 
             DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
             scope = scope == null ? DEFAULT_SCOPE : scope;
 
+            outputType = parseEnumTypeParameter(value, NotificationOutputType.class,
+                    OUTPUT_TYPE_PARAM_NAME);
+            outputType = outputType == null ? NotificationOutputType.XML : outputType;
+
             streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
                     + "/scope=" + scope);
         }
@@ -546,11 +574,12 @@ public class RestconfImpl implements RestconfService {
         final QName outputQname = QName.create(rpcQName, "output");
         final QName streamNameQname = QName.create(rpcQName, "stream-name");
 
-        final ContainerNode output = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
-                .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
+        final ContainerNode output =
+                ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
+                        .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
 
         if (!Notificator.existListenerFor(streamName)) {
-            Notificator.createListener(pathIdentifier, streamName);
+            Notificator.createListener(pathIdentifier, streamName, outputType);
         }
 
         final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
@@ -645,21 +674,48 @@ public class RestconfImpl implements RestconfService {
 
     @Override
     public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
+        boolean withDefa_used = false;
+        String withDefa = null;
+
+        for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            switch (entry.getKey()) {
+                case "with-defaults":
+                    if (!withDefa_used) {
+                        withDefa_used = true;
+                        withDefa = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("With-defaults parameter can be used only once.");
+                    }
+                    break;
+            }
+        }
+        boolean tagged = false;
+        if (withDefa_used) {
+            if (withDefa.equals("report-all-tagged")) {
+                tagged = true;
+                withDefa = null;
+            }
+            if (withDefa.equals("report-all")) {
+                withDefa = null;
+            }
+        }
+
         final InstanceIdentifierContext<?> iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
         final DOMMountPoint mountPoint = iiWithData.getMountPoint();
         NormalizedNode<?, ?> data = null;
         final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
         if (mountPoint != null) {
-            data = this.broker.readConfigurationData(mountPoint, normalizedII);
+            data = this.broker.readConfigurationData(mountPoint, normalizedII, withDefa);
         } else {
-            data = this.broker.readConfigurationData(normalizedII);
+            data = this.broker.readConfigurationData(normalizedII, withDefa);
         }
         if(data == null) {
             final String errMsg = "Request could not be completed because the relevant data model content does not exist ";
             LOG.debug(errMsg + identifier);
             throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
         }
-        return new NormalizedNodeContext(iiWithData, data, QueryParametersParser.parseWriterParameters(uriInfo));
+        return new NormalizedNodeContext(iiWithData, data,
+                QueryParametersParser.parseWriterParameters(uriInfo, tagged));
     }
 
     @Override
@@ -682,8 +738,46 @@ public class RestconfImpl implements RestconfService {
     }
 
     @Override
-    public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
+    public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload,
+            final UriInfo uriInfo) {
+        boolean insert_used = false;
+        boolean point_used = false;
+        String insert = null;
+        String point = null;
+
+        for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            switch (entry.getKey()) {
+                case "insert":
+                    if (!insert_used) {
+                        insert_used = true;
+                        insert = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("Insert parameter can be used only once.");
+                    }
+                    break;
+                case "point":
+                    if (!point_used) {
+                        point_used = true;
+                        point = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("Point parameter can be used only once.");
+                    }
+                    break;
+                default:
+                    throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
+            }
+            }
+
+        if (point_used && !insert_used) {
+            throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.");
+        }
+        if (point_used && (insert.equals("first") || insert.equals("last"))) {
+            throw new RestconfDocumentedException(
+                    "Point parameter can be used only with 'after' or 'before' values of Insert parameter.");
+        }
+
         Preconditions.checkNotNull(identifier);
+
         final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
 
         validateInput(iiWithData.getSchemaNode(), payload);
@@ -711,10 +805,11 @@ public class RestconfImpl implements RestconfService {
         while(true) {
             if (mountPoint != null) {
 
-                result = this.broker.commitMountPointDataPut(mountPoint, normalizedII, payload.getData());
+                result = this.broker.commitMountPointDataPut(mountPoint, normalizedII, payload.getData(), insert,
+                        point);
             } else {
                 result = this.broker.commitConfigurationDataPut(this.controllerContext.getGlobalSchema(), normalizedII,
-                        payload.getData());
+                        payload.getData(), insert, point);
             }
             final CountDownLatch waiter = new CountDownLatch(1);
             Futures.addCallback(result.getFutureOfPutData(), new FutureCallback<Void>() {
@@ -828,14 +923,16 @@ public class RestconfImpl implements RestconfService {
 
             if ( ! uriKeyValue.equals(dataKeyValue)) {
                 final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
-                        "' specified in the URI doesn't match the value '" + dataKeyValue + "' specified in the message body. ";
+                        "' specified in the URI doesn't match the value '" + dataKeyValue
+                        + "' specified in the message body. ";
                 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
             }
         }
     }
 
     @Override
-    public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+    public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload,
+            final UriInfo uriInfo) {
        return createConfigurationData(payload, uriInfo);
     }
 
@@ -875,25 +972,53 @@ public class RestconfImpl implements RestconfService {
         if (payload == null) {
             throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
         }
-
-        // FIXME: move this to parsing stage (we can have augmentation nodes here which do not have namespace)
-//        final URI payloadNS = payload.getData().getNodeType().getNamespace();
-//        if (payloadNS == null) {
-//            throw new RestconfDocumentedException(
-//                    "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
-//                    ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
-//        }
-
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
         final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
         final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
 
+        boolean insert_used = false;
+        boolean point_used = false;
+        String insert = null;
+        String point = null;
+
+        for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            switch (entry.getKey()) {
+                case "insert":
+                    if (!insert_used) {
+                        insert_used = true;
+                        insert = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("Insert parameter can be used only once.");
+                    }
+                    break;
+                case "point":
+                    if (!point_used) {
+                        point_used = true;
+                        point = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("Point parameter can be used only once.");
+                    }
+                    break;
+                default:
+                    throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
+            }
+        }
+
+        if (point_used && !insert_used) {
+            throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.");
+        }
+        if (point_used && (insert.equals("first") || insert.equals("last"))) {
+            throw new RestconfDocumentedException(
+                    "Point parameter can be used only with 'after' or 'before' values of Insert parameter.");
+        }
+
         CheckedFuture<Void, TransactionCommitFailedException> future;
         if (mountPoint != null) {
-            future = this.broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
+            future = this.broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData(), insert,
+                    point);
         } else {
             future = this.broker.commitConfigurationDataPost(this.controllerContext.getGlobalSchema(), normalizedII,
-                    payload.getData());
+                    payload.getData(), insert, point);
         }
 
         final CountDownLatch waiter = new CountDownLatch(1);
@@ -939,7 +1064,8 @@ public class RestconfImpl implements RestconfService {
         }
     }
 
-    private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
+    private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint,
+            final YangInstanceIdentifier normalizedII) {
         if(uriInfo == null) {
             // This is null if invoked internally
             return null;
@@ -970,18 +1096,19 @@ public class RestconfImpl implements RestconfService {
         }
 
         final CountDownLatch waiter = new CountDownLatch(1);
+        final ResultOperation result = new ResultOperation();
         Futures.addCallback(future, new FutureCallback<Void>() {
 
             @Override
             public void onSuccess(final Void result) {
-                handlerLoggerDelete(null);
+                LOG.trace("Successfuly delete data.");
                 waiter.countDown();
             }
 
             @Override
             public void onFailure(final Throwable t) {
                 waiter.countDown();
-                handlerLoggerDelete(t);
+                result.setFailed(t);
             }
 
         });
@@ -993,17 +1120,25 @@ public class RestconfImpl implements RestconfService {
             LOG.warn(msg);
             throw new RestconfDocumentedException(msg, e);
         }
-
+        if (result.failed() != null) {
+            final Throwable t = result.failed();
+            final String errMsg = "Error while deleting data";
+            LOG.info(errMsg, t);
+            throw new RestconfDocumentedException(errMsg, RestconfError.ErrorType.APPLICATION,
+                    RestconfError.ErrorTag.OPERATION_FAILED, t);
+        }
         return Response.status(Status.OK).build();
     }
 
-    protected void handlerLoggerDelete(final Throwable t) {
-        if (t != null) {
-            final String errMsg = "Error while deleting data";
-            LOG.info(errMsg, t);
-            throw new RestconfDocumentedException(errMsg, t);
-        } else {
-            LOG.trace("Successfuly delete data.");
+    private class ResultOperation {
+        private Throwable t = null;
+
+        public void setFailed(final Throwable t) {
+            this.t = t;
+        }
+
+        public Throwable failed() {
+            return this.t;
         }
     }
 
@@ -1017,17 +1152,118 @@ public class RestconfImpl implements RestconfService {
      * </ul>
      */
     @Override
-    public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
+    public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
+        boolean startTime_used = false;
+        boolean stopTime_used = false;
+        boolean filter_used = false;
+        Date start = null;
+        Date stop = null;
+        String filter = null;
+
+        for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            switch (entry.getKey()) {
+                case "start-time":
+                    if (!startTime_used) {
+                        startTime_used = true;
+                        start = parseDateFromQueryParam(entry);
+                    } else {
+                        throw new RestconfDocumentedException("Start-time parameter can be used only once.");
+                    }
+                    break;
+                case "stop-time":
+                    if (!stopTime_used) {
+                        stopTime_used = true;
+                        stop = parseDateFromQueryParam(entry);
+                    } else {
+                        throw new RestconfDocumentedException("Stop-time parameter can be used only once.");
+                    }
+                    break;
+                case "filter":
+                    if (!filter_used) {
+                        filter_used = true;
+                        filter = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("Filter parameter can be used only once.");
+                    }
+                    break;
+                default:
+                    throw new RestconfDocumentedException("Bad parameter used with notifications: " + entry.getKey());
+            }
+        }
+        if(!startTime_used && stopTime_used){
+            throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter.");
+        }
+        URI response = null;
         if (identifier.contains(DATA_SUBSCR)) {
-            return dataSubs(identifier, uriInfo);
+            response = dataSubs(identifier, uriInfo, start, stop, filter);
         } else if (identifier.contains(NOTIFICATION_STREAM)) {
-            return notifStream(identifier, uriInfo);
+            response = notifStream(identifier, uriInfo, start, stop, filter);
+        }
+
+        if(response != null){
+            // prepare node with value of location
+            final InstanceIdentifierContext<?> iid = prepareIIDSubsStreamOutput();
+            final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> builder = ImmutableLeafNodeBuilder
+                    .create().withValue(response.toString());
+            builder.withNodeIdentifier(
+                    NodeIdentifier.create(QName.create("subscribe:to:notification", "2016-10-28", "location")));
+
+            // prepare new header with location
+            final Map<String, Object> headers = new HashMap<>();
+            headers.put("Location", response);
+
+            return new NormalizedNodeContext(iid, builder.build(), headers);
         }
+
         final String msg = "Bad type of notification of sal-remote";
         LOG.warn(msg);
         throw new RestconfDocumentedException(msg);
     }
 
+    private Date parseDateFromQueryParam(final Entry<String, List<String>> entry) {
+        final DateAndTime event = new DateAndTime(entry.getValue().iterator().next());
+        String numOf_ms = "";
+        final String value = event.getValue();
+        if (value.contains(".")) {
+            numOf_ms = numOf_ms + ".";
+            final int lastChar = value.contains("Z") ? value.indexOf("Z") : (value.contains("+") ? value.indexOf("+")
+                    : (value.contains("-") ? value.indexOf("-") : value.length()));
+            for (int i = 0; i < (lastChar - value.indexOf(".") - 1); i++) {
+                numOf_ms = numOf_ms + "S";
+            }
+        }
+        String zone = "";
+        if (!value.contains("Z")) {
+            zone = zone + "XXX";
+        }
+        final DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" + numOf_ms + zone);
+
+        try {
+            return dateFormatter.parse(value.contains("Z") ? value.replace('T', ' ').substring(0, value.indexOf("Z"))
+                    : value.replace('T', ' '));
+        } catch (final ParseException e) {
+            throw new RestconfDocumentedException("Cannot parse of value in date: " + value + e);
+        }
+    }
+
+    /**
+     * @return {@link InstanceIdentifierContext} of location leaf for
+     *         notification
+     */
+    private InstanceIdentifierContext<?> prepareIIDSubsStreamOutput() {
+        final QName qnameBase = QName.create("subscribe:to:notification", "2016-10-28", "notifi");
+        final SchemaContext schemaCtx = ControllerContext.getInstance().getGlobalSchema();
+        final DataSchemaNode location = ((ContainerSchemaNode) schemaCtx
+                .findModuleByNamespaceAndRevision(qnameBase.getNamespace(), qnameBase.getRevision())
+                .getDataChildByName(qnameBase)).getDataChildByName(QName.create(qnameBase, "location"));
+        final List<PathArgument> path = new ArrayList<>();
+        path.add(NodeIdentifier.create(qnameBase));
+        path.add(NodeIdentifier.create(QName.create(qnameBase, "location")));
+
+        return new InstanceIdentifierContext<SchemaNode>(YangInstanceIdentifier.create(path), location, null,
+                schemaCtx);
+    }
+
     /**
      * Register notification listener by stream name
      *
@@ -1035,9 +1271,16 @@ public class RestconfImpl implements RestconfService {
      *            - stream name
      * @param uriInfo
      *            - uriInfo
-     * @return {@link Response}
+     * @param stop
+     *            - stop-time of getting notification
+     * @param start
+     *            - start-time of getting notification
+     * @param filter
+     *            - indicate wh ich subset of allpossible events are of interest
+     * @return {@link URI} of location
      */
-    private Response notifStream(final String identifier, final UriInfo uriInfo) {
+    private URI notifStream(final String identifier, final UriInfo uriInfo, final Date start, final Date stop,
+            final String filter) {
         final String streamName = Notificator.createStreamNameFromUri(identifier);
         if (Strings.isNullOrEmpty(streamName)) {
             throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
@@ -1050,6 +1293,7 @@ public class RestconfImpl implements RestconfService {
 
         for (final NotificationListenerAdapter listener : listeners) {
             this.broker.registerToListenNotification(listener);
+            listener.setQueryParams(start, stop, filter);
         }
 
         final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
@@ -1063,7 +1307,7 @@ public class RestconfImpl implements RestconfService {
         final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
         final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
 
-        return Response.status(Status.OK).location(uriToWebsocketServer).build();
+        return uriToWebsocketServer;
     }
 
     /**
@@ -1073,9 +1317,16 @@ public class RestconfImpl implements RestconfService {
      *            - stream name
      * @param uriInfo
      *            - uri info
-     * @return {@link Response}
+     * @param stop
+     *            - start-time of getting notification
+     * @param start
+     *            - stop-time of getting notification
+     * @param filter
+     *            - indicate which subset of all possible events are of interest
+     * @return {@link URI} of location
      */
-    private Response dataSubs(final String identifier, final UriInfo uriInfo) {
+    private URI dataSubs(final String identifier, final UriInfo uriInfo, final Date start, final Date stop,
+            final String filter) {
         final String streamName = Notificator.createStreamNameFromUri(identifier);
         if (Strings.isNullOrEmpty(streamName)) {
             throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
@@ -1085,6 +1336,7 @@ public class RestconfImpl implements RestconfService {
         if (listener == null) {
             throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
         }
+        listener.setQueryParams(start, stop, filter);
 
         final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
         final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
@@ -1112,11 +1364,12 @@ public class RestconfImpl implements RestconfService {
         final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
         final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
 
-        return Response.status(Status.OK).location(uriToWebsocketServer).build();
+        return uriToWebsocketServer;
     }
 
     @Override
-    public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext context, final UriInfo uriInfo) {
+    public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext context,
+            final UriInfo uriInfo) {
         if (context == null) {
             throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
         }
@@ -1157,7 +1410,8 @@ public class RestconfImpl implements RestconfService {
             return null;
         }
         final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode =
-                ((AugmentationNode) augNode.get()).getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName)));
+                ((AugmentationNode) augNode.get())
+                        .getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName)));
         if (!enumNode.isPresent()) {
             return null;
         }
index 75af8fe32042f9ac49a7567ac8adcf47315fd959..beb067c1036995ec3d4d1e6d3cc79a1799af66a2 100644 (file)
@@ -9,7 +9,6 @@ package org.opendaylight.netconf.sal.restconf.impl;
 
 import java.math.BigInteger;
 import java.util.concurrent.atomic.AtomicLong;
-import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
@@ -34,7 +33,8 @@ public class StatisticsRestconfServiceWrapper implements RestconfService {
     AtomicLong failurePut = new AtomicLong();
     AtomicLong failureDelete = new AtomicLong();
 
-    private static final StatisticsRestconfServiceWrapper INSTANCE = new StatisticsRestconfServiceWrapper(RestconfImpl.getInstance());
+    private static final StatisticsRestconfServiceWrapper INSTANCE =
+            new StatisticsRestconfServiceWrapper(RestconfImpl.getInstance());
 
     final RestconfService delegate;
 
@@ -48,60 +48,61 @@ public class StatisticsRestconfServiceWrapper implements RestconfService {
 
     @Override
     public Object getRoot() {
-        return delegate.getRoot();
+        return this.delegate.getRoot();
     }
 
     @Override
     public NormalizedNodeContext getModules(final UriInfo uriInfo) {
-        return delegate.getModules(uriInfo);
+        return this.delegate.getModules(uriInfo);
     }
 
     @Override
     public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
-        return delegate.getModules(identifier, uriInfo);
+        return this.delegate.getModules(identifier, uriInfo);
     }
 
     @Override
     public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
-        return delegate.getModule(identifier, uriInfo);
+        return this.delegate.getModule(identifier, uriInfo);
     }
 
     @Override
     public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
-        return delegate.getOperations(uriInfo);
+        return this.delegate.getOperations(uriInfo);
     }
 
     @Override
     public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
-        return delegate.getOperations(identifier, uriInfo);
+        return this.delegate.getOperations(identifier, uriInfo);
     }
 
     @Override
-    public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
-        rpc.incrementAndGet();
-        return delegate.invokeRpc(identifier, payload, uriInfo);
+    public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
+            final UriInfo uriInfo) {
+        this.rpc.incrementAndGet();
+        return this.delegate.invokeRpc(identifier, payload, uriInfo);
     }
 
     @Override
     public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
-        rpc.incrementAndGet();
-        return delegate.invokeRpc(identifier, noPayload, uriInfo);
+        this.rpc.incrementAndGet();
+        return this.delegate.invokeRpc(identifier, noPayload, uriInfo);
     }
 
     @Override
     public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
-        configGet.incrementAndGet();
+        this.configGet.incrementAndGet();
         NormalizedNodeContext normalizedNodeContext = null;
         try {
-            normalizedNodeContext = delegate.readConfigurationData(identifier, uriInfo);
+            normalizedNodeContext = this.delegate.readConfigurationData(identifier, uriInfo);
             if (normalizedNodeContext.getData() != null) {
-                successGetConfig.incrementAndGet();
+                this.successGetConfig.incrementAndGet();
             }
             else {
-                failureGetConfig.incrementAndGet();
+                this.failureGetConfig.incrementAndGet();
             }
-        } catch (Exception e) {
-            failureGetConfig.incrementAndGet();
+        } catch (final Exception e) {
+            this.failureGetConfig.incrementAndGet();
             throw e;
         }
         return normalizedNodeContext;
@@ -109,56 +110,58 @@ public class StatisticsRestconfServiceWrapper implements RestconfService {
 
     @Override
     public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) {
-        operationalGet.incrementAndGet();
+        this.operationalGet.incrementAndGet();
         NormalizedNodeContext normalizedNodeContext = null;
         try {
-            normalizedNodeContext = delegate.readOperationalData(identifier, uriInfo);
+            normalizedNodeContext = this.delegate.readOperationalData(identifier, uriInfo);
             if (normalizedNodeContext.getData() != null) {
-                successGetOperational.incrementAndGet();
+                this.successGetOperational.incrementAndGet();
             }
             else {
-                failureGetOperational.incrementAndGet();
+                this.failureGetOperational.incrementAndGet();
             }
-        } catch (Exception e) {
-            failureGetOperational.incrementAndGet();
+        } catch (final Exception e) {
+            this.failureGetOperational.incrementAndGet();
             throw e;
         }
         return normalizedNodeContext;
     }
 
     @Override
-    public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
-        configPut.incrementAndGet();
+    public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload,
+            final UriInfo uriInfo) {
+        this.configPut.incrementAndGet();
         Response response = null;
         try {
-            response = delegate.updateConfigurationData(identifier, payload);
+            response = this.delegate.updateConfigurationData(identifier, payload, uriInfo);
             if (response.getStatus() == Status.OK.getStatusCode()) {
-                successPut.incrementAndGet();
+                this.successPut.incrementAndGet();
             }
             else {
-                failurePut.incrementAndGet();
+                this.failurePut.incrementAndGet();
             }
-        } catch (Exception e) {
-            failurePut.incrementAndGet();
+        } catch (final Exception e) {
+            this.failurePut.incrementAndGet();
             throw e;
         }
         return response;
     }
 
     @Override
-    public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
-        configPost.incrementAndGet();
+    public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload,
+            final UriInfo uriInfo) {
+        this.configPost.incrementAndGet();
         Response response = null;
         try {
-            response = delegate.createConfigurationData(identifier, payload, uriInfo);
+            response = this.delegate.createConfigurationData(identifier, payload, uriInfo);
             if (response.getStatus() == Status.OK.getStatusCode()) {
-                successPost.incrementAndGet();
+                this.successPost.incrementAndGet();
             }
             else {
-                failurePost.incrementAndGet();
+                this.failurePost.incrementAndGet();
             }
-        } catch (Exception e) {
-            failurePost.incrementAndGet();
+        } catch (final Exception e) {
+            this.failurePost.incrementAndGet();
             throw e;
         }
         return response;
@@ -166,18 +169,18 @@ public class StatisticsRestconfServiceWrapper implements RestconfService {
 
     @Override
     public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
-        configPost.incrementAndGet();
+        this.configPost.incrementAndGet();
         Response response = null;
         try {
-            response = delegate.createConfigurationData(payload, uriInfo);
+            response = this.delegate.createConfigurationData(payload, uriInfo);
             if (response.getStatus() == Status.OK.getStatusCode()) {
-                successPost.incrementAndGet();
+                this.successPost.incrementAndGet();
             }
             else {
-                failurePost.incrementAndGet();
+                this.failurePost.incrementAndGet();
             }
-        }catch (Exception e) {
-            failurePost.incrementAndGet();
+        }catch (final Exception e) {
+            this.failurePost.incrementAndGet();
             throw e;
         }
         return response;
@@ -185,105 +188,105 @@ public class StatisticsRestconfServiceWrapper implements RestconfService {
 
     @Override
     public Response deleteConfigurationData(final String identifier) {
-        configDelete.incrementAndGet();
+        this.configDelete.incrementAndGet();
         Response response = null;
         try {
-            response = delegate.deleteConfigurationData(identifier);
+            response = this.delegate.deleteConfigurationData(identifier);
             if (response.getStatus() == Status.OK.getStatusCode()) {
-                successDelete.incrementAndGet();
+                this.successDelete.incrementAndGet();
             }
             else {
-                failureDelete.incrementAndGet();
+                this.failureDelete.incrementAndGet();
             }
-        } catch (Exception e) {
-            failureDelete.incrementAndGet();
+        } catch (final Exception e) {
+            this.failureDelete.incrementAndGet();
             throw e;
         }
         return response;
     }
 
     @Override
-    public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
-        return delegate.subscribeToStream(identifier, uriInfo);
+    public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
+        return this.delegate.subscribeToStream(identifier, uriInfo);
     }
 
     @Override
     public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
-        return delegate.getAvailableStreams(uriInfo);
+        return this.delegate.getAvailableStreams(uriInfo);
     }
 
     @Override
-    public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext payload, final UriInfo
-            uriInfo) {
-        return delegate.patchConfigurationData(identifier, payload, uriInfo);
+    public PATCHStatusContext patchConfigurationData(final String identifier, final PATCHContext payload,
+            final UriInfo uriInfo) {
+        return this.delegate.patchConfigurationData(identifier, payload, uriInfo);
     }
 
     @Override
     public PATCHStatusContext patchConfigurationData(final PATCHContext payload, final UriInfo uriInfo) {
-        return delegate.patchConfigurationData(payload, uriInfo);
+        return this.delegate.patchConfigurationData(payload, uriInfo);
     }
 
     public BigInteger getConfigDelete() {
-        return BigInteger.valueOf(configDelete.get());
+        return BigInteger.valueOf(this.configDelete.get());
     }
 
     public BigInteger getConfigGet() {
-        return BigInteger.valueOf(configGet.get());
+        return BigInteger.valueOf(this.configGet.get());
     }
 
     public BigInteger getConfigPost() {
-        return BigInteger.valueOf(configPost.get());
+        return BigInteger.valueOf(this.configPost.get());
     }
 
     public BigInteger getConfigPut() {
-        return BigInteger.valueOf(configPut.get());
+        return BigInteger.valueOf(this.configPut.get());
     }
 
     public BigInteger getOperationalGet() {
-        return BigInteger.valueOf(operationalGet.get());
+        return BigInteger.valueOf(this.operationalGet.get());
     }
 
     public BigInteger getRpc() {
-        return BigInteger.valueOf(rpc.get());
+        return BigInteger.valueOf(this.rpc.get());
     }
 
     public BigInteger getSuccessGetConfig() {
-        return BigInteger.valueOf(successGetConfig.get());
+        return BigInteger.valueOf(this.successGetConfig.get());
     }
 
     public BigInteger getSuccessGetOperational() {
-        return BigInteger.valueOf(successGetOperational.get());
+        return BigInteger.valueOf(this.successGetOperational.get());
     }
 
     public BigInteger getSuccessPost() {
-        return BigInteger.valueOf(successPost.get());
+        return BigInteger.valueOf(this.successPost.get());
     }
 
     public BigInteger getSuccessPut() {
-        return BigInteger.valueOf(successPut.get());
+        return BigInteger.valueOf(this.successPut.get());
     }
 
     public BigInteger getSuccessDelete() {
-        return BigInteger.valueOf(successDelete.get());
+        return BigInteger.valueOf(this.successDelete.get());
     }
 
     public BigInteger getFailureGetConfig() {
-        return BigInteger.valueOf(failureGetConfig.get());
+        return BigInteger.valueOf(this.failureGetConfig.get());
     }
 
     public BigInteger getFailureGetOperational() {
-        return BigInteger.valueOf(failureGetOperational.get());
+        return BigInteger.valueOf(this.failureGetOperational.get());
     }
 
     public BigInteger getFailurePost() {
-        return BigInteger.valueOf(failurePost.get());
+        return BigInteger.valueOf(this.failurePost.get());
     }
 
     public BigInteger getFailurePut() {
-        return BigInteger.valueOf(failurePut.get());
+        return BigInteger.valueOf(this.failurePut.get());
     }
 
     public BigInteger getFailureDelete() {
-        return BigInteger.valueOf(failureDelete.get());
+        return BigInteger.valueOf(this.failureDelete.get());
     }
 }
\ No newline at end of file
index 11227d0a042e7acbd66332185a83e8598df466fb..9d576c466f0c5f1a6a19c22e06f0804195b0046d 100644 (file)
@@ -8,43 +8,67 @@
 
 package org.opendaylight.netconf.sal.restconf.impl;
 
-import com.google.common.base.Optional;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
 
 public class WriterParameters {
-    private final Optional<Integer> depth;
+    private final String content;
+    private final Integer depth;
+    private final List<Set<QName>> fields;
     private final boolean prettyPrint;
+    private final boolean tagged;
 
     private WriterParameters(final WriterParametersBuilder builder) {
-        this.prettyPrint = builder.prettyPrint;
+        this.content = builder.content;
         this.depth = builder.depth;
+        this.fields = builder.fields;
+        this.prettyPrint = builder.prettyPrint;
+        this.tagged = builder.tagged;
+    }
+
+    public String getContent() {
+        return this.content;
     }
 
-    public Optional<Integer> getDepth() {
-        return depth;
+    public Integer getDepth() {
+        return this.depth;
+    }
+
+    public List<Set<QName>> getFields() {
+        return this.fields;
     }
 
     public boolean isPrettyPrint() {
-        return prettyPrint;
+        return this.prettyPrint;
+    }
+
+    public boolean isTagged() {
+        return this.tagged;
     }
 
     public static class WriterParametersBuilder {
-        private Optional<Integer> depth = Optional.absent();
+        private String content;
+        private Integer depth;
+        private List<Set<QName>> fields;
         private boolean prettyPrint;
+        private boolean tagged;
 
-        public WriterParametersBuilder() {
-        }
+        public WriterParametersBuilder() {}
 
-        public Optional<Integer> getDepth() {
-            return depth;
+        public WriterParametersBuilder setContent(final String content) {
+            this.content = content;
+            return this;
         }
 
         public WriterParametersBuilder setDepth(final int depth) {
-            this.depth = Optional.of(depth);
+            this.depth = depth;
             return this;
         }
 
-        public boolean isPrettyPrint() {
-            return prettyPrint;
+        public WriterParametersBuilder setFields(final List<Set<QName>> fields) {
+            this.fields = fields;
+            return this;
         }
 
         public WriterParametersBuilder setPrettyPrint(final boolean prettyPrint) {
@@ -55,6 +79,9 @@ public class WriterParameters {
         public WriterParameters build() {
             return new WriterParameters(this);
         }
+
+        public void setTagged(final boolean tagged) {
+            this.tagged = tagged;
+        }
     }
 }
-
index a8651c21638a93477a4f4ad933e141306ecdaf2f..ccc30a5616c82b7c082b17048afdc8c493557ec3 100644 (file)
@@ -17,12 +17,12 @@ import io.netty.util.internal.ConcurrentSet;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
+import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
 import java.util.Collection;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Random;
@@ -42,9 +42,16 @@ import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+import org.json.JSONObject;
+import org.json.XML;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -59,6 +66,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWrit
 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
@@ -66,6 +74,7 @@ import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
 
 /**
  * {@link ListenerAdapter} is responsible to track events, which occurred by changing data in data source.
@@ -85,16 +94,25 @@ public class ListenerAdapter implements DOMDataChangeListener {
     private Set<Channel> subscribers = new ConcurrentSet<>();
     private final EventBus eventBus;
     private final EventBusChangeRecorder eventBusChangeRecorder;
+    private final NotificationOutputType outputType;
+    private Date start = null;
+    private Date stop = null;
+    private String filter = null;
 
     /**
-     * Creates new {@link ListenerAdapter} listener specified by path and stream name.
+     * Creates new {@link ListenerAdapter} listener specified by path and stream
+     * name.
      *
      * @param path
      *            Path to data in data store.
      * @param streamName
      *            The name of the stream.
+     * @param outputType
+     *            - type of output on notification (JSON, XML)
      */
-    ListenerAdapter(final YangInstanceIdentifier path, final String streamName) {
+    ListenerAdapter(final YangInstanceIdentifier path, final String streamName,
+            final NotificationOutputType outputType) {
+        this.outputType = outputType;
         Preconditions.checkNotNull(path);
         Preconditions.checkArgument((streamName != null) && !streamName.isEmpty());
         this.path = path;
@@ -106,13 +124,80 @@ public class ListenerAdapter implements DOMDataChangeListener {
 
     @Override
     public void onDataChanged(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
-        if (!change.getCreatedData().isEmpty() || !change.getUpdatedData().isEmpty()
-                || !change.getRemovedPaths().isEmpty()) {
-            final String xml = prepareXmlFrom(change);
+        final Date now = new Date();
+        if (this.stop != null) {
+            if ((this.start.compareTo(now) < 0) && (this.stop.compareTo(now) > 0)) {
+                checkFilter(change);
+            }
+            if (this.stop.compareTo(now) < 0) {
+                try {
+                    this.close();
+                } catch (final Exception e) {
+                    throw new RestconfDocumentedException("Problem with unregister listener." + e);
+                }
+            }
+        } else if (this.start != null) {
+            if (this.start.compareTo(now) < 0) {
+                this.start = null;
+                checkFilter(change);
+            }
+        } else {
+            checkFilter(change);
+        }
+    }
+
+    /**
+     * Check if is filter used and then prepare and post data do client
+     *
+     * @param change
+     *            - data of notification
+     */
+    private void checkFilter(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+        final String xml = prepareXmlFrom(change);
+        if (this.filter == null) {
+            prepareAndPostData(xml);
+        } else {
+            try {
+                if (parseFilterParam(xml)) {
+                    prepareAndPostData(xml);
+                }
+            } catch (final Exception e) {
+                throw new RestconfDocumentedException("Problem while parsing filter.", e);
+            }
+        }
+    }
+
+    /**
+     * Parse and evaluate filter value by xml
+     *
+     * @param xml
+     *            - notification data in xml
+     * @return true or false - depends on filter expression and data of
+     *         notifiaction
+     * @throws Exception
+     */
+    private boolean parseFilterParam(final String xml) throws Exception {
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        final DocumentBuilder builder = factory.newDocumentBuilder();
+        final Document docOfXml = builder.parse(new InputSource(new StringReader(xml)));
+        final XPath xPath = XPathFactory.newInstance().newXPath();
+        return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN);
+    }
+
+    /**
+     * Prepare data of notification and data to client
+     *
+     * @param xml
+     */
+    private void prepareAndPostData(final String xml) {
             final Event event = new Event(EventType.NOTIFY);
-            event.setData(xml);
+            if (this.outputType.equals(NotificationOutputType.JSON)) {
+                final JSONObject jsonObject = XML.toJSONObject(xml);
+                event.setData(jsonObject.toString());
+            } else {
+                event.setData(xml);
+            }
             this.eventBus.post(event);
-        }
     }
 
     /**
@@ -230,6 +315,7 @@ public class ListenerAdapter implements DOMDataChangeListener {
         final Document doc = createDocument();
         final Element notificationElement = doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
                 "notification");
+
         doc.appendChild(notificationElement);
 
         final Element eventTimeElement = doc.createElement("eventTime");
@@ -238,6 +324,7 @@ public class ListenerAdapter implements DOMDataChangeListener {
 
         final Element dataChangedNotificationEventElement = doc.createElementNS(
                 "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "data-changed-notification");
+
         addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, change,
                 schemaContext, dataContextTree);
         notificationElement.appendChild(dataChangedNotificationEventElement);
@@ -250,7 +337,8 @@ public class ListenerAdapter implements DOMDataChangeListener {
             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
             transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
             transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
-            transformer.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(out, StandardCharsets.UTF_8)));
+            transformer.transform(new DOMSource(doc),
+                    new StreamResult(new OutputStreamWriter(out, StandardCharsets.UTF_8)));
             final byte[] charData = out.toByteArray();
             return new String(charData, "UTF-8");
         } catch (TransformerException | UnsupportedEncodingException e) {
@@ -324,8 +412,8 @@ public class ListenerAdapter implements DOMDataChangeListener {
      * @param operation
      *            {@link Operation}
      */
-    private void addValuesFromDataToElement(final Document doc, final Set<YangInstanceIdentifier> data, final Element element,
-            final Operation operation) {
+    private void addValuesFromDataToElement(final Document doc, final Set<YangInstanceIdentifier> data,
+            final Element element, final Operation operation) {
         if ((data == null) || data.isEmpty()) {
             return;
         }
@@ -363,7 +451,8 @@ public class ListenerAdapter implements DOMDataChangeListener {
      *            {@link Operation}
      * @return {@link Node} node represented by changed event element.
      */
-    private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier path, final Operation operation) {
+    private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier path,
+            final Operation operation) {
         final Element dataChangeEventElement = doc.createElement("data-change-event");
         final Element pathElement = doc.createElement("path");
         addPathAsValueToElement(path, pathElement);
@@ -455,24 +544,21 @@ public class ListenerAdapter implements DOMDataChangeListener {
      *            {@link Element}
      */
     private void addPathAsValueToElement(final YangInstanceIdentifier path, final Element element) {
-        // Map< key = namespace, value = prefix>
-        final Map<String, String> prefixes = new HashMap<>();
         final YangInstanceIdentifier normalizedPath = ControllerContext.getInstance().toXpathRepresentation(path);
         final StringBuilder textContent = new StringBuilder();
 
-        // FIXME: BUG-1281: this is duplicated code from yangtools (BUG-1275)
         for (final PathArgument pathArgument : normalizedPath.getPathArguments()) {
             if (pathArgument instanceof YangInstanceIdentifier.AugmentationIdentifier) {
                 continue;
             }
             textContent.append("/");
-            writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), prefixes);
+            writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType());
             if (pathArgument instanceof NodeIdentifierWithPredicates) {
                 final Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
                 for (final QName keyValue : predicates.keySet()) {
                     final String predicateValue = String.valueOf(predicates.get(keyValue));
                     textContent.append("[");
-                    writeIdentifierWithNamespacePrefix(element, textContent, keyValue, prefixes);
+                    writeIdentifierWithNamespacePrefix(element, textContent, keyValue);
                     textContent.append("='");
                     textContent.append(predicateValue);
                     textContent.append("'");
@@ -497,21 +583,13 @@ public class ListenerAdapter implements DOMDataChangeListener {
      *            StringBuilder
      * @param qName
      *            QName
-     * @param prefixes
-     *            Map of namespaces and prefixes.
      */
     private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent,
-            final QName qName, final Map<String, String> prefixes) {
-        final String namespace = qName.getNamespace().toString();
-        String prefix = prefixes.get(namespace);
-        if (prefix == null) {
-            prefix = generateNewPrefix(prefixes.values());
-        }
-
-        element.setAttribute("xmlns:" + prefix, namespace);
-        textContent.append(prefix);
-        prefixes.put(namespace, prefix);
+            final QName qName) {
+        final Module module = ControllerContext.getInstance().getGlobalSchema()
+                .findModuleByNamespaceAndRevision(qName.getNamespace(), qName.getRevision());
 
+        textContent.append(module.getName());
         textContent.append(":");
         textContent.append(qName.getLocalName());
     }
@@ -650,4 +728,20 @@ public class ListenerAdapter implements DOMDataChangeListener {
         }
     }
 
+    /**
+     * Set query parameters for listener
+     *
+     * @param start
+     *            - start-time of getting notification
+     * @param stop
+     *            - stop-time of getting notification
+     * @param filter
+     *            - indicate which subset of all possible events are of interest
+     */
+    public void setQueryParams(final Date start, final Date stop, final String filter) {
+        this.start = start;
+        this.stop = stop;
+        this.filter = filter;
+    }
+
 }
index 093f529acf8c3eb6880c4e9870f96a9dd4df8a7c..608ddcbca1f8d12e572536b4fba4f2f5b1c4a4a6 100644 (file)
@@ -18,11 +18,16 @@ import io.netty.util.internal.ConcurrentSet;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
+import java.io.Writer;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Set;
 import java.util.concurrent.Executors;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
@@ -33,11 +38,14 @@ import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
 import org.json.JSONObject;
-import org.json.XML;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -46,6 +54,9 @@ import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -55,6 +66,7 @@ import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
 
 /**
  * {@link NotificationListenerAdapter} is responsible to track events on
@@ -67,13 +79,19 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
     private static final TransformerFactory FACTORY = TransformerFactory.newInstance();
 
     private final String streamName;
-    private ListenerRegistration<DOMNotificationListener> registration;
-    private Set<Channel> subscribers = new ConcurrentSet<>();
     private final EventBus eventBus;
     private final EventBusChangeRecorder eventBusChangeRecorder;
 
     private final SchemaPath path;
     private final String outputType;
+    private Date start = null;
+    private Date stop = null;
+    private String filter;
+
+    private SchemaContext schemaContext;
+    private DOMNotification notification;
+    private ListenerRegistration<DOMNotificationListener> registration;
+    private Set<Channel> subscribers = new ConcurrentSet<>();
 
     /**
      * Set path of listener and stream name, register event bus.
@@ -98,17 +116,108 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
 
     @Override
     public void onNotification(final DOMNotification notification) {
-        final String xml = prepareXmlFrom(notification);
+        this.schemaContext = ControllerContext.getInstance().getGlobalSchema();
+        this.notification = notification;
+        final Date now = new Date();
+        if (this.stop != null) {
+            if ((this.start.compareTo(now) < 0) && (this.stop.compareTo(now) > 0)) {
+                checkFilter();
+            }
+            if (this.stop.compareTo(now) < 0) {
+                try {
+                    this.close();
+                } catch (final Exception e) {
+                    throw new RestconfDocumentedException("Problem with unregister listener." + e);
+                }
+            }
+        } else if (this.start != null) {
+            if (this.start.compareTo(now) < 0) {
+                this.start = null;
+                checkFilter();
+            }
+        } else {
+            checkFilter();
+        }
+    }
+
+    /**
+     * Check if is filter used and then prepare and post data do client
+     *
+     */
+    private void checkFilter() {
+        final String xml = prepareXml();
+        if (this.filter == null) {
+            prepareAndPostData(xml);
+        } else {
+            try {
+                if (parseFilterParam(xml)) {
+                    prepareAndPostData(xml);
+                }
+            } catch (final Exception e) {
+                throw new RestconfDocumentedException("Problem while parsing filter.", e);
+            }
+        }
+    }
+
+    /**
+     * Parse and evaluate filter value by xml
+     *
+     * @param xml
+     *            - notification data in xml
+     * @return true or false - depends on filter expression and data of
+     *         notifiaction
+     * @throws Exception
+     */
+    private boolean parseFilterParam(final String xml) throws Exception {
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        final DocumentBuilder builder = factory.newDocumentBuilder();
+        final Document docOfXml = builder.parse(new InputSource(new StringReader(xml)));
+        final XPath xPath = XPathFactory.newInstance().newXPath();
+        return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN);
+    }
+
+    /**
+     * Prepare data of notification and data to client
+     *
+     * @param xml
+     */
+    private void prepareAndPostData(final String xml) {
         final Event event = new Event(EventType.NOTIFY);
         if (this.outputType.equals("JSON")) {
-            final JSONObject jsonObject = XML.toJSONObject(xml);
-            event.setData(jsonObject.toString());
+            event.setData(prepareJson());
         } else {
             event.setData(xml);
         }
         this.eventBus.post(event);
     }
 
+    /**
+     * Prepare json from notification data
+     *
+     * @return json as {@link String}
+     */
+    private String prepareJson() {
+        final JSONObject json = new JSONObject();
+        json.put("ietf-restconf:notification",
+                new JSONObject(writeBodyToString()).put("event-time", ListenerAdapter.toRFC3339(new Date())));
+        return json.toString();
+    }
+
+    private String writeBodyToString() {
+        final Writer writer = new StringWriter();
+        final NormalizedNodeStreamWriter jsonStream =
+                JSONNormalizedNodeStreamWriter.createExclusiveWriter(JSONCodecFactory.create(this.schemaContext),
+                        this.notification.getType(), null, JsonWriterFactory.createJsonWriter(writer));
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+        try {
+            nodeWriter.write(this.notification.getBody());
+            nodeWriter.close();
+        } catch (final IOException e) {
+            throw new RestconfDocumentedException("Problem while writing body of notification to JSON. ", e);
+        }
+        return writer.toString();
+    }
+
     /**
      * Checks if exists at least one {@link Channel} subscriber.
      *
@@ -197,8 +306,7 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
         this.eventBus.post(event);
     }
 
-    private String prepareXmlFrom(final DOMNotification notification) {
-        final SchemaContext schemaContext = ControllerContext.getInstance().getGlobalSchema();
+    private String prepareXml() {
         final Document doc = ListenerAdapter.createDocument();
         final Element notificationElement =
                 doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0",
@@ -208,10 +316,10 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
         final Element eventTimeElement = doc.createElement("eventTime");
         eventTimeElement.setTextContent(ListenerAdapter.toRFC3339(new Date()));
         notificationElement.appendChild(eventTimeElement);
-        final String notificationNamespace = notification.getType().getLastComponent().getNamespace().toString();
+
         final Element notificationEventElement = doc.createElementNS(
-                notificationNamespace, "event");
-        addValuesToNotificationEventElement(doc, notificationEventElement, notification, schemaContext);
+                "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "create-notification-stream");
+        addValuesToNotificationEventElement(doc, notificationEventElement, this.notification, this.schemaContext);
         notificationElement.appendChild(notificationEventElement);
 
         try {
@@ -244,7 +352,9 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
             final DOMResult domResult = writeNormalizedNode(body,
                     YangInstanceIdentifier.create(body.getIdentifier()), schemaContext);
             final Node result = doc.importNode(domResult.getNode().getFirstChild(), true);
-            element.appendChild(result);
+            final Element dataElement = doc.createElement("notification");
+            dataElement.appendChild(result);
+            element.appendChild(dataElement);
         } catch (final IOException e) {
             LOG.error("Error in writer ", e);
         } catch (final XMLStreamException e) {
@@ -386,4 +496,20 @@ public class NotificationListenerAdapter implements DOMNotificationListener {
     private enum EventType {
         REGISTER, DEREGISTER, NOTIFY
     }
+
+    /**
+     * Set query parameters for listener
+     *
+     * @param start
+     *            - start-time of getting notification
+     * @param stop
+     *            - stop-time of getting notification
+     * @param filter
+     *            - indicate which subset of all possible events are of interest
+     */
+    public void setQueryParams(final Date start, final Date stop, final String filter) {
+        this.start = start;
+        this.stop = stop;
+        this.filter = filter;
+    }
 }
index 0bd38652b766634f4b28088a4210d0c005beb2e7..7c809ad77fe74d49604c1d8c6b9e41c814aa8d42 100644 (file)
@@ -14,7 +14,9 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -25,8 +27,9 @@ import org.slf4j.LoggerFactory;
  */
 public class Notificator {
 
-    private static Map<String, ListenerAdapter> listenersByStreamName = new ConcurrentHashMap<>();
-    private static Map<String, List<NotificationListenerAdapter>> notificationListenersByStreamName = new ConcurrentHashMap<>();
+    private static Map<String, ListenerAdapter> dataChangeListener = new ConcurrentHashMap<>();
+    private static Map<String, List<NotificationListenerAdapter>> notificationListenersByStreamName =
+            new ConcurrentHashMap<>();
 
     private static final Logger LOG = LoggerFactory.getLogger(Notificator.class);
     private static final Lock lock = new ReentrantLock();
@@ -38,7 +41,7 @@ public class Notificator {
      * Returns list of all stream names
      */
     public static Set<String> getStreamNames() {
-        return listenersByStreamName.keySet();
+        return dataChangeListener.keySet();
     }
 
     /**
@@ -49,7 +52,7 @@ public class Notificator {
      * @return {@link ListenerAdapter} specified by stream name.
      */
     public static ListenerAdapter getListenerFor(final String streamName) {
-        return listenersByStreamName.get(streamName);
+        return dataChangeListener.get(streamName);
     }
 
     /**
@@ -59,23 +62,28 @@ public class Notificator {
      * @return True if the listener exist, false otherwise.
      */
     public static boolean existListenerFor(final String streamName) {
-        return listenersByStreamName.containsKey(streamName);
+        return dataChangeListener.containsKey(streamName);
     }
 
     /**
-     * Creates new {@link ListenerAdapter} listener from {@link YangInstanceIdentifier} path and stream name.
+     * Creates new {@link ListenerAdapter} listener from
+     * {@link YangInstanceIdentifier} path and stream name.
      *
      * @param path
      *            Path to data in data repository.
      * @param streamName
      *            The name of the stream.
-     * @return New {@link ListenerAdapter} listener from {@link YangInstanceIdentifier} path and stream name.
+     * @param outputType
+     *            - Spcific type of output for notifications - XML or JSON
+     * @return New {@link ListenerAdapter} listener from
+     *         {@link YangInstanceIdentifier} path and stream name.
      */
-    public static ListenerAdapter createListener(final YangInstanceIdentifier path, final String streamName) {
-        final ListenerAdapter listener = new ListenerAdapter(path, streamName);
+    public static ListenerAdapter createListener(final YangInstanceIdentifier path, final String streamName,
+            final NotificationOutputType outputType) {
+        final ListenerAdapter listener = new ListenerAdapter(path, streamName, outputType);
         try {
             lock.lock();
-            listenersByStreamName.put(streamName, listener);
+            dataChangeListener.put(streamName, listener);
         } finally {
             lock.unlock();
         }
@@ -108,7 +116,7 @@ public class Notificator {
      * Removes all listeners.
      */
     public static void removeAllListeners() {
-        for (final ListenerAdapter listener : listenersByStreamName.values()) {
+        for (final ListenerAdapter listener : dataChangeListener.values()) {
             try {
                 listener.close();
             } catch (final Exception e) {
@@ -117,7 +125,7 @@ public class Notificator {
         }
         try {
             lock.lock();
-            listenersByStreamName = new ConcurrentHashMap<>();
+            dataChangeListener = new ConcurrentHashMap<>();
         } finally {
             lock.unlock();
         }
@@ -150,7 +158,7 @@ public class Notificator {
             }
             try {
                 lock.lock();
-                listenersByStreamName.remove(listener.getStreamName());
+                dataChangeListener.remove(listener.getStreamName());
             } finally {
                 lock.unlock();
             }
@@ -166,9 +174,19 @@ public class Notificator {
      */
     public static boolean existNotificationListenerFor(final String streamName) {
         return notificationListenersByStreamName.containsKey(streamName);
-
     }
 
+    /**
+     * Prepare listener for notification ({@link NotificationDefinition})
+     *
+     * @param paths
+     *            - paths of notifications
+     * @param streamName
+     *            - name of stream (generated by paths)
+     * @param outputType
+     *            - type of output for onNotification - XML or JSON
+     * @return List of {@link NotificationListenerAdapter} by paths
+     */
     public static List<NotificationListenerAdapter> createNotificationListener(final List<SchemaPath> paths,
             final String streamName, final String outputType) {
         final List<NotificationListenerAdapter> listListeners = new ArrayList<>();
similarity index 94%
rename from restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/Draft17.java
rename to restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/Draft18.java
index bfc6f0da0fa181ef9b6467e02e9de857d6108682..df4b45a904883d9d1c6c3db01f5fe31c3a69a908 100644 (file)
@@ -20,13 +20,13 @@ import org.opendaylight.yangtools.yang.common.QName;
  * </ul>
  * </ul>
  *
- * We used old revision {@link Draft17.RestconfModule#REVISION} of restconf yang
+ * We used old revision {@link Draft18.RestconfModule#REVISION} of restconf yang
  * because the latest restconf draft has to be supported by Yang 1.1 and we are
  * not. Then, this is only partial implementation of the latest restconf draft.
  */
-public final class Draft17 {
+public final class Draft18 {
 
-    private Draft17() {
+    private Draft18() {
         throw new UnsupportedOperationException("Util class");
     }
 
@@ -79,8 +79,8 @@ public final class Draft17 {
 
         public static final String ERROR_LIST_SCHEMA_NODE = "error";
 
-        public static final QName IETF_RESTCONF_QNAME = QName.create(Draft17.RestconfModule.NAMESPACE, Draft17.RestconfModule.REVISION,
-                Draft17.RestconfModule.NAME);
+        public static final QName IETF_RESTCONF_QNAME = QName.create(Draft18.RestconfModule.NAMESPACE, Draft18.RestconfModule.REVISION,
+                Draft18.RestconfModule.NAME);
 
         public static final QName ERRORS_CONTAINER_QNAME = QName.create(IETF_RESTCONF_QNAME, ERRORS_CONTAINER_SCHEMA_NODE);
 
index 846320049d4f2bbb46e47698f8cd6967e395d7f3..e69584c8a2cf571a431a6bd3da465f74635462a3 100644 (file)
@@ -16,6 +16,7 @@ import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
 import org.opendaylight.controller.sal.core.api.Provider;
@@ -25,6 +26,7 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.restconf.common.wrapper.services.ServicesWrapperImpl;
 import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
+import org.opendaylight.restconf.handlers.NotificationServiceHandler;
 import org.opendaylight.restconf.handlers.RpcServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.handlers.TransactionChainHandler;
@@ -34,7 +36,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Provider for restconf draft17.
+ * Provider for restconf draft18.
  *
  */
 public class RestConnectorProvider implements Provider, RestConnector, AutoCloseable {
@@ -82,8 +84,13 @@ public class RestConnectorProvider implements Provider, RestConnector, AutoClose
         final DOMRpcService rpcService = session.getService(DOMRpcService.class);
         final RpcServiceHandler rpcServiceHandler = new RpcServiceHandler(rpcService);
 
+        final DOMNotificationService notificationService = session.getService(DOMNotificationService.class);
+        final NotificationServiceHandler notificationServiceHandler =
+                new NotificationServiceHandler(notificationService);
+
         wrapperServices.setHandlers(schemaCtxHandler, RestConnectorProvider.mountPointServiceHandler,
-                RestConnectorProvider.transactionChainHandler, brokerHandler, rpcServiceHandler);
+                RestConnectorProvider.transactionChainHandler, brokerHandler, rpcServiceHandler,
+                notificationServiceHandler);
     }
 
     /**
index 14b808205594a29aa48fef707d29fffcfa92fdeb..5f47fcbc4609a2d8f12cc1a637eda85632b2c571 100644 (file)
@@ -14,23 +14,24 @@ import javax.ws.rs.core.Application;
 import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContentYangBodyWriter;
 import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContentYinBodyWriter;
 import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
-import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter;
-import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter;
 import org.opendaylight.netconf.sal.rest.impl.PATCHJsonBodyWriter;
 import org.opendaylight.netconf.sal.rest.impl.PATCHXmlBodyWriter;
 import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper;
 import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader;
 import org.opendaylight.restconf.common.wrapper.services.ServicesWrapperImpl;
 import org.opendaylight.restconf.jersey.providers.JsonToPATCHBodyReader;
+import org.opendaylight.restconf.jersey.providers.NormalizedNodeJsonBodyWriter;
+import org.opendaylight.restconf.jersey.providers.NormalizedNodeXmlBodyWriter;
 import org.opendaylight.restconf.jersey.providers.XmlToPATCHBodyReader;
 
 public class RestconfApplication extends Application {
 
     @Override
     public Set<Class<?>> getClasses() {
-        return ImmutableSet.<Class<?>> builder().add(NormalizedNodeJsonBodyWriter.class)
-                .add(NormalizedNodeXmlBodyWriter.class).add(JsonNormalizedNodeBodyReader.class)
-                .add(XmlNormalizedNodeBodyReader.class).add(SchemaExportContentYinBodyWriter.class)
+        return ImmutableSet.<Class<?>> builder()
+                .add(NormalizedNodeJsonBodyWriter.class).add(NormalizedNodeXmlBodyWriter.class)
+                .add(JsonNormalizedNodeBodyReader.class).add(XmlNormalizedNodeBodyReader.class)
+                .add(SchemaExportContentYinBodyWriter.class)
                 .add(JsonToPATCHBodyReader.class).add(XmlToPATCHBodyReader.class)
                 .add(PATCHJsonBodyWriter.class).add(PATCHXmlBodyWriter.class)
                 .add(SchemaExportContentYangBodyWriter.class).add(RestconfDocumentedExceptionMapper.class)
index b257d2f468dfb03e52b5175970df9a475dc05a8c..371fee4bfb09e6dcca2a2f70fdd332884f3a74b0 100644 (file)
@@ -12,7 +12,7 @@ import java.net.URI;
 import java.util.Date;
 import java.util.Set;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -73,13 +73,13 @@ public final class SchemaContextRef {
 
     /**
      * Get {@link Module} by ietf-restconf qname from
-     * {@link Draft17.RestconfModule}
+     * {@link Draft18.RestconfModule}
      *
      * @return {@link Module}
      */
     public Module getRestconfModule() {
-        return this.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME.getNamespace(),
-                Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
+        return this.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME.getNamespace(),
+                Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
     }
 
     /**
index ce948af3ea1e92101cf6cd19350221fde8a6ebfd..54fb06881765be27873f03b5380f30fcabd3b584 100644 (file)
@@ -16,6 +16,7 @@ import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
 import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
+import org.opendaylight.restconf.handlers.NotificationServiceHandler;
 import org.opendaylight.restconf.handlers.RpcServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.handlers.TransactionChainHandler;
@@ -28,10 +29,10 @@ import org.opendaylight.restconf.rest.services.impl.RestconfModulesServiceImpl;
 import org.opendaylight.restconf.rest.services.impl.RestconfOperationsServiceImpl;
 import org.opendaylight.restconf.rest.services.impl.RestconfSchemaServiceImpl;
 import org.opendaylight.restconf.rest.services.impl.RestconfStreamsServiceImpl;
-import org.opendaylight.restconf.restful.services.api.TransactionServicesWrapper;
 import org.opendaylight.restconf.restful.services.api.RestconfDataService;
 import org.opendaylight.restconf.restful.services.api.RestconfInvokeOperationsService;
 import org.opendaylight.restconf.restful.services.api.RestconfStreamsSubscriptionService;
+import org.opendaylight.restconf.restful.services.api.TransactionServicesWrapper;
 import org.opendaylight.restconf.restful.services.impl.RestconfDataServiceImpl;
 import org.opendaylight.restconf.restful.services.impl.RestconfInvokeOperationsServiceImpl;
 import org.opendaylight.restconf.restful.services.impl.RestconfStreamsSubscriptionServiceImpl;
@@ -101,14 +102,19 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ
         return this.delegRestSchService.getSchema(mountAndModuleId);
     }
 
+    @Override
+    public Response readData(final UriInfo uriInfo) {
+        return this.delegRestconfDataService.readData(uriInfo);
+    }
+
     @Override
     public Response readData(final String identifier, final UriInfo uriInfo) {
         return this.delegRestconfDataService.readData(identifier, uriInfo);
     }
 
     @Override
-    public Response putData(final String identifier, final NormalizedNodeContext payload) {
-        return this.delegRestconfDataService.putData(identifier, payload);
+    public Response putData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+        return this.delegRestconfDataService.putData(identifier, payload, uriInfo);
     }
 
     @Override
@@ -143,7 +149,7 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ
     }
 
     @Override
-    public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
+    public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
         return this.delegRestconfSubscrService.subscribeToStream(identifier, uriInfo);
     }
 
@@ -151,7 +157,7 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ
                             final DOMMountPointServiceHandler domMountPointServiceHandler,
                             final TransactionChainHandler transactionChainHandler,
                             final DOMDataBrokerHandler domDataBrokerHandler,
-                            final RpcServiceHandler rpcServiceHandler) {
+            final RpcServiceHandler rpcServiceHandler, final NotificationServiceHandler notificationServiceHandler) {
         this.delegRestModService = new RestconfModulesServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
         this.delegRestOpsService = new RestconfOperationsServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
         this.delegRestSchService = new RestconfSchemaServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
@@ -160,6 +166,8 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ
                 domMountPointServiceHandler);
         this.delegRestconfInvokeOpsService = new RestconfInvokeOperationsServiceImpl(rpcServiceHandler,
                 schemaCtxHandler);
-        this.delegRestconfSubscrService = new RestconfStreamsSubscriptionServiceImpl(domDataBrokerHandler);
+        this.delegRestconfSubscrService =
+                new RestconfStreamsSubscriptionServiceImpl(domDataBrokerHandler, notificationServiceHandler,
+                        schemaCtxHandler);
     }
 }
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/handlers/NotificationServiceHandler.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/handlers/NotificationServiceHandler.java
new file mode 100644 (file)
index 0000000..3365582
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016 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.restconf.handlers;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+
+public class NotificationServiceHandler implements Handler<DOMNotificationService> {
+
+    private final DOMNotificationService notificationService;
+
+    /**
+     * Set DOMNotificationService
+     *
+     * @param notificationService
+     *            - DOMNotificationService
+     */
+    public NotificationServiceHandler(final DOMNotificationService notificationService) {
+        this.notificationService = notificationService;
+    }
+
+    @Override
+    public DOMNotificationService get() {
+        return this.notificationService;
+    }
+
+}
index a2477cc6b51266ee81cdb264c775e22902d36e6f..37a736f32431c1b0b9eba642770e8b56d08fc027 100644 (file)
@@ -25,6 +25,4 @@ public class RpcServiceHandler implements Handler<DOMRpcService> {
     public DOMRpcService get() {
         return this.rpcService;
     }
-
-
 }
index 4fa9576856a6c1ed35db42facae9536b768a240d..5b8acd269d37fd2e8046458d9829d72bdea3973f 100644 (file)
@@ -29,7 +29,7 @@ import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @Provider
-@Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, MediaType.APPLICATION_JSON })
+@Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, MediaType.APPLICATION_JSON })
 public class JsonNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader<NormalizedNodeContext> {
 
     private final static Logger LOG = LoggerFactory.getLogger(JsonNormalizedNodeBodyReader.class);
index b0f50a6aa046a188903dc183ae57c8af788b1362..68b67ccdb9b8426df4e5a75adcd505c1828f9663 100644 (file)
@@ -35,7 +35,7 @@ import org.opendaylight.netconf.sal.restconf.impl.PATCHEntity;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @Provider
-@Consumes({Draft17.MediaTypes.PATCH + RestconfConstants.JSON})
+@Consumes({Draft18.MediaTypes.PATCH + RestconfConstants.JSON})
 public class JsonToPATCHBodyReader extends AbstractIdentifierAwareJaxRsProvider
         implements MessageBodyReader<PATCHContext> {
 
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/NormalizedNodeJsonBodyWriter.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/NormalizedNodeJsonBodyWriter.java
new file mode 100644 (file)
index 0000000..6d31d6f
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2016 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.restconf.jersey.providers;
+
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.restconf.Draft18;
+import org.opendaylight.restconf.utils.RestconfConstants;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+@Provider
+@Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, MediaType.APPLICATION_JSON })
+public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<NormalizedNodeContext> {
+
+    private static final int DEFAULT_INDENT_SPACES_NUM = 2;
+
+    @Override
+    public boolean isWriteable(final Class<?> type,
+                               final Type genericType,
+                               final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return type.equals(NormalizedNodeContext.class);
+    }
+
+    @Override
+    public long getSize(final NormalizedNodeContext t,
+                        final Class<?> type,
+                        final Type genericType,
+                        final Annotation[] annotations,
+                        final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final NormalizedNodeContext t,
+                        final Class<?> type,
+                        final Type genericType,
+                        final Annotation[] annotations,
+                        final MediaType mediaType,
+                        final MultivaluedMap<String, Object> httpHeaders,
+                        final OutputStream entityStream) throws IOException, WebApplicationException {
+        final NormalizedNode<?, ?> data = t.getData();
+        if (data == null) {
+            return;
+        }
+
+        @SuppressWarnings("unchecked")
+        final InstanceIdentifierContext<SchemaNode> context =
+                (InstanceIdentifierContext<SchemaNode>) t.getInstanceIdentifierContext();
+        final SchemaPath path = context.getSchemaNode().getPath();
+        final JsonWriter jsonWriter = createJsonWriter(entityStream,
+                t.getWriterParameters().isPrettyPrint());
+
+        jsonWriter.beginObject();
+        writeNormalizedNode(jsonWriter, path, context, data,
+                t.getWriterParameters().getDepth(), t.getWriterParameters().getFields());
+        jsonWriter.endObject();
+        jsonWriter.flush();
+    }
+
+    private void writeNormalizedNode(final JsonWriter jsonWriter,
+                                     final SchemaPath path,
+                                     final InstanceIdentifierContext<SchemaNode> context,
+                                     final NormalizedNode<?, ?> data,
+                                     final Integer depth,
+                                     final List<Set<QName>> fields) throws IOException {
+        final RestconfNormalizedNodeWriter nnWriter;
+
+        if (context.getSchemaNode() instanceof RpcDefinition) {
+            /*
+             *  RpcDefinition is not supported as initial codec in JSONStreamWriter,
+             *  so we need to emit initial output declaration..
+             */
+            nnWriter = createNormalizedNodeWriter(
+                    context,
+                    ((RpcDefinition) context.getSchemaNode()).getOutput().getPath(),
+                    jsonWriter,
+                    depth,
+                    fields);
+            jsonWriter.name("output");
+            jsonWriter.beginObject();
+            writeChildren(nnWriter, (ContainerNode) data);
+            jsonWriter.endObject();
+        } else {
+            if (SchemaPath.ROOT.equals(path)) {
+                nnWriter = createNormalizedNodeWriter(context, path, jsonWriter, depth, fields);
+            } else {
+                nnWriter = createNormalizedNodeWriter(context, path.getParent(), jsonWriter, depth, fields);
+            }
+
+            if (data instanceof MapEntryNode) {
+                // Restconf allows returning one list item. We need to wrap it
+                // in map node in order to serialize it properly
+                nnWriter.write(
+                        ImmutableNodes.mapNodeBuilder(data.getNodeType()).withChild(((MapEntryNode) data)).build());
+            } else {
+                nnWriter.write(data);
+            }
+        }
+
+        nnWriter.flush();
+    }
+
+    private void writeChildren(final RestconfNormalizedNodeWriter nnWriter,
+                               final ContainerNode data) throws IOException {
+        for (final DataContainerChild<? extends PathArgument, ?> child : data.getValue()) {
+            nnWriter.write(child);
+        }
+    }
+
+    private RestconfNormalizedNodeWriter createNormalizedNodeWriter(final InstanceIdentifierContext<SchemaNode> context,
+                                                                    final SchemaPath path,
+                                                                    final JsonWriter jsonWriter,
+                                                                    final Integer depth,
+                                                                    final List<Set<QName>> fields) {
+
+        final SchemaNode schema = context.getSchemaNode();
+        final JSONCodecFactory codecs = getCodecFactory(context);
+
+        final URI initialNs;
+        if ((schema instanceof DataSchemaNode)
+                && !((DataSchemaNode)schema).isAugmenting()
+                && !(schema instanceof SchemaContext)) {
+            initialNs = schema.getQName().getNamespace();
+        } else if (schema instanceof RpcDefinition) {
+            initialNs = schema.getQName().getNamespace();
+        } else {
+            initialNs = null;
+        }
+        final NormalizedNodeStreamWriter streamWriter = JSONNormalizedNodeStreamWriter.createNestedWriter(
+                codecs, path, initialNs, jsonWriter);
+        return ParameterAwareNormalizedNodeWriter.forStreamWriter(streamWriter, depth, fields);
+    }
+
+    private JsonWriter createJsonWriter(final OutputStream entityStream, final boolean prettyPrint) {
+        if (prettyPrint) {
+            return JsonWriterFactory.createJsonWriter(
+                    new OutputStreamWriter(entityStream, StandardCharsets.UTF_8), DEFAULT_INDENT_SPACES_NUM);
+        } else {
+            return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, StandardCharsets.UTF_8));
+        }
+    }
+
+    private JSONCodecFactory getCodecFactory(final InstanceIdentifierContext<?> context) {
+        // TODO: Performance: Cache JSON Codec factory and schema context
+        return JSONCodecFactory.create(context.getSchemaContext());
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/NormalizedNodeXmlBodyWriter.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/NormalizedNodeXmlBodyWriter.java
new file mode 100644 (file)
index 0000000..a0fb26f
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2016 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.restconf.jersey.providers;
+
+import com.google.common.base.Throwables;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Set;
+import javanet.staxutils.IndentingXMLStreamWriter;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import javax.xml.XMLConstants;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.restconf.Draft18;
+import org.opendaylight.restconf.utils.RestconfConstants;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+@Provider
+@Produces({ Draft18.MediaTypes.DATA + RestconfConstants.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<NormalizedNodeContext> {
+
+    private static final XMLOutputFactory XML_FACTORY;
+
+    static {
+        XML_FACTORY = XMLOutputFactory.newFactory();
+        XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
+    }
+
+    @Override
+    public boolean isWriteable(final Class<?> type,
+                               final Type genericType,
+                               final Annotation[] annotations,
+                               final MediaType mediaType) {
+        return type.equals(NormalizedNodeContext.class);
+    }
+
+    @Override
+    public long getSize(final NormalizedNodeContext t,
+                        final Class<?> type,
+                        final Type genericType,
+                        final Annotation[] annotations,
+                        final MediaType mediaType) {
+        return -1;
+    }
+
+    @Override
+    public void writeTo(final NormalizedNodeContext t,
+                        final Class<?> type,
+                        final Type genericType,
+                        final Annotation[] annotations,
+                        final MediaType mediaType,
+                        final MultivaluedMap<String, Object> httpHeaders,
+                        final OutputStream entityStream) throws IOException, WebApplicationException {
+        final InstanceIdentifierContext<?> pathContext = t.getInstanceIdentifierContext();
+        if (t.getData() == null) {
+            return;
+        }
+
+        XMLStreamWriter xmlWriter;
+        try {
+            xmlWriter = XML_FACTORY.createXMLStreamWriter(entityStream);
+            if (t.getWriterParameters().isPrettyPrint()) {
+                xmlWriter = new IndentingXMLStreamWriter(xmlWriter);
+            }
+        } catch (final XMLStreamException | FactoryConfigurationError e) {
+            throw new IllegalStateException(e);
+        }
+        final NormalizedNode<?, ?> data = t.getData();
+        final SchemaPath schemaPath = pathContext.getSchemaNode().getPath();
+
+        writeNormalizedNode(xmlWriter, schemaPath, pathContext, data, t.getWriterParameters().getDepth(),
+                t.getWriterParameters().getFields());
+    }
+
+    private void writeNormalizedNode(final XMLStreamWriter xmlWriter,
+                                     final SchemaPath path,
+                                     final InstanceIdentifierContext<?> pathContext,
+                                     final NormalizedNode<?, ?> data,
+                                     final Integer depth,
+                                     final List<Set<QName>> fields) throws IOException {
+        final RestconfNormalizedNodeWriter nnWriter;
+        final SchemaContext schemaCtx = pathContext.getSchemaContext();
+
+        if (pathContext.getSchemaNode() instanceof RpcDefinition) {
+            /*
+             *  RpcDefinition is not supported as initial codec in XMLStreamWriter,
+             *  so we need to emit initial output declaration..
+             */
+            nnWriter = createNormalizedNodeWriter(
+                    xmlWriter,
+                    schemaCtx,
+                    ((RpcDefinition) pathContext.getSchemaNode()).getOutput().getPath(),
+                    depth,
+                    fields);
+            writeElements(xmlWriter, nnWriter, (ContainerNode) data);
+        } else {
+            if (SchemaPath.ROOT.equals(path)) {
+                nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, path, depth, fields);
+            } else {
+                nnWriter = createNormalizedNodeWriter(xmlWriter, schemaCtx, path.getParent(), depth, fields);
+            }
+
+            if (data instanceof MapEntryNode) {
+                // Restconf allows returning one list item. We need to wrap it
+                // in map node in order to serialize it properly
+                nnWriter.write(ImmutableNodes.mapNodeBuilder(data.getNodeType()).addChild((MapEntryNode) data).build());
+            } else {
+                nnWriter.write(data);
+            }
+        }
+
+        nnWriter.flush();
+    }
+
+    private RestconfNormalizedNodeWriter createNormalizedNodeWriter(final XMLStreamWriter xmlWriter,
+                                                                    final SchemaContext schemaContext,
+                                                                    final SchemaPath schemaPath,
+                                                                    final Integer depth,
+                                                                    final List<Set<QName>> fields) {
+        final NormalizedNodeStreamWriter xmlStreamWriter = XMLStreamNormalizedNodeStreamWriter
+                .create(xmlWriter, schemaContext, schemaPath);
+        return ParameterAwareNormalizedNodeWriter.forStreamWriter(xmlStreamWriter, depth, fields);
+    }
+
+    private void writeElements(final XMLStreamWriter xmlWriter,
+                               final RestconfNormalizedNodeWriter nnWriter,
+                               final ContainerNode data) throws IOException {
+        try {
+            final QName name = data.getNodeType();
+            xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX,
+                    name.getLocalName(), name.getNamespace().toString());
+            xmlWriter.writeDefaultNamespace(name.getNamespace().toString());
+            for (final NormalizedNode<?,?> child : data.getValue()) {
+                nnWriter.write(child);
+            }
+            nnWriter.flush();
+            xmlWriter.writeEndElement();
+            xmlWriter.flush();
+        } catch (final XMLStreamException e) {
+            Throwables.propagate(e);
+        }
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriter.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriter.java
new file mode 100644 (file)
index 0000000..060cb0d
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2016 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.restconf.jersey.providers;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.netconf.sal.rest.api.RestconfNormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is an experimental iterator over a {@link NormalizedNode}. This is essentially
+ * the opposite of a {@link javax.xml.stream.XMLStreamReader} -- unlike instantiating an iterator over
+ * the backing data, this encapsulates a {@link NormalizedNodeStreamWriter} and allows
+ * us to write multiple nodes.
+ */
+@Beta
+public class ParameterAwareNormalizedNodeWriter implements RestconfNormalizedNodeWriter {
+    private static final QName ROOT_DATA_QNAME = QName.create("urn:ietf:params:xml:ns:netconf:base:1.0", "data");
+
+    private final NormalizedNodeStreamWriter writer;
+    private final Integer maxDepth;
+    protected final List<Set<QName>> fields;
+    protected int currentDepth = 0;
+
+    private ParameterAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final Integer maxDepth,
+                                               final List<Set<QName>> fields) {
+        this.writer = Preconditions.checkNotNull(writer);
+        this.maxDepth = maxDepth;
+        this.fields = fields;
+    }
+
+    protected final NormalizedNodeStreamWriter getWriter() {
+        return writer;
+    }
+
+    /**
+     * Create a new writer backed by a {@link NormalizedNodeStreamWriter}.
+     *
+     * @param writer Back-end writer
+     * @param maxDepth Maximal depth to write
+     * @param fields Selected child nodes to write
+     * @return A new instance.
+     */
+    public static ParameterAwareNormalizedNodeWriter forStreamWriter(
+            final NormalizedNodeStreamWriter writer, final Integer maxDepth, final List<Set<QName>> fields) {
+        return forStreamWriter(writer, true,  maxDepth, fields);
+    }
+
+    /**
+     * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. Unlike the simple
+     * {@link #forStreamWriter(NormalizedNodeStreamWriter, Integer, List)}
+     * method, this allows the caller to switch off RFC6020 XML compliance, providing better
+     * throughput. The reason is that the XML mapping rules in RFC6020 require the encoding
+     * to emit leaf nodes which participate in a list's key first and in the order in which
+     * they are defined in the key. For JSON, this requirement is completely relaxed and leaves
+     * can be ordered in any way we see fit. The former requires a bit of work: first a lookup
+     * for each key and then for each emitted node we need to check whether it was already
+     * emitted.
+     *
+     * @param writer Back-end writer
+     * @param orderKeyLeaves whether the returned instance should be RFC6020 XML compliant.
+     * @param maxDepth Maximal depth to write
+     * @param fields Selected child nodes to write
+     * @return A new instance.
+     */
+    public static ParameterAwareNormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer,
+                                                                     final boolean orderKeyLeaves,
+                                                                     final Integer maxDepth,
+                                                                     final List<Set<QName>> fields) {
+        if (orderKeyLeaves) {
+            return new OrderedParameterAwareNormalizedNodeWriter(writer, maxDepth, fields);
+        } else {
+            return new ParameterAwareNormalizedNodeWriter(writer, maxDepth, fields);
+        }
+    }
+
+    /**
+     * Iterate over the provided {@link NormalizedNode} and emit write
+     * events to the encapsulated {@link NormalizedNodeStreamWriter}.
+     *
+     * @param node Node
+     * @return
+     * @throws IOException when thrown from the backing writer.
+     */
+    public final ParameterAwareNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+        if (wasProcessedAsCompositeNode(node)) {
+            return this;
+        }
+
+        if (wasProcessAsSimpleNode(node)) {
+            return this;
+        }
+
+        throw new IllegalStateException("It wasn't possible to serialize node " + node);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        writer.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        writer.flush();
+        writer.close();
+    }
+
+    /**
+     * Emit a best guess of a hint for a particular set of children. It evaluates the
+     * iterable to see if the size can be easily gotten to. If it is, we hint at the
+     * real number of child nodes. Otherwise we emit UNKNOWN_SIZE.
+     *
+     * @param children Child nodes
+     * @return Best estimate of the collection size required to hold all the children.
+     */
+    static final int childSizeHint(final Iterable<?> children) {
+        return (children instanceof Collection) ? ((Collection<?>) children).size() : UNKNOWN_SIZE;
+    }
+
+    private boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
+        if (node instanceof LeafSetEntryNode) {
+            if (selectedByParameters(node, false)) {
+                final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>) node;
+                if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+                    ((NormalizedNodeStreamAttributeWriter) writer).leafSetEntryNode(nodeAsLeafList.getNodeType(),
+                            nodeAsLeafList.getValue(), nodeAsLeafList.getAttributes());
+                } else {
+                    writer.leafSetEntryNode(nodeAsLeafList.getNodeType(), nodeAsLeafList.getValue());
+                }
+            }
+            return true;
+        } else if (node instanceof LeafNode) {
+            final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node;
+            if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+                ((NormalizedNodeStreamAttributeWriter) writer).leafNode(
+                        nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue(), nodeAsLeaf.getAttributes());
+            } else {
+                writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue());
+            }
+            return true;
+        } else if (node instanceof AnyXmlNode) {
+            final AnyXmlNode anyXmlNode = (AnyXmlNode)node;
+            writer.anyxmlNode(anyXmlNode.getIdentifier(), anyXmlNode.getValue());
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Check if node should be written according to parameters fields and depth.
+     * See <a href="https://tools.ietf.org/html/draft-ietf-netconf-restconf-18#page-49">Restconf draft</a>.
+     * @param node Node to be written
+     * @param mixinParent {@code true} if parent is mixin, {@code false} otherwise
+     * @return {@code true} if node will be written, {@code false} otherwise
+     */
+    protected boolean selectedByParameters(final NormalizedNode<?, ?> node, final boolean mixinParent) {
+        // nodes to be written are not limited by fields, only by depth
+        if (fields == null) {
+            return (maxDepth == null || currentDepth < maxDepth);
+        }
+
+        // children of mixin nodes are never selected in fields but must be written if they are first in selected target
+        if (mixinParent && currentDepth == 0) {
+            return true;
+        }
+
+        // always write augmentation nodes
+        if (node instanceof AugmentationNode) {
+            return true;
+        }
+
+        // write only selected nodes
+        if (currentDepth > 0 && currentDepth <= fields.size()) {
+            if (fields.get(currentDepth - 1).contains(node.getNodeType())) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        // after this depth only depth parameter is used to determine when to write node
+        return (maxDepth == null || currentDepth < maxDepth);
+    }
+
+    /**
+     * Emit events for all children and then emit an endNode() event.
+     *
+     * @param children Child iterable
+     * @param mixinParent {@code true} if parent is mixin, {@code false} otherwise
+     * @return True
+     * @throws IOException when the writer reports it
+     */
+    protected final boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children,
+                                          final boolean mixinParent) throws IOException {
+        for (final NormalizedNode<?, ?> child : children) {
+            if (selectedByParameters(child, mixinParent)) {
+                write(child);
+            }
+        }
+        writer.endNode();
+        return true;
+    }
+
+    protected boolean writeMapEntryChildren(final MapEntryNode mapEntryNode) throws IOException {
+        if (selectedByParameters(mapEntryNode, false)) {
+            writeChildren(mapEntryNode.getValue(), false);
+        } else if (fields == null && maxDepth != null && currentDepth == maxDepth) {
+            writeOnlyKeys(mapEntryNode.getIdentifier().getKeyValues());
+        }
+        return true;
+    }
+
+    private void writeOnlyKeys(final Map<QName, Object> keyValues) throws IllegalArgumentException, IOException {
+        for (final Map.Entry<QName, Object> entry : keyValues.entrySet()) {
+            writer.leafNode(new NodeIdentifier(entry.getKey()), entry.getValue());
+        }
+        writer.endNode();
+    }
+
+    protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
+        if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+            ((NormalizedNodeStreamAttributeWriter) writer)
+                    .startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()), node.getAttributes());
+        } else {
+            writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
+        }
+        currentDepth++;
+        writeMapEntryChildren(node);
+        currentDepth--;
+        return true;
+    }
+
+    private boolean wasProcessedAsCompositeNode(final NormalizedNode<?, ?> node) throws IOException {
+        boolean processedAsCompositeNode = false;
+        if (node instanceof ContainerNode) {
+            final ContainerNode n = (ContainerNode) node;
+            if (!n.getNodeType().equals(ROOT_DATA_QNAME)) {
+                if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+                    ((NormalizedNodeStreamAttributeWriter) writer).startContainerNode(
+                            n.getIdentifier(), childSizeHint(n.getValue()), n.getAttributes());
+                } else {
+                    writer.startContainerNode(n.getIdentifier(), childSizeHint(n.getValue()));
+                }
+                currentDepth++;
+                processedAsCompositeNode = writeChildren(n.getValue(), false);
+                currentDepth--;
+            } else {
+                // write child nodes of data root container
+                for (final NormalizedNode<?, ?> child : n.getValue()) {
+                    currentDepth++;
+                    if (selectedByParameters(child, false)) {
+                        write(child);
+                    }
+                    currentDepth--;
+                    processedAsCompositeNode = true;
+                }
+            }
+        }
+        else if (node instanceof MapEntryNode) {
+            processedAsCompositeNode = writeMapEntryNode((MapEntryNode) node);
+        }
+        else if (node instanceof UnkeyedListEntryNode) {
+            final UnkeyedListEntryNode n = (UnkeyedListEntryNode) node;
+            writer.startUnkeyedListItem(n.getIdentifier(), childSizeHint(n.getValue()));
+            currentDepth++;
+            processedAsCompositeNode = writeChildren(n.getValue(), false);
+            currentDepth--;
+        }
+        else if (node instanceof ChoiceNode) {
+            final ChoiceNode n = (ChoiceNode) node;
+            writer.startChoiceNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            processedAsCompositeNode = writeChildren(n.getValue(), true);
+        }
+        else if (node instanceof AugmentationNode) {
+            final AugmentationNode n = (AugmentationNode) node;
+            writer.startAugmentationNode(n.getIdentifier());
+            processedAsCompositeNode = writeChildren(n.getValue(), true);
+        }
+        else if (node instanceof UnkeyedListNode) {
+            final UnkeyedListNode n = (UnkeyedListNode) node;
+            writer.startUnkeyedList(n.getIdentifier(), childSizeHint(n.getValue()));
+            processedAsCompositeNode = writeChildren(n.getValue(), false);
+        }
+        else if (node instanceof OrderedMapNode) {
+            final OrderedMapNode n = (OrderedMapNode) node;
+            writer.startOrderedMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            processedAsCompositeNode = writeChildren(n.getValue(), true);
+        }
+        else if (node instanceof MapNode) {
+            final MapNode n = (MapNode) node;
+            writer.startMapNode(n.getIdentifier(), childSizeHint(n.getValue()));
+            processedAsCompositeNode = writeChildren(n.getValue(), true);
+        }
+        else if (node instanceof LeafSetNode) {
+            final LeafSetNode<?> n = (LeafSetNode<?>) node;
+            if (node instanceof OrderedLeafSetNode) {
+                writer.startOrderedLeafSet(n.getIdentifier(), childSizeHint(n.getValue()));
+            } else {
+                writer.startLeafSet(n.getIdentifier(), childSizeHint(n.getValue()));
+            }
+            currentDepth++;
+            processedAsCompositeNode = writeChildren(n.getValue(), true);
+            currentDepth--;
+        }
+
+        return processedAsCompositeNode;
+    }
+
+    private static final class OrderedParameterAwareNormalizedNodeWriter extends ParameterAwareNormalizedNodeWriter {
+        private static final Logger LOG = LoggerFactory.getLogger(OrderedParameterAwareNormalizedNodeWriter.class);
+
+        OrderedParameterAwareNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final Integer maxDepth,
+                                                  final List<Set<QName>> fields) {
+            super(writer, maxDepth, fields);
+        }
+
+        @Override
+        protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException {
+            final NormalizedNodeStreamWriter writer = getWriter();
+            if (writer instanceof NormalizedNodeStreamAttributeWriter) {
+                ((NormalizedNodeStreamAttributeWriter) writer).startMapEntryNode(
+                        node.getIdentifier(), childSizeHint(node.getValue()), node.getAttributes());
+            } else {
+                writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()));
+            }
+
+            final Set<QName> qnames = node.getIdentifier().getKeyValues().keySet();
+            // Write out all the key children
+            currentDepth++;
+            for (final QName qname : qnames) {
+                final Optional<? extends NormalizedNode<?, ?>> child = node.getChild(new NodeIdentifier(qname));
+                if (child.isPresent()) {
+                    if (selectedByParameters(child.get(), false)) {
+                        write(child.get());
+                    }
+                } else {
+                    LOG.info("No child for key element {} found", qname);
+                }
+            }
+            currentDepth--;
+
+            currentDepth++;
+            // Write all the rest
+            final boolean result = writeChildren(Iterables.filter(node.getValue(), new Predicate<NormalizedNode<?, ?>>() {
+                @Override
+                public boolean apply(final NormalizedNode<?, ?> input) {
+                    if (input instanceof AugmentationNode) {
+                        return true;
+                    }
+                    if (!qnames.contains(input.getNodeType())) {
+                        return true;
+                    }
+
+                    LOG.debug("Skipping key child {}", input);
+                    return false;
+                }
+            }), false);
+            currentDepth--;
+            return result;
+        }
+    }
+}
index 8ea2f92541ccd5c0279452a963b7ddcb9cdfc47b..8c6fa6b8198c5286ace3413c35e2b7ea1997a87c 100644 (file)
@@ -34,7 +34,7 @@ import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -57,7 +57,7 @@ import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
 @Provider
-@Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+@Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
 public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsProvider implements MessageBodyReader<NormalizedNodeContext> {
 
     private final static Logger LOG = LoggerFactory.getLogger(XmlNormalizedNodeBodyReader.class);
index 1f6d5632a9d7e60ba55847e0b4788324ccc625b0..1bf1efbc9aeae1a1c24a98e71e598f2b9793d4b7 100644 (file)
@@ -35,7 +35,7 @@ import org.opendaylight.netconf.sal.restconf.impl.PATCHEntity;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -58,7 +58,7 @@ import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
 @Provider
-@Consumes({Draft17.MediaTypes.PATCH + RestconfConstants.XML})
+@Consumes({Draft18.MediaTypes.PATCH + RestconfConstants.XML})
 public class XmlToPATCHBodyReader extends AbstractIdentifierAwareJaxRsProvider implements
         MessageBodyReader<PATCHContext> {
 
index 89315be51594feae743a08ce1719fde50dac154d..13f4c4d208a8f6291abc91950046bc4099e98e87 100644 (file)
@@ -24,6 +24,7 @@ import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
@@ -34,6 +35,7 @@ import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 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.opendaylight.yangtools.yang.model.api.TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
@@ -75,7 +77,11 @@ public final class YangInstanceIdentifierDeserializer {
             if (allCharsConsumed(variables)
                     || (currentChar(variables.getOffset(), variables.getData()) == RestconfConstants.SLASH)) {
                 prepareIdentifier(qname, path, variables);
-                path.add(variables.getCurrent().getIdentifier());
+                if (variables.getCurrent() == null) {
+                    path.add(NodeIdentifier.create(qname));
+                } else {
+                    path.add(variables.getCurrent().getIdentifier());
+                }
             } else if (currentChar(variables.getOffset(),
                     variables.getData()) == ParserBuilderConstants.Deserializer.EQUAL) {
                 if (nextContextNode(qname, path, variables).getDataSchemaNode() instanceof ListSchemaNode) {
@@ -201,7 +207,7 @@ public final class YangInstanceIdentifierDeserializer {
                 return qName;
             }
         }
-        return QName.create(schemaNode.getQName().getNamespace(), schemaNode.getQName().getRevision(), value);
+        return QName.create(schemaNode.getQName().getNamespace(), schemaNode.getQName().getRevision(), nodeName);
     }
 
     private static String toNodeName(final String str) {
@@ -307,6 +313,9 @@ public final class YangInstanceIdentifierDeserializer {
     private static void prepareIdentifier(final QName qname, final List<PathArgument> path,
             final MainVarsWrapper variables) {
         final DataSchemaContextNode<?> currentNode = nextContextNode(qname, path, variables);
+        if (currentNode == null) {
+            return;
+        }
         checkValid(!currentNode.isKeyedEntry(), "Entry " + qname + " requires key or value predicate to be present",
                 variables.getData(), variables.getOffset());
     }
@@ -315,6 +324,14 @@ public final class YangInstanceIdentifierDeserializer {
             final MainVarsWrapper variables) {
         variables.setCurrent(variables.getCurrent().getChild(qname));
         DataSchemaContextNode<?> current = variables.getCurrent();
+        if (current == null) {
+            for (final RpcDefinition rpcDefinition : variables.getSchemaContext()
+                    .findModuleByNamespaceAndRevision(qname.getNamespace(), qname.getRevision()).getRpcs()) {
+                if (rpcDefinition.getQName().getLocalName().equals(qname.getLocalName())) {
+                    return null;
+                }
+            }
+        }
         checkValid(current != null, qname + " is not correct schema node identifier.", variables.getData(),
                 variables.getOffset());
         while (current.isMixin()) {
@@ -405,14 +422,15 @@ public final class YangInstanceIdentifierDeserializer {
 
         private final SchemaContext schemaContext;
         private final String data;
+
         private DataSchemaContextNode<?> current;
         private int offset;
 
         public MainVarsWrapper(final String data, final DataSchemaContextNode<?> current, final int offset,
                 final SchemaContext schemaContext) {
             this.data = data;
-            this.setCurrent(current);
-            this.setOffset(offset);
+            this.current = current;
+            this.offset = offset;
             this.schemaContext = schemaContext;
         }
 
index b9e6c139b7a747451554440f9f149aff10fe4d8c..e74bb66f47d75f9d90d85a0007a49f3257f17bd5 100644 (file)
@@ -15,7 +15,7 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 
 /**
@@ -32,7 +32,7 @@ public interface RestconfModulesService {
      */
     @GET
     @Path("data/ietf-yang-library:modules-state")
-    @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public NormalizedNodeContext getModules(@Context UriInfo uriInfo);
 
@@ -48,7 +48,7 @@ public interface RestconfModulesService {
      */
     @GET
     @Path("data/ietf-yang-library:modules-state/{identifier:.+}")
-    @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public NormalizedNodeContext getModules(@PathParam("identifier") String identifier, @Context UriInfo uriInfo);
 
@@ -65,7 +65,7 @@ public interface RestconfModulesService {
      */
     @GET
     @Path("data/ietf-yang-library:modules-state/module/{identifier:.+}")
-    @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public NormalizedNodeContext getModule(@PathParam("identifier") String identifier, @Context UriInfo uriInfo);
 }
\ No newline at end of file
index f6874d636835ae2a83ae1d543189942f131e7e6c..e6372d6408ba8a93ac89e3eefb085061e1eed90a 100644 (file)
@@ -15,7 +15,7 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 
 /**
@@ -34,7 +34,7 @@ public interface RestconfOperationsService {
      */
     @GET
     @Path("/operations")
-    @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public NormalizedNodeContext getOperations(@Context UriInfo uriInfo);
 
@@ -49,7 +49,7 @@ public interface RestconfOperationsService {
      */
     @GET
     @Path("/operations/{identifier:.+}")
-    @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public NormalizedNodeContext getOperations(@PathParam("identifier") String identifier, @Context UriInfo uriInfo);
 }
\ No newline at end of file
index 261455593eed931624514a98d6b721a0efd77183..93b52b2ef677791043aca04efefffe21a60d84ad 100644 (file)
@@ -12,7 +12,7 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import org.opendaylight.netconf.md.sal.rest.schema.SchemaExportContext;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 
 /**
@@ -30,7 +30,7 @@ public interface RestconfSchemaService {
      * @return {@link SchemaExportContext}
      */
     @GET
-    @Produces({ Draft17.MediaTypes.YANG, Draft17.MediaTypes.YIN + RestconfConstants.XML })
+    @Produces({ Draft18.MediaTypes.YANG, Draft18.MediaTypes.YIN + RestconfConstants.XML })
     @Path("data/ietf-yang-library:modules/module/{identifier:.+}/schema")
     SchemaExportContext getSchema(@PathParam("identifier") String identifier);
 }
index 732258fd2a8d680d258218675d01b41e3a9a7d2d..16912db0e85b837c2c45a0dae22a232a129ad51e 100644 (file)
@@ -14,7 +14,7 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 
 /**
@@ -33,7 +33,7 @@ public interface RestconfStreamsService {
      */
     @GET
     @Path("data/ietf-restconf-monitoring:restconf-state/streams")
-    @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     public NormalizedNodeContext getAvailableStreams(@Context UriInfo uriInfo);
 }
\ No newline at end of file
index 5b7a6ebd999441b36f456288e8e4632d2b86552b..6ccc2225ffbddce8b4bd63868efd0e8e0ed76bf8 100644 (file)
@@ -18,7 +18,7 @@ import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
@@ -117,7 +117,7 @@ public class RestconfModulesServiceImpl implements RestconfModulesService {
         final MapNode moduleMap = RestconfMappingNodeUtil
                 .restconfMappingNode(schemaContextRef.getRestconfModule(), modules);
         final DataSchemaNode moduleSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
-                schemaContextRef.getRestconfModule(), Draft17.RestconfModule.MODULE_LIST_SCHEMA_NODE);
+                schemaContextRef.getRestconfModule(), Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE);
         Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
         if (mountPoint == null) {
             return new NormalizedNodeContext(
@@ -148,7 +148,7 @@ public class RestconfModulesServiceImpl implements RestconfModulesService {
 
         final MapNode mapNodes = RestconfMappingNodeUtil.restconfMappingNode(restconfModule, modules);
         final DataSchemaNode schemaNode = RestconfSchemaUtil.getRestconfSchemaNode(restconfModule,
-                Draft17.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
+                Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
         Preconditions.checkState(schemaNode instanceof ContainerSchemaNode);
         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> modulContainerSchemaNodeBuilder = Builders
                 .containerBuilder((ContainerSchemaNode) schemaNode);
index 49247769d117db72739a11fbfa14716fb71c42cc..06f46ebc53902a34c9f6754868c88a8356fa06ec 100644 (file)
@@ -13,7 +13,7 @@ import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.streams.listeners.Notificator;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.rest.services.api.RestconfStreamsService;
@@ -56,7 +56,7 @@ public class RestconfStreamsServiceImpl implements RestconfStreamsService {
         final Set<String> availableStreams = Notificator.getStreamNames();
 
         final DataSchemaNode streamListSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
-                schemaContextRef.getRestconfModule(), Draft17.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
+                schemaContextRef.getRestconfModule(), Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
         Preconditions.checkState(streamListSchemaNode instanceof ListSchemaNode);
         final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamBuilder = Builders
                 .mapBuilder((ListSchemaNode) streamListSchemaNode);
@@ -66,7 +66,7 @@ public class RestconfStreamsServiceImpl implements RestconfStreamsService {
         }
 
         final DataSchemaNode streamContSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(
-                schemaContextRef.getRestconfModule(), Draft17.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
+                schemaContextRef.getRestconfModule(), Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
         Preconditions.checkState(streamContSchemaNode instanceof ContainerSchemaNode);
         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder = Builders
                 .containerBuilder((ContainerSchemaNode) streamContSchemaNode);
index 4ada40c5feeba6c6ff6228210ce49f46d3e11edd..6b5876cb58ee8f7a4381a50e6bf73051513370cb 100644 (file)
@@ -24,7 +24,7 @@ import org.opendaylight.netconf.sal.rest.impl.PATCH;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 
 /**
@@ -45,10 +45,22 @@ public interface RestconfDataService {
      */
     @GET
     @Path("/data/{identifier:.+}")
-    @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
-    Response readData(@Encoded @PathParam("identifier") String identifier,
-            @Context UriInfo uriInfo);
+    Response readData(@Encoded @PathParam("identifier") String identifier, @Context UriInfo uriInfo);
+
+    /**
+     * Get target data resource from data root.
+     *
+     * @param uriInfo
+     *            - URI info
+     * @return {@link NormalizedNodeContext}
+     */
+    @GET
+    @Path("/data")
+    @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+            MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+    Response readData(@Context UriInfo uriInfo);
 
     /**
      * Create or replace the target data resource.
@@ -61,9 +73,10 @@ public interface RestconfDataService {
      */
     @PUT
     @Path("/data/{identifier:.+}")
-    @Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
-    Response putData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload);
+    Response putData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload,
+            @Context UriInfo uriInfo);
 
     /**
      * Create a data resource in target.
@@ -78,7 +91,7 @@ public interface RestconfDataService {
      */
     @POST
     @Path("/data/{identifier:.+}")
-    @Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     Response postData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload,
             @Context UriInfo uriInfo);
@@ -94,7 +107,7 @@ public interface RestconfDataService {
      */
     @POST
     @Path("/data")
-    @Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     Response postData(NormalizedNodeContext payload, @Context UriInfo uriInfo);
 
@@ -123,9 +136,9 @@ public interface RestconfDataService {
      */
     @PATCH
     @Path("/data/{identifier:.+}")
-    @Consumes({ Draft17.MediaTypes.PATCH + RestconfConstants.JSON, Draft17.MediaTypes.PATCH + RestconfConstants.XML })
-    @Produces({ Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.JSON,
-            Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.XML })
+    @Consumes({ Draft18.MediaTypes.PATCH + RestconfConstants.JSON, Draft18.MediaTypes.PATCH + RestconfConstants.XML })
+    @Produces({ Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.JSON,
+            Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.XML })
     PATCHStatusContext patchData(@Encoded @PathParam("identifier") String identifier, PATCHContext context,
             @Context UriInfo uriInfo);
 
@@ -140,8 +153,8 @@ public interface RestconfDataService {
      */
     @PATCH
     @Path("/data")
-    @Consumes({ Draft17.MediaTypes.PATCH + RestconfConstants.JSON, Draft17.MediaTypes.PATCH + RestconfConstants.XML })
-    @Produces({ Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.JSON,
-            Draft17.MediaTypes.PATCH_STATUS + RestconfConstants.XML })
+    @Consumes({ Draft18.MediaTypes.PATCH + RestconfConstants.JSON, Draft18.MediaTypes.PATCH + RestconfConstants.XML })
+    @Produces({ Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.JSON,
+            Draft18.MediaTypes.PATCH_STATUS + RestconfConstants.XML })
     PATCHStatusContext patchData(PATCHContext context, @Context UriInfo uriInfo);
 }
index df58decd354be87e7cfcd03f3424a5c95a47a32b..ebe762813a45a0beaca574b83156ccee5dc97407 100644 (file)
@@ -17,7 +17,7 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 
 /**
@@ -41,9 +41,9 @@ public interface RestconfInvokeOperationsService {
      */
     @POST
     @Path("/operations/{identifier:.+}")
-    @Produces({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
-    @Consumes({ Draft17.MediaTypes.DATA + RestconfConstants.JSON, Draft17.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+    @Consumes({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON,
             MediaType.APPLICATION_XML, MediaType.TEXT_XML })
     NormalizedNodeContext invokeRpc(@Encoded @PathParam("identifier") String identifier,
             NormalizedNodeContext payload, @Context UriInfo uriInfo);
index 0528d89c44d3740814fb007e945215f811afdc02..278a8f7af6ce85b0b386524eb3b7017df2474093 100644 (file)
@@ -12,8 +12,8 @@ import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.core.Context;
-import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 
 /**
  * Subscribing to streams
@@ -28,9 +28,10 @@ public interface RestconfStreamsSubscriptionService {
      *            - name of stream
      * @param uriInfo
      *            - URI info
-     * @return {@link Response}
+     * @return {@link NormalizedNodeContext}
      */
     @GET
     @Path("data/ietf-restconf-monitoring:restconf-state/streams/stream/{identifier:.+}")
-    Response subscribeToStream(@Encoded @PathParam("identifier") String identifier, @Context UriInfo uriInfo);
+    NormalizedNodeContext subscribeToStream(@Encoded @PathParam("identifier") String identifier,
+            @Context UriInfo uriInfo);
 }
index cb17434392c1870ce602082d4719e8366a671abf..a4d63a23ed060a1c5a87b9f5e23478d219ec8c33 100644 (file)
@@ -11,6 +11,8 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.List;
+import java.util.Map.Entry;
 import java.util.TimeZone;
 import javax.annotation.Nonnull;
 import javax.ws.rs.core.Response;
@@ -24,6 +26,7 @@ import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
+import org.opendaylight.netconf.sal.restconf.impl.WriterParameters;
 import org.opendaylight.restconf.RestConnectorProvider;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
@@ -62,16 +65,47 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         this.mountPointServiceHandler = mountPointServiceHandler;
     }
 
+    @Override
+    public Response readData(final UriInfo uriInfo) {
+        return readData(null, uriInfo);
+    }
+
     @Override
     public Response readData(final String identifier, final UriInfo uriInfo) {
-        Preconditions.checkNotNull(identifier);
         final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
-
         final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
                 identifier, schemaContextRef.get(), Optional.of(this.mountPointServiceHandler.get()));
-        final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
-        final String value = uriInfo.getQueryParameters().getFirst(RestconfDataServiceConstant.CONTENT);
 
+        boolean withDefa_used = false;
+        String withDefa = null;
+
+        for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            switch (entry.getKey()) {
+                case "with-defaults":
+                    if (!withDefa_used) {
+                        withDefa_used = true;
+                        withDefa = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("With-defaults parameter can be used only once.");
+                    }
+                    break;
+            }
+        }
+        boolean tagged = false;
+        if (withDefa_used) {
+            if (withDefa.equals("report-all-tagged")) {
+                tagged = true;
+                withDefa = null;
+            }
+            if (withDefa.equals("report-all")) {
+                withDefa = null;
+            }
+        }
+
+        final WriterParameters parameters = ReadDataTransactionUtil.parseUriParameters(
+                instanceIdentifier, uriInfo, tagged);
+
+        final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
         final DOMTransactionChain transactionChain;
         if (mountPoint == null) {
             transactionChain = this.transactionChainHandler.get();
@@ -79,9 +113,10 @@ public class RestconfDataServiceImpl implements RestconfDataService {
             transactionChain = transactionChainOfMountPoint(mountPoint);
         }
 
-        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(instanceIdentifier, mountPoint,
-                transactionChain);
-        final NormalizedNode<?, ?> node = ReadDataTransactionUtil.readData(value, transactionNode);
+        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
+                instanceIdentifier, mountPoint, transactionChain);
+        final NormalizedNode<?, ?> node =
+                ReadDataTransactionUtil.readData(parameters.getContent(), transactionNode, withDefa);
         if (node == null) {
             throw new RestconfDocumentedException(
                     "Request could not be completed because the relevant data model content does not exist",
@@ -94,19 +129,56 @@ public class RestconfDataServiceImpl implements RestconfDataService {
                 + node.getNodeType().getLocalName() + '"';
         final Response resp;
 
-        if ((value == null) || value.contains(RestconfDataServiceConstant.ReadData.CONFIG)) {
-            resp = Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node)).header("ETag", etag)
-                    .header("Last-Modified", dateFormatGmt.format(new Date())).build();
+        if ((parameters.getContent().equals(RestconfDataServiceConstant.ReadData.ALL))
+                    || parameters.getContent().equals(RestconfDataServiceConstant.ReadData.CONFIG)) {
+            resp = Response.status(200)
+                    .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters))
+                    .header("ETag", etag)
+                    .header("Last-Modified", dateFormatGmt.format(new Date()))
+                    .build();
         } else {
-            resp = Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node)).build();
+            resp = Response.status(200)
+                    .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters))
+                    .build();
         }
+
         return resp;
     }
 
     @Override
-    public Response putData(final String identifier, final NormalizedNodeContext payload) {
+    public Response putData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
         Preconditions.checkNotNull(payload);
 
+        boolean insert_used = false;
+        boolean point_used = false;
+        String insert = null;
+        String point = null;
+
+        for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            switch (entry.getKey()) {
+                case "insert":
+                    if (!insert_used) {
+                        insert_used = true;
+                        insert = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("Insert parameter can be used only once.");
+                    }
+                    break;
+                case "point":
+                    if (!point_used) {
+                        point_used = true;
+                        point = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("Point parameter can be used only once.");
+                    }
+                    break;
+                default:
+                    throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
+            }
+        }
+
+        checkQueryParams(insert_used, point_used, insert);
+
         final InstanceIdentifierContext<? extends SchemaNode> iid = payload
                 .getInstanceIdentifierContext();
 
@@ -127,7 +199,17 @@ public class RestconfDataServiceImpl implements RestconfDataService {
 
         final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
                 payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
-        return PutDataTransactionUtil.putData(payload, ref, transactionNode);
+        return PutDataTransactionUtil.putData(payload, ref, transactionNode, insert, point);
+    }
+
+    private void checkQueryParams(final boolean insert_used, final boolean point_used, final String insert) {
+        if (point_used && !insert_used) {
+            throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.");
+        }
+        if (point_used && (insert.equals("first") || insert.equals("last"))) {
+            throw new RestconfDocumentedException(
+                    "Point parameter can be used only with 'after' or 'before' values of Insert parameter.");
+        }
     }
 
     @Override
@@ -139,6 +221,36 @@ public class RestconfDataServiceImpl implements RestconfDataService {
     public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
         Preconditions.checkNotNull(payload);
 
+        boolean insert_used = false;
+        boolean point_used = false;
+        String insert = null;
+        String point = null;
+
+        for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            switch (entry.getKey()) {
+                case "insert":
+                    if (!insert_used) {
+                        insert_used = true;
+                        insert = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("Insert parameter can be used only once.");
+                    }
+                    break;
+                case "point":
+                    if (!point_used) {
+                        point_used = true;
+                        point = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("Point parameter can be used only once.");
+                    }
+                    break;
+                default:
+                    throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
+            }
+        }
+
+        checkQueryParams(insert_used, point_used, insert);
+
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
         final DOMTransactionChain transactionChain;
         final SchemaContextRef ref;
@@ -151,7 +263,7 @@ public class RestconfDataServiceImpl implements RestconfDataService {
         }
         final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
                 payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
-        return PostDataTransactionUtil.postData(uriInfo, payload, transactionNode, ref);
+        return PostDataTransactionUtil.postData(uriInfo, payload, transactionNode, ref, insert, point);
     }
 
     @Override
index 222c13902d8c679bcf90c49286a79fed1cee0586..f885e78c2638762288a218a9cf02c57460532aee 100644 (file)
@@ -13,6 +13,9 @@ import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
 import org.opendaylight.restconf.handlers.RpcServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
@@ -51,7 +54,14 @@ public class RestconfInvokeOperationsServiceImpl implements RestconfInvokeOperat
 
         if (mountPoint == null) {
             if (namespace.toString().equals(RestconfStreamsConstants.SAL_REMOTE_NAMESPACE)) {
-                response = CreateStreamUtil.createStream(payload, refSchemaCtx);
+                if (identifier.contains(RestconfStreamsConstants.CREATE_DATA_SUBSCR)) {
+                    response = CreateStreamUtil.createDataChangeNotifiStream(payload, refSchemaCtx);
+                } else if (identifier.contains(RestconfStreamsConstants.CREATE_NOTIFICATION_STREAM)) {
+                    response = CreateStreamUtil.createYangNotifiStream(payload, refSchemaCtx);
+                } else {
+                    throw new RestconfDocumentedException("Not supported operation", ErrorType.RPC,
+                            ErrorTag.OPERATION_NOT_SUPPORTED);
+                }
             } else {
                 response = RestconfInvokeOperationsUtil.invokeRpc(payload.getData(), schemaPath,
                         this.rpcServiceHandler);
index c9d53ae91804896de6aca46b863d1eb5f9cf80ed..9852330a0e532a0e84e5989d43c296db0d405df5 100644 (file)
@@ -8,22 +8,26 @@
 package org.opendaylight.restconf.restful.services.impl;
 
 import java.net.URI;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriBuilder;
+import java.util.Map.Entry;
 import javax.ws.rs.core.UriInfo;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
-import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
-import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
-import org.opendaylight.netconf.sal.streams.listeners.Notificator;
 import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
+import org.opendaylight.restconf.handlers.NotificationServiceHandler;
+import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.restful.services.api.RestconfStreamsSubscriptionService;
 import org.opendaylight.restconf.restful.utils.RestconfStreamsConstants;
 import org.opendaylight.restconf.restful.utils.SubscribeToStreamUtil;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,41 +41,84 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu
 
     private final DOMDataBrokerHandler domDataBrokerHandler;
 
-    public RestconfStreamsSubscriptionServiceImpl(final DOMDataBrokerHandler domDataBrokerHandler) {
+    private final NotificationServiceHandler notificationServiceHandler;
+
+    private final SchemaContextHandler schemaHandler;
+
+    public RestconfStreamsSubscriptionServiceImpl(final DOMDataBrokerHandler domDataBrokerHandler,
+            final NotificationServiceHandler notificationServiceHandler, final SchemaContextHandler schemaHandler) {
         this.domDataBrokerHandler = domDataBrokerHandler;
+        this.notificationServiceHandler = notificationServiceHandler;
+        this.schemaHandler = schemaHandler;
     }
 
     @Override
-    public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
-        final Map<String, String> mapOfValues = SubscribeToStreamUtil.mapValuesFromUri(identifier);
+    public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
+        boolean startTime_used = false;
+        boolean stopTime_used = false;
+        boolean filter_used = false;
+        Date start = null;
+        Date stop = null;
+        String filter = null;
 
-        final LogicalDatastoreType ds = SubscribeToStreamUtil.parseURIEnum(LogicalDatastoreType.class,
-                mapOfValues.get(RestconfStreamsConstants.DATASTORE_PARAM_NAME));
-        if (ds == null) {
-            final String msg = "Stream name doesn't contains datastore value (pattern /datastore=)";
-            LOG.debug(msg);
-            throw new RestconfDocumentedException(msg, ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
+        for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            switch (entry.getKey()) {
+                case "start-time":
+                    if (!startTime_used) {
+                        startTime_used = true;
+                        start = SubscribeToStreamUtil.parseDateFromQueryParam(entry);
+                    } else {
+                        throw new RestconfDocumentedException("Start-time parameter can be used only once.");
+                    }
+                    break;
+                case "stop-time":
+                    if (!stopTime_used) {
+                        stopTime_used = true;
+                        stop = SubscribeToStreamUtil.parseDateFromQueryParam(entry);
+                    } else {
+                        throw new RestconfDocumentedException("Stop-time parameter can be used only once.");
+                    }
+                    break;
+                case "filter":
+                    if (!filter_used) {
+                        filter_used = true;
+                        filter = entry.getValue().iterator().next();
+                    }
+                    break;
+                default:
+                    throw new RestconfDocumentedException("Bad parameter used with notifications: " + entry.getKey());
+            }
         }
-
-        final DataChangeScope scope = SubscribeToStreamUtil.parseURIEnum(DataChangeScope.class,
-                mapOfValues.get(RestconfStreamsConstants.SCOPE_PARAM_NAME));
-        if (scope == null) {
-            final String msg = "Stream name doesn't contains datastore value (pattern /scope=)";
-            LOG.warn(msg);
-            throw new RestconfDocumentedException(msg, ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
+        if (!startTime_used && stopTime_used) {
+            throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter.");
+        }
+        URI response = null;
+        if (identifier.contains(RestconfStreamsConstants.DATA_SUBSCR)) {
+            response =
+                    SubscribeToStreamUtil.dataSubs(identifier, uriInfo, start, stop, this.domDataBrokerHandler, filter);
+        } else if (identifier.contains(RestconfStreamsConstants.NOTIFICATION_STREAM)) {
+            response = SubscribeToStreamUtil.notifStream(identifier, uriInfo, start, stop,
+                    this.notificationServiceHandler, filter);
         }
 
-        final String streamName = Notificator.createStreamNameFromUri(identifier);
-
-        final ListenerAdapter listener = Notificator.getListenerFor(streamName);
-        SubscribeToStreamUtil.registration(ds, scope, listener, this.domDataBrokerHandler.get());
+        if (response != null) {
+            // prepare node with value of location
+            final InstanceIdentifierContext<?> iid =
+                    SubscribeToStreamUtil.prepareIIDSubsStreamOutput(this.schemaHandler);
+            final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> builder =
+                    ImmutableLeafNodeBuilder.create().withValue(response.toString());
+            builder.withNodeIdentifier(
+                    NodeIdentifier.create(QName.create("subscribe:to:notification", "2016-10-28", "location")));
 
-        final int port = SubscribeToStreamUtil.prepareNotificationPort();
+            // prepare new header with location
+            final Map<String, Object> headers = new HashMap<>();
+            headers.put("Location", response);
 
-        final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
-        final UriBuilder uriToWebSocketServer = uriBuilder.port(port).scheme(RestconfStreamsConstants.SCHEMA_SUBSCIBRE_URI);
-        final URI uri = uriToWebSocketServer.replacePath(streamName).build();
+            return new NormalizedNodeContext(iid, builder.build(), headers);
+        }
 
-        return Response.status(Status.OK).location(uri).build();
+        final String msg = "Bad type of notification of sal-remote";
+        LOG.warn(msg);
+        throw new RestconfDocumentedException(msg);
     }
 }
index e4f867c61331ff2e0e2fb7eadf661e19b9183789..b513f81672799404369c18c74d7321e050dc2040 100644 (file)
@@ -8,7 +8,12 @@
 package org.opendaylight.restconf.restful.utils;
 
 import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.CheckedFuture;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
@@ -20,6 +25,7 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.netconf.sal.streams.listeners.Notificator;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
 import org.opendaylight.restconf.utils.parser.ParserIdentifier;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -27,9 +33,14 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,6 +56,7 @@ import org.slf4j.LoggerFactory;
 public final class CreateStreamUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(CreateStreamUtil.class);
+    private static final String OUTPUT_TYPE_PARAM_NAME = "notification-output-type";
 
     private CreateStreamUtil() {
         throw new UnsupportedOperationException("Util class");
@@ -85,12 +97,12 @@ public final class CreateStreamUtil {
      *         </pre>
      *
      */
-    public static DOMRpcResult createStream(final NormalizedNodeContext payload,
+    public static DOMRpcResult createDataChangeNotifiStream(final NormalizedNodeContext payload,
             final SchemaContextRef refSchemaCtx) {
         final ContainerNode data = (ContainerNode) payload.getData();
         final QName qname = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
         final YangInstanceIdentifier path = preparePath(data, qname);
-        final String streamName = prepareStream(path, refSchemaCtx.get(), data);
+        final String streamName = prepareDataChangeNotifiStreamName(path, refSchemaCtx.get(), data);
 
         final QName outputQname = QName.create(qname, "output");
         final QName streamNameQname = QName.create(qname, "stream-name");
@@ -98,15 +110,26 @@ public final class CreateStreamUtil {
         final ContainerNode output = ImmutableContainerNodeBuilder.create()
                 .withNodeIdentifier(new NodeIdentifier(outputQname))
                 .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
+        final NotificationOutputType outputType = prepareOutputType(data);
 
         if (!Notificator.existListenerFor(streamName)) {
-            Notificator.createListener(path, streamName);
+            Notificator.createListener(path, streamName, outputType);
         }
 
         return new DefaultDOMRpcResult(output);
     }
 
-    private static String prepareStream(final YangInstanceIdentifier path, final SchemaContext schemaContext,
+    /**
+     * @param data
+     *            - data of notification
+     * @return output type fo notification
+     */
+    private static NotificationOutputType prepareOutputType(final ContainerNode data) {
+        NotificationOutputType outputType = parseEnum(data, NotificationOutputType.class, OUTPUT_TYPE_PARAM_NAME);
+        return outputType = outputType == null ? NotificationOutputType.XML : outputType;
+    }
+
+    private static String prepareDataChangeNotifiStreamName(final YangInstanceIdentifier path, final SchemaContext schemaContext,
             final ContainerNode data) {
         LogicalDatastoreType ds = parseEnum(data, LogicalDatastoreType.class,
                 RestconfStreamsConstants.DATASTORE_PARAM_NAME);
@@ -115,7 +138,8 @@ public final class CreateStreamUtil {
         DataChangeScope scope = parseEnum(data, DataChangeScope.class, RestconfStreamsConstants.SCOPE_PARAM_NAME);
         scope = scope == null ? RestconfStreamsConstants.DEFAULT_SCOPE : scope;
 
-        final String streamName = Notificator
+        final String streamName = RestconfStreamsConstants.DATA_SUBSCR + "/"
+                + Notificator
                 .createStreamNameFromUri(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext)
                 + RestconfStreamsConstants.DS_URI + ds + RestconfStreamsConstants.SCOPE_URI + scope);
         return streamName;
@@ -155,4 +179,69 @@ public final class CreateStreamUtil {
         }
         return (YangInstanceIdentifier) pathValue;
     }
+
+    /**
+     * Create stream with POST operation via RPC
+     *
+     * @param payload
+     *            - input of RPC
+     * @param refSchemaCtx
+     *            - schemaContext
+     * @return {@link DOMRpcResult}
+     */
+    public static DOMRpcResult createYangNotifiStream(final NormalizedNodeContext payload,
+            final SchemaContextRef refSchemaCtx) {
+        final ContainerNode data = (ContainerNode) payload.getData();
+        LeafSetNode leafSet = null;
+        String outputType = "XML";
+        for (final DataContainerChild<? extends PathArgument, ?> dataChild : data.getValue()) {
+            if (dataChild instanceof LeafSetNode) {
+                leafSet = (LeafSetNode) dataChild;
+            } else if (dataChild instanceof AugmentationNode) {
+                outputType = (String) (((AugmentationNode) dataChild).getValue()).iterator().next().getValue();
+            }
+        }
+
+        final Collection<LeafSetEntryNode> entryNodes = leafSet.getValue();
+        final List<SchemaPath> paths = new ArrayList<>();
+        String streamName = RestconfStreamsConstants.CREATE_NOTIFICATION_STREAM + "/";
+
+        final Iterator<LeafSetEntryNode> iterator = entryNodes.iterator();
+        while (iterator.hasNext()) {
+            final QName valueQName = QName.create((String) iterator.next().getValue());
+            final Module module = refSchemaCtx.findModuleByNamespaceAndRevision(valueQName.getModule().getNamespace(),
+                    valueQName.getModule().getRevision());
+            Preconditions.checkNotNull(module,
+                    "Module for namespace " + valueQName.getModule().getNamespace() + " does not exist");
+            NotificationDefinition notifiDef = null;
+            for (final NotificationDefinition notification : module.getNotifications()) {
+                if (notification.getQName().equals(valueQName)) {
+                    notifiDef = notification;
+                    break;
+                }
+            }
+            final String moduleName = module.getName();
+            Preconditions.checkNotNull(notifiDef,
+                    "Notification " + valueQName + "doesn't exist in module " + moduleName);
+            paths.add(notifiDef.getPath());
+            streamName = streamName + moduleName + ":" + valueQName.getLocalName();
+            if (iterator.hasNext()) {
+                streamName = streamName + ",";
+            }
+        }
+
+        final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
+        final QName outputQname = QName.create(rpcQName, "output");
+        final QName streamNameQname = QName.create(rpcQName, "notification-stream-identifier");
+
+        final ContainerNode output =
+                ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
+                        .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
+
+        if (!Notificator.existNotificationListenerFor(streamName)) {
+            Notificator.createNotificationListener(paths, streamName, outputType);
+        }
+
+        return new DefaultDOMRpcResult(output);
+    }
 }
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ParametersUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ParametersUtil.java
new file mode 100644 (file)
index 0000000..bd85e54
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 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.restconf.restful.utils;
+
+import com.google.common.collect.Sets;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+
+class ParametersUtil {
+
+    private ParametersUtil() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+    /**
+     * Check if URI does not contain not allowed parameters for specified operation
+     *
+     * @param operationType
+     *            - type of operation (READ, POST, PUT, DELETE...)
+     * @param usedParameters
+     *            - parameters used in URI request
+     * @param allowedParameters
+     *            - allowed parameters for operation
+     */
+    static void checkParametersTypes(@Nonnull final String operationType,
+                                     @Nonnull final Set<String> usedParameters,
+                                     @Nonnull final String... allowedParameters) {
+        final Set<String> notAllowedParameters = Sets.newHashSet(usedParameters);
+        notAllowedParameters.removeAll(Sets.newHashSet(allowedParameters));
+
+        if (!notAllowedParameters.isEmpty()) {
+            throw new RestconfDocumentedException(
+                    "Not allowed parameters for " + operationType + " operation: " + notAllowedParameters,
+                    RestconfError.ErrorType.PROTOCOL,
+                    RestconfError.ErrorTag.INVALID_VALUE);
+        }
+    }
+
+    /**
+     * Check if URI does not contain value for the same parameter more than once
+     *
+     * @param parameterValues
+     *            - URI parameter values
+     * @param parameterName
+     *            - URI parameter name
+     */
+    static void checkParameterCount(@Nonnull final List<String> parameterValues, @Nonnull final String parameterName) {
+        if (parameterValues.size() > 1) {
+            throw new RestconfDocumentedException(
+                    "Parameter " + parameterName + " can appear at most once in request URI",
+                    ErrorType.PROTOCOL,
+                    ErrorTag.INVALID_VALUE);
+        }
+    }
+}
index f0958d79e9345a77d1ec25756802d57c26ef0b34..23ce4f406a0f3d13a175f88ab0219adc5832b217 100644 (file)
@@ -16,15 +16,23 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
 import org.opendaylight.restconf.utils.parser.ParserIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,13 +60,16 @@ public final class PostDataTransactionUtil {
      *            - wrapper for transaction data
      * @param schemaContextRef
      *            - reference to actual {@link SchemaContext}
+     * @param point
+     * @param insert
      * @return {@link CheckedFuture}
      */
     public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload,
-            final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef) {
+            final TransactionVarsWrapper transactionNode, final SchemaContextRef schemaContextRef, final String insert,
+            final String point) {
         final CheckedFuture<Void, TransactionCommitFailedException> future = submitData(
                 payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData(),
-                transactionNode, schemaContextRef.get());
+                transactionNode, schemaContextRef.get(), insert, point);
         final URI location = PostDataTransactionUtil.resolveLocation(uriInfo, transactionNode, schemaContextRef);
         final ResponseFactory dataFactory = new ResponseFactory(null, location);
         FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory);
@@ -76,14 +87,200 @@ public final class PostDataTransactionUtil {
      *            - wrapper for data to transaction
      * @param schemaContext
      *            - schema context of data
+     * @param point
+     *            - query parameter
+     * @param insert
+     *            - query parameter
      * @return {@link CheckedFuture}
      */
     private static CheckedFuture<Void, TransactionCommitFailedException> submitData(final YangInstanceIdentifier path,
             final NormalizedNode<?, ?> data, final TransactionVarsWrapper transactionNode,
-            final SchemaContext schemaContext) {
-        final DOMTransactionChain transactionChain = transactionNode.getTransactionChain();
-        final DOMDataReadWriteTransaction transaction = transactionChain.newReadWriteTransaction();
+            final SchemaContext schemaContext, final String insert, final String point) {
+        final DOMTransactionChain domTransactionChain = transactionNode.getTransactionChain();
+        final DOMDataReadWriteTransaction newReadWriteTransaction = domTransactionChain.newReadWriteTransaction();
+        if (insert == null) {
+            makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+            return newReadWriteTransaction.submit();
+        } else {
+            final DataSchemaNode schemaNode = PutDataTransactionUtil.checkListAndOrderedType(schemaContext, path);
+            switch (insert) {
+                case "first":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final NormalizedNode<?, ?> readData =
+                                PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
+                                        schemaNode);
+                        final OrderedMapNode readList = (OrderedMapNode) readData;
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+                            return newReadWriteTransaction.submit();
+                        } else {
+                            newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
+                                    path.getParent().getParent());
+                            simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+                                    schemaContext, domTransactionChain);
+                            makePost(path, readData, schemaContext, domTransactionChain,
+                                    newReadWriteTransaction);
+                            return newReadWriteTransaction.submit();
+                        }
+                    } else {
+                        final NormalizedNode<?, ?> readData =
+                                PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain, schemaNode);
+
+                        final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+                            return newReadWriteTransaction.submit();
+                        } else {
+                            newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION,
+                                    path.getParent().getParent());
+                            simplePost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+                                    schemaContext, domTransactionChain);
+                            makePost(path, readData, schemaContext, domTransactionChain, newReadWriteTransaction);
+                            return newReadWriteTransaction.submit();
+                        }
+                    }
+                case "last":
+                    makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+                    return newReadWriteTransaction.submit();
+                case "before":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final NormalizedNode<?, ?> readData =
+                                PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
+                                        schemaNode);
+                        final OrderedMapNode readList = (OrderedMapNode) readData;
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+                            return newReadWriteTransaction.submit();
+                        } else {
+                            insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
+                                    data, schemaContext, point, readList, true, domTransactionChain);
+                            return newReadWriteTransaction.submit();
+                        }
+                    } else {
+                        final NormalizedNode<?, ?> readData =
+                                PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
+                                        schemaNode);
+
+                        final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+                            return newReadWriteTransaction.submit();
+                        } else {
+                            insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
+                                    path, data, schemaContext, point, readLeafList, true, domTransactionChain);
+                            return newReadWriteTransaction.submit();
+                        }
+                    }
+                case "after":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final NormalizedNode<?, ?> readData =
+                                PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
+                                        schemaNode);
+                        final OrderedMapNode readList = (OrderedMapNode) readData;
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+                            return newReadWriteTransaction.submit();
+                        } else {
+                            insertWithPointListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path,
+                                    data, schemaContext, point, readList, false, domTransactionChain);
+                            return newReadWriteTransaction.submit();
+                        }
+                    } else {
+                        final NormalizedNode<?, ?> readData =
+                                PutDataTransactionUtil.readList(path.getParent(), schemaContext, domTransactionChain,
+                                        schemaNode);
+
+                        final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            makePost(path, data, schemaContext, domTransactionChain, newReadWriteTransaction);
+                            return newReadWriteTransaction.submit();
+                        } else {
+                            insertWithPointLeafListPost(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION,
+                                    path, data, schemaContext, point, readLeafList, true, domTransactionChain);
+                            return newReadWriteTransaction.submit();
+                        }
+                    }
+                default:
+                    throw new RestconfDocumentedException(
+                            "Used bad value of insert parameter. Possible values are first, last, before or after, "
+                                    + "but was: " + insert);
+            }
+        }
+    }
+
+    private static void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
+            final boolean before, final DOMTransactionChain domTransactionChain) {
+        rWTransaction.delete(datastore, path.getParent().getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree =
+                ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (h == p) {
+                TransactionUtil.checkItemDoesNotExists(domTransactionChain, rWTransaction, datastore, path,
+                        RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+                rWTransaction.put(datastore, path, payload);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
+            TransactionUtil.checkItemDoesNotExists(domTransactionChain, rWTransaction, datastore, childPath,
+                    RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+            rWTransaction.put(datastore, childPath, nodeChild);
+            h++;
+        }
+    }
+
+    private static void insertWithPointListPost(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final SchemaContext schemaContext, final String point, final MapNode readList, final boolean before,
+            final DOMTransactionChain domTransactionChain) {
+        rWTransaction.delete(datastore, path.getParent().getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree =
+                ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (h == p) {
+                TransactionUtil.checkItemDoesNotExists(domTransactionChain, rWTransaction, datastore, path,
+                        RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+                rWTransaction.put(datastore, path, payload);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
+            TransactionUtil.checkItemDoesNotExists(domTransactionChain, rWTransaction, datastore, childPath,
+                    RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+            rWTransaction.put(datastore, childPath, mapEntryNode);
+            h++;
+        }
+    }
 
+    private static void makePost(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data,
+            final SchemaContext schemaContext, final DOMTransactionChain transactionChain,
+            final DOMDataReadWriteTransaction transaction) {
         if (data instanceof MapNode) {
             boolean merge = false;
             for (final MapEntryNode child : ((MapNode) data).getValue()) {
@@ -108,8 +305,6 @@ public final class PostDataTransactionUtil {
             TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
             transaction.put(LogicalDatastoreType.CONFIGURATION, path, data);
         }
-
-        return transaction.submit();
     }
 
     /**
@@ -136,4 +331,13 @@ public final class PostDataTransactionUtil {
 
         return uriBuilder.build();
     }
+
+    private static void simplePost(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final SchemaContext schemaContext, final DOMTransactionChain transactionChain) {
+        TransactionUtil.checkItemDoesNotExists(transactionChain, rWTransaction, datastore, path,
+                RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
+        rWTransaction.put(datastore, path, payload);
+    }
 }
index 8db0b433f40507cce4d20c9aa48c018575a19b9a..19fb343d11e39c40ed9e86bdfde21c61b6ebed7f 100644 (file)
@@ -14,8 +14,11 @@ import java.util.Map;
 import javax.ws.rs.core.Response;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.netconf.md.sal.rest.common.RestconfValidationUtils;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
@@ -27,8 +30,18 @@ import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
@@ -131,15 +144,20 @@ public final class PutDataTransactionUtil {
      * @param schemaCtxRef
      *            - reference to {@link SchemaContext}
      * @param transactionNode
+     *            - wrapper of variables for transaction
+     * @param point
+     *            - query parameter
+     * @param insert
+     *            - query parameter
      * @return {@link CheckedFuture}
      */
     public static Response putData(final NormalizedNodeContext payload,
-            final SchemaContextRef schemaCtxRef, final TransactionVarsWrapper transactionNode) {
+            final SchemaContextRef schemaCtxRef, final TransactionVarsWrapper transactionNode, final String insert, final String point) {
         final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
         final ResponseFactory responseFactory = new ResponseFactory(
                 ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode));
         final CheckedFuture<Void, TransactionCommitFailedException> submitData = submitData(path, schemaCtxRef.get(),
-                transactionNode.getTransactionChain().newWriteOnlyTransaction(), payload.getData());
+                transactionNode.getTransactionChain(), payload.getData(), insert, point);
         FutureCallbackTx.addCallback(submitData, RestconfDataServiceConstant.PutData.PUT_TX_TYPE, responseFactory);
         return responseFactory.build();
     }
@@ -151,17 +169,241 @@ public final class PutDataTransactionUtil {
      *            - path of data
      * @param schemaContext
      *            - {@link SchemaContext}
-     * @param writeTx
+     * @param domTransactionChain
      *            - write transaction
      * @param data
      *            - data
+     * @param point
+     *            - query parameter
+     * @param insert
+     *            - query parameter
      * @return {@link CheckedFuture}
      */
     private static CheckedFuture<Void, TransactionCommitFailedException> submitData(final YangInstanceIdentifier path,
-            final SchemaContext schemaContext,
-            final DOMDataWriteTransaction writeTx, final NormalizedNode<?, ?> data) {
+            final SchemaContext schemaContext, final DOMTransactionChain domTransactionChain,
+            final NormalizedNode<?, ?> data, final String insert, final String point) {
+        final DOMDataReadWriteTransaction newReadWriteTransaction = domTransactionChain.newReadWriteTransaction();
+        if (insert == null) {
+            return makePut(path, schemaContext, newReadWriteTransaction, data);
+        } else {
+            final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
+            switch (insert) {
+                case "first":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+                        final OrderedMapNode readList = (OrderedMapNode) readData;
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
+                            simplePut(LogicalDatastoreType.CONFIGURATION, path, newReadWriteTransaction, schemaContext, data);
+                            listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), newReadWriteTransaction, schemaContext,
+                                    readList);
+                            return newReadWriteTransaction.submit();
+                        }
+                    } else {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+
+                        final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            newReadWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, path.getParent());
+                            simplePut(LogicalDatastoreType.CONFIGURATION, path, newReadWriteTransaction, schemaContext, data);
+                            listPut(LogicalDatastoreType.CONFIGURATION, path.getParent(), newReadWriteTransaction, schemaContext,
+                                    readLeafList);
+                            return newReadWriteTransaction.submit();
+                        }
+                    }
+                case "last":
+                    return makePut(path, schemaContext, newReadWriteTransaction, data);
+                case "before":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+                        final OrderedMapNode readList = (OrderedMapNode) readData;
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            insertWithPointListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+                                    schemaContext, point, readList, true);
+                            return newReadWriteTransaction.submit();
+                        }
+                    } else {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+
+                        final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            insertWithPointLeafListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+                                    schemaContext, point, readLeafList, true);
+                            return newReadWriteTransaction.submit();
+                        }
+                    }
+                case "after":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+                        final OrderedMapNode readList = (OrderedMapNode) readData;
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            insertWithPointListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+                                    schemaContext, point, readList, false);
+                            return newReadWriteTransaction.submit();
+                        }
+                    } else {
+                        final NormalizedNode<?, ?> readData =
+                                readList(path, schemaContext, domTransactionChain, schemaNode);
+
+                        final OrderedLeafSetNode<?> readLeafList = (OrderedLeafSetNode<?>) readData;
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            return makePut(path, schemaContext, newReadWriteTransaction, data);
+                        } else {
+                            insertWithPointLeafListPut(newReadWriteTransaction, LogicalDatastoreType.CONFIGURATION, path, data,
+                                    schemaContext, point, readLeafList, true);
+                            return newReadWriteTransaction.submit();
+                        }
+                    }
+                default:
+                    throw new RestconfDocumentedException(
+                            "Used bad value of insert parameter. Possible values are first, last, before or after, "
+                                    + "but was: " + insert);
+            }
+        }
+    }
+
+    public static NormalizedNode<?, ?> readList(final YangInstanceIdentifier path, final SchemaContext schemaContext,
+            final DOMTransactionChain domTransactionChain, final DataSchemaNode schemaNode) {
+        final InstanceIdentifierContext<?> iid = new InstanceIdentifierContext<SchemaNode>(
+                path.getParent(), schemaNode, null, schemaContext);
+        final TransactionVarsWrapper transactionNode =
+                new TransactionVarsWrapper(iid, null, domTransactionChain);
+        final NormalizedNode<?, ?> readData = ReadDataTransactionUtil
+                .readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode);
+        return readData;
+    }
+
+    private static void insertWithPointLeafListPut(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data, final SchemaContext schemaContext, final String point,
+            final OrderedLeafSetNode<?> readLeafList, final boolean before) {
+        rWTransaction.delete(datastore, path.getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (h == p) {
+                simplePut(datastore, path, rWTransaction, schemaContext, data);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
+            rWTransaction.put(datastore, childPath, nodeChild);
+            h++;
+        }
+    }
+
+    private static void insertWithPointListPut(final DOMDataReadWriteTransaction writeTx,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+            final NormalizedNode<?, ?> data, final SchemaContext schemaContext, final String point,
+            final OrderedMapNode readList, final boolean before) {
+        writeTx.delete(datastore, path.getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+        writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (h == p) {
+                simplePut(datastore, path, writeTx, schemaContext, data);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
+            writeTx.put(datastore, childPath, mapEntryNode);
+            h++;
+        }
+    }
+
+    private static void listPut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+            final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
+            final OrderedLeafSetNode<?> payload) {
+        final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+        writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
+        for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) payload).getValue()) {
+            final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+            writeTx.put(datastore, childPath, child);
+        }
+    }
+
+    private static void listPut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+            final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
+            final OrderedMapNode payload) {
+        final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+        writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
+        for (final MapEntryNode child : ((MapNode) payload).getValue()) {
+            final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+            writeTx.put(datastore, childPath, child);
+        }
+    }
+
+    private static void simplePut(final LogicalDatastoreType configuration, final YangInstanceIdentifier path,
+            final DOMDataReadWriteTransaction writeTx, final SchemaContext schemaContext,
+            final NormalizedNode<?, ?> data) {
+        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
+        writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
+    }
+
+    private static CheckedFuture<Void, TransactionCommitFailedException> makePut(final YangInstanceIdentifier path,
+            final SchemaContext schemaContext, final DOMDataWriteTransaction writeTx, final NormalizedNode<?, ?> data) {
         TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTx);
         writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
         return writeTx.submit();
     }
+
+    public static DataSchemaNode checkListAndOrderedType(final SchemaContext ctx, final YangInstanceIdentifier path) {
+        final YangInstanceIdentifier parent = path.getParent();
+        final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
+        final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
+
+        if (dataSchemaNode instanceof ListSchemaNode) {
+            if (!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
+                throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
+            }
+            return dataSchemaNode;
+        }
+        if (dataSchemaNode instanceof LeafListSchemaNode) {
+            if (!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
+                throw new RestconfDocumentedException(
+                        "Insert parameter can be used only with ordered-by user leaf-list.");
+            }
+            return dataSchemaNode;
+        }
+        throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
+    }
 }
index fe840990ed1f4db44d5eb076c9e3dc9aea81170b..56cb54627821fdf4d1aafffb0d69a57652dfc7c7 100644 (file)
@@ -8,20 +8,30 @@
 package org.opendaylight.restconf.restful.utils;
 
 import com.google.common.base.Optional;
+import com.google.common.primitives.Ints;
 import com.google.common.util.concurrent.CheckedFuture;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import javax.ws.rs.core.UriInfo;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
-import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
-import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
+import org.opendaylight.netconf.sal.restconf.impl.WriterParameters;
+import org.opendaylight.netconf.sal.restconf.impl.WriterParameters.WriterParametersBuilder;
 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
+import org.opendaylight.restconf.utils.parser.ParserFieldsParameter;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
@@ -29,6 +39,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
@@ -38,8 +49,15 @@ import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+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.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
  * Util class for read data from data store via transaction.
@@ -56,6 +74,110 @@ public final class ReadDataTransactionUtil {
         throw new UnsupportedOperationException("Util class.");
     }
 
+    /**
+     * Parse parameters from URI request and check their types and values.
+     *
+     *
+     * @param identifier
+     *            - {@link InstanceIdentifierContext}
+     * @param uriInfo
+     *            - URI info
+     * @param tagged
+     *            - set tagged for {@link WriterParameters}
+     * @return {@link WriterParameters}
+     */
+    public static @Nonnull WriterParameters parseUriParameters(@Nonnull final InstanceIdentifierContext<?> identifier,
+            @Nullable final UriInfo uriInfo, final boolean tagged) {
+        return parseParams(identifier, uriInfo, tagged);
+    }
+
+    /**
+     * Parse parameters from URI request and check their types and values.
+     *
+     *
+     * @param identifier
+     *            - {@link InstanceIdentifierContext}
+     * @param uriInfo
+     *            - URI info
+     * @return {@link WriterParameters}
+     */
+    public static @Nonnull WriterParameters parseUriParameters(@Nonnull final InstanceIdentifierContext<?> identifier,
+                                                               @Nullable final UriInfo uriInfo) {
+        return parseParams(identifier, uriInfo, false);
+    }
+
+    private static WriterParameters parseParams(final InstanceIdentifierContext<?> identifier, final UriInfo uriInfo,
+            final boolean tagged) {
+        final WriterParametersBuilder builder = new WriterParametersBuilder();
+        builder.setTagged(tagged);
+
+        if (uriInfo == null) {
+            return builder.build();
+        }
+
+        // check only allowed parameters
+        ParametersUtil.checkParametersTypes(
+                RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+                uriInfo.getQueryParameters().keySet(),
+                RestconfDataServiceConstant.ReadData.CONTENT,
+                RestconfDataServiceConstant.ReadData.DEPTH,
+                RestconfDataServiceConstant.ReadData.FIELDS, RestconfDataServiceConstant.ReadData.WITH_DEFAULTS);
+
+        // read parameters from URI or set default values
+        final List<String> content = uriInfo.getQueryParameters().getOrDefault(
+                RestconfDataServiceConstant.ReadData.CONTENT,
+                Collections.singletonList(RestconfDataServiceConstant.ReadData.ALL));
+        final List<String> depth = uriInfo.getQueryParameters().getOrDefault(
+                RestconfDataServiceConstant.ReadData.DEPTH,
+                Collections.singletonList(RestconfDataServiceConstant.ReadData.UNBOUNDED));
+        // fields
+        final List<String> fields = uriInfo.getQueryParameters().getOrDefault(
+                RestconfDataServiceConstant.ReadData.FIELDS,
+                Collections.emptyList());
+
+        // parameter can be in URI at most once
+        ParametersUtil.checkParameterCount(content, RestconfDataServiceConstant.ReadData.CONTENT);
+        ParametersUtil.checkParameterCount(depth, RestconfDataServiceConstant.ReadData.DEPTH);
+        ParametersUtil.checkParameterCount(fields, RestconfDataServiceConstant.ReadData.FIELDS);
+
+        // check and set content
+        final String contentValue = content.get(0);
+        if (!contentValue.equals(RestconfDataServiceConstant.ReadData.ALL)) {
+            if (!contentValue.equals(RestconfDataServiceConstant.ReadData.CONFIG)
+                    && !contentValue.equals(RestconfDataServiceConstant.ReadData.NONCONFIG)) {
+                throw new RestconfDocumentedException(
+                        new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
+                                "Invalid content parameter: " + contentValue, null,
+                                "The content parameter value must be either config, nonconfig or all (default)"));
+            }
+        }
+
+        builder.setContent(content.get(0));
+
+        // check and set depth
+        if (!depth.get(0).equals(RestconfDataServiceConstant.ReadData.UNBOUNDED)) {
+            final Integer value = Ints.tryParse(depth.get(0));
+
+            if ((value == null)
+                    || (!((value >= RestconfDataServiceConstant.ReadData.MIN_DEPTH)
+                        && (value <= RestconfDataServiceConstant.ReadData.MAX_DEPTH)))) {
+                throw new RestconfDocumentedException(
+                        new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
+                                "Invalid depth parameter: " + depth, null,
+                                "The depth parameter must be an integer between 1 and 65535 or \"unbounded\""));
+            } else {
+                builder.setDepth(value);
+            }
+        }
+
+        // check and set fields
+        if (!fields.isEmpty()) {
+            builder.setFields(ParserFieldsParameter.parseFieldsParameter(identifier, fields.get(0)));
+        }
+
+        return builder.build();
+    }
+
     /**
      * Read specific type of data from data store via transaction.
      *
@@ -65,31 +187,170 @@ public final class ReadDataTransactionUtil {
      *            - {@link TransactionVarsWrapper} - wrapper for variables
      * @return {@link NormalizedNode}
      */
-    public static @Nullable NormalizedNode<?, ?> readData(@Nullable final String valueOfContent,
-                                                          @Nonnull final TransactionVarsWrapper transactionNode) {
-        final NormalizedNode<?, ?> data;
-        if (valueOfContent != null) {
-            switch (valueOfContent) {
-                case RestconfDataServiceConstant.ReadData.CONFIG:
-                    transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
-                    data = readDataViaTransaction(transactionNode);
-                    break;
-                case RestconfDataServiceConstant.ReadData.NONCONFIG:
-                    transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
-                    data = readDataViaTransaction(transactionNode);
-                    break;
-                case RestconfDataServiceConstant.ReadData.ALL:
-                    data = readAllData(transactionNode);
-                    break;
-                default:
-                    throw new RestconfDocumentedException("Bad query parameter for content.", ErrorType.APPLICATION,
-                            ErrorTag.INVALID_VALUE);
-            }
+    public static @Nullable NormalizedNode<?, ?> readData(@Nonnull final String valueOfContent,
+            @Nonnull final TransactionVarsWrapper transactionNode) {
+        return readData(valueOfContent, transactionNode, null);
+    }
+
+    /**
+     * Read specific type of data from data store via transaction.
+     *
+     * @param valueOfContent
+     *            - type of data to read (config, state, all)
+     * @param transactionNode
+     *            - {@link TransactionVarsWrapper} - wrapper for variables
+     * @param withDefa
+     *            - vaule of with-defaults parameter
+     * @return {@link NormalizedNode}
+     */
+    public static @Nullable NormalizedNode<?, ?> readData(@Nonnull final String valueOfContent,
+            @Nonnull final TransactionVarsWrapper transactionNode, final String withDefa) {
+        switch (valueOfContent) {
+            case RestconfDataServiceConstant.ReadData.CONFIG:
+                transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
+                if (withDefa == null) {
+                    return readDataViaTransaction(transactionNode);
+                } else {
+                    return prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
+                            transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa);
+                }
+            case RestconfDataServiceConstant.ReadData.NONCONFIG:
+                transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
+                return readDataViaTransaction(transactionNode);
+
+            case RestconfDataServiceConstant.ReadData.ALL:
+                return readAllData(transactionNode, withDefa);
+
+            default:
+                throw new RestconfDocumentedException(
+                        new RestconfError(RestconfError.ErrorType.PROTOCOL, RestconfError.ErrorTag.INVALID_VALUE,
+                                "Invalid content parameter: " + valueOfContent, null,
+                                "The content parameter value must be either config, nonconfig or all (default)"));
+        }
+    }
+
+    private static NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
+            final YangInstanceIdentifier path, final String withDefa) {
+        boolean trim;
+        switch (withDefa) {
+            case "trim":
+                trim = true;
+                break;
+            case "explicit":
+                trim = false;
+                break;
+            default:
+                throw new RestconfDocumentedException("");
+        }
+
+        final SchemaContext ctx = ControllerContext.getInstance().getGlobalSchema();
+        final DataSchemaContextTree baseSchemaCtxTree = DataSchemaContextTree.from(ctx);
+        final DataSchemaNode baseSchemaNode = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+        if (result instanceof ContainerNode) {
+            final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder =
+                    Builders.containerBuilder((ContainerSchemaNode) baseSchemaNode);
+            buildCont(builder, (ContainerNode) result, baseSchemaCtxTree, path, trim);
+            return builder.build();
         } else {
-            data = readAllData(transactionNode);
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
+                    Builders.mapEntryBuilder((ListSchemaNode) baseSchemaNode);
+            buildMapEntryBuilder(builder, (MapEntryNode) result, baseSchemaCtxTree, path, trim,
+                    ((ListSchemaNode) baseSchemaNode).getKeyDefinition());
+            return builder.build();
+        }
+    }
+
+    private static void buildMapEntryBuilder(
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder,
+            final MapEntryNode result, final DataSchemaContextTree baseSchemaCtxTree,
+            final YangInstanceIdentifier actualPath, final boolean trim, final List<QName> keys) {
+        for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
+            final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
+            final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+            if (child instanceof ContainerNode) {
+                final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> childBuilder =
+                        Builders.containerBuilder((ContainerSchemaNode) childSchema);
+                buildCont(childBuilder, (ContainerNode) child, baseSchemaCtxTree, path, trim);
+                builder.withChild(childBuilder.build());
+            } else if (child instanceof MapNode) {
+                final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
+                        Builders.mapBuilder((ListSchemaNode) childSchema);
+                buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
+                        ((ListSchemaNode) childSchema).getKeyDefinition());
+                builder.withChild(childBuilder.build());
+            } else if (child instanceof LeafNode) {
+                final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
+                final String nodeVal = ((LeafNode<String>) child).getValue();
+                final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
+                        Builders.leafBuilder((LeafSchemaNode) childSchema);
+                if (keys.contains(child.getNodeType())) {
+                    leafBuilder.withValue(((LeafNode) child).getValue());
+                    builder.withChild(leafBuilder.build());
+                } else {
+                    if (trim) {
+                        if ((defaultVal == null) || !defaultVal.equals(nodeVal)) {
+                            leafBuilder.withValue(((LeafNode) child).getValue());
+                            builder.withChild(leafBuilder.build());
+                        }
+                    } else {
+                        if ((defaultVal != null) && defaultVal.equals(nodeVal)) {
+                            leafBuilder.withValue(((LeafNode) child).getValue());
+                            builder.withChild(leafBuilder.build());
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private static void buildList(final CollectionNodeBuilder<MapEntryNode, MapNode> builder, final MapNode result,
+            final DataSchemaContextTree baseSchemaCtxTree, final YangInstanceIdentifier path, final boolean trim,
+            final List<QName> keys) {
+        for (final MapEntryNode mapEntryNode : result.getValue()) {
+            final YangInstanceIdentifier actualNode = path.node(mapEntryNode.getIdentifier());
+            final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(actualNode).getDataSchemaNode();
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
+                    Builders.mapEntryBuilder((ListSchemaNode) childSchema);
+            buildMapEntryBuilder(mapEntryBuilder, mapEntryNode, baseSchemaCtxTree, actualNode, trim, keys);
+            builder.withChild(mapEntryBuilder.build());
         }
+    }
 
-        return data;
+    private static void buildCont(final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder,
+            final ContainerNode result, final DataSchemaContextTree baseSchemaCtxTree,
+            final YangInstanceIdentifier actualPath, final boolean trim) {
+        for (final DataContainerChild<? extends PathArgument, ?> child : result.getValue()) {
+            final YangInstanceIdentifier path = actualPath.node(child.getIdentifier());
+            final DataSchemaNode childSchema = baseSchemaCtxTree.getChild(path).getDataSchemaNode();
+            if (child instanceof ContainerNode) {
+                final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builderChild =
+                        Builders.containerBuilder((ContainerSchemaNode) childSchema);
+                buildCont(builderChild, result, baseSchemaCtxTree, actualPath, trim);
+                builder.withChild(builderChild.build());
+            } else if (child instanceof MapNode) {
+                final CollectionNodeBuilder<MapEntryNode, MapNode> childBuilder =
+                        Builders.mapBuilder((ListSchemaNode) childSchema);
+                buildList(childBuilder, (MapNode) child, baseSchemaCtxTree, path, trim,
+                        ((ListSchemaNode) childSchema).getKeyDefinition());
+                builder.withChild(childBuilder.build());
+            } else if (child instanceof LeafNode) {
+                final String defaultVal = ((LeafSchemaNode) childSchema).getDefault();
+                final String nodeVal = ((LeafNode<String>) child).getValue();
+                final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> leafBuilder =
+                        Builders.leafBuilder((LeafSchemaNode) childSchema);
+                if (trim) {
+                    if ((defaultVal == null) || !defaultVal.equals(nodeVal)) {
+                        leafBuilder.withValue(((LeafNode) child).getValue());
+                        builder.withChild(leafBuilder.build());
+                    }
+                } else {
+                    if ((defaultVal != null) && defaultVal.equals(nodeVal)) {
+                        leafBuilder.withValue(((LeafNode) child).getValue());
+                        builder.withChild(leafBuilder.build());
+                    }
+                }
+            }
+        }
     }
 
     /**
@@ -117,16 +378,24 @@ public final class ReadDataTransactionUtil {
      *
      * @param transactionNode
      *            - {@link TransactionVarsWrapper} - wrapper for variables
+     * @param withDefa
      * @return {@link NormalizedNode}
      */
-    private static @Nullable NormalizedNode<?, ?> readAllData(@Nonnull final TransactionVarsWrapper transactionNode) {
+    private static @Nullable NormalizedNode<?, ?> readAllData(@Nonnull final TransactionVarsWrapper transactionNode,
+            final String withDefa) {
         // PREPARE STATE DATA NODE
         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
         final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode);
 
         // PREPARE CONFIG DATA NODE
         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
-        final NormalizedNode<?, ?> configDataNode = readDataViaTransaction(transactionNode);
+        final NormalizedNode<?, ?> configDataNode;
+        if (withDefa == null) {
+            configDataNode = readDataViaTransaction(transactionNode);
+        } else {
+            configDataNode = prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
+                    transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa);
+        }
 
         // if no data exists
         if ((stateDataNode == null) && (configDataNode == null)) {
index bc9f4d64633708db728cd9edc5844df8fbd1ad78..a2ef70b9a854010a40a16fba46c9e65070cc2e87 100644 (file)
@@ -21,7 +21,6 @@ import org.opendaylight.yangtools.yang.common.QNameModule;
  */
 public final class RestconfDataServiceConstant {
 
-    public static final String CONTENT = "content";
     public static final QName NETCONF_BASE_QNAME;
     static {
         try {
@@ -43,11 +42,23 @@ public final class RestconfDataServiceConstant {
      *
      */
     public final class ReadData {
+        // URI parameters
+        public static final String CONTENT = "content";
+        public static final String DEPTH = "depth";
+        public static final String FIELDS = "fields";
 
+        // content values
         public static final String CONFIG = "config";
-        public static final String NONCONFIG = "nonconfig";
         public static final String ALL = "all";
+        public static final String NONCONFIG = "nonconfig";
+
+        // depth values
+        public static final String UNBOUNDED = "unbounded";
+        public static final int MIN_DEPTH = 1;
+        public static final int MAX_DEPTH = 65535;
+
         public static final String READ_TYPE_TX = "READ";
+        public static final String WITH_DEFAULTS = "with-defaults";
 
         private ReadData() {
             throw new UnsupportedOperationException("Util class.");
index c5dc5d251334c8cf953427671dcb218aabd7b206..09da9e8983a61a5767f7c0920e9b914b1c680eaf 100644 (file)
@@ -59,6 +59,12 @@ public final class RestconfStreamsConstants {
 
     public static final String SCHEMA_SUBSCIBRE_URI = "ws";
 
+    public static final CharSequence DATA_SUBSCR = "data-change-event-subscription";
+    public static final CharSequence CREATE_DATA_SUBSCR = "create-" + DATA_SUBSCR;
+
+    public static final CharSequence NOTIFICATION_STREAM = "notification-stream";
+    public static final CharSequence CREATE_NOTIFICATION_STREAM = "create-" + NOTIFICATION_STREAM;
+
     static {
         Date eventSubscriptionAugRevision;
         try {
@@ -70,7 +76,8 @@ public final class RestconfStreamsConstants {
         }
         SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, eventSubscriptionAugRevision);
         SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets
-                .newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore")));
+                .newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore"),
+                        QName.create(SAL_REMOTE_AUGMENT, "notification-output-type")));
     }
 
     private RestconfStreamsConstants() {
index c07551c182ed62e323ff455ca94621ed913b60f8..f6d45e56258e22ae4ef6aa37b2cec16335a2d19c 100644 (file)
@@ -7,17 +7,49 @@
  */
 package org.opendaylight.restconf.restful.utils;
 
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import java.net.URI;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.Notificator;
 import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer;
+import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
+import org.opendaylight.restconf.handlers.NotificationServiceHandler;
+import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.utils.RestconfConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Subscribe to stream util class
@@ -25,6 +57,8 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
  */
 public final class SubscribeToStreamUtil {
 
+    private static final Logger LOG = LoggerFactory.getLogger(SubscribeToStreamUtil.class);
+
     private SubscribeToStreamUtil() {
         throw new UnsupportedOperationException("Util class");
     }
@@ -77,7 +111,7 @@ public final class SubscribeToStreamUtil {
      * @param domDataBroker
      *            - data broker for register data change listener
      */
-    public static void registration(final LogicalDatastoreType ds, final DataChangeScope scope,
+    private static void registration(final LogicalDatastoreType ds, final DataChangeScope scope,
             final ListenerAdapter listener, final DOMDataBroker domDataBroker) {
         if (listener.isListening()) {
             return;
@@ -95,7 +129,7 @@ public final class SubscribeToStreamUtil {
      *
      * @return port
      */
-    public static int prepareNotificationPort() {
+    private static int prepareNotificationPort() {
         int port = RestconfStreamsConstants.NOTIFICATION_PORT;
         try {
             final WebSocketServer webSocketServer = WebSocketServer.getInstance();
@@ -106,4 +140,164 @@ public final class SubscribeToStreamUtil {
         return port;
     }
 
+    /**
+     * Register listeners by streamName in identifier to listen to yang notifications
+     *
+     * @param identifier
+     *            - identifier as stream name
+     * @param uriInfo
+     *            - for getting base URI information
+     * @param start
+     *            - start-time query parameter
+     * @param stop
+     *            - stop-time query parameter
+     * @param notifiServiceHandler
+     *            - DOMNotificationService handler for register listeners
+     * @param filter
+     *            - indicate which subset of all possible events are of interest
+     * @return location for listening
+     */
+    public static URI notifStream(final String identifier, final UriInfo uriInfo, final Date start, final Date stop,
+            final NotificationServiceHandler notifiServiceHandler, final String filter) {
+        final String streamName = Notificator.createStreamNameFromUri(identifier);
+        if (Strings.isNullOrEmpty(streamName)) {
+            throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+        }
+        final List<NotificationListenerAdapter> listeners = Notificator.getNotificationListenerFor(streamName);
+        if ((listeners == null) || listeners.isEmpty()) {
+            throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL,
+                    ErrorTag.UNKNOWN_ELEMENT);
+        }
+
+        for (final NotificationListenerAdapter listener : listeners) {
+            registerToListenNotification(listener, notifiServiceHandler);
+            listener.setQueryParams(start, stop, filter);
+        }
+
+        final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
+        int notificationPort = RestconfStreamsConstants.NOTIFICATION_PORT;
+        try {
+            final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
+            notificationPort = webSocketServerInstance.getPort();
+        } catch (final NullPointerException e) {
+            WebSocketServer.createInstance(RestconfStreamsConstants.NOTIFICATION_PORT);
+        }
+        final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
+        final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
+
+        return uriToWebsocketServer;
+    }
+
+    private static void registerToListenNotification(final NotificationListenerAdapter listener,
+            final NotificationServiceHandler notificationServiceHandler) {
+        if (listener.isListening()) {
+            return;
+        }
+
+        final SchemaPath path = listener.getSchemaPath();
+        final ListenerRegistration<DOMNotificationListener> registration =
+                notificationServiceHandler.get().registerNotificationListener(listener, path);
+
+        listener.setRegistration(registration);
+    }
+
+    /**
+     * Prepare InstanceIdentifierContext for Location leaf
+     *
+     * @param schemaHandler
+     *            - schemaContext handler
+     * @return InstanceIdentifier of Location leaf
+     */
+    public static InstanceIdentifierContext<?> prepareIIDSubsStreamOutput(final SchemaContextHandler schemaHandler) {
+        final QName qnameBase = QName.create("subscribe:to:notification", "2016-10-28", "notifi");
+        final DataSchemaNode location = ((ContainerSchemaNode) schemaHandler.get()
+                .findModuleByNamespaceAndRevision(qnameBase.getNamespace(), qnameBase.getRevision())
+                .getDataChildByName(qnameBase)).getDataChildByName(QName.create(qnameBase, "location"));
+        final List<PathArgument> path = new ArrayList<>();
+        path.add(NodeIdentifier.create(qnameBase));
+        path.add(NodeIdentifier.create(QName.create(qnameBase, "location")));
+
+        return new InstanceIdentifierContext<SchemaNode>(YangInstanceIdentifier.create(path), location, null,
+                schemaHandler.get());
+    }
+
+    /**
+     * Register listener by streamName in identifier to listen to yang notifications
+     *
+     * @param identifier
+     *            - identifier as stream name
+     * @param uriInfo
+     *            - for getting base URI information
+     * @param start
+     *            - start-time query parameter
+     * @param stop
+     *            - stop-time query parameter
+     * @param domDataBrokerHandler
+     *            - DOMDataBroker handler for register listener
+     * @param filter
+     *            - indicate which subset of all possible events are of interest
+     * @return location for listening
+     */
+    public static URI dataSubs(final String identifier, final UriInfo uriInfo, final Date start, final Date stop,
+            final DOMDataBrokerHandler domDataBrokerHandler, final String filter) {
+        final Map<String, String> mapOfValues = SubscribeToStreamUtil.mapValuesFromUri(identifier);
+
+        final LogicalDatastoreType ds = SubscribeToStreamUtil.parseURIEnum(LogicalDatastoreType.class,
+                mapOfValues.get(RestconfStreamsConstants.DATASTORE_PARAM_NAME));
+        if (ds == null) {
+            final String msg = "Stream name doesn't contains datastore value (pattern /datastore=)";
+            LOG.debug(msg);
+            throw new RestconfDocumentedException(msg, ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
+        }
+
+        final DataChangeScope scope = SubscribeToStreamUtil.parseURIEnum(DataChangeScope.class,
+                mapOfValues.get(RestconfStreamsConstants.SCOPE_PARAM_NAME));
+        if (scope == null) {
+            final String msg = "Stream name doesn't contains datastore value (pattern /scope=)";
+            LOG.warn(msg);
+            throw new RestconfDocumentedException(msg, ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
+        }
+
+        final String streamName = Notificator.createStreamNameFromUri(identifier);
+
+        final ListenerAdapter listener = Notificator.getListenerFor(streamName);
+        Preconditions.checkNotNull(listener, "Listener doesn't exist : " + streamName);
+
+        listener.setQueryParams(start, stop, filter);
+
+        SubscribeToStreamUtil.registration(ds, scope, listener, domDataBrokerHandler.get());
+
+        final int port = SubscribeToStreamUtil.prepareNotificationPort();
+
+        final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
+        final UriBuilder uriToWebSocketServer =
+                uriBuilder.port(port).scheme(RestconfStreamsConstants.SCHEMA_SUBSCIBRE_URI);
+        return uriToWebSocketServer.replacePath(streamName).build();
+    }
+
+    public static Date parseDateFromQueryParam(final Entry<String, List<String>> entry) {
+        final DateAndTime event = new DateAndTime(entry.getValue().iterator().next());
+        String numOf_ms = "";
+        final String value = event.getValue();
+        if (value.contains(".")) {
+            numOf_ms = numOf_ms + ".";
+            final int lastChar = value.contains("Z") ? value.indexOf("Z") : (value.contains("+") ? value.indexOf("+")
+                    : (value.contains("-") ? value.indexOf("-") : value.length()));
+            for (int i = 0; i < (lastChar - value.indexOf(".") - 1); i++) {
+                numOf_ms = numOf_ms + "S";
+            }
+        }
+        String zone = "";
+        if (!value.contains("Z")) {
+            zone = zone + "XXX";
+        }
+        final DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" + numOf_ms + zone);
+
+        try {
+            return dateFormatter.parse(value.contains("Z") ? value.replace('T', ' ').substring(0, value.indexOf("Z"))
+                    : value.replace('T', ' '));
+        } catch (final ParseException e) {
+            throw new RestconfDocumentedException("Cannot parse of value in date: " + value + e);
+        }
+    }
 }
index ded6d9739a71e36eca2857d088e4054d4cf3aa74..0eb2e48a9b936d2e3af242d9a7a86a6b7d0263b8 100644 (file)
@@ -10,7 +10,7 @@ package org.opendaylight.restconf.utils.mapping;
 import com.google.common.base.Preconditions;
 import java.util.Collection;
 import java.util.Set;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
@@ -50,7 +50,7 @@ public final class RestconfMappingNodeUtil {
      */
     public static MapNode restconfMappingNode(final Module restconfModule, final Set<Module> modules) {
         final DataSchemaNode modulListSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode(restconfModule,
-                Draft17.RestconfModule.MODULE_LIST_SCHEMA_NODE);
+                Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE);
         Preconditions.checkState(modulListSchemaNode instanceof ListSchemaNode);
 
         final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserFieldsParameter.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/parser/ParserFieldsParameter.java
new file mode 100644 (file)
index 0000000..9b0270e
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2016 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.restconf.utils.parser;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants.Deserializer;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class ParserFieldsParameter {
+    private static final char COLON = ':';
+    private static final char SEMICOLON = ';';
+    private static final char SLASH = '/';
+    private static final char STARTING_PARENTHESIS = '(';
+    private static final char CLOSING_PARENTHESIS = ')';
+
+    /**
+     * Parse fields parameter and return complete list of child nodes organized into levels.
+     * @param identifier identifier context created from request URI
+     * @param input input value of fields parameter
+     * @return {@link List}
+     */
+    public static @Nonnull List<Set<QName>> parseFieldsParameter(@Nonnull final InstanceIdentifierContext<?> identifier,
+                                                                 @Nonnull final String input) {
+        final List<Set<QName>> parsed = new ArrayList<>();
+        final SchemaContext context = identifier.getSchemaContext();
+        final QNameModule startQNameModule = identifier.getSchemaNode().getQName().getModule();
+        final DataSchemaContextNode<?> startNode = DataSchemaContextNode.fromDataSchemaNode(
+                (DataSchemaNode) identifier.getSchemaNode());
+
+        if (startNode == null) {
+            throw new RestconfDocumentedException(
+                    "Start node missing in " + input, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+        }
+
+        parseInput(input, startQNameModule, startNode, parsed, context);
+        return parsed;
+    }
+
+    /**
+     * Parse input value of fields parameter and create list of sets. Each set represents one level of child nodes.
+     * @param input input value of fields parameter
+     * @param startQNameModule starting qname module
+     * @param startNode starting node
+     * @param parsed list of results
+     * @param context schema context
+     */
+    private static void parseInput(@Nonnull final String input,
+                                   @Nonnull final QNameModule startQNameModule,
+                                   @Nonnull final DataSchemaContextNode<?> startNode,
+                                   @Nonnull final List<Set<QName>> parsed,
+                                   @Nonnull final SchemaContext context) {
+        int currentPosition = 0;
+        int startPosition = 0;
+        DataSchemaContextNode<?> currentNode = startNode;
+        QNameModule currentQNameModule = startQNameModule;
+
+        Set<QName> currentLevel = new HashSet<>();
+        parsed.add(currentLevel);
+
+        while (currentPosition < input.length()) {
+            final char currentChar = input.charAt(currentPosition);
+
+            if (Deserializer.IDENTIFIER.matches(currentChar) || currentChar == '/') {
+                if (currentChar == SLASH) {
+                    // add parsed identifier to results for current level
+                    currentNode = addChildToResult(
+                            currentNode,
+                            input.substring(startPosition, currentPosition), currentQNameModule, currentLevel);
+                    // go one level down
+                    currentLevel = new HashSet<>();
+                    parsed.add(currentLevel);
+
+                    currentPosition++;
+                    startPosition = currentPosition;
+                } else {
+                    currentPosition++;
+                }
+
+                continue;
+            }
+
+            switch (currentChar) {
+                case COLON :
+                    // new namespace and revision found
+                    currentQNameModule = context.findModuleByName(
+                            input.substring(startPosition, currentPosition), null).getQNameModule();
+                    currentPosition++;
+                    break;
+                case STARTING_PARENTHESIS:
+                    // add current child to parsed results for current level
+                    final DataSchemaContextNode<?> child = addChildToResult(
+                            currentNode,
+                            input.substring(startPosition, currentPosition), currentQNameModule, currentLevel);
+                    // call with child node as new start node for one level down
+                    int closingParenthesis = currentPosition
+                            + findClosingParenthesis(input.substring(currentPosition + 1));
+                    parseInput(
+                            input.substring(currentPosition + 1, closingParenthesis),
+                            currentQNameModule,
+                            child,
+                            parsed,
+                            context);
+
+                    // closing parenthesis must be at the end of input or separator and one more character is expected
+                    currentPosition = closingParenthesis + 1;
+                    if (currentPosition != input.length()) {
+                        if (currentPosition + 1 < input.length()) {
+                            if (input.charAt(currentPosition) == SEMICOLON) {
+                                currentPosition++;
+                            } else {
+                                throw new RestconfDocumentedException(
+                                        "Missing semicolon character after "
+                                                + child.getIdentifier().getNodeType().getLocalName()
+                                                + " child nodes",
+                                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+                            }
+                        } else {
+                            throw new RestconfDocumentedException(
+                                    "Unexpected character '"
+                                            + input.charAt(currentPosition)
+                                            + "' found in fields parameter value",
+                                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+                        }
+                    }
+
+                    break;
+                case SEMICOLON:
+                    // complete identifier found
+                    addChildToResult(
+                            currentNode,
+                            input.substring(startPosition, currentPosition), currentQNameModule, currentLevel);
+                    currentPosition++;
+                    break;
+                default:
+                    throw new RestconfDocumentedException(
+                            "Unexpected character '" + currentChar + "' found in fields parameter value",
+                            ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+            }
+
+            startPosition = currentPosition;
+        }
+
+        // parse input to end
+        if (startPosition < input.length()) {
+            addChildToResult(currentNode, input.substring(startPosition), currentQNameModule, currentLevel);
+        }
+    }
+
+    /**
+     * Add parsed child of current node to result for current level.
+     * @param currentNode current node
+     * @param identifier parsed identifier of child node
+     * @param currentQNameModule current namespace and revision in {@link QNameModule}
+     * @param level current nodes level
+     * @return {@link DataSchemaContextNode}
+     */
+    private static @Nonnull DataSchemaContextNode<?> addChildToResult(
+            @Nonnull final DataSchemaContextNode<?> currentNode,
+            @Nonnull final String identifier,
+            @Nonnull final QNameModule currentQNameModule,
+            @Nonnull final Set<QName> level) {
+        final QName childQName = QName.create(
+                currentQNameModule.getNamespace().toString(),
+                identifier,
+                currentQNameModule.getRevision());
+
+        // resolve parent node
+        final DataSchemaContextNode<?> parentNode = resolveMixinNode(
+                currentNode, level, currentNode.getIdentifier().getNodeType());
+        if (parentNode == null) {
+            throw new RestconfDocumentedException(
+                    "Not-mixin node missing in " + currentNode.getIdentifier().getNodeType().getLocalName(),
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+        }
+
+        // resolve child node
+        final DataSchemaContextNode<?> childNode = resolveMixinNode(
+                parentNode.getChild(childQName), level, childQName);
+        if (childNode == null) {
+            throw new RestconfDocumentedException(
+                    "Child " + identifier + " node missing in "
+                            + currentNode.getIdentifier().getNodeType().getLocalName(),
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+        }
+
+        // add final childNode node to level nodes
+        level.add(childNode.getIdentifier().getNodeType());
+        return childNode;
+    }
+
+    /**
+     * Resolve mixin node by searching for inner nodes until not mixin node or null is found.
+     * All nodes expect of not mixin node are added to current level nodes.
+     * @param node initial mixin or not-mixin node
+     * @param level current nodes level
+     * @param qName qname of initial node
+     * @return {@link DataSchemaContextNode}
+     */
+    private static @Nullable DataSchemaContextNode<?> resolveMixinNode(@Nullable final DataSchemaContextNode<?> node,
+                                                                       @Nonnull final Set<QName> level,
+                                                                       @Nonnull final QName qName) {
+        DataSchemaContextNode<?> currentNode = node;
+        while (currentNode != null && currentNode.isMixin()) {
+            level.add(qName);
+            currentNode = currentNode.getChild(qName);
+        }
+
+        return currentNode;
+    }
+
+    /**
+     * Find position of matching parenthesis increased by one, but at most equals to input size.
+     * @param input input where to find for closing parenthesis
+     * @return int position of closing parenthesis increased by one
+     */
+    private static int findClosingParenthesis(@Nonnull final String input) {
+        int position = 0;
+        int count = 1;
+
+        while (position < input.length()) {
+            final char currentChar = input.charAt(position);
+
+            if (currentChar == STARTING_PARENTHESIS) {
+                count++;
+            }
+
+            if (currentChar == CLOSING_PARENTHESIS) {
+                count--;
+            }
+
+            if (count == 0) {
+                break;
+            }
+
+            position++;
+        }
+
+        // closing parenthesis was not found
+        if (position >= input.length()) {
+            throw new RestconfDocumentedException("Missing closing parenthesis in fields parameter",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+        }
+
+        return ++position;
+    }
+}
index fc2703f98be3a9eb3a2a4496e2554f0a9e05919d..0ed61c60b602b8b130264f8bfc8bffca63e778d6 100644 (file)
@@ -7,10 +7,6 @@
  */
 package org.opendaylight.restconf.utils.parser;
 
-import com.google.common.base.Optional;
-import com.google.common.base.Splitter;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
 import java.text.ParseException;
 import java.util.Date;
 import java.util.Iterator;
@@ -30,10 +26,15 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import com.google.common.base.Optional;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 
 /**
  * Util class for parsing identifier
@@ -70,7 +71,7 @@ public final class ParserIdentifier {
             final String identifier,
             final SchemaContext schemaContext,
             final Optional<DOMMountPointService> mountPointService) {
-        if (identifier != null && identifier.contains(RestconfConstants.MOUNT)) {
+        if ((identifier != null) && identifier.contains(RestconfConstants.MOUNT)) {
             if (!mountPointService.isPresent()) {
                 throw new RestconfDocumentedException("Mount point service is not available");
             }
@@ -93,16 +94,37 @@ public final class ParserIdentifier {
 
             final DataSchemaContextNode<?> child = DataSchemaContextTree.from(
                     mountPoint.get().getSchemaContext()).getChild(pathYangInstanceIdentifier);
-
-            return new InstanceIdentifierContext<SchemaNode>(
-                    pathYangInstanceIdentifier, child.getDataSchemaNode(), mountPoint.get(),
+            if (child != null) {
+                return new InstanceIdentifierContext<SchemaNode>(pathYangInstanceIdentifier, child.getDataSchemaNode(),
+                        mountPoint.get(), mountPoint.get().getSchemaContext());
+            }
+            final QName rpcQName = mountYangInstanceIdentifier.getLastPathArgument().getNodeType();
+            RpcDefinition def = null;
+            for (final RpcDefinition rpcDefinition : schemaContext
+                    .findModuleByNamespaceAndRevision(rpcQName.getNamespace(), rpcQName.getRevision()).getRpcs()) {
+                if (rpcDefinition.getQName().getLocalName().equals(rpcQName.getLocalName())) {
+                    def = rpcDefinition;
+                    break;
+                }
+            }
+            return new InstanceIdentifierContext<RpcDefinition>(mountYangInstanceIdentifier, def, mountPoint.get(),
                     mountPoint.get().getSchemaContext());
         } else {
             final YangInstanceIdentifier deserialize = IdentifierCodec.deserialize(identifier, schemaContext);
             final DataSchemaContextNode<?> child = DataSchemaContextTree.from(schemaContext).getChild(deserialize);
 
-            return new InstanceIdentifierContext<SchemaNode>(
-                    deserialize, child.getDataSchemaNode(), null, schemaContext);
+            if(child != null){
+                return new InstanceIdentifierContext<SchemaNode>(deserialize, child.getDataSchemaNode(), null, schemaContext);
+            }
+            final QName rpcQName = deserialize.getLastPathArgument().getNodeType();
+            RpcDefinition def = null;
+            for (final RpcDefinition rpcDefinition : schemaContext.findModuleByNamespaceAndRevision(rpcQName.getNamespace(), rpcQName.getRevision()).getRpcs()) {
+                if (rpcDefinition.getQName().getLocalName().equals(rpcQName.getLocalName())) {
+                    def = rpcDefinition;
+                    break;
+                }
+            }
+            return new InstanceIdentifierContext<RpcDefinition>(deserialize, def, null, schemaContext);
         }
     }
 
index 984dd58abf3e5575198b12f0da5a70966b315db8..2b7e7559e3e30b64a458df493858b7103de415f3 100644 (file)
@@ -13,7 +13,7 @@ import java.util.Set;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
@@ -44,7 +44,7 @@ public final class RestconfSchemaUtil {
 
         final Set<GroupingDefinition> groupings = restconfModule.getGroupings();
         final GroupingDefinition restGroup = findSchemaNodeInCollection(groupings,
-                Draft17.RestconfModule.RESTCONF_GROUPING_SCHEMA_NODE);
+                Draft18.RestconfModule.RESTCONF_GROUPING_SCHEMA_NODE);
         final Collection<DataSchemaNode> childNodes = restGroup.getChildNodes();
         final DataSchemaNode restCont = childNodes.iterator().next();
 
@@ -64,31 +64,31 @@ public final class RestconfSchemaUtil {
     private static DataSchemaNode findSchemaNode(final DataSchemaNode restCont, final String schemaNodeName) {
         switch (schemaNodeName) {
             //MODULES
-            case Draft17.RestconfModule.MODULE_LIST_SCHEMA_NODE:
+            case Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE:
                 final DataSchemaNode moduleListSchNode = findSchemaNodeInCollection(
                         ((DataNodeContainer) findSchemaNode(restCont,
-                                Draft17.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE)).getChildNodes(),
-                        Draft17.RestconfModule.MODULE_LIST_SCHEMA_NODE);
+                                Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE)).getChildNodes(),
+                        Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE);
                 Preconditions.checkNotNull(moduleListSchNode);
                 return moduleListSchNode;
-            case Draft17.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE:
+            case Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE:
                 final DataSchemaNode modulesContSchNode = findSchemaNodeInCollection(((DataNodeContainer) restCont).getChildNodes(),
-                        Draft17.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
+                        Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE);
                 Preconditions.checkNotNull(modulesContSchNode);
                 return modulesContSchNode;
 
             //STREAMS
-            case Draft17.MonitoringModule.STREAM_LIST_SCHEMA_NODE:
+            case Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE:
                 final DataSchemaNode streamListSchNode = findSchemaNodeInCollection(
                         ((DataNodeContainer) findSchemaNode(restCont,
-                                Draft17.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE)).getChildNodes(),
-                        Draft17.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
+                                Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE)).getChildNodes(),
+                        Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE);
                 Preconditions.checkNotNull(streamListSchNode);
                 return streamListSchNode;
-            case Draft17.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE:
+            case Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE:
                 final DataSchemaNode streamsContSchNode = findSchemaNodeInCollection(
                         ((DataNodeContainer) restCont).getChildNodes(),
-                        Draft17.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
+                        Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE);
                 Preconditions.checkNotNull(streamsContSchNode);
                 return streamsContSchNode;
             default:
index e93219b820b2b63b1f500f5f812e3953278e9c4d..c71898e415d78089dbc82e223c3bfc54a972ca45 100644 (file)
@@ -60,7 +60,7 @@
 
     <servlet-mapping>
         <servlet-name>Restconf</servlet-name>
-        <url-pattern>/17/*</url-pattern>
+        <url-pattern>/18/*</url-pattern>
     </servlet-mapping>
 
     <filter>
index 8f5088f8bd0c8bf346bb9d131d43f4d5042611d6..7726a9d0bd6dff38c863a7309a778cd03a97a445 100644 (file)
@@ -12,6 +12,17 @@ module sal-remote-augment {
     revision "2014-07-08" {
     }
 
+    grouping notification-output-type-grouping{
+        leaf notification-output-type {
+            type enumeration {
+                enum JSON;
+                enum XML;
+            }
+            default "XML";
+            description "Input parameter which type of output will be parsed on notification";
+        }
+    }
+
     augment "/salrmt:create-data-change-event-subscription/salrmt:input" {
         leaf datastore {
             type enumeration {
@@ -26,17 +37,11 @@ module sal-remote-augment {
                 enum SUBTREE;
             }
         }
+        uses notification-output-type-grouping;
     }
 
     augment "/salrmt:create-notification-stream/salrmt:input" {
-        leaf notification-output-type {
-            type enumeration {
-                enum JSON;
-                enum XML;
-            }
-            default "XML";
-            description "Input parameter which type of output will be parsed on notification";
-        }
+        uses notification-output-type-grouping;
     }
 
 }
diff --git a/restconf/sal-rest-connector/src/main/yang/subscribe-to-notification.yang b/restconf/sal-rest-connector/src/main/yang/subscribe-to-notification.yang
new file mode 100644 (file)
index 0000000..5fe7df7
--- /dev/null
@@ -0,0 +1,18 @@
+module subscribe-to-notification {
+
+    yang-version 1;
+    namespace "subscribe:to:notification";
+    prefix "subs-to-notifi";
+
+    description
+        "Added input parameters to rpc create-data-change-event-subscription and to create-notification-stream";
+
+    revision "2016-10-28" {
+    }
+
+    container "notifi"{
+        leaf "location"{
+            type string;
+        }
+    }
+}
index 1b450d482e9817ee6cab191a7199dc3ca45c4a26..a12ab5e1e66a63ba2fdd6bf803f6d37748d7a675 100644 (file)
@@ -19,9 +19,11 @@ import java.io.FileNotFoundException;
 import java.net.URI;
 import java.util.List;
 import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
+import org.mockito.Mockito;
 import org.opendaylight.controller.sal.restconf.impl.test.TestUtils;
 import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
@@ -75,7 +77,7 @@ public class RestPutListDataTest {
         restconfImpl.setControllerContext(controllerContext);
         final PutResult result = mock(PutResult.class);
         when(brokerFacade.commitConfigurationDataPut(any(SchemaContext.class), any(YangInstanceIdentifier.class),
-                any(NormalizedNode.class)))
+                any(NormalizedNode.class), Mockito.anyString(), Mockito.anyString()))
                         .thenReturn(result);
         when(result.getFutureOfPutData()).thenReturn(mock(CheckedFuture.class));
         when(result.getStatus()).thenReturn(Status.OK);
@@ -200,7 +202,8 @@ public class RestPutListDataTest {
         final NormalizedNodeContext testCompositeContext = new NormalizedNodeContext(new InstanceIdentifierContext<>(
                 null, testNodeSchemaNode, null, schemaContextTestModule), testNodeContainer.build());
 
-        restconfImpl.updateConfigurationData(toUri(uriKey1, uriKey2), testCompositeContext);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        restconfImpl.updateConfigurationData(toUri(uriKey1, uriKey2), testCompositeContext, uriInfo);
     }
 
     public void putListDataWithWrapperTest(final String uriKey1, final String uriKey2, final String payloadKey1,
index ed3aba5fab41bf5bd0364e9cfc597e26243ef5e1..70f854973a9bc7ed023190b7b99e53251fa3ec49 100644 (file)
@@ -22,7 +22,6 @@ import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
-
 import com.google.common.base.Optional;
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.CheckedFuture;
@@ -61,6 +60,7 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -97,10 +97,10 @@ public class BrokerFacadeTest {
     private final BrokerFacade brokerFacade = BrokerFacade.getInstance();
     private final NormalizedNode<?, ?> dummyNode = createDummyNode("test:module", "2014-01-09", "interfaces");
     private final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> dummyNodeInFuture =
-            wrapDummyNode(dummyNode);
+            wrapDummyNode(this.dummyNode);
     private final QName qname = TestUtils.buildQName("interfaces","test:module", "2014-01-09");
-    private final SchemaPath type = SchemaPath.create(true, qname);
-    private final YangInstanceIdentifier instanceID = YangInstanceIdentifier.builder().node(qname).build();
+    private final SchemaPath type = SchemaPath.create(true, this.qname);
+    private final YangInstanceIdentifier instanceID = YangInstanceIdentifier.builder().node(this.qname).build();
 
     @Before
     public void setUp() throws Exception {
@@ -193,7 +193,7 @@ public class BrokerFacadeTest {
         when(this.rwTransaction.read(LogicalDatastoreType.CONFIGURATION, this.instanceID)).thenReturn(readFuture);
 
         final PutResult result = this.brokerFacade.commitConfigurationDataPut(mock(SchemaContext.class),
-                this.instanceID, this.dummyNode);
+                this.instanceID, this.dummyNode, null, null);
 
         final Future<Void> actualFuture = result.getFutureOfPutData();
 
@@ -216,7 +216,7 @@ public class BrokerFacadeTest {
         when(this.rwTransaction.submit()).thenReturn(expFuture);
 
         final CheckedFuture<Void, TransactionCommitFailedException> actualFuture = this.brokerFacade
-                .commitConfigurationDataPost(mock(SchemaContext.class), this.instanceID, this.dummyNode);
+                .commitConfigurationDataPost(mock(SchemaContext.class), this.instanceID, this.dummyNode, null, null);
 
         assertSame("commitConfigurationDataPost", expFuture, actualFuture);
 
@@ -234,7 +234,8 @@ public class BrokerFacadeTest {
                 .thenReturn(successFuture);
         try {
             // Schema context is only necessary for ensuring parent structure
-            this.brokerFacade.commitConfigurationDataPost((SchemaContext) null, this.instanceID, this.dummyNode);
+            this.brokerFacade.commitConfigurationDataPost((SchemaContext) null, this.instanceID, this.dummyNode, null,
+                    null);
         } catch (final RestconfDocumentedException e) {
             assertEquals("getErrorTag", RestconfError.ErrorTag.DATA_EXISTS, e.getErrors().get(0).getErrorTag());
             throw e;
@@ -299,7 +300,8 @@ public class BrokerFacadeTest {
 
     @Test
     public void testRegisterToListenDataChanges() {
-        final ListenerAdapter listener = Notificator.createListener(this.instanceID, "stream");
+        final ListenerAdapter listener = Notificator.createListener(this.instanceID, "stream",
+                NotificationOutputType.XML);
 
         @SuppressWarnings("unchecked")
         final ListenerRegistration<DOMDataChangeListener> mockRegistration = mock(ListenerRegistration.class);
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ExpressionParserTest.java
new file mode 100644 (file)
index 0000000..61cd81d
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2016 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.restconf.impl.test;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+public class ExpressionParserTest {
+
+    private Collection<File> xmls;
+
+    @Before
+    public void setup() throws Exception {
+        this.xmls = TestRestconfUtils.loadFiles("/notifications/xml/output/");
+    }
+
+    @Test
+    public void trueDownFilterTest() throws Exception {
+        final boolean parser =
+                parser("notification/data-changed-notification/data-change-event/data/toasterStatus='down'",
+                        "data_change_notification_toaster_status_DOWN.xml");
+        Assert.assertTrue(parser);
+    }
+
+    @Test
+    public void falseDownFilterTest() throws Exception {
+        final boolean parser =
+                parser("notification/data-changed-notification/data-change-event/data/toasterStatus='up'",
+                        "data_change_notification_toaster_status_DOWN.xml");
+        Assert.assertFalse(parser);
+    }
+
+    @Test
+    public void trueNumberEqualsFilterTest() throws Exception {
+        final boolean parser = parser(
+                "notification/data-changed-notification/data-change-event/data/toasterStatus=1",
+                "data_change_notification_toaster_status_NUMBER.xml");
+        Assert.assertTrue(parser);
+    }
+
+    @Test
+    public void falseNumberEqualsFilterTest() throws Exception {
+        final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus=0",
+                "data_change_notification_toaster_status_NUMBER.xml");
+        Assert.assertFalse(parser);
+    }
+
+    @Test
+    public void trueNumberLessFilterTest() throws Exception {
+        final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<2",
+                "data_change_notification_toaster_status_NUMBER.xml");
+        Assert.assertTrue(parser);
+    }
+
+    @Test
+    public void falseNumberLessFilterTest() throws Exception {
+        final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<0",
+                "data_change_notification_toaster_status_NUMBER.xml");
+        Assert.assertFalse(parser);
+    }
+
+    @Test
+    public void trueNumberLessEqualsFilterTest() throws Exception {
+        final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<=2",
+                "data_change_notification_toaster_status_NUMBER.xml");
+        Assert.assertTrue(parser);
+    }
+
+    @Test
+    public void falseNumberLessEqualsFilterTest() throws Exception {
+        final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus<=-1",
+                "data_change_notification_toaster_status_NUMBER.xml");
+        Assert.assertFalse(parser);
+    }
+
+    @Test
+    public void trueNumberGreaterFilterTest() throws Exception {
+        final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>0",
+                "data_change_notification_toaster_status_NUMBER.xml");
+        Assert.assertTrue(parser);
+    }
+
+    @Test
+    public void falseNumberGreaterFilterTest() throws Exception {
+        final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>5",
+                "data_change_notification_toaster_status_NUMBER.xml");
+        Assert.assertFalse(parser);
+    }
+
+    @Test
+    public void trueNumberGreaterEqualsFilterTest() throws Exception {
+        final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>=0",
+                "data_change_notification_toaster_status_NUMBER.xml");
+        Assert.assertTrue(parser);
+    }
+
+    @Test
+    public void falseNumberGreaterEqualsFilterTest() throws Exception {
+        final boolean parser = parser("notification/data-changed-notification/data-change-event/data/toasterStatus>=5",
+                "data_change_notification_toaster_status_NUMBER.xml");
+        Assert.assertFalse(parser);
+    }
+
+    private boolean parser(final String filter, final String fileName) throws Exception {
+        File xml = null;
+        for (final File file : this.xmls) {
+            if (file.getName().equals(fileName)) {
+                xml = file;
+            }
+        }
+        final YangInstanceIdentifier path = Mockito.mock(YangInstanceIdentifier.class);
+        final ListenerAdapter listener = Notificator.createListener(path, "streamName", NotificationOutputType.JSON);
+        listener.setQueryParams(null, null, filter);
+        final Method m = listener.getClass().getDeclaredMethod("parseFilterParam", String.class);
+        m.setAccessible(true);
+
+        return (boolean) m.invoke(listener, readFile(xml));
+    }
+
+    private String readFile(final File xml) throws Exception {
+        final BufferedReader br = new BufferedReader(new FileReader(xml));
+        try {
+            final StringBuilder sb = new StringBuilder();
+            String line = br.readLine();
+
+            while (line != null) {
+                sb.append(line);
+                sb.append("\n");
+                line = br.readLine();
+            }
+            return sb.toString();
+        } finally {
+            br.close();
+        }
+    }
+
+}
index b472f1781f52e14dc082961b05d92975753d496a..6729ea1b01286d69008f7f292708bddf9148e4fa 100644 (file)
@@ -28,13 +28,18 @@ import com.google.common.util.concurrent.Futures;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
@@ -71,6 +76,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
  *
  * @author Thomas Pantelis
  */
+@Deprecated
 public class JSONRestconfServiceImplTest {
     static final String IETF_INTERFACES_NS = "urn:ietf:params:xml:ns:yang:ietf-interfaces";
     static final String IETF_INTERFACES_VERSION = "2013-07-04";
@@ -124,19 +130,23 @@ public class JSONRestconfServiceImplTest {
     public void testPut() throws Exception {
         final PutResult result = mock(PutResult.class);
         when(brokerFacade.commitConfigurationDataPut(notNull(SchemaContext.class),
-                notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class))).thenReturn(result);
+                notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class), Mockito.anyString(),
+                Mockito.anyString())).thenReturn(result);
         when(result.getFutureOfPutData())
                 .thenReturn(Futures.immediateCheckedFuture(null));
         when(result.getStatus()).thenReturn(Status.OK);
         final String uriPath = "ietf-interfaces:interfaces/interface/eth0";
         final String payload = loadData("/parts/ietf-interfaces_interfaces.json");
-
-        this.service.put(uriPath, payload);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        this.service.put(uriPath, payload, uriInfo);
 
         final ArgumentCaptor<YangInstanceIdentifier> capturedPath = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
         final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
         verify(brokerFacade).commitConfigurationDataPut(notNull(SchemaContext.class), capturedPath.capture(),
-                capturedNode.capture());
+                capturedNode.capture(), Mockito.anyString(), Mockito.anyString());
 
         verifyPath(capturedPath.getValue(), INTERFACES_QNAME, INTERFACE_QNAME,
                 new Object[]{INTERFACE_QNAME, NAME_QNAME, "eth0"});
@@ -157,18 +167,23 @@ public class JSONRestconfServiceImplTest {
         final DOMMountPoint mockMountPoint = setupTestMountPoint();
         final PutResult result = mock(PutResult.class);
         when(brokerFacade.commitMountPointDataPut(notNull(DOMMountPoint.class),
-                notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class))).thenReturn(result);
+                notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class), Mockito.anyString(),
+                Mockito.anyString())).thenReturn(result);
         when(result.getFutureOfPutData()).thenReturn(Futures.immediateCheckedFuture(null));
         when(result.getStatus()).thenReturn(Status.OK);
         final String uriPath = "ietf-interfaces:interfaces/yang-ext:mount/test-module:cont/cont1";
         final String payload = loadData("/full-versions/testCont1Data.json");
 
-        this.service.put(uriPath, payload);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        this.service.put(uriPath, payload, uriInfo);
 
         final ArgumentCaptor<YangInstanceIdentifier> capturedPath = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
         final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
         verify(brokerFacade).commitMountPointDataPut(same(mockMountPoint), capturedPath.capture(),
-                capturedNode.capture());
+                capturedNode.capture(), Mockito.anyString(), Mockito.anyString());
 
         verifyPath(capturedPath.getValue(), TEST_CONT_QNAME, TEST_CONT1_QNAME);
 
@@ -182,33 +197,46 @@ public class JSONRestconfServiceImplTest {
     @Test(expected = OperationFailedException.class)
     public void testPutFailure() throws Throwable {
         final PutResult result = mock(PutResult.class);
+
         when(result.getFutureOfPutData())
                 .thenReturn(Futures.immediateFailedCheckedFuture(new TransactionCommitFailedException("mock")));
         when(result.getStatus()).thenReturn(Status.OK);
         when(brokerFacade.commitConfigurationDataPut(notNull(SchemaContext.class),
-                notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class))).thenReturn(result);
+                notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class), Mockito.anyString(),
+                Mockito.anyString())).thenReturn(result);
 
         final String uriPath = "ietf-interfaces:interfaces/interface/eth0";
         final String payload = loadData("/parts/ietf-interfaces_interfaces.json");
 
-        this.service.put(uriPath, payload);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        this.service.put(uriPath, payload, uriInfo);
     }
 
     @SuppressWarnings("rawtypes")
     @Test
     public void testPost() throws Exception {
         doReturn(Futures.immediateCheckedFuture(null)).when(brokerFacade).commitConfigurationDataPost(
-                any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+                any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class),
+                Mockito.anyString(), Mockito.anyString());
 
         final String uriPath = null;
         final String payload = loadData("/parts/ietf-interfaces_interfaces_absolute_path.json");
 
-        this.service.post(uriPath, payload);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        final UriBuilder uriBuilder = UriBuilder.fromPath("");
+        Mockito.when(uriInfo.getBaseUriBuilder()).thenReturn(uriBuilder);
+        this.service.post(uriPath, payload, uriInfo);
 
         final ArgumentCaptor<YangInstanceIdentifier> capturedPath = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
         final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
         verify(brokerFacade).commitConfigurationDataPost(notNull(SchemaContext.class), capturedPath.capture(),
-                capturedNode.capture());
+                capturedNode.capture(), Mockito.anyString(), Mockito.anyString());
 
         verifyPath(capturedPath.getValue(), INTERFACES_QNAME);
 
@@ -237,17 +265,24 @@ public class JSONRestconfServiceImplTest {
     public void testPostBehindMountPoint() throws Exception {
         final DOMMountPoint mockMountPoint = setupTestMountPoint();
         doReturn(Futures.immediateCheckedFuture(null)).when(brokerFacade).commitConfigurationDataPost(
-                notNull(DOMMountPoint.class), notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class));
+                notNull(DOMMountPoint.class), notNull(YangInstanceIdentifier.class), notNull(NormalizedNode.class),
+                Mockito.anyString(), Mockito.anyString());
 
         final String uriPath = "ietf-interfaces:interfaces/yang-ext:mount/test-module:cont";
         final String payload = loadData("/full-versions/testCont1Data.json");
 
-        this.service.post(uriPath, payload);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        final UriBuilder uriBuilder = UriBuilder.fromPath("");
+        Mockito.when(uriInfo.getBaseUriBuilder()).thenReturn(uriBuilder);
+        this.service.post(uriPath, payload, uriInfo);
 
         final ArgumentCaptor<YangInstanceIdentifier> capturedPath = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
         final ArgumentCaptor<NormalizedNode> capturedNode = ArgumentCaptor.forClass(NormalizedNode.class);
         verify(brokerFacade).commitConfigurationDataPost(same(mockMountPoint), capturedPath.capture(),
-                capturedNode.capture());
+                capturedNode.capture(), Mockito.anyString(), Mockito.anyString());
 
         verifyPath(capturedPath.getValue(), TEST_CONT_QNAME, TEST_CONT1_QNAME);
 
@@ -262,12 +297,18 @@ public class JSONRestconfServiceImplTest {
     public void testPostFailure() throws Throwable {
         doReturn(Futures.immediateFailedCheckedFuture(new TransactionCommitFailedException("mock"))).when(brokerFacade)
                 .commitConfigurationDataPost(any(SchemaContext.class), any(YangInstanceIdentifier.class),
-                        any(NormalizedNode.class));
+                        any(NormalizedNode.class), Mockito.anyString(), Mockito.anyString());
 
         final String uriPath = null;
         final String payload = loadData("/parts/ietf-interfaces_interfaces_absolute_path.json");
 
-        this.service.post(uriPath, payload);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        final UriBuilder uriBuilder = UriBuilder.fromPath("");
+        Mockito.when(uriInfo.getBaseUriBuilder()).thenReturn(uriBuilder);
+        this.service.post(uriPath, payload, uriInfo);
     }
 
     @Test
@@ -305,16 +346,24 @@ public class JSONRestconfServiceImplTest {
 
     @Test
     public void testGetWithNoData() throws OperationFailedException {
-        doReturn(null).when(brokerFacade).readConfigurationData(notNull(YangInstanceIdentifier.class));
+        doReturn(null).when(brokerFacade).readConfigurationData(notNull(YangInstanceIdentifier.class),
+                Mockito.anyString());
         final String uriPath = "ietf-interfaces:interfaces";
-        this.service.get(uriPath, LogicalDatastoreType.CONFIGURATION);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        this.service.get(uriPath, LogicalDatastoreType.CONFIGURATION, uriInfo);
     }
 
     @Test(expected=OperationFailedException.class)
     public void testGetFailure() throws Exception {
         final String invalidUriPath = "/ietf-interfaces:interfaces/invalid";
-
-        this.service.get(invalidUriPath, LogicalDatastoreType.CONFIGURATION);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        this.service.get(invalidUriPath, LogicalDatastoreType.CONFIGURATION, uriInfo);
     }
 
     @SuppressWarnings("rawtypes")
@@ -402,14 +451,21 @@ public class JSONRestconfServiceImplTest {
                 .build();
 
         if(datastoreType == LogicalDatastoreType.CONFIGURATION) {
-            doReturn(entryNode).when(brokerFacade).readConfigurationData(notNull(YangInstanceIdentifier.class));
+            doReturn(entryNode).when(brokerFacade).readConfigurationData(notNull(YangInstanceIdentifier.class),
+                    Mockito.anyString());
         } else {
             doReturn(entryNode).when(brokerFacade).readOperationalData(notNull(YangInstanceIdentifier.class));
         }
 
         final String uriPath = "/ietf-interfaces:interfaces/interface/eth0";
-
-        final Optional<String> optionalResp = this.service.get(uriPath, datastoreType);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        Mockito.when(uriInfo.getQueryParameters(false)).thenReturn(value);
+        Mockito.when(value.getFirst("depth")).thenReturn("");
+
+        final Optional<String> optionalResp = this.service.get(uriPath, datastoreType, uriInfo);
         assertEquals("Response present", true, optionalResp.isPresent());
         final String jsonResp = optionalResp.get();
 
@@ -421,7 +477,7 @@ public class JSONRestconfServiceImplTest {
 
         final ArgumentCaptor<YangInstanceIdentifier> capturedPath = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
         if (datastoreType == LogicalDatastoreType.CONFIGURATION) {
-            verify(brokerFacade).readConfigurationData(capturedPath.capture());
+            verify(brokerFacade).readConfigurationData(capturedPath.capture(), Mockito.anyString());
         } else {
             verify(brokerFacade).readOperationalData(capturedPath.capture());
         }
index b886637e95c1ceaa09bc7e488e5f730aea8976ca..4cd5aea672639165caa16933d64e06a5d9115edf 100644 (file)
@@ -27,6 +27,7 @@ import org.glassfish.jersey.test.JerseyTest;
 import org.junit.BeforeClass;
 import org.junit.Ignore;
 import org.junit.Test;
+import org.mockito.Mockito;
 import org.opendaylight.netconf.sal.rest.api.Draft02;
 import org.opendaylight.netconf.sal.rest.api.RestconfService;
 import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
@@ -142,19 +143,27 @@ public class MediaTypesTest extends JerseyTest {
         final String uriPrefix = "/config/";
         final String uriPath = "ietf-interfaces:interfaces";
         final String uri = uriPrefix + uriPath;
-        when(restconfService.updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class))).thenReturn(null);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        when(restconfService.updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class), uriInfo))
+                .thenReturn(null);
         put(uri, null, Draft02.MediaTypes.DATA + JSON, jsonData);
-        verify(restconfService, times(1)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+        verify(restconfService, times(1)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+                uriInfo);
         put(uri, null, Draft02.MediaTypes.DATA + XML, xmlData);
-        verify(restconfService, times(2)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+        verify(restconfService, times(2)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+                uriInfo);
         put(uri, null, MediaType.APPLICATION_JSON, jsonData);
-        verify(restconfService, times(3)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+        verify(restconfService, times(3)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+                uriInfo);
         put(uri, null, MediaType.APPLICATION_XML, xmlData);
-        verify(restconfService, times(4)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+        verify(restconfService, times(4)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+                uriInfo);
         put(uri, null, MediaType.TEXT_XML, xmlData);
-        verify(restconfService, times(5)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+        verify(restconfService, times(5)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+                uriInfo);
         put(uri, "fooMediaType", MediaType.TEXT_XML, xmlData);
-        verify(restconfService, times(6)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class));
+        verify(restconfService, times(6)).updateConfigurationData(eq(uriPath), any(NormalizedNodeContext.class),
+                uriInfo);
     }
 
     @Test
@@ -235,12 +244,12 @@ public class MediaTypesTest extends JerseyTest {
 
     private int post(final String uri, final String acceptMediaType, final String contentTypeMediaType, final String data) {
         if (acceptMediaType == null) {
-            if (contentTypeMediaType == null || data == null) {
+            if ((contentTypeMediaType == null) || (data == null)) {
                 return target(uri).request().post(null).getStatus();
             }
             return target(uri).request().post(Entity.entity(data, contentTypeMediaType)).getStatus();
         }
-        if (contentTypeMediaType == null || data == null) {
+        if ((contentTypeMediaType == null) || (data == null)) {
             return target(uri).request(acceptMediaType).post(null).getStatus();
         }
         return target(uri).request(acceptMediaType).post(Entity.entity(data, contentTypeMediaType)).getStatus();
index b5435e149a1c25ae48646a4662f039a80168646f..a341ec9121c912419e33875498882cdf2fb38fe6 100644 (file)
@@ -13,6 +13,7 @@ import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import java.io.FileNotFoundException;
 import java.io.UnsupportedEncodingException;
@@ -24,6 +25,7 @@ import org.glassfish.jersey.server.ResourceConfig;
 import org.glassfish.jersey.test.JerseyTest;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter;
 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter;
@@ -85,4 +87,14 @@ public class RestDeleteOperationTest extends JerseyTest {
         response = target(uri).request(MediaType.APPLICATION_XML).delete();
         assertEquals(500, response.getStatus());
     }
+
+    @Test
+    public void deleteFailTest() throws Exception {
+        final String uri = "/config/test-interface:interfaces";
+        final Exception exception = new TransactionCommitFailedException("failed test");
+        final CheckedFuture future = Futures.immediateFailedCheckedFuture(exception);
+        when(brokerFacade.commitConfigurationDataDelete(any(YangInstanceIdentifier.class))).thenReturn(future);
+        final Response response = target(uri).request(MediaType.APPLICATION_XML).delete();
+        assertEquals(500, response.getStatus());
+    }
 }
index d5fd89777b0157641fe2abf2637a44e6504ac6ec..1bcfa8fae05d269363f3604bfb8903513ac4c593 100644 (file)
@@ -46,6 +46,7 @@ import org.glassfish.jersey.test.JerseyTest;
 import org.junit.BeforeClass;
 import org.junit.Ignore;
 import org.junit.Test;
+import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
@@ -172,7 +173,8 @@ public class RestGetOperationTest extends JerseyTest {
     @SuppressWarnings("unchecked")
     @Test
     public void getDataWithUrlMountPoint() throws UnsupportedEncodingException, URISyntaxException, ParseException {
-        when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class))).thenReturn(
+        when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class),
+                Mockito.anyString())).thenReturn(
                 prepareCnDataForMountPointTest(false));
         final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
         when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
@@ -202,8 +204,8 @@ public class RestGetOperationTest extends JerseyTest {
     @Test
     public void getDataWithSlashesBehindMountPoint() throws Exception {
         final YangInstanceIdentifier awaitedInstanceIdentifier = prepareInstanceIdentifierForList();
-        when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), eq(awaitedInstanceIdentifier))).thenReturn(
-                prepareCnDataForSlashesBehindMountPointTest());
+        when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), eq(awaitedInstanceIdentifier),
+                Mockito.anyString())).thenReturn(prepareCnDataForSlashesBehindMountPointTest());
         final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
         when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
         final DOMMountPointService mockMountService = mock(DOMMountPointService.class);
@@ -238,7 +240,8 @@ public class RestGetOperationTest extends JerseyTest {
     @Test
     public void getDataMountPointIntoHighestElement() throws UnsupportedEncodingException, URISyntaxException,
             ParseException {
-        when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class))).thenReturn(
+        when(brokerFacade.readConfigurationData(any(DOMMountPoint.class), any(YangInstanceIdentifier.class),
+                Mockito.anyString())).thenReturn(
                 prepareCnDataForMountPointTest(true));
         final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
         when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
@@ -270,7 +273,7 @@ public class RestGetOperationTest extends JerseyTest {
                         .withChild(ImmutableNodes.leafNode(newTestModuleQName("type"), newTestModuleQName("test-identity")))
                         .withChild(ImmutableNodes.leafNode(newTestModuleQName("name"), "foo"))
                         .withChild(ImmutableNodes.leafNode(newTestModuleQName("data"), "bar")).build()).build();
-        when(brokerFacade.readConfigurationData(iid)).thenReturn(data);
+        when(brokerFacade.readConfigurationData(iid, null)).thenReturn(data);
 
         final String uri = "/config/test-module:modules/module/test-module:test-identity/foo";
         assertEquals(200, get(uri, MediaType.APPLICATION_XML));
@@ -670,7 +673,8 @@ public class RestGetOperationTest extends JerseyTest {
 
     @SuppressWarnings("unchecked")
     private void mockReadConfigurationDataMethod() {
-        when(brokerFacade.readConfigurationData(any(YangInstanceIdentifier.class))).thenReturn(answerFromGet);
+        when(brokerFacade.readConfigurationData(any(YangInstanceIdentifier.class), Mockito.anyString()))
+                .thenReturn(answerFromGet);
     }
 
     @SuppressWarnings("rawtypes")
index 4983b7cac40295bf047fac39d91a65535dcf1be3..ca3fb86c748083d8840873901657ea769bcf097a 100644 (file)
@@ -33,6 +33,7 @@ import org.junit.BeforeClass;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
@@ -112,7 +113,7 @@ public class RestPostOperationTest extends JerseyTest {
         setSchemaControllerContext(schemaContextYangsIetf);
         when(
                 brokerFacade.commitConfigurationDataPost(any(DOMMountPoint.class), any(YangInstanceIdentifier.class),
-                        any(NormalizedNode.class))).thenReturn(mock(CheckedFuture.class));
+                        any(NormalizedNode.class), null, null)).thenReturn(mock(CheckedFuture.class));
 
         final DOMMountPoint mountInstance = mock(DOMMountPoint.class);
         when(mountInstance.getSchemaContext()).thenReturn(schemaContextTestModule);
@@ -137,7 +138,8 @@ public class RestPostOperationTest extends JerseyTest {
         final RpcResult<TransactionStatus> rpcResult = new DummyRpcResult.Builder<TransactionStatus>().result(
                 TransactionStatus.COMMITED).build();
 
-        when(brokerFacade.commitConfigurationDataPost((SchemaContext)null, any(YangInstanceIdentifier.class), any(NormalizedNode.class)))
+        when(brokerFacade.commitConfigurationDataPost((SchemaContext) null, any(YangInstanceIdentifier.class),
+                any(NormalizedNode.class), null, null))
                 .thenReturn(mock(CheckedFuture.class));
 
         final ArgumentCaptor<YangInstanceIdentifier> instanceIdCaptor = ArgumentCaptor.forClass(YangInstanceIdentifier.class);
@@ -157,7 +159,8 @@ public class RestPostOperationTest extends JerseyTest {
         // FIXME : NEVER test a nr. of call some service in complex test suite
 //        verify(brokerFacade, times(2))
         verify(brokerFacade, times(1))
-                .commitConfigurationDataPost((SchemaContext)null, instanceIdCaptor.capture(), compNodeCaptor.capture());
+                .commitConfigurationDataPost((SchemaContext) null, instanceIdCaptor.capture(), compNodeCaptor.capture(),
+                        null, null);
 //        identifier = "[(urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)interfaces, (urn:ietf:params:xml:ns:yang:test-interface?revision=2014-07-01)block]";
         assertEquals(identifier, ImmutableList.copyOf(instanceIdCaptor.getValue().getPathArguments()).toString());
     }
@@ -166,7 +169,8 @@ public class RestPostOperationTest extends JerseyTest {
     public void createConfigurationDataNullTest() throws UnsupportedEncodingException {
         initMocking();
 
-        when(brokerFacade.commitConfigurationDataPost(any(SchemaContext.class), any(YangInstanceIdentifier.class),any(NormalizedNode.class)))
+        when(brokerFacade.commitConfigurationDataPost(any(SchemaContext.class), any(YangInstanceIdentifier.class),
+                any(NormalizedNode.class), Mockito.anyString(), Mockito.anyString()))
                 .thenReturn(Futures.<Void, TransactionCommitFailedException>immediateCheckedFuture(null));
 
         //FIXME : find who is set schemaContext
index 972ab2d428fb521c764ee211e7fec844ceafa9ad..79374d029f43585d48a4ea6f5cbcb1487cec08e0 100644 (file)
@@ -10,7 +10,10 @@ package org.opendaylight.controller.sal.restconf.impl.test;
 
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
+import java.util.HashSet;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -76,7 +79,11 @@ public class RestPutConfigTest {
 
         mockingBrokerPut(iiCx.getInstanceIdentifier(), data);
 
-        this.restconfService.updateConfigurationData(identifier, payload);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        this.restconfService.updateConfigurationData(identifier, payload, uriInfo);
     }
 
     @Test
@@ -93,7 +100,11 @@ public class RestPutConfigTest {
 
         mockingBrokerPut(iiCx.getInstanceIdentifier(), data);
 
-        this.restconfService.updateConfigurationData(identifier, payload);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        this.restconfService.updateConfigurationData(identifier, payload, uriInfo);
     }
 
     @Test(expected=RestconfDocumentedException.class)
@@ -116,13 +127,17 @@ public class RestPutConfigTest {
 
         mockingBrokerPut(iiCx.getInstanceIdentifier(), data);
 
-        this.restconfService.updateConfigurationData(identifier, payload);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(uriInfo.getQueryParameters()).thenReturn(value);
+        this.restconfService.updateConfigurationData(identifier, payload, uriInfo);
     }
 
     private void mockingBrokerPut(final YangInstanceIdentifier yii, final NormalizedNode<?, ?> data) {
         final PutResult result = Mockito.mock(PutResult.class);
         final CheckedFuture<Void, TransactionCommitFailedException> checkedFuture = Futures.immediateCheckedFuture(null);
-        Mockito.when(this.brokerFacade.commitConfigurationDataPut(this.schemaCx, yii, data))
+        Mockito.when(this.brokerFacade.commitConfigurationDataPut(this.schemaCx, yii, data, null, null))
                 .thenReturn(result);
         Mockito.when(result.getFutureOfPutData()).thenReturn(checkedFuture);
         Mockito.when(result.getStatus()).thenReturn(Status.OK);
index 2271291182d5bb9c39e9f4c0706443cbdb93a015..3eafe2f4075f2720a494da5cd083974d3d5f495b 100644 (file)
@@ -130,7 +130,7 @@ public class RestPutOperationTest extends JerseyTest {
         final PutResult result = mock(PutResult.class);
         when(
                 brokerFacade.commitMountPointDataPut(any(DOMMountPoint.class), any(YangInstanceIdentifier.class),
-                        any(NormalizedNode.class))).thenReturn(result);
+                        any(NormalizedNode.class), null, null)).thenReturn(result);
         when(result.getFutureOfPutData()).thenReturn(dummyFuture);
         when(result.getStatus()).thenReturn(Status.OK);
 
@@ -153,7 +153,7 @@ public class RestPutOperationTest extends JerseyTest {
         final CheckedFuture<Void, TransactionCommitFailedException> dummyFuture = Futures.immediateCheckedFuture(null);
         final PutResult result = mock(PutResult.class);
         doReturn(result).when(brokerFacade).commitMountPointDataPut(any(DOMMountPoint.class),
-                any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+                any(YangInstanceIdentifier.class), any(NormalizedNode.class), null, null);
         when(result.getFutureOfPutData()).thenReturn(dummyFuture);
         when(result.getStatus()).thenReturn(Status.OK);
 
@@ -175,13 +175,14 @@ public class RestPutOperationTest extends JerseyTest {
 
         doThrow(OptimisticLockFailedException.class).
             when(brokerFacade).commitConfigurationDataPut(
-                        any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+                        any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), null,
+                        null);
 
         assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
 
         doThrow(OptimisticLockFailedException.class).doReturn(mock(PutResult.class)).when(brokerFacade)
                 .commitConfigurationDataPut(any(SchemaContext.class), any(YangInstanceIdentifier.class),
-                        any(NormalizedNode.class));
+                        any(NormalizedNode.class), null, null);
 
         assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
     }
@@ -193,7 +194,8 @@ public class RestPutOperationTest extends JerseyTest {
 
         doThrow(TransactionCommitFailedException.class).
             when(brokerFacade).commitConfigurationDataPut(
-                        any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+                        any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), null,
+                        null);
 
         assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
     }
@@ -206,10 +208,10 @@ public class RestPutOperationTest extends JerseyTest {
         final PutResult putResMock = mock(PutResult.class);
         if (noErrors) {
             doReturn(putResMock).when(brokerFacade).commitConfigurationDataPut(
-                    any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+                    any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), null, null);
         } else {
             doThrow(RestconfDocumentedException.class).when(brokerFacade).commitConfigurationDataPut(
-                    any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+                    any(SchemaContext.class), any(YangInstanceIdentifier.class), any(NormalizedNode.class), null, null);
         }
     }
 
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfImplNotificationSubscribingTest.java
new file mode 100644 (file)
index 0000000..2fdb0ba
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2016 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.restconf.impl.test;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl;
+import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class RestconfImplNotificationSubscribingTest {
+
+    private final String identifier = "data-change-event-subscription/datastore=OPERATIONAL/scope=ONE";
+
+    @Mock
+    private BrokerFacade broker;
+
+    @Mock
+    private DOMDataBroker domDataBroker;
+
+    @Mock
+    private UriInfo uriInfo;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        this.broker.setDomDataBroker(this.domDataBroker);
+        RestconfImpl.getInstance().setBroker(this.broker);
+        ControllerContext.getInstance().setGlobalSchema(TestRestconfUtils.loadSchemaContext("/notifications"));
+
+        final YangInstanceIdentifier path = Mockito.mock(YangInstanceIdentifier.class);
+        Notificator.createListener(path, this.identifier, NotificationOutputType.XML);
+    }
+
+    @Test
+    public void startTimeTest() {
+        final List<Entry<String, List<String>>> list = new ArrayList<>();
+        final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+        Mockito.when(entry.getKey()).thenReturn("start-time");
+        final List<String> time = new ArrayList<>();
+        time.add("2014-10-25T10:02:00Z");
+        Mockito.when(entry.getValue()).thenReturn(time);
+        list.add(entry);
+
+        subscribe(list);
+        Notificator.removeAllListeners();
+    }
+
+    @Test
+    public void milisecsTest() {
+        final List<Entry<String, List<String>>> list = new ArrayList<>();
+        final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+        Mockito.when(entry.getKey()).thenReturn("start-time");
+        final List<String> time = new ArrayList<>();
+        time.add("2014-10-25T10:02:00.12345Z");
+        Mockito.when(entry.getValue()).thenReturn(time);
+        list.add(entry);
+
+        subscribe(list);
+        Notificator.removeAllListeners();
+    }
+
+    @Test
+    public void zonesPlusTest() {
+        final List<Entry<String, List<String>>> list = new ArrayList<>();
+        final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+        Mockito.when(entry.getKey()).thenReturn("start-time");
+        final List<String> time = new ArrayList<>();
+        time.add("2014-10-25T10:02:00+01:00");
+        Mockito.when(entry.getValue()).thenReturn(time);
+        list.add(entry);
+
+        subscribe(list);
+        Notificator.removeAllListeners();
+    }
+
+    @Test
+    public void zonesMinusTest() {
+        final List<Entry<String, List<String>>> list = new ArrayList<>();
+        final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+        Mockito.when(entry.getKey()).thenReturn("start-time");
+        final List<String> time = new ArrayList<>();
+        time.add("2014-10-25T10:02:00-01:00");
+        Mockito.when(entry.getValue()).thenReturn(time);
+        list.add(entry);
+
+        subscribe(list);
+        Notificator.removeAllListeners();
+    }
+
+    @Test
+    public void startAndStopTimeTest() {
+        final List<Entry<String, List<String>>> list = new ArrayList<>();
+        final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+        Mockito.when(entry.getKey()).thenReturn("start-time");
+        final List<String> time = new ArrayList<>();
+        time.add("2014-10-25T10:02:00Z");
+        Mockito.when(entry.getValue()).thenReturn(time);
+
+        final Entry<String, List<String>> entry2 = Mockito.mock(Entry.class);
+        Mockito.when(entry2.getKey()).thenReturn("stop-time");
+        final List<String> time2 = new ArrayList<>();
+        time2.add("2014-10-25T12:31:00Z");
+        Mockito.when(entry2.getValue()).thenReturn(time2);
+
+        list.add(entry);
+        list.add(entry2);
+
+        subscribe(list);
+        Notificator.removeAllListeners();
+    }
+
+    @Test(expected = RestconfDocumentedException.class)
+    public void stopTimeTest() {
+        final List<Entry<String, List<String>>> list = new ArrayList<>();
+        final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+        Mockito.when(entry.getKey()).thenReturn("stop-time");
+        final List<String> time = new ArrayList<>();
+        time.add("2014-10-25T12:31:00Z");
+        Mockito.when(entry.getValue()).thenReturn(time);
+        list.add(entry);
+
+        subscribe(list);
+        Notificator.removeAllListeners();
+    }
+
+    @Test(expected = RestconfDocumentedException.class)
+    public void badParamTest() {
+        final List<Entry<String, List<String>>> list = new ArrayList<>();
+        final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+        Mockito.when(entry.getKey()).thenReturn("time");
+        final List<String> time = new ArrayList<>();
+        time.add("2014-10-25T12:31:00Z");
+        Mockito.when(entry.getValue()).thenReturn(time);
+        list.add(entry);
+
+        subscribe(list);
+        Notificator.removeAllListeners();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void badValueTest() {
+        final List<Entry<String, List<String>>> list = new ArrayList<>();
+        final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+        Mockito.when(entry.getKey()).thenReturn("start-time");
+        final List<String> time = new ArrayList<>();
+        time.add("badvalue");
+        Mockito.when(entry.getValue()).thenReturn(time);
+        list.add(entry);
+
+        subscribe(list);
+        Notificator.removeAllListeners();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void badZonesTest() {
+        final List<Entry<String, List<String>>> list = new ArrayList<>();
+        final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+        Mockito.when(entry.getKey()).thenReturn("start-time");
+        final List<String> time = new ArrayList<>();
+        time.add("2014-10-25T10:02:00Z+1:00");
+        Mockito.when(entry.getValue()).thenReturn(time);
+        list.add(entry);
+
+        subscribe(list);
+        Notificator.removeAllListeners();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void badMilisecsTest() {
+        final List<Entry<String, List<String>>> list = new ArrayList<>();
+        final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+        Mockito.when(entry.getKey()).thenReturn("start-time");
+        final List<String> time = new ArrayList<>();
+        time.add("2014-10-25T10:02:00:0026Z");
+        Mockito.when(entry.getValue()).thenReturn(time);
+        list.add(entry);
+
+        subscribe(list);
+        Notificator.removeAllListeners();
+    }
+
+    @Test
+    public void onNotifiTest() throws Exception {
+        final YangInstanceIdentifier path = Mockito.mock(YangInstanceIdentifier.class);
+        final ListenerAdapter listener = Notificator.createListener(path, this.identifier, NotificationOutputType.XML);
+
+        final List<Entry<String, List<String>>> list = new ArrayList<>();
+        final Entry<String, List<String>> entry = Mockito.mock(Entry.class);
+        Mockito.when(entry.getKey()).thenReturn("start-time");
+        final List<String> time = new ArrayList<>();
+        time.add("2014-10-25T10:02:00Z");
+        Mockito.when(entry.getValue()).thenReturn(time);
+        list.add(entry);
+
+        subscribe(list);
+
+        final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change =
+                Mockito.mock(AsyncDataChangeEvent.class);
+        Field start = listener.getClass().getDeclaredField("start");
+        start.setAccessible(true);
+        Date startOrig = (Date) start.get(listener);
+        Assert.assertNotNull(startOrig);
+        listener.onDataChanged(change);
+
+        start = listener.getClass().getDeclaredField("start");
+        start.setAccessible(true);
+        startOrig = (Date) start.get(listener);
+        Assert.assertNull(startOrig);
+    }
+
+    private void subscribe(final List<Entry<String, List<String>>> entries) {
+        final MultivaluedMap<String, String> map = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(this.uriInfo.getQueryParameters()).thenReturn(map);
+        final UriBuilder uriBuilder = UriBuilder.fromPath("http://localhost:8181/" + this.identifier);
+        Mockito.when(this.uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder);
+        final Set<Entry<String, List<String>>> set = new HashSet<>();
+        for(final Entry<String, List<String>> entry : entries){
+            set.add(entry);
+        }
+        Mockito.when(map.entrySet()).thenReturn(set);
+        RestconfImpl.getInstance().subscribeToStream(this.identifier, this.uriInfo);
+    }
+
+}
index 7c4b350c6cf622a3259dfe43b0d5c8d051991591..dc7e2e9e9fc7038d45b0db93bc2cd075080f3ca4 100644 (file)
@@ -16,7 +16,6 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
-
 import com.google.common.base.Optional;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
@@ -24,9 +23,12 @@ import com.google.common.util.concurrent.Futures;
 import java.io.FileNotFoundException;
 import java.net.URI;
 import java.text.ParseException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
 import java.util.Set;
 import javax.ws.rs.core.MultivaluedHashMap;
-import javax.ws.rs.core.Response;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import org.junit.Before;
@@ -111,7 +113,8 @@ public class RestconfImplTest {
         doReturn(mount).when(iiCtx).getMountPoint();
         final DOMRpcService rpcService = mock(DOMRpcService.class);
         doReturn(Optional.of(rpcService)).when(mount).getService(DOMRpcService.class);
-        doReturn(Futures.immediateCheckedFuture(mock(DOMRpcResult.class))).when(rpcService).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
+        doReturn(Futures.immediateCheckedFuture(mock(DOMRpcResult.class))).when(rpcService)
+                .invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
         this.restconfImpl.invokeRpc("randomId", ctx, uriInfo);
         this.restconfImpl.invokeRpc("ietf-netconf", ctx, uriInfo);
         verify(rpcService, times(2)).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class));
@@ -174,13 +177,16 @@ public class RestconfImplTest {
         when(uriBuilder.build()).thenReturn(new URI(""));
         when(uriBuilder.scheme("ws")).thenReturn(uriBuilder);
         when(uriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder);
+        final MultivaluedMap<String, String> map = mock(MultivaluedMap.class);
+        final Set<Entry<String, List<String>>> set = new HashSet<>();
+        when(map.entrySet()).thenReturn(set);
+        when(uriInfo.getQueryParameters()).thenReturn(map);
 
         final BrokerFacade brokerFacade = mock(BrokerFacade.class);
         this.restconfImpl.setBroker(brokerFacade);
 
         // subscribe to stream and verify response
-        final Response response = this.restconfImpl.subscribeToStream(identifier, uriInfo);
-        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        final NormalizedNodeContext response = this.restconfImpl.subscribeToStream(identifier, uriInfo);
 
         // remove test notification stream
         Notificator.removeAllListeners();
index ee608eae7788453b50755c96810c13c7f012b59d..0575a0ab9e68112d242f1f6c6fbc264a2d161e59 100644 (file)
@@ -13,6 +13,7 @@ import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil.getRevisionFormat;
+import com.google.common.base.Preconditions;
 import java.io.FileNotFoundException;
 import java.text.ParseException;
 import java.util.Date;
@@ -21,8 +22,8 @@ import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
+import org.mockito.Mockito;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
@@ -32,20 +33,26 @@ import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfImpl;
 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
 
@@ -66,13 +73,11 @@ public class URIParametersParsing {
     }
 
     @Test
-    @Ignore // URI parsing test - not able to catch a motivation + bad mocking response now - it needs to change Controller RPC table holder approach
     public void resolveURIParametersConcreteValues() {
         resolveURIParameters("OPERATIONAL", "SUBTREE", LogicalDatastoreType.OPERATIONAL, DataChangeScope.SUBTREE);
     }
 
     @Test
-    @Ignore // URI parsing test - not able to catch a motivation + bad mocking response now - it needs to change Controller RPC table holder approach
     public void resolveURIParametersDefaultValues() {
         resolveURIParameters(null, null, LogicalDatastoreType.CONFIGURATION, DataChangeScope.BASE);
     }
@@ -86,7 +91,7 @@ public class URIParametersParsing {
         final String datastoreValue = datastore == null ? "CONFIGURATION" : datastore;
         final String scopeValue = scope == null ? "BASE" : scope + "";
         Notificator.createListener(iiBuilder.build(), "dummyStreamName/datastore=" + datastoreValue + "/scope="
-                + scopeValue);
+                + scopeValue, NotificationOutputType.XML);
 
         final UriInfo mockedUriInfo = mock(UriInfo.class);
         @SuppressWarnings("unchecked")
@@ -99,16 +104,13 @@ public class URIParametersParsing {
          final UriBuilder uriBuilder = UriBuilder.fromUri("www.whatever.com");
          when(mockedUriInfo.getAbsolutePathBuilder()).thenReturn(uriBuilder);
 
-//       when(mockedBrokerFacade.invokeRpc(any(SchemaPath.class), any(NormalizedNode.class)))
-//       .thenReturn(Futures.<DOMRpcResult, DOMRpcException> immediateCheckedFuture(new DefaultDOMRpcResult(Builders.containerBuilder().build())));
-
         this.restconf.invokeRpc("sal-remote:create-data-change-event-subscription", prepareDomRpcNode(datastore, scope),
                 mockedUriInfo);
 
-        final ListenerAdapter listener = Notificator.getListenerFor("opendaylight-inventory:nodes/datastore="
+        final ListenerAdapter listener =
+                Notificator.getListenerFor("data-change-event-subscription/opendaylight-inventory:nodes/datastore="
                 + datastoreValue + "/scope=" + scopeValue);
         assertNotNull(listener);
-
     }
 
     private NormalizedNodeContext prepareDomRpcNode(final String datastore, final String scope) {
@@ -123,7 +125,9 @@ public class URIParametersParsing {
         final Module rpcSalRemoteModule = schema.findModuleByName("sal-remote", revDate);
         final Set<RpcDefinition> setRpcs = rpcSalRemoteModule.getRpcs();
         final QName rpcQName = QName.create(rpcSalRemoteModule.getQNameModule(), "create-data-change-event-subscription");
-        final QName rpcInputQName = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote","2014-01-14","input");
+        final QName rpcInputQName =
+                QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "input");
+        final RpcDefinition rpcDef = Mockito.mock(RpcDefinition.class);
         ContainerSchemaNode rpcInputSchemaNode = null;
         for (final RpcDefinition rpc : setRpcs) {
             if (rpcQName.isEqualWithoutRevision(rpc.getQName())) {
@@ -133,29 +137,52 @@ public class URIParametersParsing {
         }
         assertNotNull("RPC ContainerSchemaNode was not found!", rpcInputSchemaNode);
 
-        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> container = Builders.containerBuilder(rpcInputSchemaNode);
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> container =
+                Builders.containerBuilder(rpcInputSchemaNode);
 
-        final QName pathQName = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "path");
+        final QName pathQName =
+                QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "path");
         final DataSchemaNode pathSchemaNode = rpcInputSchemaNode.getDataChildByName(pathQName);
         assertTrue(pathSchemaNode instanceof LeafSchemaNode);
         final LeafNode<Object> pathNode = (Builders.leafBuilder((LeafSchemaNode) pathSchemaNode)
-                .withValue(YangInstanceIdentifier.builder().node(QName.create("urn:opendaylight:inventory", "2013-08-19", "nodes")).build())).build();
+                .withValue(YangInstanceIdentifier.builder()
+                        .node(QName.create("urn:opendaylight:inventory", "2013-08-19", "nodes")).build())).build();
         container.withChild(pathNode);
 
+        final AugmentationSchema augmentationSchema = rpcInputSchemaNode.getAvailableAugmentations().iterator().next();
+        Preconditions.checkNotNull(augmentationSchema);
+        final DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> augmentationBuilder =
+                Builders.augmentationBuilder(augmentationSchema);
+
         final QName dataStoreQName = QName.create("urn:sal:restconf:event:subscription", "2014-7-8", "datastore");
-        final DataSchemaNode dsSchemaNode = rpcInputSchemaNode.getDataChildByName(dataStoreQName);
+        final DataSchemaNode dsSchemaNode = augmentationSchema.getDataChildByName(dataStoreQName);
         assertTrue(dsSchemaNode instanceof LeafSchemaNode);
         final LeafNode<Object> dsNode = (Builders.leafBuilder((LeafSchemaNode) dsSchemaNode)
                 .withValue(datastore)).build();
-        container.withChild(dsNode);
+        augmentationBuilder.withChild(dsNode);
 
         final QName scopeQName = QName.create("urn:sal:restconf:event:subscription", "2014-7-8", "scope");
-        final DataSchemaNode scopeSchemaNode = rpcInputSchemaNode.getDataChildByName(scopeQName);
+        final DataSchemaNode scopeSchemaNode = augmentationSchema.getDataChildByName(scopeQName);
         assertTrue(scopeSchemaNode instanceof LeafSchemaNode);
         final LeafNode<Object> scopeNode = (Builders.leafBuilder((LeafSchemaNode) scopeSchemaNode)
                 .withValue(scope)).build();
-        container.withChild(scopeNode);
+        augmentationBuilder.withChild(scopeNode);
+
+        final QName outputQName =
+                QName.create("urn:sal:restconf:event:subscription", "2014-7-8", "notification-output-type");
+        final DataSchemaNode outputSchemaNode = augmentationSchema.getDataChildByName(outputQName);
+        assertTrue(outputSchemaNode instanceof LeafSchemaNode);
+        final LeafNode<Object> outputNode =
+                (Builders.leafBuilder((LeafSchemaNode) outputSchemaNode).withValue("XML")).build();
+        augmentationBuilder.withChild(outputNode);
+
+        container.withChild(augmentationBuilder.build());
+
+        when(rpcDef.getInput()).thenReturn(rpcInputSchemaNode);
+        when(rpcDef.getPath()).thenReturn(SchemaPath.create(true, rpcQName));
+        when(rpcDef.getQName()).thenReturn(rpcQName);
 
-        return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpcInputSchemaNode, null, schema), container.build());
+        return new NormalizedNodeContext(new InstanceIdentifierContext<RpcDefinition>(null, rpcDef, null, schema),
+                container.build());
     }
 }
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/streams/listeners/NotificationListenerTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/streams/listeners/NotificationListenerTest.java
new file mode 100644 (file)
index 0000000..12d623b
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2016 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.streams.listeners;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.sal.restconf.impl.test.TestUtils;
+import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
+import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.Notificator;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
+import org.opendaylight.yangtools.util.SingletonSet;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public class NotificationListenerTest {
+
+    private SchemaContext schmeaCtx;
+
+    @Before
+    public void init() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        ControllerContext.getInstance().setGlobalSchema(TestUtils.loadSchemaContext("/notifications"));
+        this.schmeaCtx = ControllerContext.getInstance().getGlobalSchema();
+    }
+
+    @Test
+    public void notifi_leafTest() throws Exception {
+        final QNameModule moduleQName =
+                QNameModule.create(new URI("notifi:mod"), new SimpleDateFormat("yyyy-MM-dd").parse("2016-11-23"));
+        final SchemaPath schemaPathNotifi = SchemaPath.create(false, QName.create(moduleQName, "notifi-leaf"));
+
+        final DOMNotification notificationData = mock(DOMNotification.class);
+
+        final LeafNode leaf = mockLeaf(QName.create(moduleQName, "lf"));
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi.getLastComponent(), leaf);
+
+        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getBody()).thenReturn(notifiBody);
+
+        final String result = prepareJson(notificationData, schemaPathNotifi);
+
+        assertTrue(result.contains("ietf-restconf:notification"));
+        assertTrue(result.contains("event-time"));
+        assertTrue(result.contains("notifi-module:notifi-leaf"));
+        assertTrue(result.contains("lf" + '"' + ":" + '"' + "value"));
+    }
+
+    @Test
+    public void notifi_cont_leafTest() throws Exception {
+        final QNameModule moduleQName =
+                QNameModule.create(new URI("notifi:mod"), new SimpleDateFormat("yyyy-MM-dd").parse("2016-11-23"));
+
+        final SchemaPath schemaPathNotifi =
+                SchemaPath.create(false, QName.create(moduleQName, "notifi-cont"));
+
+        final DOMNotification notificationData = mock(DOMNotification.class);
+
+        final LeafNode leaf = mockLeaf(QName.create(moduleQName, "lf"));
+        final ContainerNode cont = mockCont(QName.create(moduleQName, "cont"), leaf);
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi.getLastComponent(), cont);
+
+        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getBody()).thenReturn(notifiBody);
+
+        final String result = prepareJson(notificationData, schemaPathNotifi);
+
+        assertTrue(result.contains("ietf-restconf:notification"));
+        assertTrue(result.contains("event-time"));
+        assertTrue(result.contains("notifi-module:notifi-cont"));
+        assertTrue(result.contains("cont"));
+        assertTrue(result.contains("lf" + '"' + ":" + '"' + "value"));
+    }
+
+    @Test
+    public void notifi_list_Test() throws Exception {
+        final QNameModule moduleQName =
+                QNameModule.create(new URI("notifi:mod"), new SimpleDateFormat("yyyy-MM-dd").parse("2016-11-23"));
+
+        final SchemaPath schemaPathNotifi = SchemaPath.create(false, QName.create(moduleQName, "notifi-list"));
+
+        final DOMNotification notificationData = mock(DOMNotification.class);
+
+        final LeafNode leaf = mockLeaf(QName.create(moduleQName, "lf"));
+        final MapEntryNode entry = mockMapEntry(QName.create(moduleQName, "lst"), leaf);
+        final MapNode list = mockList(QName.create(moduleQName, "lst"), entry);
+        final ContainerNode cont = mockCont(QName.create(moduleQName, "cont"), list);
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi.getLastComponent(), cont);
+
+        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getBody()).thenReturn(notifiBody);
+
+        final String result = prepareJson(notificationData, schemaPathNotifi);
+
+        assertTrue(result.contains("ietf-restconf:notification"));
+        assertTrue(result.contains("event-time"));
+        assertTrue(result.contains("notifi-module:notifi-list"));
+        assertTrue(result.contains("lst"));
+        assertTrue(result.contains("lf" + '"' + ":" + '"' + "value"));
+    }
+
+    @Test
+    public void notifi_grpTest() throws Exception {
+        final QNameModule moduleQName =
+                QNameModule.create(new URI("notifi:mod"), new SimpleDateFormat("yyyy-MM-dd").parse("2016-11-23"));
+
+        final SchemaPath schemaPathNotifi = SchemaPath.create(false, QName.create(moduleQName, "notifi-grp"));
+
+        final DOMNotification notificationData = mock(DOMNotification.class);
+
+        final LeafNode leaf = mockLeaf(QName.create(moduleQName, "lf"));
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi.getLastComponent(), leaf);
+
+        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getBody()).thenReturn(notifiBody);
+
+        final String result = prepareJson(notificationData, schemaPathNotifi);
+
+        assertTrue(result.contains("ietf-restconf:notification"));
+        assertTrue(result.contains("event-time"));
+        assertTrue(result.contains("lf" + '"' + ":" + '"' + "value"));
+    }
+
+    @Test
+    public void notifi_augmTest() throws Exception {
+        final QNameModule moduleQName =
+                QNameModule.create(new URI("notifi:mod"), new SimpleDateFormat("yyyy-MM-dd").parse("2016-11-23"));
+
+        final SchemaPath schemaPathNotifi = SchemaPath.create(false, QName.create(moduleQName, "notifi-augm"));
+
+        final DOMNotification notificationData = mock(DOMNotification.class);
+
+        final LeafNode leaf = mockLeaf(QName.create(moduleQName, "lf-augm"));
+        final AugmentationNode augm = mockAugm(leaf);
+        final ContainerNode notifiBody = mockCont(schemaPathNotifi.getLastComponent(), augm);
+
+        when(notificationData.getType()).thenReturn(schemaPathNotifi);
+        when(notificationData.getBody()).thenReturn(notifiBody);
+
+        final String result = prepareJson(notificationData, schemaPathNotifi);
+
+        assertTrue(result.contains("ietf-restconf:notification"));
+        assertTrue(result.contains("event-time"));
+        assertTrue(result.contains("lf-augm" + '"' + ":" + '"' + "value"));
+    }
+
+    private AugmentationNode mockAugm(final LeafNode leaf) {
+        final AugmentationNode augm = mock(AugmentationNode.class);
+        final AugmentationIdentifier augmId = new AugmentationIdentifier(SingletonSet.of(leaf.getNodeType()));
+        when(augm.getIdentifier()).thenReturn(augmId);
+
+        final Collection<DataContainerChild<? extends PathArgument, ?>> childs = new ArrayList<>();
+        childs.add(leaf);
+
+        when(augm.getValue()).thenReturn(childs);
+        return augm;
+    }
+
+    private MapEntryNode mockMapEntry(final QName entryQName, final LeafNode leaf) {
+        final MapEntryNode entry = mock(MapEntryNode.class);
+        final Map<QName, Object> keyValues = new HashMap<>();
+        keyValues.put(leaf.getNodeType(), "value");
+        final NodeIdentifierWithPredicates nodeId = new NodeIdentifierWithPredicates(leaf.getNodeType(), keyValues);
+        when(entry.getIdentifier()).thenReturn(nodeId);
+        when(entry.getChild(any())).thenReturn(Optional.of(leaf));
+
+        final Collection<DataContainerChild<? extends PathArgument, ?>> childs = new ArrayList<>();
+        childs.add(leaf);
+
+        when(entry.getValue()).thenReturn(childs);
+        return entry;
+    }
+
+    private MapNode mockList(final QName listQName, final MapEntryNode... entries) {
+        final MapNode list = mock(MapNode.class);
+        when(list.getIdentifier()).thenReturn(NodeIdentifier.create(listQName));
+        when(list.getNodeType()).thenReturn(listQName);
+        when(list.getValue()).thenReturn(Lists.newArrayList(entries));
+        return list;
+    }
+
+    private ContainerNode mockCont(final QName contQName, final DataContainerChild<? extends PathArgument, ?> child) {
+        final ContainerNode cont = mock(ContainerNode.class);
+        when(cont.getIdentifier()).thenReturn(NodeIdentifier.create(contQName));
+        when(cont.getNodeType()).thenReturn(contQName);
+
+        final Collection<DataContainerChild<? extends PathArgument, ?>> childs = new ArrayList<>();
+        childs.add(child);
+        when(cont.getValue()).thenReturn(childs);
+        return cont;
+    }
+
+    private LeafNode mockLeaf(final QName leafQName) {
+        final LeafNode child = mock(LeafNode.class);
+        when(child.getNodeType()).thenReturn(leafQName);
+        when(child.getIdentifier()).thenReturn(NodeIdentifier.create(leafQName));
+        when(child.getValue()).thenReturn("value");
+        return child;
+    }
+
+    private String prepareJson(final DOMNotification notificationData, final SchemaPath schemaPathNotifi)
+            throws Exception {
+        final List<SchemaPath> paths = new ArrayList<>();
+        paths.add(schemaPathNotifi);
+        final List<NotificationListenerAdapter> listNotifi =
+                Notificator.createNotificationListener(paths, "stream-name", NotificationOutputType.JSON.toString());
+        final NotificationListenerAdapter notifi = listNotifi.get(0);
+
+        final Class<?> vars[] = {};
+        final Method prepareJsonM = notifi.getClass().getDeclaredMethod("prepareJson", vars);
+        prepareJsonM.setAccessible(true);
+
+        final Field notification = notifi.getClass().getDeclaredField("notification");
+        notification.setAccessible(true);
+        notification.set(notifi, notificationData);
+
+        final Field schema = notifi.getClass().getDeclaredField("schemaContext");
+        schema.setAccessible(true);
+        schema.set(notifi, this.schmeaCtx);
+
+        final String result = (String) prepareJsonM.invoke(notifi, null);
+        Preconditions.checkNotNull(result);
+        return result;
+    }
+}
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/netconf/sal/rest/impl/DepthAwareNormalizedNodeWriterTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/netconf/sal/rest/impl/DepthAwareNormalizedNodeWriterTest.java
new file mode 100644 (file)
index 0000000..ba8aee8
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2016 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.netconf.sal.rest.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+public class DepthAwareNormalizedNodeWriterTest {
+
+    @Mock
+    private NormalizedNodeStreamWriter writer;
+    @Mock
+    private ContainerNode containerNodeData;
+    @Mock
+    private MapNode mapNodeData;
+    @Mock
+    private MapEntryNode mapEntryNodeData;
+    @Mock
+    private LeafSetNode<String> leafSetNodeData;
+    @Mock
+    private LeafSetEntryNode<String> leafSetEntryNodeData;
+    @Mock
+    private LeafNode<String> keyLeafNodeData;
+    @Mock
+    private LeafNode<String> anotherLeafNodeData;
+
+    private NodeIdentifier containerNodeIdentifier;
+    private NodeIdentifier mapNodeIdentifier;
+    private NodeIdentifierWithPredicates mapEntryNodeIdentifier;
+    private NodeIdentifier leafSetNodeIdentifier;
+    private NodeWithValue<?> leafSetEntryNodeIdentifier;
+    private NodeIdentifier keyLeafNodeIdentifier;
+    private NodeIdentifier anotherLeafNodeIdentifier;
+
+    private Collection<DataContainerChild<?, ?>> containerNodeValue;
+    private Collection<MapEntryNode> mapNodeValue;
+    private Collection<DataContainerChild<?, ?>> mapEntryNodeValue;
+    private Collection<LeafSetEntryNode<String>> leafSetNodeValue;
+    private String leafSetEntryNodeValue;
+    private String keyLeafNodeValue;
+    private String anotherLeafNodeValue;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        // identifiers
+        containerNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "container"));
+        Mockito.when(containerNodeData.getIdentifier()).thenReturn(containerNodeIdentifier);
+
+        mapNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "list"));
+        Mockito.when(mapNodeData.getIdentifier()).thenReturn(mapNodeIdentifier);
+
+        final QName leafSetEntryNodeQName = QName.create("namespace", "leaf-set-entry");
+        leafSetEntryNodeValue = "leaf-set-value";
+        leafSetEntryNodeIdentifier = new NodeWithValue<>(leafSetEntryNodeQName, leafSetEntryNodeValue);
+        Mockito.when(leafSetEntryNodeData.getIdentifier()).thenReturn(leafSetEntryNodeIdentifier);
+        Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeQName);
+
+        leafSetNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "leaf-set"));
+        Mockito.when(leafSetNodeData.getIdentifier()).thenReturn(leafSetNodeIdentifier);
+
+        final QName mapEntryNodeKey = QName.create("namespace", "key-field");
+        keyLeafNodeIdentifier = NodeIdentifier.create(mapEntryNodeKey);
+        keyLeafNodeValue = "key-value";
+
+        mapEntryNodeIdentifier = new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+                QName.create("namespace", "list-entry"), Collections.singletonMap(mapEntryNodeKey, keyLeafNodeValue));
+        Mockito.when(mapEntryNodeData.getIdentifier()).thenReturn(mapEntryNodeIdentifier);
+        Mockito.when(mapEntryNodeData.getChild(keyLeafNodeIdentifier)).thenReturn(Optional.of(keyLeafNodeData));
+
+        Mockito.when(keyLeafNodeData.getValue()).thenReturn(keyLeafNodeValue);
+        Mockito.when(keyLeafNodeData.getIdentifier()).thenReturn(keyLeafNodeIdentifier);
+
+        anotherLeafNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "another-field"));
+        anotherLeafNodeValue = "another-value";
+
+        Mockito.when(anotherLeafNodeData.getValue()).thenReturn(anotherLeafNodeValue);
+        Mockito.when(anotherLeafNodeData.getIdentifier()).thenReturn(anotherLeafNodeIdentifier);
+
+        // values
+        Mockito.when(leafSetEntryNodeData.getValue()).thenReturn(leafSetEntryNodeValue);
+
+        leafSetNodeValue = Collections.singletonList(leafSetEntryNodeData);
+        Mockito.when(leafSetNodeData.getValue()).thenReturn(leafSetNodeValue);
+
+        containerNodeValue = Collections.singleton(leafSetNodeData);
+        Mockito.when(containerNodeData.getValue()).thenReturn(containerNodeValue);
+
+        mapEntryNodeValue = Sets.newHashSet(keyLeafNodeData, anotherLeafNodeData);
+        Mockito.when(mapEntryNodeData.getValue()).thenReturn(mapEntryNodeValue);
+
+        mapNodeValue = Collections.singleton(mapEntryNodeData);
+        Mockito.when(mapNodeData.getValue()).thenReturn(mapNodeValue);
+    }
+
+    /**
+     * Test write {@link ContainerNode} with children but write data only to depth 1 (children will not be written).
+     */
+    @Test
+    public void writeContainerWithoutChildrenTest() throws Exception {
+        final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(writer, 1);
+
+        depthWriter.write(containerNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write {@link ContainerNode} with children and write also all its children.
+     */
+    @Test
+    public void writeContainerWithChildrenTest() throws Exception {
+        final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+                writer, Integer.MAX_VALUE);
+
+        depthWriter.write(containerNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+                leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+        inOrder.verify(writer, Mockito.times(2)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link MapNode} with children but write data only to depth 1 (children will not be written).
+     */
+    @Test
+    public void writeMapNodeWithoutChildrenTest() throws Exception {
+        final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(writer, 1);
+
+        depthWriter.write(mapNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+        inOrder.verify(writer, Mockito.times(2)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write {@link MapNode} with children and write also all its children.
+     *
+     * FIXME
+     * Although ordered writer is used leaves are not written in expected order.
+     *
+     */
+    @Ignore
+    @Test
+    public void writeMapNodeWithChildrenTest() throws Exception {
+        final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+                writer, Integer.MAX_VALUE);
+
+        depthWriter.write(mapNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        inOrder.verify(writer, Mockito.times(2)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+        // FIXME this assertion is not working because leaves are not written in expected order
+        inOrder.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+        inOrder.verify(writer, Mockito.times(2)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link LeafSetNode} with depth 1 (children will not be written).
+     */
+    @Test
+    public void writeLeafSetNodeWithoutChildrenTest() throws Exception {
+        final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+                writer, 1);
+
+        depthWriter.write(leafSetNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link LeafSetNode} when all its children will be written.
+     */
+    @Test
+    public void writeLeafSetNodeWithChildrenTest() throws Exception {
+        final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+                writer, Integer.MAX_VALUE);
+
+        depthWriter.write(leafSetNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+                leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link LeafSetEntryNode}.
+     */
+    @Test
+    public void writeLeafSetEntryNodeTest() throws Exception {
+        final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+                writer, Integer.MAX_VALUE);
+
+        depthWriter.write(leafSetEntryNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+                leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link MapEntryNode} unordered to depth 1 to write only keys.
+     */
+    @Test
+    public void writeMapEntryNodeUnorderedOnlyKeysTest() throws Exception {
+        final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+                writer, false, 1);
+
+        depthWriter.write(mapEntryNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        // write only the key
+        inOrder.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link MapEntryNode} unordered with full depth.
+     */
+    @Test
+    public void writeMapEntryNodeUnorderedTest() throws Exception {
+        final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+                writer, false, Integer.MAX_VALUE);
+
+        depthWriter.write(mapEntryNodeData);
+
+        // unordered
+        Mockito.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        Mockito.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+        Mockito.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+        Mockito.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link MapEntryNode} ordered with depth 1 (children will not be written).
+     */
+    @Test
+    public void writeMapEntryNodeOrderedWithoutChildrenTest() throws Exception {
+        final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+                writer, true, 1);
+
+        depthWriter.write(mapEntryNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link MapEntryNode} ordered and write also all its children.
+     *
+     * FIXME
+     * Although ordered writer is used leaves are not written in expected order.
+     *
+     */
+    @Ignore
+    @Test
+    public void writeMapEntryNodeOrderedTest() throws Exception {
+        final DepthAwareNormalizedNodeWriter depthWriter = DepthAwareNormalizedNodeWriter.forStreamWriter(
+                writer, true, Integer.MAX_VALUE);
+
+        depthWriter.write(mapEntryNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        inOrder.verify(writer, Mockito.times(2)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+        // FIXME this assertion is not working because leaves are not written in expected order
+        inOrder.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+}
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriterDepthTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriterDepthTest.java
new file mode 100644 (file)
index 0000000..f26441f
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2016 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.restconf.jersey.providers;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+/**
+ * Unit test for {@link ParameterAwareNormalizedNodeWriter} used with depth parameter.
+ */
+public class ParameterAwareNormalizedNodeWriterDepthTest {
+
+    @Mock
+    private NormalizedNodeStreamWriter writer;
+    @Mock
+    private ContainerNode containerNodeData;
+    @Mock
+    private MapNode mapNodeData;
+    @Mock
+    private MapEntryNode mapEntryNodeData;
+    @Mock
+    private LeafSetNode<String> leafSetNodeData;
+    @Mock
+    private LeafSetEntryNode<String> leafSetEntryNodeData;
+    @Mock
+    private LeafNode<String> keyLeafNodeData;
+    @Mock
+    private LeafNode<String> anotherLeafNodeData;
+
+    private NodeIdentifier containerNodeIdentifier;
+    private NodeIdentifier mapNodeIdentifier;
+    private NodeIdentifierWithPredicates mapEntryNodeIdentifier;
+    private NodeIdentifier leafSetNodeIdentifier;
+    private NodeWithValue<?> leafSetEntryNodeIdentifier;
+    private NodeIdentifier keyLeafNodeIdentifier;
+    private NodeIdentifier anotherLeafNodeIdentifier;
+
+    private Collection<DataContainerChild<?, ?>> containerNodeValue;
+    private Collection<MapEntryNode> mapNodeValue;
+    private Collection<DataContainerChild<?, ?>> mapEntryNodeValue;
+    private Collection<LeafSetEntryNode<String>> leafSetNodeValue;
+    private String leafSetEntryNodeValue;
+    private String keyLeafNodeValue;
+    private String anotherLeafNodeValue;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        // identifiers
+        containerNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "container"));
+        Mockito.when(containerNodeData.getIdentifier()).thenReturn(containerNodeIdentifier);
+        Mockito.when(containerNodeData.getNodeType()).thenReturn(containerNodeIdentifier.getNodeType());
+
+        mapNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "list"));
+        Mockito.when(mapNodeData.getIdentifier()).thenReturn(mapNodeIdentifier);
+
+        final QName leafSetEntryNodeQName = QName.create("namespace", "leaf-set-entry");
+        leafSetEntryNodeValue = "leaf-set-value";
+        leafSetEntryNodeIdentifier = new NodeWithValue<>(leafSetEntryNodeQName, leafSetEntryNodeValue);
+        Mockito.when(leafSetEntryNodeData.getIdentifier()).thenReturn(leafSetEntryNodeIdentifier);
+        Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeQName);
+
+        leafSetNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "leaf-set"));
+        Mockito.when(leafSetNodeData.getIdentifier()).thenReturn(leafSetNodeIdentifier);
+
+        final QName mapEntryNodeKey = QName.create("namespace", "key-field");
+        keyLeafNodeIdentifier = NodeIdentifier.create(mapEntryNodeKey);
+        keyLeafNodeValue = "key-value";
+
+        mapEntryNodeIdentifier = new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+                QName.create("namespace", "list-entry"), Collections.singletonMap(mapEntryNodeKey, keyLeafNodeValue));
+        Mockito.when(mapEntryNodeData.getIdentifier()).thenReturn(mapEntryNodeIdentifier);
+        Mockito.when(mapEntryNodeData.getChild(keyLeafNodeIdentifier)).thenReturn(Optional.of(keyLeafNodeData));
+
+        Mockito.when(keyLeafNodeData.getValue()).thenReturn(keyLeafNodeValue);
+        Mockito.when(keyLeafNodeData.getIdentifier()).thenReturn(keyLeafNodeIdentifier);
+
+        anotherLeafNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "another-field"));
+        anotherLeafNodeValue = "another-value";
+
+        Mockito.when(anotherLeafNodeData.getValue()).thenReturn(anotherLeafNodeValue);
+        Mockito.when(anotherLeafNodeData.getIdentifier()).thenReturn(anotherLeafNodeIdentifier);
+
+        // values
+        Mockito.when(leafSetEntryNodeData.getValue()).thenReturn(leafSetEntryNodeValue);
+
+        leafSetNodeValue = Collections.singletonList(leafSetEntryNodeData);
+        Mockito.when(leafSetNodeData.getValue()).thenReturn(leafSetNodeValue);
+
+        containerNodeValue = Collections.singleton(leafSetNodeData);
+        Mockito.when(containerNodeData.getValue()).thenReturn(containerNodeValue);
+
+        mapEntryNodeValue = Sets.newHashSet(keyLeafNodeData, anotherLeafNodeData);
+        Mockito.when(mapEntryNodeData.getValue()).thenReturn(mapEntryNodeValue);
+
+        mapNodeValue = Collections.singleton(mapEntryNodeData);
+        Mockito.when(mapNodeData.getValue()).thenReturn(mapNodeValue);
+    }
+
+    /**
+     * Test write {@link ContainerNode} with children but write data only to depth 1 (children will not be written).
+     * Depth parameter is limited to 1.
+     */
+    @Test
+    public void writeContainerWithoutChildrenDepthTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter
+                .forStreamWriter(writer, 1, null);
+
+        parameterWriter.write(containerNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write {@link ContainerNode} with children and write also all its children.
+     * Depth parameter has higher value than maximal children depth.
+     */
+    @Test
+    public void writeContainerWithChildrenDepthTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, Integer.MAX_VALUE, null);
+
+        parameterWriter.write(containerNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+                leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+        inOrder.verify(writer, Mockito.times(2)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link MapNode} with children but write data only to depth 1 (children will not be written).
+     * Depth parameter limits depth to 1.
+     */
+    @Test
+    public void writeMapNodeWithoutChildrenDepthTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter
+                .forStreamWriter(writer, 1, null);
+
+        parameterWriter.write(mapNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        inOrder.verify(writer, Mockito.times(2)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write {@link MapNode} with children and write also all its children.
+     * Depth parameter has higher value than maximal children depth.
+     *
+     *
+     * FIXME
+     * Although ordered writer is used leaves are not written in expected order.
+     *
+     */
+    @Ignore
+    @Test
+    public void writeMapNodeWithChildrenDepthTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, Integer.MAX_VALUE, null);
+
+        parameterWriter.write(mapNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        inOrder.verify(writer, Mockito.times(2)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+        // FIXME this assertion is not working because leaves are not written in expected order
+        inOrder.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+        inOrder.verify(writer, Mockito.times(2)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link LeafSetNode} with depth 1 (children will not be written).
+     * Depth parameter limits depth to 1.
+     */
+    @Test
+    public void writeLeafSetNodeWithoutChildrenDepthTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, 1, null);
+
+        parameterWriter.write(leafSetNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link LeafSetNode} when all its children will be written.
+     * Depth parameter has higher value than maximal children depth.
+     */
+    @Test
+    public void writeLeafSetNodeWithChildrenDepthTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, Integer.MAX_VALUE, null);
+
+        parameterWriter.write(leafSetNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+                leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link LeafSetEntryNode}.
+     * Depth parameter has higher value than maximal children depth.
+     */
+    @Test
+    public void writeLeafSetEntryNodeDepthTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, Integer.MAX_VALUE, null);
+
+        parameterWriter.write(leafSetEntryNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+                leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link MapEntryNode} unordered to depth 1 to write only keys.
+     * Depth parameter limits depth to 1.
+     */
+    @Test
+    public void writeMapEntryNodeUnorderedOnlyKeysDepthTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, false, 1, null);
+
+        parameterWriter.write(mapEntryNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        // write only the key
+        inOrder.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link MapEntryNode} unordered with full depth.
+     * Depth parameter has higher value than maximal children depth.
+     */
+    @Test
+    public void writeMapEntryNodeUnorderedDepthTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, false, Integer.MAX_VALUE, null);
+
+        parameterWriter.write(mapEntryNodeData);
+
+        // unordered
+        Mockito.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        Mockito.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+        Mockito.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+        Mockito.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link MapEntryNode} ordered with depth 1 (children will not be written).
+     * Depth parameter limits depth to 1.
+     */
+    @Test
+    public void writeMapEntryNodeOrderedWithoutChildrenTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, true, 1, null);
+
+        parameterWriter.write(mapEntryNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write with {@link MapEntryNode} ordered and write also all its children.
+     * Depth parameter has higher value than maximal children depth.
+     *
+     * FIXME
+     * Although ordered writer is used leaves are not written in expected order.
+     *
+     */
+    @Ignore
+    @Test
+    public void writeMapEntryNodeOrderedTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, true, Integer.MAX_VALUE, null);
+
+        parameterWriter.write(mapEntryNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        inOrder.verify(writer, Mockito.times(2)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+        // FIXME this assertion is not working because leaves are not written in expected order
+        inOrder.verify(writer, Mockito.times(1)).leafNode(anotherLeafNodeIdentifier, anotherLeafNodeValue);
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+}
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriterFieldsTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriterFieldsTest.java
new file mode 100644 (file)
index 0000000..b6c2da7
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2016 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.restconf.jersey.providers;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+/**
+ * Unit test for {@link ParameterAwareNormalizedNodeWriter} used with fields parameter.
+ */
+public class ParameterAwareNormalizedNodeWriterFieldsTest {
+
+    @Mock
+    private NormalizedNodeStreamWriter writer;
+    @Mock
+    private ContainerNode containerNodeData;
+    @Mock
+    private MapNode mapNodeData;
+    @Mock
+    private MapEntryNode mapEntryNodeData;
+    @Mock
+    private LeafSetNode<String> leafSetNodeData;
+    @Mock
+    private LeafSetEntryNode<String> leafSetEntryNodeData;
+    @Mock
+    private LeafNode<String> keyLeafNodeData;
+
+    private NodeIdentifier containerNodeIdentifier;
+    private NodeIdentifier mapNodeIdentifier;
+    private NodeIdentifierWithPredicates mapEntryNodeIdentifier;
+    private NodeIdentifier leafSetNodeIdentifier;
+    private NodeWithValue<?> leafSetEntryNodeIdentifier;
+    private NodeIdentifier keyLeafNodeIdentifier;
+
+    private Collection<DataContainerChild<?, ?>> containerNodeValue;
+    private Collection<MapEntryNode> mapNodeValue;
+    private Collection<DataContainerChild<?, ?>> mapEntryNodeValue;
+    private Collection<LeafSetEntryNode<String>> leafSetNodeValue;
+    private String leafSetEntryNodeValue;
+    private String keyLeafNodeValue;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        // identifiers
+        containerNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "container"));
+        Mockito.when(containerNodeData.getIdentifier()).thenReturn(containerNodeIdentifier);
+        Mockito.when(containerNodeData.getNodeType()).thenReturn(containerNodeIdentifier.getNodeType());
+
+        mapNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "list"));
+        Mockito.when(mapNodeData.getIdentifier()).thenReturn(mapNodeIdentifier);
+        Mockito.when(mapNodeData.getNodeType()).thenReturn(mapNodeIdentifier.getNodeType());
+
+        final QName leafSetEntryNodeQName = QName.create("namespace", "leaf-set-entry");
+        leafSetEntryNodeValue = "leaf-set-value";
+        leafSetEntryNodeIdentifier = new NodeWithValue<>(leafSetEntryNodeQName, leafSetEntryNodeValue);
+        Mockito.when(leafSetEntryNodeData.getIdentifier()).thenReturn(leafSetEntryNodeIdentifier);
+        Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeIdentifier.getNodeType());
+        Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeQName);
+
+        leafSetNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "leaf-set"));
+        Mockito.when(leafSetNodeData.getIdentifier()).thenReturn(leafSetNodeIdentifier);
+        Mockito.when(leafSetNodeData.getNodeType()).thenReturn(leafSetNodeIdentifier.getNodeType());
+
+        final QName mapEntryNodeKey = QName.create("namespace", "key-field");
+        keyLeafNodeIdentifier = NodeIdentifier.create(mapEntryNodeKey);
+        keyLeafNodeValue = "key-value";
+
+        mapEntryNodeIdentifier = new NodeIdentifierWithPredicates(
+                QName.create("namespace", "list-entry"), Collections.singletonMap(mapEntryNodeKey, keyLeafNodeValue));
+        Mockito.when(mapEntryNodeData.getIdentifier()).thenReturn(mapEntryNodeIdentifier);
+        Mockito.when(mapEntryNodeData.getNodeType()).thenReturn(mapEntryNodeIdentifier.getNodeType());
+        Mockito.when(mapEntryNodeData.getChild(keyLeafNodeIdentifier)).thenReturn(Optional.of(keyLeafNodeData));
+
+        Mockito.when(keyLeafNodeData.getValue()).thenReturn(keyLeafNodeValue);
+        Mockito.when(keyLeafNodeData.getIdentifier()).thenReturn(keyLeafNodeIdentifier);
+        Mockito.when(keyLeafNodeData.getNodeType()).thenReturn(keyLeafNodeIdentifier.getNodeType());
+
+        // values
+        Mockito.when(leafSetEntryNodeData.getValue()).thenReturn(leafSetEntryNodeValue);
+
+        leafSetNodeValue = Collections.singletonList(leafSetEntryNodeData);
+        Mockito.when(leafSetNodeData.getValue()).thenReturn(leafSetNodeValue);
+
+        containerNodeValue = Collections.singleton(leafSetNodeData);
+        Mockito.when(containerNodeData.getValue()).thenReturn(containerNodeValue);
+
+        mapEntryNodeValue = Sets.newHashSet(keyLeafNodeData);
+        Mockito.when(mapEntryNodeData.getValue()).thenReturn(mapEntryNodeValue);
+
+        mapNodeValue = Collections.singleton(mapEntryNodeData);
+        Mockito.when(mapNodeData.getValue()).thenReturn(mapNodeValue);
+    }
+
+    /**
+     * Test write {@link ContainerNode} when children which will be written are limited.
+     * Fields parameter selects 0/1 of container children to be written.
+     */
+    @Test
+    public void writeContainerWithLimitedFieldsTest() throws Exception {
+        final List<Set<QName>> limitedFields = new ArrayList<>();
+        limitedFields.add(Sets.newHashSet());
+
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, null, limitedFields);
+
+        parameterWriter.write(containerNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write {@link ContainerNode} when all its children are selected to be written.
+     * Fields parameter selects 1/1 of container children to be written.
+     */
+    @Test
+    public void writeContainerAllFieldsTest() throws Exception {
+        final List<Set<QName>> limitedFields = new ArrayList<>();
+        limitedFields.add(Sets.newHashSet(leafSetNodeIdentifier.getNodeType()));
+
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, null, limitedFields);
+
+        parameterWriter.write(containerNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+                leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+        inOrder.verify(writer, Mockito.times(2)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write {@link MapEntryNode} as child of {@link MapNode} when children which will be written are limited.
+     * Fields parameter selects 0/1 of map entry node children to be written.
+     */
+    @Test
+    public void writeMapEntryNodeWithLimitedFieldsTest() throws Exception {
+        final List<Set<QName>> limitedFields = new ArrayList<>();
+        limitedFields.add(Sets.newHashSet());
+
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, null, limitedFields);
+
+        parameterWriter.write(mapNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        inOrder.verify(writer, Mockito.times(2)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write {@link MapEntryNode} as child of {@link MapNode} when all its children will be written.
+     * Fields parameter selects 1/1 of map entry node children to be written.
+     */
+    @Test
+    public void writeMapNodeAllFieldsTest() throws Exception {
+        final List<Set<QName>> limitedFields = new ArrayList<>();
+        limitedFields.add(Sets.newHashSet(keyLeafNodeData.getNodeType()));
+
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, null, limitedFields);
+
+        parameterWriter.write(mapNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startMapNode(mapNodeIdentifier, mapNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).startMapEntryNode(mapEntryNodeIdentifier, mapEntryNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).leafNode(keyLeafNodeIdentifier, keyLeafNodeValue);
+        inOrder.verify(writer, Mockito.times(2)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+}
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriterParametersTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/jersey/providers/ParameterAwareNormalizedNodeWriterParametersTest.java
new file mode 100644 (file)
index 0000000..9604fcd
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2016 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.restconf.jersey.providers;
+
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+
+/**
+ * Unit test for {@link ParameterAwareNormalizedNodeWriter} used with all parameters.
+ */
+public class ParameterAwareNormalizedNodeWriterParametersTest {
+
+    @Mock
+    private NormalizedNodeStreamWriter writer;
+    @Mock
+    private ContainerNode containerNodeData;
+    @Mock
+    private LeafSetNode<String> leafSetNodeData;
+    @Mock
+    private LeafSetEntryNode<String> leafSetEntryNodeData;
+    @Mock
+    private ContainerNode rootDataContainerData;
+
+    private NodeIdentifier containerNodeIdentifier;
+    private NodeIdentifier leafSetNodeIdentifier;
+    private NodeWithValue<?> leafSetEntryNodeIdentifier;
+    private NodeIdentifier rootDataContainerIdentifier;
+
+    private Collection<DataContainerChild<?, ?>> containerNodeValue;
+    private Collection<LeafSetEntryNode<String>> leafSetNodeValue;
+    private String leafSetEntryNodeValue;
+    private Collection<DataContainerChild<?, ?>> rootDataContainerValue;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        // identifiers
+        containerNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "container"));
+        Mockito.when(containerNodeData.getIdentifier()).thenReturn(containerNodeIdentifier);
+        Mockito.when(containerNodeData.getNodeType()).thenReturn(containerNodeIdentifier.getNodeType());
+
+        final QName leafSetEntryNodeQName = QName.create("namespace", "leaf-set-entry");
+        leafSetEntryNodeValue = "leaf-set-value";
+        leafSetEntryNodeIdentifier = new NodeWithValue<>(leafSetEntryNodeQName, leafSetEntryNodeValue);
+        Mockito.when(leafSetEntryNodeData.getIdentifier()).thenReturn(leafSetEntryNodeIdentifier);
+        Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeIdentifier.getNodeType());
+        Mockito.when(leafSetEntryNodeData.getNodeType()).thenReturn(leafSetEntryNodeQName);
+
+        leafSetNodeIdentifier = NodeIdentifier.create(QName.create("namespace", "leaf-set"));
+        Mockito.when(leafSetNodeData.getIdentifier()).thenReturn(leafSetNodeIdentifier);
+        Mockito.when(leafSetNodeData.getNodeType()).thenReturn(leafSetNodeIdentifier.getNodeType());
+
+        rootDataContainerIdentifier = NodeIdentifier.create(
+                QName.create("urn:ietf:params:xml:ns:netconf:base:1.0", "data"));
+        Mockito.when(rootDataContainerData.getIdentifier()).thenReturn(rootDataContainerIdentifier);
+        Mockito.when(rootDataContainerData.getNodeType()).thenReturn(rootDataContainerIdentifier.getNodeType());
+
+        // values
+        Mockito.when(leafSetEntryNodeData.getValue()).thenReturn(leafSetEntryNodeValue);
+
+        leafSetNodeValue = Collections.singletonList(leafSetEntryNodeData);
+        Mockito.when(leafSetNodeData.getValue()).thenReturn(leafSetNodeValue);
+
+        containerNodeValue = Collections.singleton(leafSetNodeData);
+        Mockito.when(containerNodeData.getValue()).thenReturn(containerNodeValue);
+
+        rootDataContainerValue = Collections.singleton(leafSetNodeData);
+        Mockito.when(rootDataContainerData.getValue()).thenReturn(rootDataContainerValue);
+    }
+
+    /**
+     * Test write {@link ContainerNode} when all its children are selected to be written by fields parameter.
+     * Depth parameter is also used and limits output to depth 1.
+     * Fields parameter has effect limiting depth parameter in the way that selected nodes and its ancestors are
+     * written regardless of their depth (some of container children have depth > 1).
+     * Fields parameter selects all container children to be written and also all children of those children.
+     */
+    @Test
+    public void writeContainerParameterPrioritiesTest() throws Exception {
+        final List<Set<QName>> limitedFields = new ArrayList<>();
+        limitedFields.add(Sets.newHashSet(leafSetNodeIdentifier.getNodeType()));
+        limitedFields.add(Sets.newHashSet(leafSetEntryNodeIdentifier.getNodeType()));
+
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, 1, limitedFields);
+
+        parameterWriter.write(containerNodeData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startContainerNode(containerNodeIdentifier, containerNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+                leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+        inOrder.verify(writer, Mockito.times(2)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+
+    /**
+     * Test write {@link ContainerNode} which represents data at restconf/data root.
+     * No parameters are used.
+     */
+    @Test
+    public void writeRootDataTest() throws Exception {
+        final ParameterAwareNormalizedNodeWriter parameterWriter = ParameterAwareNormalizedNodeWriter.forStreamWriter(
+                writer, null, null);
+
+        parameterWriter.write(rootDataContainerData);
+
+        final InOrder inOrder = Mockito.inOrder(writer);
+        inOrder.verify(writer, Mockito.times(1)).startLeafSet(leafSetNodeIdentifier, leafSetNodeValue.size());
+        inOrder.verify(writer, Mockito.times(1)).leafSetEntryNode(
+                leafSetEntryNodeIdentifier.getNodeType(), leafSetEntryNodeValue);
+        inOrder.verify(writer, Mockito.times(1)).endNode();
+        Mockito.verifyNoMoreInteractions(writer);
+    }
+}
\ No newline at end of file
index 523625ad82b3329db21152591adf08ac5f2749f0..ed959d3ae1ae8046eba28a817739aefa936d9ddd 100644 (file)
@@ -47,7 +47,7 @@ import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.restconf.Draft17.RestconfModule;
+import org.opendaylight.restconf.Draft18.RestconfModule;
 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.rest.services.api.RestconfModulesService;
index 636ebdeab0f098c4936c8f367763bde9d7cf65aa..8ec10ceacf03b3275aacfa652f9f000208e12f65 100644 (file)
@@ -29,7 +29,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.md.sal.dom.broker.impl.mount.DOMMountPointServiceImpl;
 import org.opendaylight.controller.md.sal.dom.broker.spi.mount.SimpleDOMMountPoint;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.utils.RestconfConstants;
@@ -193,8 +193,8 @@ class RestconfModulesServiceTestUtils {
 
         when(schemaContext.findModuleByNamespaceAndRevision(any(URI.class), any(Date.class))).thenAnswer(invocation -> {
             final Object[] args = invocation.getArguments();
-            if ((args[0] == Draft17.RestconfModule.IETF_RESTCONF_QNAME.getNamespace())
-                    && (args[1] == Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision())) {
+            if ((args[0] == Draft18.RestconfModule.IETF_RESTCONF_QNAME.getNamespace())
+                    && (args[1] == Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision())) {
                 return parseCustomRestconfSource(restconfModuleName).findModuleByName(
                         restconfModuleName, (Date) args[1]);
             } else {
index 1e339ad18ec0bdae6ee06e77b5af49e7096ec910..8cc5d3b8dfd0196f92929621730783e705ded2a5 100644 (file)
@@ -37,11 +37,12 @@ import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
 import org.opendaylight.netconf.sal.streams.listeners.Notificator;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.rest.services.api.RestconfStreamsService;
 import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeConstants;
 import org.opendaylight.restconf.utils.mapping.RestconfMappingStreamConstants;
+import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
@@ -82,9 +83,12 @@ public class RestconfStreamsServiceTest {
         Notificator.removeAllListeners();
 
         // put test streams
-        Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(0));
-        Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(1));
-        Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(2));
+        Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(0),
+                NotificationOutputType.XML);
+        Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(1),
+                NotificationOutputType.XML);
+        Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(2),
+                NotificationOutputType.XML);
     }
 
     @AfterClass
@@ -108,8 +112,8 @@ public class RestconfStreamsServiceTest {
     public void getAvailableStreamsTest() throws Exception {
         // prepare conditions - get correct Restconf module
         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
-        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
-                .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+                .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
                 .thenReturn(getTestingRestconfModule("ietf-restconf"));
 
         // make test
@@ -142,8 +146,8 @@ public class RestconfStreamsServiceTest {
     public void getAvailableStreamsMissingRestconfModuleNegativeTest() {
         // prepare conditions - get null Restconf module
         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
-        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
-                .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision())).thenReturn(null);
+        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+                .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision())).thenReturn(null);
 
         // make test
         this.thrown.expect(NullPointerException.class);
@@ -159,8 +163,8 @@ public class RestconfStreamsServiceTest {
     public void getAvailableStreamsMissingListStreamNegativeTest() {
         // prepare conditions - get Restconf module with missing list stream
         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
-        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
-                .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+                .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
                 .thenReturn(getTestingRestconfModule("restconf-module-with-missing-list-stream"));
 
         // make test and verify
@@ -186,8 +190,8 @@ public class RestconfStreamsServiceTest {
     public void getAvailableStreamsMissingContainerStreamsNegativeTest() {
         // prepare conditions - get Restconf module with missing container streams
         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
-        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
-                .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+                .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
                 .thenReturn(getTestingRestconfModule("restconf-module-with-missing-container-streams"));
 
         // make test and verify
@@ -212,8 +216,8 @@ public class RestconfStreamsServiceTest {
     public void getAvailableStreamsIllegalListStreamNegativeTest() {
         // prepare conditions - get Restconf module with illegal list stream
         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
-        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
-                .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+                .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
                 .thenReturn(getTestingRestconfModule("restconf-module-with-illegal-list-stream"));
 
         // make test
@@ -229,8 +233,8 @@ public class RestconfStreamsServiceTest {
     public void getAvailableStreamsIllegalContainerStreamsNegativeTest() {
         // prepare conditions - get Restconf module with illegal container streams
         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
-        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
-                .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+                .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
                 .thenReturn(getTestingRestconfModule("restconf-module-with-illegal-container-streams"));
 
         // make test
@@ -246,8 +250,8 @@ public class RestconfStreamsServiceTest {
     public void getAvailableStreamsIllegalLeafDescriptionNegativeTest() {
         // prepare conditions - get Restconf module with illegal leaf description in list stream
         when(this.contextHandler.get()).thenReturn(this.mockSchemaContext);
-        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft17.RestconfModule.IETF_RESTCONF_QNAME
-                .getNamespace(), Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
+        when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME
+                .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()))
                 .thenReturn(getTestingRestconfModule("restconf-module-with-illegal-leaf-description"));
 
         // make test
@@ -262,7 +266,7 @@ public class RestconfStreamsServiceTest {
      * @return Restconf module
      */
     private Module getTestingRestconfModule(final String s) {
-        return this.schemaContext.findModuleByName(s, Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
+        return this.schemaContext.findModuleByName(s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
     }
 
     /**
index 485b522cf26e6c08ec3f9b631dce48578a2e4fb3..70110f0cc82324cccb75177bff93e86db4461add 100644 (file)
@@ -17,13 +17,15 @@ import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.Futures;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
@@ -110,84 +112,88 @@ public class RestconfDataServiceImplTest {
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
-        containerPlayerQname = QName.create(baseQName, "player");
-        leafQname = QName.create(baseQName, "gap");
+        final MultivaluedMap<String, String> value = Mockito.mock(MultivaluedMap.class);
+        Mockito.when(value.entrySet()).thenReturn(new HashSet<>());
+        Mockito.when(this.uriInfo.getQueryParameters()).thenReturn(value);
+
+        this.baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
+        this.containerPlayerQname = QName.create(this.baseQName, "player");
+        this.leafQname = QName.create(this.baseQName, "gap");
 
-        final QName containerLibraryQName = QName.create(baseQName, "library");
-        final QName listPlaylistQName = QName.create(baseQName, "playlist");
+        final QName containerLibraryQName = QName.create(this.baseQName, "library");
+        final QName listPlaylistQName = QName.create(this.baseQName, "playlist");
 
         final LeafNode buildLeaf = Builders.leafBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQname))
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(this.leafQname))
                 .withValue(0.2)
                 .build();
 
-        buildPlayerCont = Builders.containerBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerPlayerQname))
+        this.buildPlayerCont = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(this.containerPlayerQname))
                 .withChild(buildLeaf)
                 .build();
 
-        buildLibraryCont = Builders.containerBuilder()
+        this.buildLibraryCont = Builders.containerBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerLibraryQName))
                 .build();
 
-        buildPlaylistList = Builders.mapBuilder()
+        this.buildPlaylistList = Builders.mapBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listPlaylistQName))
                 .build();
 
-        buildBaseCont = Builders.containerBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
-                .withChild(buildPlayerCont)
+        this.buildBaseCont = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(this.baseQName))
+                .withChild(this.buildPlayerCont)
                 .build();
 
         // config contains one child the same as in operational and one additional
-        buildBaseContConfig = Builders.containerBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
-                .withChild(buildPlayerCont)
-                .withChild(buildLibraryCont)
+        this.buildBaseContConfig = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(this.baseQName))
+                .withChild(this.buildPlayerCont)
+                .withChild(this.buildLibraryCont)
                 .build();
 
         // operational contains one child the same as in config and one additional
-        buildBaseContOperational = Builders.containerBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
-                .withChild(buildPlayerCont)
-                .withChild(buildPlaylistList)
+        this.buildBaseContOperational = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(this.baseQName))
+                .withChild(this.buildPlayerCont)
+                .withChild(this.buildPlaylistList)
                 .build();
 
-        iidBase = YangInstanceIdentifier.builder()
-                .node(baseQName)
+        this.iidBase = YangInstanceIdentifier.builder()
+                .node(this.baseQName)
                 .build();
 
-        contextRef = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
-        schemaNode = DataSchemaContextTree.from(contextRef.get()).getChild(iidBase).getDataSchemaNode();
+        this.contextRef = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
+        this.schemaNode = DataSchemaContextTree.from(this.contextRef.get()).getChild(this.iidBase).getDataSchemaNode();
 
         final SchemaContextHandler schemaContextHandler = new SchemaContextHandler();
 
-        schemaContextHandler.onGlobalContextUpdated(contextRef.get());
-        dataService = new RestconfDataServiceImpl(schemaContextHandler, transactionChainHandler, mountPointServiceHandler);
-        doReturn(domTransactionChain).when(transactionChainHandler).get();
-        doReturn(read).when(domTransactionChain).newReadOnlyTransaction();
-        doReturn(readWrite).when(domTransactionChain).newReadWriteTransaction();
-        doReturn(write).when(domTransactionChain).newWriteOnlyTransaction();
-        doReturn(mountPointService).when(mountPointServiceHandler).get();
-        doReturn(Optional.of(mountPoint)).when(mountPointService).getMountPoint(any(YangInstanceIdentifier.class));
-        doReturn(contextRef.get()).when(mountPoint).getSchemaContext();
-        doReturn(Optional.of(mountDataBroker)).when(mountPoint).getService(DOMDataBroker.class);
-        doReturn(transactionChain).when(mountDataBroker).createTransactionChain(any(TransactionChainListener.class));
-        doReturn(read).when(transactionChain).newReadOnlyTransaction();
-        doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
+        schemaContextHandler.onGlobalContextUpdated(this.contextRef.get());
+        this.dataService = new RestconfDataServiceImpl(schemaContextHandler, this.transactionChainHandler, this.mountPointServiceHandler);
+        doReturn(this.domTransactionChain).when(this.transactionChainHandler).get();
+        doReturn(this.read).when(this.domTransactionChain).newReadOnlyTransaction();
+        doReturn(this.readWrite).when(this.domTransactionChain).newReadWriteTransaction();
+        doReturn(this.write).when(this.domTransactionChain).newWriteOnlyTransaction();
+        doReturn(this.mountPointService).when(this.mountPointServiceHandler).get();
+        doReturn(Optional.of(this.mountPoint)).when(this.mountPointService).getMountPoint(any(YangInstanceIdentifier.class));
+        doReturn(this.contextRef.get()).when(this.mountPoint).getSchemaContext();
+        doReturn(Optional.of(this.mountDataBroker)).when(this.mountPoint).getService(DOMDataBroker.class);
+        doReturn(this.transactionChain).when(this.mountDataBroker).createTransactionChain(any(TransactionChainListener.class));
+        doReturn(this.read).when(this.transactionChain).newReadOnlyTransaction();
+        doReturn(this.readWrite).when(this.transactionChain).newReadWriteTransaction();
     }
 
     @Test
     public void testReadData() {
-        doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
-        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.OPERATIONAL, iidBase);
-        final Response response = dataService.readData("example-jukebox:jukebox", uriInfo);
+        doReturn(new MultivaluedHashMap<String, String>()).when(this.uriInfo).getQueryParameters();
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+                .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.OPERATIONAL, this.iidBase);
+        final Response response = this.dataService.readData("example-jukebox:jukebox", this.uriInfo);
         assertNotNull(response);
         assertEquals(200, response.getStatus());
-        assertEquals(buildBaseCont, ((NormalizedNodeContext) response.getEntity()).getData());
+        assertEquals(this.buildBaseCont, ((NormalizedNodeContext) response.getEntity()).getData());
     }
 
     /**
@@ -196,14 +202,14 @@ public class RestconfDataServiceImplTest {
      */
     @Test
     public void testReadDataMountPoint() {
-        doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
-        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseContConfig))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseContOperational))).when(read)
-                .read(LogicalDatastoreType.OPERATIONAL, iidBase);
+        doReturn(new MultivaluedHashMap<String, String>()).when(this.uriInfo).getQueryParameters();
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContConfig))).when(this.read)
+                .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContOperational))).when(this.read)
+                .read(LogicalDatastoreType.OPERATIONAL, this.iidBase);
 
-        final Response response = dataService.readData(
-                "example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", uriInfo);
+        final Response response = this.dataService.readData(
+                "example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox", this.uriInfo);
 
         assertNotNull(response);
         assertEquals(200, response.getStatus());
@@ -212,31 +218,91 @@ public class RestconfDataServiceImplTest {
         final NormalizedNode<?, ?> data = ((NormalizedNodeContext) response.getEntity()).getData();
         assertTrue(data instanceof ContainerNode);
         assertEquals(3, ((ContainerNode) data).getValue().size());
-        assertTrue(((ContainerNode) data).getChild(buildPlayerCont.getIdentifier()).isPresent());
-        assertTrue(((ContainerNode) data).getChild(buildLibraryCont.getIdentifier()).isPresent());
-        assertTrue(((ContainerNode) data).getChild(buildPlaylistList.getIdentifier()).isPresent());
+        assertTrue(((ContainerNode) data).getChild(this.buildPlayerCont.getIdentifier()).isPresent());
+        assertTrue(((ContainerNode) data).getChild(this.buildLibraryCont.getIdentifier()).isPresent());
+        assertTrue(((ContainerNode) data).getChild(this.buildPlaylistList.getIdentifier()).isPresent());
     }
 
     @Test(expected = RestconfDocumentedException.class)
     public void testReadDataNoData() {
-        doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
-        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION,
-                iidBase);
-        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.OPERATIONAL,
-                iidBase);
-        dataService.readData("example-jukebox:jukebox", uriInfo);
+        doReturn(new MultivaluedHashMap<String, String>()).when(this.uriInfo).getQueryParameters();
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.CONFIGURATION,
+                this.iidBase);
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.OPERATIONAL,
+                this.iidBase);
+        this.dataService.readData("example-jukebox:jukebox", this.uriInfo);
+    }
+
+    /**
+     * Read data from config datastore according to content parameter
+     */
+    @Test
+    public void testReadDataConfigTest() {
+        final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+        parameters.put("content", Collections.singletonList("config"));
+
+        doReturn(parameters).when(this.uriInfo).getQueryParameters();
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContConfig))).when(this.read)
+                .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContOperational))).when(this.read)
+                .read(LogicalDatastoreType.OPERATIONAL, this.iidBase);
+
+        final Response response = this.dataService.readData("example-jukebox:jukebox", this.uriInfo);
+
+        assertNotNull(response);
+        assertEquals(200, response.getStatus());
+
+        // response must contain only config data
+        final NormalizedNode<?, ?> data = ((NormalizedNodeContext) response.getEntity()).getData();
+
+        // config data present
+        assertTrue(((ContainerNode) data).getChild(this.buildPlayerCont.getIdentifier()).isPresent());
+        assertTrue(((ContainerNode) data).getChild(this.buildLibraryCont.getIdentifier()).isPresent());
+
+        // state data absent
+        assertFalse(((ContainerNode) data).getChild(this.buildPlaylistList.getIdentifier()).isPresent());
+    }
+
+    /**
+     * Read data from operational datastore according to content parameter
+     */
+    @Test
+    public void testReadDataOperationalTest() {
+        final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+        parameters.put("content", Collections.singletonList("nonconfig"));
+
+        doReturn(parameters).when(this.uriInfo).getQueryParameters();
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContConfig))).when(this.read)
+                .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseContOperational))).when(this.read)
+                .read(LogicalDatastoreType.OPERATIONAL, this.iidBase);
+
+        final Response response = this.dataService.readData("example-jukebox:jukebox", this.uriInfo);
+
+        assertNotNull(response);
+        assertEquals(200, response.getStatus());
+
+        // response must contain only operational data
+        final NormalizedNode<?, ?> data = ((NormalizedNodeContext) response.getEntity()).getData();
+
+        // state data present
+        assertTrue(((ContainerNode) data).getChild(this.buildPlayerCont.getIdentifier()).isPresent());
+        assertTrue(((ContainerNode) data).getChild(this.buildPlaylistList.getIdentifier()).isPresent());
+
+        // config data absent
+        assertFalse(((ContainerNode) data).getChild(this.buildLibraryCont.getIdentifier()).isPresent());
     }
 
     @Test
     public void testPutData() {
-        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
-        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseCont);
-
-        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, payload.getData());
-        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
-        final Response response = dataService.putData(null, payload);
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, null, this.contextRef.get());
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
+
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+                .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+        final Response response = this.dataService.putData(null, payload, this.uriInfo);
         assertNotNull(response);
         assertEquals(200, response.getStatus());
     }
@@ -246,31 +312,31 @@ public class RestconfDataServiceImplTest {
         final DOMDataBroker dataBroker = Mockito.mock(DOMDataBroker.class);
         final DOMMountPoint mountPoint = Mockito.mock(DOMMountPoint.class);
         doReturn(Optional.of(dataBroker)).when(mountPoint).getService(DOMDataBroker.class);
-        doReturn(transactionChainHandler.get()).when(dataBroker).createTransactionChain(RestConnectorProvider.transactionListener);
-        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, mountPoint, contextRef.get());
-        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseCont);
-
-        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, payload.getData());
-        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
-        final Response response = dataService.putData(null, payload);
+        doReturn(this.transactionChainHandler.get()).when(dataBroker).createTransactionChain(RestConnectorProvider.transactionListener);
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, mountPoint, this.contextRef.get());
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
+
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+                .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+        final Response response = this.dataService.putData(null, payload, this.uriInfo);
         assertNotNull(response);
         assertEquals(200, response.getStatus());
     }
 
     @Test
     public void testPostData() {
-        final QName listQname = QName.create(baseQName, "playlist");
-        final QName listKeyQname = QName.create(baseQName, "name");
+        final QName listQname = QName.create(this.baseQName, "playlist");
+        final QName listKeyQname = QName.create(this.baseQName, "name");
         final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey =
                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(listQname, listKeyQname, "name of band");
         final LeafNode<Object> content = Builders.leafBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "name")))
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(this.baseQName, "name")))
                 .withValue("name of band")
                 .build();
         final LeafNode<Object> content2 = Builders.leafBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(this.baseQName, "description")))
                 .withValue("band description")
                 .build();
         final MapEntryNode mapEntryNode = Builders.mapEntryBuilder()
@@ -283,28 +349,28 @@ public class RestconfDataServiceImplTest {
                 .withChild(mapEntryNode)
                 .build();
 
-        doReturn(new MultivaluedHashMap<String, String>()).when(uriInfo).getQueryParameters();
-        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, null, null, contextRef.get());
+        doReturn(new MultivaluedHashMap<String, String>()).when(this.uriInfo).getQueryParameters();
+        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(this.iidBase, null, null, this.contextRef.get());
         final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildList);
-        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iidBase);
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
         final MapNode data = (MapNode) payload.getData();
         final YangInstanceIdentifier.NodeIdentifierWithPredicates identifier = data.getValue().iterator().next().getIdentifier();
         final YangInstanceIdentifier node = payload.getInstanceIdentifierContext().getInstanceIdentifier().node(identifier);
-        doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
-        doNothing().when(readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
-        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
-        doReturn(UriBuilder.fromUri("http://localhost:8181/restconf/15/")).when(uriInfo).getBaseUriBuilder();
+        doReturn(Futures.immediateCheckedFuture(false)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
+        doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+        doReturn(UriBuilder.fromUri("http://localhost:8181/restconf/15/")).when(this.uriInfo).getBaseUriBuilder();
 
-        final Response response = dataService.postData(null, payload, uriInfo);
+        final Response response = this.dataService.postData(null, payload, this.uriInfo);
         assertEquals(201, response.getStatus());
     }
 
     @Test
     public void testDeleteData() {
-        doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
-        doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
-        final Response response = dataService.deleteData("example-jukebox:jukebox");
+        doNothing().when(this.readWrite).delete(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+        doReturn(Futures.immediateCheckedFuture(true)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        final Response response = this.dataService.deleteData("example-jukebox:jukebox");
         assertNotNull(response);
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
     }
@@ -314,36 +380,36 @@ public class RestconfDataServiceImplTest {
      */
     @Test
     public void testDeleteDataMountPoint() {
-        doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
-        doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
-        final Response response = dataService.deleteData("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox");
+        doNothing().when(this.readWrite).delete(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+        doReturn(Futures.immediateCheckedFuture(true)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        final Response response = this.dataService.deleteData("example-jukebox:jukebox/yang-ext:mount/example-jukebox:jukebox");
         assertNotNull(response);
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
     }
 
     @Test
     public void testPatchData() throws Exception {
-        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
+        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, null, this.contextRef.get());
         final List<PATCHEntity> entity = new ArrayList<>();
-        final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
-                .node(containerPlayerQname)
-                .node(leafQname)
+        final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(this.iidBase)
+                .node(this.containerPlayerQname)
+                .node(this.leafQname)
                 .build();
-        entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
-        entity.add(new PATCHEntity("replace data", "REPLACE", iidBase, buildBaseCont));
+        entity.add(new PATCHEntity("create data", "CREATE", this.iidBase, this.buildBaseCont));
+        entity.add(new PATCHEntity("replace data", "REPLACE", this.iidBase, this.buildBaseCont));
         entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
         final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
 
-        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, buildBaseCont);
-        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
-        doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
-        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
-        doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
-        final PATCHStatusContext status = dataService.patchData(patch, uriInfo);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+                .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, this.buildBaseCont);
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.write).submit();
+        doNothing().when(this.readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+        doReturn(Futures.immediateCheckedFuture(false)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doReturn(Futures.immediateCheckedFuture(true)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
+        final PATCHStatusContext status = this.dataService.patchData(patch, this.uriInfo);
         assertTrue(status.isOk());
         assertEquals(3, status.getEditCollection().size());
         assertEquals("replace data", status.getEditCollection().get(1).getEditId());
@@ -352,27 +418,27 @@ public class RestconfDataServiceImplTest {
     @Test
     public void testPatchDataMountPoint() throws Exception {
         final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(
-                iidBase, schemaNode, mountPoint, contextRef.get());
+                this.iidBase, this.schemaNode, this.mountPoint, this.contextRef.get());
         final List<PATCHEntity> entity = new ArrayList<>();
-        final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
-                .node(containerPlayerQname)
-                .node(leafQname)
+        final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(this.iidBase)
+                .node(this.containerPlayerQname)
+                .node(this.leafQname)
                 .build();
-        entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
-        entity.add(new PATCHEntity("replace data", "REPLACE", iidBase, buildBaseCont));
+        entity.add(new PATCHEntity("create data", "CREATE", this.iidBase, this.buildBaseCont));
+        entity.add(new PATCHEntity("replace data", "REPLACE", this.iidBase, this.buildBaseCont));
         entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
         final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
 
-        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, buildBaseCont);
-        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
-        doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
-        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
-        doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doReturn(Futures.immediateCheckedFuture(true)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+                .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, this.buildBaseCont);
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.write).submit();
+        doNothing().when(this.readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+        doReturn(Futures.immediateCheckedFuture(false)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doReturn(Futures.immediateCheckedFuture(true)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
 
-        final PATCHStatusContext status = dataService.patchData(patch, uriInfo);
+        final PATCHStatusContext status = this.dataService.patchData(patch, this.uriInfo);
         assertTrue(status.isOk());
         assertEquals(3, status.getEditCollection().size());
         assertNull(status.getGlobalErrors());
@@ -388,27 +454,27 @@ public class RestconfDataServiceImplTest {
 
         broker.setAccessible(true);
         broker.set(RestConnectorProvider.class, mock(DOMDataBroker.class));
-        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(iidBase, schemaNode, null, contextRef.get());
+        final InstanceIdentifierContext<? extends SchemaNode> iidContext = new InstanceIdentifierContext<>(this.iidBase, this.schemaNode, null, this.contextRef.get());
         final List<PATCHEntity> entity = new ArrayList<>();
-        final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(iidBase)
-                .node(containerPlayerQname)
-                .node(leafQname)
+        final YangInstanceIdentifier iidleaf = YangInstanceIdentifier.builder(this.iidBase)
+                .node(this.containerPlayerQname)
+                .node(this.leafQname)
                 .build();
-        entity.add(new PATCHEntity("create data", "CREATE", iidBase, buildBaseCont));
+        entity.add(new PATCHEntity("create data", "CREATE", this.iidBase, this.buildBaseCont));
         entity.add(new PATCHEntity("remove data", "REMOVE", iidleaf));
         entity.add(new PATCHEntity("delete data", "DELETE", iidleaf));
         final PATCHContext patch = new PATCHContext(iidContext, entity, "test patch id");
 
-        doReturn(Futures.immediateCheckedFuture(Optional.of(buildBaseCont))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, iidBase, buildBaseCont);
-        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
-        doNothing().when(readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
-        doReturn(Futures.immediateCheckedFuture(null)).when(readWrite).submit();
-        doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidBase);
-        doReturn(Futures.immediateCheckedFuture(false)).when(readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
-        doReturn(true).when(readWrite).cancel();
-        final PATCHStatusContext status = dataService.patchData(patch, uriInfo);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(this.buildBaseCont))).when(this.read)
+                .read(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, this.iidBase, this.buildBaseCont);
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.write).submit();
+        doNothing().when(this.readWrite).delete(LogicalDatastoreType.CONFIGURATION, iidleaf);
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+        doReturn(Futures.immediateCheckedFuture(false)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iidBase);
+        doReturn(Futures.immediateCheckedFuture(false)).when(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, iidleaf);
+        doReturn(true).when(this.readWrite).cancel();
+        final PATCHStatusContext status = this.dataService.patchData(patch, this.uriInfo);
 
         handler.set(RestConnectorProvider.class, null);
         handler.setAccessible(false);
index 5a57b15d63796b38293bed2659c778ca704cd0c9..4eb01e9397c5fbd6fd4da3911727566d64582b8c 100644 (file)
@@ -8,89 +8,77 @@
 
 package org.opendaylight.restconf.restful.services.impl;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import java.util.Iterator;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import javax.ws.rs.core.UriInfo;
+import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
+import org.opendaylight.restconf.handlers.RpcServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 
 public class RestconfInvokeOperationsServiceImplTest {
 
-    private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/streams";
+    private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/invoke-rpc";
 
-    @Test
-    public void testInvokeRpc() throws Exception {
+    private RestconfInvokeOperationsServiceImpl invokeOperationsService;
+
+    @Mock
+    private RpcServiceHandler rpcServiceHandler;
+
+    @Mock
+    private DOMRpcService rpcService;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
         final SchemaContextRef contextRef = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
         final SchemaContextHandler schemaContextHandler = new SchemaContextHandler();
         schemaContextHandler.onGlobalContextUpdated(contextRef.get());
-        final RestconfInvokeOperationsServiceImpl invokeOperationsService = new RestconfInvokeOperationsServiceImpl(null, schemaContextHandler);
-
-        final QName qname = QName.create("http://netconfcentral.org/ns/toaster", "2009-11-20", "toasterStatus");
-        final QName qname1 = QName.create("http://netconfcentral.org/ns/toaster", "2009-11-20", "toaster");
-        final QName rpcQnameInput = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "input");
-        final QName inputQname = QName.create(rpcQnameInput, "path");
-        final YangInstanceIdentifier iid = YangInstanceIdentifier.builder()
-                .node(rpcQnameInput)
-                .build();
-        final YangInstanceIdentifier iidAsLeafValue = YangInstanceIdentifier.builder()
-                .node(qname1)
-                .node(qname)
-                .build();
-
-        final LeafNode contentLeaf = Builders.leafBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(inputQname))
-                .withValue(iidAsLeafValue)
-                .build();
-        final ContainerNode input = Builders.containerBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(rpcQnameInput))
-                .withChild(contentLeaf)
-                .build();
-        final Iterator<RpcDefinition> iterator = contextRef.get().getOperations().iterator();
-        RpcDefinition rpcDef = null;
-        while (iterator.hasNext()) {
-            rpcDef = iterator.next();
-            if ("create-data-change-event-subscription".equals(rpcDef.getQName().getLocalName())) {
-                break;
-            }
-        }
+        this.invokeOperationsService =
+                new RestconfInvokeOperationsServiceImpl(this.rpcServiceHandler, schemaContextHandler);
+        Mockito.when(this.rpcServiceHandler.get()).thenReturn(this.rpcService);
+    }
 
-        final InstanceIdentifierContext<RpcDefinition> iidContext = new InstanceIdentifierContext<>(iid, rpcDef, null, contextRef.get());
-        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, input);
-        final NormalizedNodeContext context = invokeOperationsService.invokeRpc(null, payload, null);
+    @Test
+    public void testInvokeRpc() throws Exception {
+        final String identifier = "invoke-rpc-module:rpcTest";
+        final NormalizedNode result = Mockito.mock(NormalizedNode.class);
+        final NormalizedNodeContext payload = prepNNC(result);
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
 
-        final QName rpcQnameOutput = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "output");
-        final QName outputQname = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "2014-01-14", "stream-name");
-        final LeafNode contentLeaf2 = Builders.leafBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(outputQname))
-                .withValue("toaster:toaster/toasterStatus/datastore=CONFIGURATION/scope=BASE")
-                .build();
-        final ContainerNode output = Builders.containerBuilder()
-                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(rpcQnameOutput))
-                .withChild(contentLeaf2)
-                .build();
-        final InstanceIdentifierContext<RpcDefinition> iidContextResult = new InstanceIdentifierContext<>(null, rpcDef, null, contextRef.get());
-        final NormalizedNodeContext payloadResult = new NormalizedNodeContext(iidContextResult, output);
+        final NormalizedNodeContext rpc = this.invokeOperationsService.invokeRpc(identifier, payload, uriInfo);
+        Assert.assertEquals(result, rpc.getData());
+    }
 
-        assertNotNull(context);
-        assertEquals(payloadResult.getData(), context.getData());
-        assertEquals(payloadResult.getInstanceIdentifierContext().getSchemaNode(),
-                context.getInstanceIdentifierContext().getSchemaNode());
-        assertEquals(payloadResult.getInstanceIdentifierContext().getSchemaContext(),
-                context.getInstanceIdentifierContext().getSchemaContext());
-        assertNull(context.getInstanceIdentifierContext().getMountPoint());
-        assertNull(context.getInstanceIdentifierContext().getInstanceIdentifier());
+    private NormalizedNodeContext prepNNC(final NormalizedNode result) {
+        final InstanceIdentifierContext context = Mockito.mock(InstanceIdentifierContext.class);
+        final RpcDefinition schemaNode = Mockito.mock(RpcDefinition.class);
+        final QName qname = QName.create("invoke:rpc:module", "2013-12-3", "rpcTest");
+        final SchemaPath schemaPath = SchemaPath.create(true, qname);
+        Mockito.when(schemaNode.getPath()).thenReturn(schemaPath);
+        Mockito.when(schemaNode.getQName()).thenReturn(qname);
+        Mockito.when(context.getSchemaNode()).thenReturn(schemaNode);
+        final NormalizedNode<?, ?> data = Mockito.mock(NormalizedNode.class);
+        final DOMRpcResult domRpcResult = Mockito.mock(DOMRpcResult.class);
+        final CheckedFuture<DOMRpcResult, DOMRpcException> checkdFuture = Futures.immediateCheckedFuture(domRpcResult);
+        Mockito.when(this.rpcService.invokeRpc(schemaPath, data)).thenReturn(checkdFuture);
+        Mockito.when(domRpcResult.getResult()).thenReturn(result);
+        return new NormalizedNodeContext(context, data);
     }
 
 }
index c147f28a5ee9515cd825e3f0a544099e8fb134c4..cabbd00525cde19ba0d36d61d9a7ec3fd782d9c7 100644 (file)
@@ -12,12 +12,15 @@ import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-
 import java.lang.reflect.Field;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import javax.ws.rs.core.Response;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import org.junit.AfterClass;
@@ -25,32 +28,47 @@ import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.Notificator;
 import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
+import org.opendaylight.restconf.handlers.NotificationServiceHandler;
+import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 
 public class RestconfStreamsSubscriptionServiceImplTest {
 
-    private static final String uri = "/restconf/15/data/ietf-restconf-monitoring:restconf-state/streams/stream/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE";
+    private static final String uri = "/restconf/18/data/ietf-restconf-monitoring:restconf-state/streams/stream/"
+            + "toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE";
     private static Field listenersByStreamName;
 
     @Mock
     private DOMDataBrokerHandler dataBrokerHandler;
     @Mock
     private UriInfo uriInfo;
+    @Mock
+    private NotificationServiceHandler notificationServiceHandler;
+
+    private final SchemaContextHandler schemaHandler = new SchemaContextHandler();
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         final DOMDataBroker dataBroker = mock(DOMDataBroker.class);
         final ListenerRegistration<DOMDataChangeListener> listener = mock(ListenerRegistration.class);
-        doReturn(dataBroker).when(dataBrokerHandler).get();
+        doReturn(dataBroker).when(this.dataBrokerHandler).get();
         doReturn(listener).when(dataBroker).registerDataChangeListener(any(), any(), any(), any());
+        final MultivaluedMap<String, String> map = Mockito.mock(MultivaluedMap.class);
+        final Set<Entry<String, List<String>>> set = new HashSet<>();
+        Mockito.when(map.entrySet()).thenReturn(set);
+        Mockito.when(this.uriInfo.getQueryParameters()).thenReturn(map);
+        this.schemaHandler.onGlobalContextUpdated(TestRestconfUtils.loadSchemaContext("/notifications"));
     }
 
     @BeforeClass
@@ -58,8 +76,10 @@ public class RestconfStreamsSubscriptionServiceImplTest {
         final Map<String, ListenerAdapter> listenersByStreamNameSetter = new HashMap<>();
         final ListenerAdapter adapter = mock(ListenerAdapter.class);
         doReturn(false).when(adapter).isListening();
-        listenersByStreamNameSetter.put("toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", adapter);
-        listenersByStreamName = Notificator.class.getDeclaredField("listenersByStreamName");
+        listenersByStreamNameSetter.put(
+                "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE",
+                adapter);
+        listenersByStreamName = Notificator.class.getDeclaredField("dataChangeListener");
 
         listenersByStreamName.setAccessible(true);
         listenersByStreamName.set(Notificator.class, listenersByStreamNameSetter);
@@ -75,27 +95,38 @@ public class RestconfStreamsSubscriptionServiceImplTest {
     @Test
     public void testSubscribeToStream() {
         final UriBuilder uriBuilder = UriBuilder.fromUri(uri);
-        doReturn(uriBuilder).when(uriInfo).getAbsolutePathBuilder();
-        final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(dataBrokerHandler);
-        final Response response = streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", uriInfo);
-        assertEquals(200, response.getStatus());
-        assertEquals("ws://:8181/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", response.getHeaderString("Location"));
+        doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
+        final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
+                new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler,
+                        this.schemaHandler);
+        final NormalizedNodeContext response = streamsSubscriptionService
+                .subscribeToStream(
+                        "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE",
+                        this.uriInfo);
+        assertEquals(
+                "ws://:8181/data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE",
+                response.getNewHeaders().get("Location").toString());
     }
 
     @Test(expected = RestconfDocumentedException.class)
     public void testSubscribeToStreamMissingDatastoreInPath() {
         final UriBuilder uriBuilder = UriBuilder.fromUri(uri);
-        doReturn(uriBuilder).when(uriInfo).getAbsolutePathBuilder();
-        final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(dataBrokerHandler);
-        final Response response = streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/scope=ONE", uriInfo);
+        doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
+        final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
+                new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler,
+                        this.schemaHandler);
+        streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/scope=ONE", this.uriInfo);
     }
 
     @Test(expected = RestconfDocumentedException.class)
     public void testSubscribeToStreamMissingScopeInPath() {
         final UriBuilder uriBuilder = UriBuilder.fromUri(uri);
-        doReturn(uriBuilder).when(uriInfo).getAbsolutePathBuilder();
-        final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(dataBrokerHandler);
-        final Response response = streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL", uriInfo);
+        doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder();
+        final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService =
+                new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler,
+                        this.schemaHandler);
+        streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL",
+                this.uriInfo);
     }
 
 }
index 7ca762705b66cb7ef73b3084a35d6ce5d1502ea6..4e8875789b2f22963c0835b6b2129b26efd79cc3 100644 (file)
@@ -11,7 +11,6 @@ package org.opendaylight.restconf.restful.utils;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-
 import java.util.Collections;
 import java.util.Set;
 import org.junit.Before;
@@ -48,36 +47,38 @@ public class CreateStreamUtilTest {
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        refSchemaCtx = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
+        this.refSchemaCtx = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
     }
 
     @Test
     public void createStreamTest() {
-        payload = prepareDomPayload("create-data-change-event-subscription", "input", "toaster", "path");
-        final DOMRpcResult result = CreateStreamUtil.createStream(payload, refSchemaCtx);
+        this.payload = prepareDomPayload("create-data-change-event-subscription", "input", "toaster", "path");
+        final DOMRpcResult result = CreateStreamUtil.createDataChangeNotifiStream(this.payload, this.refSchemaCtx);
         assertEquals(result.getErrors(), Collections.emptyList());
         final NormalizedNode<?, ?> testedNn = result.getResult();
         assertNotNull(testedNn);
-        final NormalizedNodeContext contextRef = prepareDomPayload("create-data-change-event-subscription", "output", "toaster:toaster/datastore=CONFIGURATION/scope=BASE", "stream-name");
+        final NormalizedNodeContext contextRef = prepareDomPayload("create-data-change-event-subscription", "output",
+                "data-change-event-subscription/toaster:toaster/datastore=CONFIGURATION/scope=BASE", "stream-name");
         assertEquals(contextRef.getData(), testedNn);
     }
 
     @Test(expected = RestconfDocumentedException.class)
     public void createStreamWrongValueTest() {
-        payload = prepareDomPayload("create-data-change-event-subscription", "input", "String value", "path");
-        final DOMRpcResult result = CreateStreamUtil.createStream(payload, refSchemaCtx);
+        this.payload = prepareDomPayload("create-data-change-event-subscription", "input", "String value", "path");
+        final DOMRpcResult result = CreateStreamUtil.createDataChangeNotifiStream(this.payload, this.refSchemaCtx);
         assertEquals(result.getErrors(), Collections.emptyList());
     }
 
     @Test(expected = RestconfDocumentedException.class)
     public void createStreamWrongInputRpcTest() {
-        payload = prepareDomPayload("create-data-change-event-subscription2", "input", "toaster", "path2");
-        final DOMRpcResult result = CreateStreamUtil.createStream(payload, refSchemaCtx);
+        this.payload = prepareDomPayload("create-data-change-event-subscription2", "input", "toaster", "path2");
+        final DOMRpcResult result = CreateStreamUtil.createDataChangeNotifiStream(this.payload, this.refSchemaCtx);
         assertEquals(result.getErrors(), Collections.emptyList());
     }
 
-    private NormalizedNodeContext prepareDomPayload(final String rpcName, final String inputOutput, final String toasterValue, final String inputOutputName) {
-        final SchemaContext schema = refSchemaCtx.get();
+    private NormalizedNodeContext prepareDomPayload(final String rpcName, final String inputOutput,
+            final String toasterValue, final String inputOutputName) {
+        final SchemaContext schema = this.refSchemaCtx.get();
         final Module rpcModule = schema.findModuleByName("sal-remote", null);
         assertNotNull(rpcModule);
         final QName rpcQName = QName.create(rpcModule.getQNameModule(), rpcName);
@@ -92,7 +93,8 @@ public class CreateStreamUtilTest {
         }
         assertNotNull(rpcInputSchemaNode);
 
-        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> container = Builders.containerBuilder(rpcInputSchemaNode);
+        final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> container =
+                Builders.containerBuilder(rpcInputSchemaNode);
 
         final QName lfQName = QName.create(rpcModule.getQNameModule(), inputOutputName);
         final DataSchemaNode lfSchemaNode = rpcInputSchemaNode.getDataChildByName(lfQName);
@@ -110,6 +112,7 @@ public class CreateStreamUtilTest {
                 .withValue(o)).build();
         container.withChild(lfNode);
 
-        return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpcInputSchemaNode, null, schema), container.build());
+        return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpcInputSchemaNode, null, schema),
+                container.build());
     }
 }
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/ParametersUtilTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/utils/ParametersUtilTest.java
new file mode 100644 (file)
index 0000000..b55ffd9
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2016 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.restconf.restful.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+
+/**
+ * Unit test for {@link ParametersUtil}
+ */
+public class ParametersUtilTest {
+
+    /**
+     * Test when all parameters are allowed
+     */
+    @Test
+    public void checkParametersTypesTest() {
+        ParametersUtil.checkParametersTypes(
+                RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+                Sets.newHashSet("content"),
+                RestconfDataServiceConstant.ReadData.CONTENT, RestconfDataServiceConstant.ReadData.DEPTH);
+    }
+
+    /**
+     * Test when not allowed parameter type is used
+     */
+    @Test
+    public void checkParametersTypesNegativeTest() {
+        try {
+            ParametersUtil.checkParametersTypes(
+                    RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+                    Sets.newHashSet("not-allowed-parameter"),
+                    RestconfDataServiceConstant.ReadData.CONTENT, RestconfDataServiceConstant.ReadData.DEPTH);
+
+            Assert.fail("Test expected to fail due to not allowed parameter used with operation");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Test when parameter is present at most once
+     */
+    @Test
+    public void checkParameterCountTest() {
+        ParametersUtil.checkParameterCount(Lists.newArrayList("all"), RestconfDataServiceConstant.ReadData.CONTENT);
+    }
+
+    /**
+     * Test when parameter is present more than once
+     */
+    @Test
+    public void checkParameterCountNegativeTest() {
+        try {
+            ParametersUtil.checkParameterCount(Lists.newArrayList("config", "nonconfig", "all"),
+                    RestconfDataServiceConstant.ReadData.CONTENT);
+
+            Assert.fail("Test expected to fail due to multiple values of the same parameter");
+        } catch (final RestconfDocumentedException e) {
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+}
\ No newline at end of file
index a86d9ce7a84a23d26aa0a91e5a0ad60aeef849b2..337c4cbc047d711793f4a0958df9777cae720abf 100644 (file)
@@ -12,7 +12,6 @@ import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
-
 import com.google.common.util.concurrent.Futures;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
@@ -131,7 +130,8 @@ public class PostDataTransactionUtilTest {
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
         doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
         final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain);
-        final Response response = PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx);
+        final Response response =
+                PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx, null, null);
         assertEquals(201, response.getStatus());
         verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
@@ -149,7 +149,8 @@ public class PostDataTransactionUtilTest {
         doNothing().when(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, payload.getData());
         doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
         final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain);
-        final Response response = PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx);
+        final Response response =
+                PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx, null, null);
         assertEquals(201, response.getStatus());
         verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, node);
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, node, data.getValue().iterator().next());
@@ -168,9 +169,10 @@ public class PostDataTransactionUtilTest {
                 payload.getData());
         doReturn(Futures.immediateFailedCheckedFuture(new DOMException((short) 414, "Post request failed"))).when(this.readWrite).submit();
         final TransactionVarsWrapper wrapper = new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain);
-        final Response response = PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx);
+        final Response response =
+                PostDataTransactionUtil.postData(this.uriInfo, payload, wrapper, this.refSchemaCtx, null, null);
         assertEquals(Response.Status.INTERNAL_SERVER_ERROR, response.getStatusInfo());
-        verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, iid2);
+        verify(this.readWrite).exists(LogicalDatastoreType.CONFIGURATION, this.iid2);
         verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
     }
 
index 21bdfd0d618b64ae63739930cbeb3ad773507550..672dca2896f2e081cae9fa292cdbb7021bf3ffc7 100644 (file)
@@ -11,7 +11,6 @@ package org.opendaylight.restconf.restful.utils;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
-
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.Futures;
 import org.junit.Before;
@@ -70,8 +69,8 @@ public class PutDataTransactionUtilTest {
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        refSchemaCtx = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
-        schema = refSchemaCtx.get();
+        this.refSchemaCtx = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
+        this.schema = this.refSchemaCtx.get();
 
         final QName baseQName = QName.create("http://example.com/ns/example-jukebox", "2015-04-04", "jukebox");
         final QName containerQname = QName.create(baseQName, "player");
@@ -84,34 +83,34 @@ public class PutDataTransactionUtilTest {
         final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeWithKey2 =
                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(listQname, listKeyQname, "name of band 2");
 
-        iid = YangInstanceIdentifier.builder()
+        this.iid = YangInstanceIdentifier.builder()
                 .node(baseQName)
                 .node(containerQname)
                 .node(leafQname)
                 .build();
-        schemaNode = DataSchemaContextTree.from(schema).getChild(iid).getDataSchemaNode();
+        this.schemaNode = DataSchemaContextTree.from(this.schema).getChild(this.iid).getDataSchemaNode();
 
-        iid2 = YangInstanceIdentifier.builder()
+        this.iid2 = YangInstanceIdentifier.builder()
                 .node(baseQName)
                 .build();
-        schemaNode2 = DataSchemaContextTree.from(schema).getChild(iid2).getDataSchemaNode();
+        this.schemaNode2 = DataSchemaContextTree.from(this.schema).getChild(this.iid2).getDataSchemaNode();
 
-        iid3 = YangInstanceIdentifier.builder()
+        this.iid3 = YangInstanceIdentifier.builder()
                 .node(baseQName)
                 .node(listQname)
                 .node(nodeWithKey)
                 .build();
-        schemaNode3 = DataSchemaContextTree.from(schema).getChild(iid3).getDataSchemaNode();
+        this.schemaNode3 = DataSchemaContextTree.from(this.schema).getChild(this.iid3).getDataSchemaNode();
 
-        buildLeaf = Builders.leafBuilder()
+        this.buildLeaf = Builders.leafBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(leafQname))
                 .withValue(0.2)
                 .build();
         final ContainerNode buildPlayerCont = Builders.containerBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(containerQname))
-                .withChild(buildLeaf)
+                .withChild(this.buildLeaf)
                 .build();
-        buildBaseCont = Builders.containerBuilder()
+        this.buildBaseCont = Builders.containerBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
                 .withChild(buildPlayerCont)
                 .build();
@@ -123,7 +122,7 @@ public class PutDataTransactionUtilTest {
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create(baseQName, "description")))
                 .withValue("band description")
                 .build();
-        buildListEntry = Builders.mapEntryBuilder()
+        this.buildListEntry = Builders.mapEntryBuilder()
                 .withNodeIdentifier(nodeWithKey)
                 .withChild(content)
                 .withChild(content2)
@@ -143,10 +142,10 @@ public class PutDataTransactionUtilTest {
                 .build();
         final MapNode buildList = Builders.mapBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(listQname))
-                .withChild(buildListEntry)
+                .withChild(this.buildListEntry)
                 .withChild(buildListEntry2)
                 .build();
-        buildBaseContWithList = Builders.containerBuilder()
+        this.buildBaseContWithList = Builders.containerBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(baseQName))
                 .withChild(buildList)
                 .build();
@@ -155,99 +154,102 @@ public class PutDataTransactionUtilTest {
 
     @Test
     public void testValidInputData() throws Exception {
-        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
-        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
         PutDataTransactionUtil.validInputData(iidContext.getSchemaNode(), payload);
     }
 
     @Test
     public void testValidTopLevelNodeName() throws Exception {
-        InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
-        NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
+        InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+        NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
         PutDataTransactionUtil.validTopLevelNodeName(iidContext.getInstanceIdentifier(), payload);
 
-        iidContext = new InstanceIdentifierContext<>(iid2, schemaNode2, null, schema);
-        payload = new NormalizedNodeContext(iidContext, buildBaseCont);
+        iidContext = new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
+        payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
         PutDataTransactionUtil.validTopLevelNodeName(iidContext.getInstanceIdentifier(), payload);
     }
 
     @Test(expected = RestconfDocumentedException.class)
     public void testValidTopLevelNodeNamePathEmpty() throws Exception {
-        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
-        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
         PutDataTransactionUtil.validTopLevelNodeName(YangInstanceIdentifier.EMPTY, payload);
     }
 
     @Test(expected = RestconfDocumentedException.class)
     public void testValidTopLevelNodeNameWrongTopIdentifier() throws Exception {
-        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
-        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
-        PutDataTransactionUtil.validTopLevelNodeName(iid.getAncestor(1), payload);
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
+        PutDataTransactionUtil.validTopLevelNodeName(this.iid.getAncestor(1), payload);
     }
 
     @Test
     public void testValidateListKeysEqualityInPayloadAndUri() throws Exception {
-        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid3, schemaNode3, null, schema);
-        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildListEntry);
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid3, this.schemaNode3, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildListEntry);
         PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
     }
 
     @Test
     public void testPutContainerData() throws Exception {
-        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid2, schemaNode2, null, schema);
-        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseCont);
-
-        doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
-        doReturn(read).when(transactionChain).newReadOnlyTransaction();
-        doReturn(write).when(transactionChain).newWriteOnlyTransaction();
-        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iid2);
-        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
-                payload.getData());
-        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
-
-        PutDataTransactionUtil.putData(payload, refSchemaCtx,
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChain));
-        verify(read).read(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier());
-        verify(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseCont);
+
+        doReturn(this.readWrite).when(this.transactionChain).newReadWriteTransaction();
+        doReturn(this.read).when(this.transactionChain).newReadOnlyTransaction();
+        doReturn(this.write).when(this.transactionChain).newWriteOnlyTransaction();
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.CONFIGURATION, this.iid2);
+        doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
                 payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+
+        PutDataTransactionUtil.putData(payload, this.refSchemaCtx,
+                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain), null,
+                null);
+        verify(this.read).read(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier());
+        verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
+                payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
     }
 
     @Test
     public void testPutleafData() throws Exception {
-        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid, schemaNode, null, schema);
-        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildLeaf);
-
-        doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
-        doReturn(read).when(transactionChain).newReadOnlyTransaction();
-        doReturn(write).when(transactionChain).newWriteOnlyTransaction();
-        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iid);
-        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
-                payload.getData());
-        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
-
-        PutDataTransactionUtil.putData(payload, refSchemaCtx,
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChain));
-        verify(read).read(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier());
-        verify(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid, this.schemaNode, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildLeaf);
+
+        doReturn(this.readWrite).when(this.transactionChain).newReadWriteTransaction();
+        doReturn(this.read).when(this.transactionChain).newReadOnlyTransaction();
+        doReturn(this.write).when(this.transactionChain).newWriteOnlyTransaction();
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.CONFIGURATION, this.iid);
+        doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
                 payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+
+        PutDataTransactionUtil.putData(payload, this.refSchemaCtx,
+                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain), null,
+                null);
+        verify(this.read).read(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier());
+        verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION,
+                payload.getInstanceIdentifierContext().getInstanceIdentifier(), payload.getData());
     }
 
     @Test
     public void testPutListData() throws Exception {
-        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(iid2, schemaNode2, null, schema);
-        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, buildBaseContWithList);
-
-        doReturn(readWrite).when(transactionChain).newReadWriteTransaction();
-        doReturn(read).when(transactionChain).newReadOnlyTransaction();
-        doReturn(write).when(transactionChain).newWriteOnlyTransaction();
-        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read).read(LogicalDatastoreType.CONFIGURATION, iid2);
-        doNothing().when(write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
+        final InstanceIdentifierContext<DataSchemaNode> iidContext = new InstanceIdentifierContext<>(this.iid2, this.schemaNode2, null, this.schema);
+        final NormalizedNodeContext payload = new NormalizedNodeContext(iidContext, this.buildBaseContWithList);
+
+        doReturn(this.readWrite).when(this.transactionChain).newReadWriteTransaction();
+        doReturn(this.read).when(this.transactionChain).newReadOnlyTransaction();
+        doReturn(this.write).when(this.transactionChain).newWriteOnlyTransaction();
+        doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(this.read).read(LogicalDatastoreType.CONFIGURATION, this.iid2);
+        doNothing().when(this.write).put(LogicalDatastoreType.CONFIGURATION, payload.getInstanceIdentifierContext().getInstanceIdentifier(),
                 payload.getData());
-        doReturn(Futures.immediateCheckedFuture(null)).when(write).submit();
-        PutDataTransactionUtil.putData(payload, refSchemaCtx,
-                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, transactionChain));
-        verify(read).read(LogicalDatastoreType.CONFIGURATION, iid2);
-        verify(write).put(LogicalDatastoreType.CONFIGURATION, iid2, payload.getData());
+        doReturn(Futures.immediateCheckedFuture(null)).when(this.readWrite).submit();
+        PutDataTransactionUtil.putData(payload, this.refSchemaCtx,
+                new TransactionVarsWrapper(payload.getInstanceIdentifierContext(), null, this.transactionChain), null,
+                null);
+        verify(this.read).read(LogicalDatastoreType.CONFIGURATION, this.iid2);
+        verify(this.readWrite).put(LogicalDatastoreType.CONFIGURATION, this.iid2, payload.getData());
     }
 
 }
index c06cc4532f3a1ddb4d965c831caf18787c0578df..9713d4f929316b91b235985ad56458ad7ed14811 100644 (file)
@@ -9,20 +9,30 @@
 package org.opendaylight.restconf.restful.utils;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
 
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.Futures;
+import java.util.Collections;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.UriInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.netconf.sal.restconf.impl.WriterParameters;
 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -30,121 +40,140 @@ import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public class ReadDataTransactionUtilTest {
 
-    private static final TestData data = new TestData();
-    private static final YangInstanceIdentifier.NodeIdentifier nodeIdentifier = new YangInstanceIdentifier
+    private static final TestData DATA = new TestData();
+    private static final YangInstanceIdentifier.NodeIdentifier NODE_IDENTIFIER = new YangInstanceIdentifier
             .NodeIdentifier(QName.create("ns", "2016-02-28", "container"));
 
     private TransactionVarsWrapper wrapper;
     @Mock
     private DOMTransactionChain transactionChain;
     @Mock
-    private InstanceIdentifierContext<?> context;
+    private InstanceIdentifierContext<ContainerSchemaNode> context;
     @Mock
     private DOMDataReadOnlyTransaction read;
+    @Mock
+    private SchemaContext schemaContext;
+    @Mock
+    private ContainerSchemaNode containerSchemaNode;
+    @Mock
+    private LeafSchemaNode containerChildNode;
+    private QName containerChildQName;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        doReturn(read).when(transactionChain).newReadOnlyTransaction();
+        containerChildQName = QName.create("ns", "2016-02-28", "container-child");
+
+        when(transactionChain.newReadOnlyTransaction()).thenReturn(read);
+        when(context.getSchemaContext()).thenReturn(schemaContext);
+        when(context.getSchemaNode()).thenReturn(containerSchemaNode);
+        when(containerSchemaNode.getQName()).thenReturn(NODE_IDENTIFIER.getNodeType());
+        when(containerChildNode.getQName()).thenReturn(containerChildQName);
+        when(containerSchemaNode.getDataChildByName(containerChildQName)).thenReturn(containerChildNode);
+
         wrapper = new TransactionVarsWrapper(this.context, null, this.transactionChain);
     }
 
     @Test
     public void readDataConfigTest() {
-        doReturn(Futures.immediateCheckedFuture(Optional.of(data.data3))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, data.path);
-        doReturn(data.path).when(context).getInstanceIdentifier();
+        doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data3))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
+        doReturn(DATA.path).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
         final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
-        assertEquals(data.data3, normalizedNode);
+        assertEquals(DATA.data3, normalizedNode);
     }
 
     @Test
     public void readAllHavingOnlyConfigTest() {
-        doReturn(Futures.immediateCheckedFuture(Optional.of(data.data3))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, data.path);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data3))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
         doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read)
-                .read(LogicalDatastoreType.OPERATIONAL, data.path);
-        doReturn(data.path).when(context).getInstanceIdentifier();
+                .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
+        doReturn(DATA.path).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
         final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
-        assertEquals(data.data3, normalizedNode);
+        assertEquals(DATA.data3, normalizedNode);
     }
 
     @Test
     public void readAllHavingOnlyNonConfigTest() {
-        doReturn(Futures.immediateCheckedFuture(Optional.of(data.data2))).when(read)
-                .read(LogicalDatastoreType.OPERATIONAL, data.path2);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data2))).when(read)
+                .read(LogicalDatastoreType.OPERATIONAL, DATA.path2);
         doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, data.path2);
-        doReturn(data.path2).when(context).getInstanceIdentifier();
+                .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
+        doReturn(DATA.path2).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
         final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
-        assertEquals(data.data2, normalizedNode);
+        assertEquals(DATA.data2, normalizedNode);
     }
 
     @Test
     public void readDataNonConfigTest() {
-        doReturn(Futures.immediateCheckedFuture(Optional.of(data.data2))).when(read)
-                .read(LogicalDatastoreType.OPERATIONAL, data.path2);
-        doReturn(data.path2).when(context).getInstanceIdentifier();
+        doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data2))).when(read)
+                .read(LogicalDatastoreType.OPERATIONAL, DATA.path2);
+        doReturn(DATA.path2).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.NONCONFIG;
         final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
-        assertEquals(data.data2, normalizedNode);
+        assertEquals(DATA.data2, normalizedNode);
     }
 
     @Test
     public void readContainerDataAllTest() {
-        doReturn(Futures.immediateCheckedFuture(Optional.of(data.data3))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, data.path);
-        doReturn(Futures.immediateCheckedFuture(Optional.of(data.data4))).when(read)
-                .read(LogicalDatastoreType.OPERATIONAL, data.path);
-        doReturn(data.path).when(context).getInstanceIdentifier();
+        doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data3))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data4))).when(read)
+                .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
+        doReturn(DATA.path).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
         final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
         final ContainerNode checkingData = Builders
                 .containerBuilder()
-                .withNodeIdentifier(nodeIdentifier)
-                .withChild(data.contentLeaf)
-                .withChild(data.contentLeaf2)
+                .withNodeIdentifier(NODE_IDENTIFIER)
+                .withChild(DATA.contentLeaf)
+                .withChild(DATA.contentLeaf2)
                 .build();
         assertEquals(checkingData, normalizedNode);
     }
 
     @Test
     public void readContainerDataConfigNoValueOfContentTest() {
-        doReturn(Futures.immediateCheckedFuture(Optional.of(data.data3))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, data.path);
-        doReturn(Futures.immediateCheckedFuture(Optional.of(data.data4))).when(read)
-                .read(LogicalDatastoreType.OPERATIONAL, data.path);
-        doReturn(data.path).when(context).getInstanceIdentifier();
-        final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(null, wrapper);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data3))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.data4))).when(read)
+                .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
+        doReturn(DATA.path).when(context).getInstanceIdentifier();
+        final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
+                RestconfDataServiceConstant.ReadData.ALL, wrapper);
         final ContainerNode checkingData = Builders
                 .containerBuilder()
-                .withNodeIdentifier(nodeIdentifier)
-                .withChild(data.contentLeaf)
-                .withChild(data.contentLeaf2)
+                .withNodeIdentifier(NODE_IDENTIFIER)
+                .withChild(DATA.contentLeaf)
+                .withChild(DATA.contentLeaf2)
                 .build();
         assertEquals(checkingData, normalizedNode);
     }
 
     @Test
     public void readListDataAllTest() {
-        doReturn(Futures.immediateCheckedFuture(Optional.of(data.listData))).when(read)
-                .read(LogicalDatastoreType.OPERATIONAL, data.path3);
-        doReturn(Futures.immediateCheckedFuture(Optional.of(data.listData2))).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, data.path3);
-        doReturn(data.path3).when(context).getInstanceIdentifier();
+        doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.listData))).when(read)
+                .read(LogicalDatastoreType.OPERATIONAL, DATA.path3);
+        doReturn(Futures.immediateCheckedFuture(Optional.of(DATA.listData2))).when(read)
+                .read(LogicalDatastoreType.CONFIGURATION, DATA.path3);
+        doReturn(DATA.path3).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
         final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
         final MapNode checkingData = Builders
                 .mapBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create("ns", "2016-02-28", "list")))
-                .withChild(data.checkData)
+                .withChild(DATA.checkData)
                 .build();
         assertEquals(checkingData, normalizedNode);
     }
@@ -152,8 +181,8 @@ public class ReadDataTransactionUtilTest {
     @Test
     public void readDataWrongPathOrNoContentTest() {
         doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(read)
-                .read(LogicalDatastoreType.CONFIGURATION, data.path2);
-        doReturn(data.path2).when(context).getInstanceIdentifier();
+                .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
+        doReturn(DATA.path2).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
         final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
         assertNull(normalizedNode);
@@ -162,7 +191,163 @@ public class ReadDataTransactionUtilTest {
     @Test(expected = RestconfDocumentedException.class)
     public void readDataFailTest() {
         final String valueOfContent = RestconfDataServiceConstant.ReadData.READ_TYPE_TX;
-        final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, null);
+        final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
+                valueOfContent, wrapper);
         assertNull(normalizedNode);
     }
+
+    /**
+     * Test of parsing default parameters from URI request
+     */
+    @Test
+    public void parseUriParametersDefaultTest() {
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+        // no parameters, default values should be used
+        when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+        final WriterParameters parsedParameters = ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+
+        assertEquals("Not correctly parsed URI parameter",
+                RestconfDataServiceConstant.ReadData.ALL, parsedParameters.getContent());
+        assertNull("Not correctly parsed URI parameter",
+                parsedParameters.getDepth());
+        assertNull("Not correctly parsed URI parameter",
+                parsedParameters.getFields());
+    }
+
+    /**
+     * Test of parsing user defined parameters from URI request
+     */
+    @Test
+    public void parseUriParametersUserDefinedTest() {
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+        final String content = "config";
+        final String depth = "10";
+        final String fields = containerChildQName.getLocalName();
+
+        parameters.put("content", Collections.singletonList(content));
+        parameters.put("depth", Collections.singletonList(depth));
+        parameters.put("fields", Collections.singletonList(fields));
+
+        when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+        final WriterParameters parsedParameters = ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+
+        // content
+        assertEquals("Not correctly parsed URI parameter",
+                content, parsedParameters.getContent());
+
+        // depth
+        assertNotNull("Not correctly parsed URI parameter",
+                parsedParameters.getDepth());
+        assertEquals("Not correctly parsed URI parameter",
+                depth, parsedParameters.getDepth().toString());
+
+        // fields
+        assertNotNull("Not correctly parsed URI parameter",
+                parsedParameters.getFields());
+        assertEquals("Not correctly parsed URI parameter",
+                1, parsedParameters.getFields().size());
+        assertEquals("Not correctly parsed URI parameter",
+                1, parsedParameters.getFields().get(0).size());
+        assertEquals("Not correctly parsed URI parameter",
+                containerChildQName, parsedParameters.getFields().get(0).iterator().next());
+    }
+
+    /**
+     * Negative test of parsing request URI parameters when content parameter has not allowed value.
+     */
+    @Test
+    public void parseUriParametersContentParameterNegativeTest() {
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+        parameters.put("content", Collections.singletonList("not-allowed-parameter-value"));
+        when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+        try {
+            ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+            fail("Test expected to fail due to not allowed parameter value");
+        } catch (final RestconfDocumentedException e) {
+            // Bad request
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Negative test of parsing request URI parameters when depth parameter has not allowed value.
+     */
+    @Test
+    public void parseUriParametersDepthParameterNegativeTest() {
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+        // inserted value is not allowed
+        parameters.put("depth", Collections.singletonList("bounded"));
+        when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+        try {
+            ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+            fail("Test expected to fail due to not allowed parameter value");
+        } catch (final RestconfDocumentedException e) {
+            // Bad request
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Negative test of parsing request URI parameters when depth parameter has not allowed value (less than minimum).
+     */
+    @Test
+    public void parseUriParametersDepthMinimalParameterNegativeTest() {
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+        // inserted value is too low
+        parameters.put(
+                "depth", Collections.singletonList(String.valueOf(RestconfDataServiceConstant.ReadData.MIN_DEPTH - 1)));
+        when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+        try {
+            ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+            fail("Test expected to fail due to not allowed parameter value");
+        } catch (final RestconfDocumentedException e) {
+            // Bad request
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Negative test of parsing request URI parameters when depth parameter has not allowed value (more than maximum).
+     */
+    @Test
+    public void parseUriParametersDepthMaximalParameterNegativeTest() {
+        final UriInfo uriInfo = Mockito.mock(UriInfo.class);
+        final MultivaluedHashMap<String, String> parameters = new MultivaluedHashMap<>();
+
+        // inserted value is too high
+        parameters.put(
+                "depth", Collections.singletonList(String.valueOf(RestconfDataServiceConstant.ReadData.MAX_DEPTH + 1)));
+        when(uriInfo.getQueryParameters()).thenReturn(parameters);
+
+        try {
+            ReadDataTransactionUtil.parseUriParameters(context, uriInfo);
+            fail("Test expected to fail due to not allowed parameter value");
+        } catch (final RestconfDocumentedException e) {
+            // Bad request
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
 }
index 7cec6de6317d901a5f59db44a9f118a94fe5a2e9..1bd3011adbb70537524cc2f3795f20c3f431297f 100644 (file)
@@ -35,9 +35,9 @@ import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
-import org.opendaylight.restconf.Draft17;
-import org.opendaylight.restconf.Draft17.MonitoringModule;
-import org.opendaylight.restconf.Draft17.RestconfModule;
+import org.opendaylight.restconf.Draft18;
+import org.opendaylight.restconf.Draft18.MonitoringModule;
+import org.opendaylight.restconf.Draft18.RestconfModule;
 import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
@@ -756,7 +756,7 @@ public class RestconfMappingNodeUtilTest {
      */
     private Module getTestingRestconfModule(final String s) {
         return RestconfMappingNodeUtilTest.schemaContext.findModuleByName(
-                s, Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
+                s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
     }
 
     /**
diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/parser/ParserFieldsParameterTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/parser/ParserFieldsParameterTest.java
new file mode 100644 (file)
index 0000000..4ba3023
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2016 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.restconf.utils.parser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.net.URI;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Unit test for {@link ParserFieldsParameter}
+ */
+public class ParserFieldsParameterTest {
+
+    @Mock
+    private InstanceIdentifierContext<ContainerSchemaNode> identifierContext;
+
+    // container jukebox
+    @Mock
+    private ContainerSchemaNode containerJukebox;
+    private QName jukeboxQName;
+
+    // container player
+    @Mock
+    private ContainerSchemaNode containerPlayer;
+    private QName playerQName;
+
+    // container library
+    @Mock
+    private ContainerSchemaNode containerLibrary;
+    private QName libraryQName;
+
+    // container augmented library
+    @Mock
+    private ContainerSchemaNode augmentedContainerLibrary;
+    private QName augmentedLibraryQName;
+
+    // list album
+    @Mock
+    private ListSchemaNode listAlbum;
+    private QName albumQName;
+
+    // leaf name
+    @Mock
+    private LeafSchemaNode leafName;
+    private QName nameQName;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        final SchemaContext schemaContext = TestRestconfUtils.loadSchemaContext("/jukebox");
+
+        final QNameModule qNameModule = QNameModule.create(URI.create("http://example.com/ns/example-jukebox"),
+                new SimpleDateFormat("yyyy-MM-dd").parse("2015-04-04"));
+
+        jukeboxQName = QName.create(qNameModule, "jukebox");
+        playerQName = QName.create(qNameModule, "player");
+        libraryQName = QName.create(qNameModule, "library");
+        augmentedLibraryQName = QName.create(
+                QNameModule.create(
+                        URI.create("http://example.com/ns/augmented-jukebox"),
+                        new SimpleDateFormat("yyyy-MM-dd").parse("2016-05-05")),
+                "augmented-library");
+        albumQName = QName.create(qNameModule, "album");
+        nameQName = QName.create(qNameModule, "name");
+
+        Mockito.when(identifierContext.getSchemaContext()).thenReturn(schemaContext);
+        Mockito.when(containerJukebox.getQName()).thenReturn(jukeboxQName);
+        Mockito.when(identifierContext.getSchemaNode()).thenReturn(containerJukebox);
+
+        Mockito.when(containerLibrary.getQName()).thenReturn(libraryQName);
+        Mockito.when(containerJukebox.getDataChildByName(libraryQName)).thenReturn(containerLibrary);
+
+        Mockito.when(augmentedContainerLibrary.getQName()).thenReturn(augmentedLibraryQName);
+        Mockito.when(containerJukebox.getDataChildByName(augmentedLibraryQName)).thenReturn(augmentedContainerLibrary);
+
+        Mockito.when(containerPlayer.getQName()).thenReturn(playerQName);
+        Mockito.when(containerJukebox.getDataChildByName(playerQName)).thenReturn(containerPlayer);
+
+        Mockito.when(listAlbum.getQName()).thenReturn(albumQName);
+        Mockito.when(containerLibrary.getDataChildByName(albumQName)).thenReturn(listAlbum);
+
+        Mockito.when(leafName.getQName()).thenReturn(nameQName);
+        Mockito.when(listAlbum.getDataChildByName(nameQName)).thenReturn(leafName);
+    }
+
+    /**
+     * Test parse fields parameter containing only one child selected
+     */
+    @Test
+    public void parseFieldsParameterSimplePathTest() {
+        final String input = "library";
+        final List<Set<QName>> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+
+        assertNotNull(parsedFields);
+        assertEquals(1, parsedFields.size());
+        assertEquals(1, parsedFields.get(0).size());
+        assertTrue(parsedFields.get(0).contains(libraryQName));
+    }
+
+    /**
+     * Test parse fields parameter containing two child nodes selected
+     */
+    @Test
+    public void parseFieldsParameterDoublePathTest() {
+        final String input = "library;player";
+        final List<Set<QName>> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+
+        assertNotNull(parsedFields);
+        assertEquals(1, parsedFields.size());
+        assertEquals(2, parsedFields.get(0).size());
+        assertTrue(parsedFields.get(0).contains(libraryQName));
+        assertTrue(parsedFields.get(0).contains(playerQName));
+    }
+
+    /**
+     * Test parse fields parameter containing sub-children selected delimited by slash
+     */
+    @Test
+    public void parseFieldsParameterSubPathTest() {
+        final String input = "library/album/name";
+        final List<Set<QName>> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+
+        assertNotNull(parsedFields);
+        assertEquals(3, parsedFields.size());
+
+        assertEquals(1, parsedFields.get(0).size());
+        assertTrue(parsedFields.get(0).contains(libraryQName));
+
+        assertEquals(1, parsedFields.get(1).size());
+        assertTrue(parsedFields.get(1).contains(albumQName));
+
+        assertEquals(1, parsedFields.get(2).size());
+        assertTrue(parsedFields.get(2).contains(nameQName));
+    }
+
+    /**
+     * Test parse fields parameter containing sub-children selected delimited by parenthesis
+     */
+    @Test
+    public void parseFieldsParameterChildrenPathTest() {
+        final String input = "library(album(name))";
+        final List<Set<QName>> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+
+        assertNotNull(parsedFields);
+        assertEquals(3, parsedFields.size());
+
+        assertEquals(1, parsedFields.get(0).size());
+        assertTrue(parsedFields.get(0).contains(libraryQName));
+
+        assertEquals(1, parsedFields.get(1).size());
+        assertTrue(parsedFields.get(1).contains(albumQName));
+
+        assertEquals(1, parsedFields.get(2).size());
+        assertTrue(parsedFields.get(2).contains(nameQName));
+    }
+
+    /**
+     * Test parse fields parameter when augmentation with different namespace is used
+     */
+    @Test
+    public void parseFieldsParameterNamespaceTest() {
+        final String input = "augmented-jukebox:augmented-library";
+        final List<Set<QName>> parsedFields = ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+
+        assertNotNull(parsedFields);
+        assertEquals(1, parsedFields.size());
+
+        assertEquals(1, parsedFields.get(0).size());
+        assertTrue(parsedFields.get(0).contains(augmentedLibraryQName));
+    }
+
+    /**
+     * Test parse fields parameter containing not expected character
+     */
+    @Test
+    public void parseFieldsParameterNotExpectedCharacterNegativeTest() {
+        final String input = "*";
+
+        try {
+            ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+            fail("Test should fail due to not expected character used in parameter input value");
+        } catch (final RestconfDocumentedException e) {
+            // Bad request
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Test parse fields parameter with missing closing parenthesis
+     */
+    @Test
+    public void parseFieldsParameterMissingParenthesisNegativeTest() {
+        final String input = "library(";
+
+        try {
+            ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+            fail("Test should fail due to missing closing parenthesis");
+        } catch (final RestconfDocumentedException e) {
+            // Bad request
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Test parse fields parameter when not existing child node selected
+     */
+    @Test
+    public void parseFieldsParameterMissingChildNodeNegativeTest() {
+        final String input = "library(not-existing)";
+
+        try {
+            ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+            fail("Test should fail due to missing child node in parent node");
+        } catch (final RestconfDocumentedException e) {
+            // Bad request
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Test parse fields parameter with unexpected character after parenthesis
+     */
+    @Test
+    public void parseFieldsParameterAfterParenthesisNegativeTest() {
+        final String input = "library(album);";
+
+        try {
+            ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+            fail("Test should fail due to unexpected character after parenthesis");
+        } catch (final RestconfDocumentedException e) {
+            // Bad request
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+
+    /**
+     * Test parse fields parameter with missing semicolon after parenthesis
+     */
+    @Test
+    public void parseFieldsParameterMissingSemicolonNegativeTest() {
+        final String input = "library(album)player";
+
+        try {
+            ParserFieldsParameter.parseFieldsParameter(identifierContext, input);
+            fail("Test should fail due to missing semicolon after parenthesis");
+        } catch (final RestconfDocumentedException e) {
+            // Bad request
+            assertEquals("Error type is not correct", ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType());
+            assertEquals("Error tag is not correct", ErrorTag.INVALID_VALUE, e.getErrors().get(0).getErrorTag());
+            assertEquals("Error status code is not correct", 400, e.getErrors().get(0).getErrorTag().getStatusCode());
+        }
+    }
+}
\ No newline at end of file
index 617f601c317ea45baff72a68de34f8e7a25b14de..1af04476d86abf756406cd3a1048188d3f00a79a 100644 (file)
@@ -11,8 +11,8 @@ package org.opendaylight.restconf.utils.schema.context;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
-import static org.opendaylight.restconf.Draft17.MonitoringModule;
-import static org.opendaylight.restconf.Draft17.RestconfModule;
+import static org.opendaylight.restconf.Draft18.MonitoringModule;
+import static org.opendaylight.restconf.Draft18.RestconfModule;
 
 import com.google.common.collect.Sets;
 import java.util.NoSuchElementException;
@@ -23,7 +23,7 @@ import org.junit.rules.ExpectedException;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
-import org.opendaylight.restconf.Draft17;
+import org.opendaylight.restconf.Draft18;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -389,6 +389,6 @@ public class RestconfSchemaUtilTest {
      * @return Restconf module
      */
     private Module getTestingRestconfModule(final String s) {
-        return schemaContext.findModuleByName(s, Draft17.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
+        return schemaContext.findModuleByName(s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
     }
 }
index 83934568cc05ab1f3fc1fe031a91f228679fb901..f88f17b5872b360298f4609c5fc0af9680783739 100644 (file)
@@ -26,6 +26,14 @@ module sal-remote-augment {
                 enum SUBTREE;
             }
         }
+        leaf notification-output-type {
+            type enumeration {
+                enum JSON;
+                enum XML;
+            }
+            default "XML";
+            description "Input parameter which type of output will be parsed on notification";
+        }
     }
 
 }
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/resources/full-versions/yangs/subscribe-to-notification.yang b/restconf/sal-rest-connector/src/test/resources/full-versions/yangs/subscribe-to-notification.yang
new file mode 100644 (file)
index 0000000..5fe7df7
--- /dev/null
@@ -0,0 +1,18 @@
+module subscribe-to-notification {
+
+    yang-version 1;
+    namespace "subscribe:to:notification";
+    prefix "subs-to-notifi";
+
+    description
+        "Added input parameters to rpc create-data-change-event-subscription and to create-notification-stream";
+
+    revision "2016-10-28" {
+    }
+
+    container "notifi"{
+        leaf "location"{
+            type string;
+        }
+    }
+}
diff --git a/restconf/sal-rest-connector/src/test/resources/jukebox/augmented-jukebox@2016-05-05.yang b/restconf/sal-rest-connector/src/test/resources/jukebox/augmented-jukebox@2016-05-05.yang
new file mode 100644 (file)
index 0000000..abbd5d0
--- /dev/null
@@ -0,0 +1,16 @@
+module augmented-jukebox {
+
+      namespace "http://example.com/ns/augmented-jukebox";
+      prefix "augmented-jbox";
+
+      revision "2016-05-05" {
+        description "Initial version.";
+      }
+
+      import example-jukebox {prefix jbox; revision-date "2015-04-04";}
+
+      augment "/jbox:jukebox" {
+        container augmented-library {
+        }
+     }
+   }
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/resources/notifications/notifi-module.yang b/restconf/sal-rest-connector/src/test/resources/notifications/notifi-module.yang
new file mode 100644 (file)
index 0000000..70d45f1
--- /dev/null
@@ -0,0 +1,49 @@
+module notifi-module {
+    namespace "notifi:mod";
+    prefix notm;
+
+    revision "2016-11-23" {
+    }
+
+    notification notifi-leaf {
+        leaf lf {
+            type string;
+        }
+    }
+
+    notification notifi-cont {
+        container cont {
+            leaf lf {
+                type string;
+            }
+        }
+    }
+
+    notification notifi-list {
+        list lst {
+            key lf;
+            leaf lf {
+                type string;
+            }
+        }
+    }
+
+    notification notifi-grp {
+        uses grp;
+    }
+
+    grouping grp {
+        leaf lf {
+            type string;
+        }
+    }
+
+    notification notifi-augm {
+    }
+
+    augment notifi-augm {
+        leaf lf-augm {
+            type string;
+        }
+    }
+}
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/resources/notifications/subscribe-to-notification.yang b/restconf/sal-rest-connector/src/test/resources/notifications/subscribe-to-notification.yang
new file mode 100644 (file)
index 0000000..5fe7df7
--- /dev/null
@@ -0,0 +1,18 @@
+module subscribe-to-notification {
+
+    yang-version 1;
+    namespace "subscribe:to:notification";
+    prefix "subs-to-notifi";
+
+    description
+        "Added input parameters to rpc create-data-change-event-subscription and to create-notification-stream";
+
+    revision "2016-10-28" {
+    }
+
+    container "notifi"{
+        leaf "location"{
+            type string;
+        }
+    }
+}
diff --git a/restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_DOWN.xml b/restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_DOWN.xml
new file mode 100644 (file)
index 0000000..43babd2
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+    <eventTime>2016-11-10T04:45:31+01:00</eventTime>
+    <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
+        <data-change-event>
+            <path>/toaster:toaster/toaster:toasterStatus</path>
+            <operation>updated</operation>
+            <data>
+                <toasterStatus xmlns="http://netconfcentral.org/ns/toaster">down</toasterStatus>
+            </data>
+        </data-change-event>
+    </data-changed-notification>
+</notification>
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_NUMBER.xml b/restconf/sal-rest-connector/src/test/resources/notifications/xml/output/data_change_notification_toaster_status_NUMBER.xml
new file mode 100644 (file)
index 0000000..8a4a866
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+    <eventTime>2016-11-10T04:45:31+01:00</eventTime>
+    <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
+        <data-change-event>
+            <path>/toaster:toaster/toaster:toasterStatus</path>
+            <operation>updated</operation>
+            <data>
+                <toasterStatus xmlns="http://netconfcentral.org/ns/toaster">1</toasterStatus>
+            </data>
+        </data-change-event>
+    </data-changed-notification>
+</notification>
\ No newline at end of file
diff --git a/restconf/sal-rest-connector/src/test/resources/ordered/by/user/ordered-by-user-example.yang b/restconf/sal-rest-connector/src/test/resources/ordered/by/user/ordered-by-user-example.yang
new file mode 100644 (file)
index 0000000..2684336
--- /dev/null
@@ -0,0 +1,44 @@
+module ordered-example {
+  namespace "ordered:example";
+  prefix "oex";
+
+  revision 2016-11-13 {
+    description
+      "Initial revision.";
+  }
+
+    container cont {
+        list playlist {
+            key name;
+
+            leaf name {
+              type string;
+            }
+            list song {
+              key index;
+              ordered-by user;
+
+              leaf index {
+                type uint32;
+              }
+              leaf id {
+                type instance-identifier;
+                mandatory true;
+                description
+                  "Song identifier. Must identify an instance of
+                   /songs-cont/songs/song-name.";
+              }
+            }
+          }
+    }
+
+    container songs-cont{
+        list songs{
+            key song-name;
+
+            leaf song-name{
+                type string;
+            }
+        }
+    }
+}
\ No newline at end of file