Split Restconf implementations (draft02 and RFC) - move 54/62854/6
authorJakub Toth <jakub.toth@pantheon.tech>
Thu, 7 Sep 2017 08:28:14 +0000 (10:28 +0200)
committerJakub Toth <jakub.toth@pantheon.tech>
Wed, 13 Sep 2017 10:40:56 +0000 (12:40 +0200)
sal-rest-connector

  * move Patch parts, RestUtil, RestconfSchemaUtil,
    RestconfValidationUtils, IdentityValuesDTO to common module
  * move packages from bierman02 to rfc8040:
    * /restconf/base/services/ ->
        /restconf/nb/rfc8040/services/simple
    * /restconf/common/wrapper/services ->
        /restconf/nb/rfc8040/services/wrapper
    * /restconf/common/references/ ->
        /restconf/nb/rfc8040/references
    * /restconf/handlers -> /restconf/nb/rfc8040/handlers
    * /restconf/parser -> /restconf/nb/rfc8040/utils/parser
    * /restconf/restful -> /restconf/nb/rfc8040/rests
    * /restconf/utils -> /restconf/nb/rfc8040/utils

Change-Id: I8259d69ab2378a25ddcf41d0b6f14dd65afc2507
Signed-off-by: Jakub Toth <jakub.toth@pantheon.tech>
174 files changed:
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/Patch.java [moved from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/Patch.java with 93% similarity]
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/PatchContext.java [moved from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PatchContext.java with 96% similarity]
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/PatchEditOperation.java [moved from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PatchEditOperation.java with 95% similarity]
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/PatchEntity.java [moved from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PatchEntity.java with 97% similarity]
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/PatchStatusContext.java [moved from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PatchStatusContext.java with 95% similarity]
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/PatchStatusEntity.java [moved from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PatchStatusEntity.java with 94% similarity]
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/IdentityValuesDTO.java [moved from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/IdentityValuesDTO.java with 98% similarity]
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/RestUtil.java [moved from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestUtil.java with 95% similarity]
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/RestconfSchemaUtil.java [moved from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtil.java with 96% similarity]
restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/validation/RestconfValidationUtils.java [moved from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/common/RestconfValidationUtils.java with 98% similarity]
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/md/sal/rest/schema/SchemaRetrievalServiceImpl.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonToPatchBodyReader.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/PatchJsonBodyWriter.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/PatchXmlBodyWriter.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestconfCompositeWrapper.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlToPatchBodyReader.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/BrokerFacade.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/ControllerContext.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestCodec.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/RestconfImpl.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/StatisticsRestconfServiceWrapper.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/RestConnectorProvider.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/base/services/impl/FakeContainerSchemaNode.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/base/services/impl/FakeImportedModule.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/base/services/impl/FakeLeafSchemaNode.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/base/services/impl/FakeModuleImport.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/base/services/impl/FakeRestconfModule.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/base/services/impl/RestconfImpl.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/base/services/impl/RestconfOperationsServiceImpl.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/base/services/impl/RestconfSchemaServiceImpl.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/common/references/SchemaContextRef.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/common/wrapper/services/ServicesWrapperImpl.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/handlers/DOMDataBrokerHandler.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/handlers/DOMMountPointServiceHandler.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/handlers/Handler.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/handlers/NotificationServiceHandler.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/handlers/RpcServiceHandler.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/handlers/SchemaContextHandler.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/handlers/SchemaContextListenerHandler.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/handlers/TransactionChainHandler.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/jersey/providers/AbstractToPatchBodyReader.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/jersey/providers/JsonToPatchBodyReader.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/jersey/providers/XmlToPatchBodyReader.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/parser/IdentifierCodec.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/parser/builder/YangInstanceIdentifierDeserializer.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/parser/builder/YangInstanceIdentifierSerializer.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/services/api/RestconfDataService.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/services/api/RestconfInvokeOperationsService.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/services/api/RestconfStreamsSubscriptionService.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/services/api/TransactionServicesWrapper.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImpl.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfInvokeOperationsServiceImpl.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/transaction/TransactionVarsWrapper.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/CreateStreamUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/DeleteDataTransactionUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/FutureCallbackTx.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/FutureDataFactory.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/NormalizedNodeFactory.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/ParametersUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/PatchDataTransactionUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/PostDataTransactionUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/PutDataTransactionUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/ReadDataTransactionUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/ResolveEnumUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/ResponseFactory.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/RestconfDataServiceConstant.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/RestconfInvokeOperationsUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/RestconfStreamsConstants.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/RpcResultFactory.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/restful/utils/TransactionUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/utils/RestconfConstants.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeConstants.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingStreamConstants.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/utils/parser/ParserFieldsParameter.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/utils/parser/ParserIdentifier.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/utils/parser/builder/ParserBuilderConstants.java
restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/utils/validation/RestconfValidation.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/AbstractBodyReaderTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonPatchBodyReader.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestJsonPatchBodyReaderMountPoint.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlPatchBodyReader.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/rest/impl/test/providers/TestXmlPatchBodyReaderMountPoint.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/netconf/sal/restconf/impl/InstanceIdentifierCodecImplTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/restconf/jersey/providers/AbstractBodyReaderTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/restconf/jersey/providers/JsonPatchBodyReaderMountPointTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/restconf/jersey/providers/JsonPatchBodyReaderTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/restconf/jersey/providers/XmlPatchBodyReaderMountPointTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/restconf/jersey/providers/XmlPatchBodyReaderTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfDataServiceImplTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/restconf/restful/utils/PatchDataTransactionUtilTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/restconf/restful/utils/ReadDataTransactionUtilTest.java
restconf/restconf-nb-bierman02/src/test/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtilTest.java
restconf/restconf-nb-rfc8040/pom.xml
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/RestConnectorProvider.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/RestconfConnector.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/codecs/RestCodec.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/codecs/StringModuleInstanceIdentifierCodec.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/DOMDataBrokerHandler.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/DOMMountPointServiceHandler.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/Handler.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/NotificationServiceHandler.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/RpcServiceHandler.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/SchemaContextHandler.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/SchemaContextListenerHandler.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/TransactionChainHandler.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/references/SchemaContextRef.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/RestconfDataService.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/RestconfInvokeOperationsService.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/RestconfStreamsSubscriptionService.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/TransactionServicesWrapper.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImpl.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfStreamsSubscriptionServiceImpl.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/TransactionVarsWrapper.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/CreateStreamUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureDataFactory.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/NormalizedNodeFactory.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ParametersUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PatchDataTransactionUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ResolveEnumUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ResponseFactory.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfDataServiceConstant.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfInvokeOperationsUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfStreamsConstants.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RpcResultFactory.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/SubscribeToStreamUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/TransactionUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/api/BaseServicesWrapper.java [moved from restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/simple/services/api/BaseServicesWrapper.java with 89% similarity]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/api/RestconfOperationsService.java [moved from restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/simple/services/api/RestconfOperationsService.java with 96% similarity]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/api/RestconfSchemaService.java [moved from restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/simple/services/api/RestconfSchemaService.java with 94% similarity]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/api/RestconfService.java [moved from restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/simple/services/api/RestconfService.java with 94% similarity]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeContainerSchemaNode.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeImportedModule.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeLeafSchemaNode.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeModuleImport.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeRestconfModule.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/RestconfImpl.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/RestconfOperationsServiceImpl.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/RestconfSchemaServiceImpl.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/wrapper/ServicesWrapperImpl.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractCommonSubscriber.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractNotificationsData.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractQueryParams.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/BaseListenerInterface.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/Event.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/EventBusChangeRecorder.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/EventType.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenerAdapter.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/NotificationListenerAdapter.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/Notificator.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/websockets/WebSocketServer.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/websockets/WebSocketServerHandler.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/websockets/WebSocketServerInitializer.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/RestconfConstants.java
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/mapping/RestconfMappingNodeConstants.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/mapping/RestconfMappingNodeUtil.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/mapping/RestconfMappingStreamConstants.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/IdentifierCodec.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserFieldsParameter.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserIdentifier.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializer.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierSerializer.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/builder/ParserBuilderConstants.java [new file with mode: 0644]
restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/validations/RestconfValidation.java [new file with mode: 0644]

similarity index 93%
rename from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/Patch.java
rename to restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/Patch.java
index a5d4163c0334a7bad858202220cc3c488c5dd56d..6f4abeef83b94d1382ef7655bd0bcd34ca5d5d48 100644 (file)
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.netconf.sal.rest.impl;
+package org.opendaylight.restconf.common.patch;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
similarity index 96%
rename from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PatchContext.java
rename to restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/PatchContext.java
index e3791c65f20d8a14f1bd95bffe941b00d3cda14d..5e867c0838a995f275ba3275b786ea8054e3ecf3 100644 (file)
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.netconf.sal.restconf.impl;
+package org.opendaylight.restconf.common.patch;
 
 import com.google.common.base.Preconditions;
 import java.util.List;
similarity index 95%
rename from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PatchEditOperation.java
rename to restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/PatchEditOperation.java
index 6d833116f1f9963e2bc5bd3bea85bb9ecd8a0049..c94ccd263514e977f027a195fa0e96aef2983a47 100644 (file)
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.netconf.sal.restconf.impl;
+package org.opendaylight.restconf.common.patch;
 
 /**
  * Each YANG patch edit specifies one edit operation on the target data
similarity index 97%
rename from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PatchEntity.java
rename to restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/PatchEntity.java
index 8b4750e5e805c7c5acbe43f9382afa625e15c491..790892304c9c46bdc79dd2a66ad28f8432f9630f 100644 (file)
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.netconf.sal.restconf.impl;
+package org.opendaylight.restconf.common.patch;
 
 import com.google.common.base.Preconditions;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
similarity index 95%
rename from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PatchStatusContext.java
rename to restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/PatchStatusContext.java
index 51b6efe610c7fcf15d0b47ba920d23a4c39dacdb..95360f3cfec17482125bb6099add409813d144da 100644 (file)
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.netconf.sal.restconf.impl;
+package org.opendaylight.restconf.common.patch;
 
 import java.util.List;
 import org.opendaylight.restconf.common.errors.RestconfError;
similarity index 94%
rename from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/PatchStatusEntity.java
rename to restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/patch/PatchStatusEntity.java
index efe78258f52e73d413f4e0a653febf589330e9f7..7363c671d1966647949f83a2039e432285a238f2 100644 (file)
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.netconf.sal.restconf.impl;
+package org.opendaylight.restconf.common.patch;
 
 import java.util.List;
 import org.opendaylight.restconf.common.errors.RestconfError;
similarity index 98%
rename from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/restconf/impl/IdentityValuesDTO.java
rename to restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/IdentityValuesDTO.java
index 1b73749a82719bcb2953093b4d83322308fe5a41..8b9841dc4aa496e255b7ba981fb274cffb5d312d 100644 (file)
@@ -5,7 +5,7 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-package org.opendaylight.netconf.sal.restconf.impl;
+package org.opendaylight.restconf.common.util;
 
 import java.util.ArrayList;
 import java.util.Collections;
similarity index 95%
rename from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/RestUtil.java
rename to restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/RestUtil.java
index 4b65d41e4a747ff8e401f24be3d270ca1acec1b8..9deb2ca0329ed30416db6f913d6d9dfdb4013294 100644 (file)
@@ -5,16 +5,15 @@
  * 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;
+package org.opendaylight.restconf.common.util;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import javax.xml.stream.events.StartElement;
-import org.opendaylight.netconf.sal.restconf.impl.IdentityValuesDTO;
-import org.opendaylight.netconf.sal.restconf.impl.IdentityValuesDTO.IdentityValue;
-import org.opendaylight.netconf.sal.restconf.impl.IdentityValuesDTO.Predicate;
+import org.opendaylight.restconf.common.util.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.restconf.common.util.IdentityValuesDTO.Predicate;
 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 
 public final class RestUtil {
similarity index 96%
rename from restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtil.java
rename to restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/RestconfSchemaUtil.java
index c01bfb30d7f31bc415696d8dc47aedbb397f4b3a..261e32a4e7e15bb164d4e1b458b128b85a51b29f 100644 (file)
@@ -5,7 +5,7 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-package org.opendaylight.restconf.utils.schema.context;
+package org.opendaylight.restconf.common.util;
 
 import java.util.Collection;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
@@ -6,7 +6,7 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-package org.opendaylight.netconf.md.sal.rest.common;
+package org.opendaylight.restconf.common.validation;
 
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
index 71092e749ac1f24e7e16f0f4d6be411f1f45e5d9..bdfdec7cec2e654b2c89d6c4ea08437b6040bfce 100644 (file)
@@ -12,13 +12,13 @@ import com.google.common.collect.Iterables;
 import java.text.ParseException;
 import java.util.Date;
 import java.util.Iterator;
-import org.opendaylight.netconf.md.sal.rest.common.RestconfValidationUtils;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
 import org.opendaylight.restconf.common.schema.SchemaExportContext;
+import org.opendaylight.restconf.common.validation.RestconfValidationUtils;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
index 007717f201468a23ddf8f48a94f8e1fc68876c5c..43059042d0ba87bd58dce5733614a74ff82b2c28 100644 (file)
@@ -22,11 +22,11 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.sal.rest.api.Draft02.MediaTypes;
-import org.opendaylight.netconf.sal.rest.impl.Patch;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchStatusContext;
 import org.opendaylight.restconf.base.services.api.RestconfOperationsService;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.patch.Patch;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.restful.services.api.RestconfDataService;
 import org.opendaylight.restconf.restful.services.api.RestconfInvokeOperationsService;
 import org.opendaylight.restconf.restful.services.api.RestconfStreamsSubscriptionService;
index ea2aab4cdeb0f0c41ad1783e180d41724406129a..326a06b8f4ff83a01b3ca73c3e00d46ebcc2b9f8 100644 (file)
@@ -29,13 +29,13 @@ import javax.ws.rs.ext.Provider;
 import org.opendaylight.netconf.sal.rest.api.Draft02;
 import org.opendaylight.netconf.sal.rest.api.RestconfService;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEntity;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchEditOperation;
+import org.opendaylight.restconf.common.patch.PatchEntity;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
index 68bf0182c13052773adce5f564b0020fcb43994e..cf7c9fa8fef15fc93145807d803a0eba66b53ab6 100644 (file)
@@ -24,9 +24,9 @@ import javax.ws.rs.ext.MessageBodyWriter;
 import javax.ws.rs.ext.Provider;
 import org.opendaylight.netconf.sal.rest.api.Draft02;
 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.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.common.patch.PatchStatusEntity;
 import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
 
 
index 1c695ac2d70ae4a325ca8b8c60adfc5ae03da963..c1ca200d13dc59ea80548004b5da2fdd56b0f995 100644 (file)
@@ -26,9 +26,9 @@ import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import org.opendaylight.netconf.sal.rest.api.Draft02;
 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.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.common.patch.PatchStatusEntity;
 
 @Provider
 @Produces({ Draft02.MediaTypes.PATCH_STATUS + RestconfService.XML })
index 918fea5e503b164bd7f32fd9abaf198851c466cf..5242aeb2c85daae9b050fabe06c7417cb434ff23 100644 (file)
@@ -13,9 +13,9 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.md.sal.rest.schema.SchemaRetrievalService;
 import org.opendaylight.netconf.sal.rest.api.RestconfService;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchStatusContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.common.schema.SchemaExportContext;
 
 public class RestconfCompositeWrapper implements RestconfService, SchemaRetrievalService {
index 0d01c54a565ffe8255a1e869872456dbe13f431f..f3841a90f63a63466cb7118e74acc79bb0654419 100644 (file)
@@ -31,13 +31,13 @@ import javax.xml.stream.XMLStreamException;
 import javax.xml.transform.dom.DOMSource;
 import org.opendaylight.netconf.sal.rest.api.Draft02;
 import org.opendaylight.netconf.sal.rest.api.RestconfService;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEntity;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchEditOperation;
+import org.opendaylight.restconf.common.patch.PatchEntity;
 import org.opendaylight.yangtools.util.xml.UntrustedXML;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
index 140799bbc414ae14a132bfdea2bb855954142889..d2fd19307991a4c3acc1c9ec6561fc9a32011396 100644 (file)
@@ -47,6 +47,11 @@ import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchEditOperation;
+import org.opendaylight.restconf.common.patch.PatchEntity;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.common.patch.PatchStatusEntity;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcError;
index 82b6879472f01b654c62b98ccd5f1d1233c3757b..6890b0d175092a6bb47ba73340b73bfd1c2f767b 100644 (file)
@@ -36,11 +36,11 @@ import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.netconf.sal.rest.api.Draft02.RestConfModule;
-import org.opendaylight.netconf.sal.rest.impl.RestUtil;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.util.RestUtil;
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
index 0202b722cc5d29cb1d78a468a0ce66751f09742a..c35a9b07d47149bb41a82f2921782add7416c2b7 100644 (file)
@@ -13,10 +13,11 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
-import org.opendaylight.netconf.sal.rest.impl.RestUtil;
 import org.opendaylight.netconf.sal.rest.impl.StringModuleInstanceIdentifierCodec;
-import org.opendaylight.netconf.sal.restconf.impl.IdentityValuesDTO.IdentityValue;
-import org.opendaylight.netconf.sal.restconf.impl.IdentityValuesDTO.Predicate;
+import org.opendaylight.restconf.common.util.IdentityValuesDTO;
+import org.opendaylight.restconf.common.util.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.restconf.common.util.IdentityValuesDTO.Predicate;
+import org.opendaylight.restconf.common.util.RestUtil;
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
index 978c7ff41541fdc72367c1b26230642a49a6146c..8a869a58ccae495e7164a2665238400f7ff0e2ed 100644 (file)
@@ -58,7 +58,6 @@ import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailab
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
-import org.opendaylight.netconf.md.sal.rest.common.RestconfValidationUtils;
 import org.opendaylight.netconf.sal.rest.api.Draft02;
 import org.opendaylight.netconf.sal.rest.api.RestconfService;
 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
@@ -70,6 +69,9 @@ import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.common.validation.RestconfValidationUtils;
 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;
index afc4eedb2c9d7b495c3f31e554da49b51f9298cf..dffcf875073de2e19840218ae72f95fecec6b629 100644 (file)
@@ -14,6 +14,8 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.netconf.sal.rest.api.RestconfService;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
 
 public class StatisticsRestconfServiceWrapper implements RestconfService {
 
index 646a3cdf49f0759c26667d3dba9247f7662c5a97..171d89bf2db8aa942d07b91f45b9ec97cbb9b5be 100644 (file)
@@ -34,7 +34,9 @@ import org.slf4j.LoggerFactory;
 /**
  * Provider for restconf draft18.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public class RestConnectorProvider implements RestConnector, AutoCloseable {
 
     private static final Logger LOG = LoggerFactory.getLogger(RestConnectorProvider.class);
@@ -66,8 +68,9 @@ public class RestConnectorProvider implements RestConnector, AutoCloseable {
 
     private SchemaContextHandler schemaCtxHandler;
 
-    public RestConnectorProvider(DOMDataBroker domDataBroker, SchemaService schemaService, DOMRpcService rpcService,
-            DOMNotificationService notificationService, DOMMountPointService mountPointService) {
+    public RestConnectorProvider(final DOMDataBroker domDataBroker, final SchemaService schemaService,
+            final DOMRpcService rpcService, final DOMNotificationService notificationService,
+            final DOMMountPointService mountPointService) {
         this.schemaService = Preconditions.checkNotNull(schemaService);
         this.rpcService = Preconditions.checkNotNull(rpcService);
         this.notificationService = Preconditions.checkNotNull(notificationService);
index 6e91b549a4b1b103ea3443ccdabdd2102e2a9c6a..1ee6cb86c3f87e0cddc19f0f240ee44c3de69325 100644 (file)
@@ -27,10 +27,12 @@ import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.UsesNode;
 
 /**
- * Special case only use by GET restconf/operations (since moment of old Yang
- * parser and old yang model API removal) to build and use fake container for
- * module.
+ * Special case only use by GET restconf/operations (since moment of old Yang parser and old yang model API
+ * removal) to build and use fake container for module.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 class FakeContainerSchemaNode implements ContainerSchemaNode {
     static final SchemaPath PATH =
             SchemaPath.create(true, QName.create(FakeRestconfModule.QNAME, "operations").intern());
index 85f6abb296a6b06dd769338b0e77de48c3e0c143..4bde6ccaa3c0366107c130119fdc20e16890bfec 100644 (file)
@@ -32,6 +32,13 @@ import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.UsesNode;
 
+/**
+ * Special case only use by GET restconf/operations (since moment of old Yang parser and old yang model API
+ * removal) to build and use fake container for module.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ */
+@Deprecated
 final class FakeImportedModule extends ForwardingObject implements Module {
 
     private final Module delegate;
index 40f1ca489b6aab1d4848054045f3609636f55595..6d5ab4296b9fb589bf2a3a2382db782a0b233080 100644 (file)
@@ -18,10 +18,12 @@ import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
 
 /**
- * Special case only use by GET restconf/operations (since moment of old Yang
- * parser and old yang model API removal) to build and use fake leaf like child
- * in container.
+ * Special case only use by GET restconf/operations (since moment of old Yang parser and old yang model API
+ * removal) to build and use fake leaf like child in container.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 final class FakeLeafSchemaNode implements LeafSchemaNode {
 
     private final SchemaPath path;
index 8f10f718d26a0aeaa4038c994188fb719c5abfce..e952c671bbc67ccc1e1d9ec3dc7b432607982933 100644 (file)
@@ -15,8 +15,10 @@ import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 /**
  * Fake {@link ModuleImport} implementation used to attach corrent prefix mapping to fake RPCs.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  * @author Robert Varga
  */
+@Deprecated
 final class FakeModuleImport implements ModuleImport {
     private final Module module;
 
index 23ad3cb6d9ebadd9907255e37dbf5fa5ce59cc08..dde3e5d428894630bd38850c5252a3778e7375f7 100644 (file)
@@ -38,10 +38,12 @@ import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.UsesNode;
 
 /**
- * Special case only use by GET restconf/operations (since moment of old Yang
- * parser and old yang model API removal) to build and use fake module to create
- * new schema context.
+ * Special case only use by GET restconf/operations (since moment of old Yang parser and old yang model API
+ * removal) to build and use fake module to create new schema context.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 final class FakeRestconfModule implements Module {
 
     static final QNameModule QNAME;
index d261f5f4c9873b24e586690093ff1795435290f2..572636ddd1000712909eefded426cc2e7a6f4eeb 100644 (file)
@@ -23,6 +23,12 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 
+/**
+ * Implementation of RestconfService.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ */
+@Deprecated
 public class RestconfImpl implements RestconfService {
 
     private final SchemaContextHandler schemaContextHandler;
index 91275e558d4a6be7ee703592b96b1a2606164e91..5ff0a089b642ada2989a47f43b5d7a5324b88676 100644 (file)
@@ -42,7 +42,9 @@ import org.slf4j.LoggerFactory;
 /**
  * Implementation of {@link RestconfOperationsService}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public class RestconfOperationsServiceImpl implements RestconfOperationsService {
 
     private static final Logger LOG = LoggerFactory.getLogger(RestconfOperationsServiceImpl.class);
index 08c903ae825b6f77619968144f049a6193fabe25..c9caeccadbb66e9c6e4c50907b12d602d6333a19 100644 (file)
@@ -18,7 +18,9 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 /**
  * Implementation of {@link RestconfSchemaService}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public class RestconfSchemaServiceImpl implements RestconfSchemaService {
 
     private final SchemaContextHandler schemaContextHandler;
index bf5e4cd0b591746fe087742d6e61b11b4d07fb6b..c6c1296a1f85471b6ac625967de5455df29d045a 100644 (file)
@@ -18,12 +18,13 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
- * This class creates {@link SoftReference} of actual {@link SchemaContext}
- * object and even if the {@link SchemaContext} changes, this will be sticks
- * reference to the old {@link SchemaContext} and provides work with the old
- * {@link SchemaContext}.
+ * This class creates {@link SoftReference} of actual {@link SchemaContext} object and even if the
+ * {@link SchemaContext} changes, this will be sticks reference to the old {@link SchemaContext} and provides
+ * work with the old {@link SchemaContext}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class SchemaContextRef {
 
     private final SoftReference<SchemaContext> schemaContextRef;
index d43525cf6c5c3ea1b3b1087a0d0be36c0972ba06..0ab0d122a51cac5c452b914a98424a9e9ad068ce 100644 (file)
@@ -10,8 +10,6 @@ package org.opendaylight.restconf.common.wrapper.services;
 import javax.ws.rs.Path;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchStatusContext;
 import org.opendaylight.restconf.base.services.api.BaseServicesWrapper;
 import org.opendaylight.restconf.base.services.api.RestconfOperationsService;
 import org.opendaylight.restconf.base.services.api.RestconfSchemaService;
@@ -20,6 +18,8 @@ import org.opendaylight.restconf.base.services.impl.RestconfImpl;
 import org.opendaylight.restconf.base.services.impl.RestconfOperationsServiceImpl;
 import org.opendaylight.restconf.base.services.impl.RestconfSchemaServiceImpl;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.common.schema.SchemaExportContext;
 import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
@@ -42,7 +42,9 @@ import org.opendaylight.restconf.restful.services.impl.RestconfStreamsSubscripti
  * <li>{@link TransactionServicesWrapper}
  * </ul>
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 @Path("/")
 public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServicesWrapper {
 
index 1383637c1b426240596331fa5a5ce5f478bdce09..c48504d95e2d6f2b1df7bc083ecabe01831b3cd9 100644 (file)
@@ -12,7 +12,10 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 /**
  * Implementation of {@link DOMDataBrokerHandler}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ *
  */
+@Deprecated
 public class DOMDataBrokerHandler implements Handler<DOMDataBroker> {
 
     private final DOMDataBroker broker;
index 7a7d142813d9fcd3e0e1124353963cfe6855d912..4c56039b5f8a078866f4d0cac483acaebedd11c6 100644 (file)
@@ -13,7 +13,9 @@ import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 /**
  * Implementation of {@link DOMMountPointServiceHandler}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public class DOMMountPointServiceHandler implements Handler<DOMMountPointService> {
 
     private final DOMMountPointService domMountPointService;
index efcd20840598bfba17dbf9b4febf9b588cc44185..ea311021c345005786205766c93ada26dae00b1e 100644 (file)
@@ -12,8 +12,10 @@ package org.opendaylight.restconf.handlers;
  * Handler for handling object prepared by provider for Restconf services.
  *
  * @param <T>
- *             specific type go object for handling it
+ *            specific type go object for handling it
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 interface Handler<T> {
 
     /**
@@ -29,5 +31,5 @@ interface Handler<T> {
      * @param object
      *             new object to update old object
      */
-    default void update(T object) {}
+    default void update(final T object) {}
 }
index f7f107798a7bdc8fb30da2a269dc3c79ff31b1a6..d251dd71d4f5f93b6f1e2c97b2b70511f8f0968b 100644 (file)
@@ -9,6 +9,12 @@ package org.opendaylight.restconf.handlers;
 
 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
 
+/**
+ * Handling DOMNotificationService.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ */
+@Deprecated
 public class NotificationServiceHandler implements Handler<DOMNotificationService> {
 
     private final DOMNotificationService notificationService;
index 0c6028d74e71bd6e26404c422233627b032ccc95..70547e86fb79022f59d1a20baf5675fd5d1d0ca6 100644 (file)
@@ -12,7 +12,9 @@ import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 /**
  * Implementation of {@link RpcServiceHandler}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public class RpcServiceHandler implements Handler<DOMRpcService> {
 
     private final DOMRpcService rpcService;
index 184ccf90291c8ea6e49ff1f81f1257f7965f0fcc..4b1349b390cd50643c8fa6f7a6a788b6d81b1e21 100644 (file)
@@ -31,7 +31,9 @@ import org.slf4j.LoggerFactory;
 /**
  * Implementation of {@link SchemaContextHandler}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public class SchemaContextHandler implements SchemaContextListenerHandler {
 
     private static final Logger LOG = LoggerFactory.getLogger(SchemaContextHandler.class);
index fa41c9820435f125164ccdb427083a9494e5f007..41f6055015fb1084a47142044818022065de0bac 100644 (file)
@@ -10,6 +10,12 @@ package org.opendaylight.restconf.handlers;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
 
+/**
+ * SchemaContextListener handler.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ */
+@Deprecated
 interface SchemaContextListenerHandler extends Handler<SchemaContext>, SchemaContextListener {
 
 }
index 207996bf93174b18bb3bc3b686fbc4136783dc64..9da6c1885afebf036fae10c11bf3585d2fa88db3 100644 (file)
@@ -15,7 +15,9 @@ import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 /**
  * Implementation of {@link TransactionChainHandler}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public class TransactionChainHandler implements Handler<DOMTransactionChain> {
 
     private DOMTransactionChain transactionChain;
index 6b84b1d303cdf9851ae95fe643dc9ad7d7668549..74be3b4a3791298da395c048835167d8af6cdbb3 100644 (file)
@@ -7,8 +7,8 @@
  */
 package org.opendaylight.restconf.jersey.providers;
 
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.patch.PatchContext;
 
 /**
  * Common superclass for readers producing {@link PatchContext}.
index 7383cdf621d31e89100be0aff7fc6ac483665630..c10db1a70f9f08011ebd1e6c64b40ab58147f518 100644 (file)
@@ -23,14 +23,14 @@ import javax.ws.rs.Consumes;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.ext.Provider;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEntity;
 import org.opendaylight.restconf.Rfc8040;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchEditOperation;
+import org.opendaylight.restconf.common.patch.PatchEntity;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
index b05aa8098ccf30b0ba554b1ac2762af630b17ff0..f88bf069b98644ea80149236deced760e7c112e1 100644 (file)
@@ -24,14 +24,14 @@ import javax.ws.rs.ext.Provider;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.transform.dom.DOMSource;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEntity;
 import org.opendaylight.restconf.Rfc8040;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchEditOperation;
+import org.opendaylight.restconf.common.patch.PatchEntity;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.yangtools.util.xml.UntrustedXML;
 import org.opendaylight.yangtools.yang.common.QName;
index 10eddb9fa4af3d11c37be9ace49a5326cbc73816..478ee30b02711271e5177c3475be845af436c1b0 100644 (file)
@@ -13,11 +13,12 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
- * Codec for identifier to serialize {@link YangInstanceIdentifier} to
- * {@link String} and deserialize {@link String} to
- * {@link YangInstanceIdentifier}.
+ * Codec for identifier to serialize {@link YangInstanceIdentifier} to {@link String} and deserialize
+ * {@link String} to {@link YangInstanceIdentifier}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class IdentifierCodec {
 
     private IdentifierCodec() {
index 2bfa9f3aa3450b5ac0ff72df92228eb4058de3da..c1c319fd7321dd3b475f89807a561c013df71012 100644 (file)
@@ -14,13 +14,13 @@ import com.google.common.collect.ImmutableMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import org.opendaylight.netconf.md.sal.rest.common.RestconfValidationUtils;
-import org.opendaylight.netconf.sal.rest.impl.RestUtil;
 import org.opendaylight.netconf.sal.restconf.impl.RestCodec;
 import org.opendaylight.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.common.util.RestUtil;
+import org.opendaylight.restconf.common.util.RestconfSchemaUtil;
+import org.opendaylight.restconf.common.validation.RestconfValidationUtils;
 import org.opendaylight.restconf.utils.RestconfConstants;
 import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants;
-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;
@@ -43,10 +43,11 @@ import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
 
 /**
- * Deserializer for {@link String} to {@link YangInstanceIdentifier} for
- * restconf.
+ * Deserializer for {@link String} to {@link YangInstanceIdentifier} for restconf.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class YangInstanceIdentifierDeserializer {
 
     private YangInstanceIdentifierDeserializer() {
index 75bd7322c655a9075a40d11e6f2bb9db7d8e5a0e..0f59274e59f718ce8ea34541cf5d775e5f16514f 100644 (file)
@@ -27,7 +27,10 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 /**
  * Serializer for {@link YangInstanceIdentifier} to {@link String} for restconf.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class YangInstanceIdentifierSerializer {
 
     private YangInstanceIdentifierSerializer() {
index b06be1ed5f90750efd7c30800408c217ceba07ca..3d8c1b1b611671f10686195a3958721ca83ac6ae 100644 (file)
@@ -20,18 +20,20 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
-import org.opendaylight.netconf.sal.rest.impl.Patch;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchStatusContext;
 import org.opendaylight.restconf.Rfc8040;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.patch.Patch;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.utils.RestconfConstants;
 
 /**
- * The "{+restconf}/data" subtree represents the datastore resource type, which
- * is a collection of configuration data and state data nodes.
+ * The "{+restconf}/data" subtree represents the datastore resource type, which is a collection of
+ * configuration data and state data nodes.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public interface RestconfDataService {
 
     /**
index 29bc799dfa7fe70ccf444e54dc8b86e527ec5e96..ee64d7754479713f20ea22489bc2f0a0ffc2ec2c 100644 (file)
@@ -21,10 +21,12 @@ import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.utils.RestconfConstants;
 
 /**
- * An operation resource represents a protocol operation defined with the YANG
- * "rpc" statement. It is invoked using a POST method on the operation resource.
+ * An operation resource represents a protocol operation defined with the YANG "rpc" statement. It is invoked
+ * using a POST method on the operation resource.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public interface RestconfInvokeOperationsService {
 
     /**
index 98a9be4ebaadfcbb97df2fbc2f088044fca640a5..16790a69cf76ea368c68cbbc20fe72504c5c19be 100644 (file)
@@ -18,7 +18,9 @@ import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 /**
  * Subscribing to streams.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public interface RestconfStreamsSubscriptionService {
 
     /**
index 5021f714501032f05775ec08243ff56d639c9870..f84fc206e260e1da33614f27ef1a982189d594f5 100644 (file)
@@ -15,7 +15,9 @@ package org.opendaylight.restconf.restful.services.api;
  * <li>{@link RestconfStreamsSubscriptionService}
  * </ul>
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public interface TransactionServicesWrapper
         extends RestconfDataService, RestconfInvokeOperationsService, RestconfStreamsSubscriptionService {
 
index c13cf84b2cc28c9e634d9393f38d46fc5f9e1993..ba1fb57d89d205fb9bae6e96393861753dd75b37 100644 (file)
@@ -25,14 +25,14 @@ import javax.ws.rs.core.UriInfo;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchStatusContext;
 import org.opendaylight.restconf.RestConnectorProvider;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.context.WriterParameters;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
@@ -55,7 +55,10 @@ import org.slf4j.LoggerFactory;
 
 /**
  * Implementation of {@link RestconfDataService}.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public class RestconfDataServiceImpl implements RestconfDataService {
 
     private static final Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class);
index 7e768cbd888cd5da817ecbfc36f384e5312bea08..cd7026189dc70be5b04b97cf31f7785da3763309 100644 (file)
@@ -30,7 +30,9 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 /**
  * Implementation of {@link RestconfInvokeOperationsService}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public class RestconfInvokeOperationsServiceImpl implements RestconfInvokeOperationsService {
 
     private final RpcServiceHandler rpcServiceHandler;
index da103d4b375c291f9cb9200a0b0a24d2663b8cd4..e688d25754f35b8be949cc5423b59117f3b134e6 100644 (file)
@@ -41,7 +41,9 @@ import org.slf4j.LoggerFactory;
 /**
  * Implementation of {@link RestconfStreamsSubscriptionService}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSubscriptionService {
 
     private static final Logger LOG = LoggerFactory.getLogger(RestconfStreamsSubscriptionServiceImpl.class);
index 7ae3d67201fc36dc48202b82f014b4888cffbb04..ad9e609b0b3729bba9da6e94a6ad9bc7630adb54 100644 (file)
@@ -15,7 +15,9 @@ import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 /**
  * This class represent delegation wrapper for transaction variables.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class TransactionVarsWrapper {
 
     private final InstanceIdentifierContext<?> instanceIdentifier;
index 16e6009213b2d78e3308b34c7875e90cf237f4de..463796ff756a3ddf47c3a3081deed46cc971f74e 100644 (file)
@@ -43,13 +43,14 @@ import org.slf4j.LoggerFactory;
 
 /**
  * Util class for streams.
- *
  * <ul>
  * <li>create stream
  * <li>subscribe
  * </ul>
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class CreateStreamUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(CreateStreamUtil.class);
index 0c880cd99fcc7bc78a4be72ec86fb6b3a91df27c..8db909d59ebc147a81db3e56d48681429b61ee7f 100644 (file)
@@ -19,7 +19,9 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 /**
  * Util class for delete specific data in config DS.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class DeleteDataTransactionUtil {
 
     private DeleteDataTransactionUtil() {
index e81ccbc63b72707e44191e7bf38c35d402d424c9..e0ffc41a01af0874f359823783272e5ebdc6e045 100644 (file)
@@ -21,7 +21,9 @@ import org.slf4j.LoggerFactory;
 /**
  * Add callback for future objects and result set to the data factory.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 final class FutureCallbackTx {
 
     private static final Logger LOG = LoggerFactory.getLogger(FutureCallbackTx.class);
@@ -50,7 +52,7 @@ final class FutureCallbackTx {
             final T result = listenableFuture.checkedGet();
             dataFactory.setResult(result);
             LOG.trace("Transaction({}) SUCCESSFUL", txType);
-        } catch (Exception e) {
+        } catch (final Exception e) {
             dataFactory.setFailureStatus();
             LOG.warn("Transaction({}) FAILED!", txType, e);
             if (e instanceof DOMRpcException) {
index c6373bc6b60f118300b9260fb69d2e72dd9b3258..25ac7419b5e4af2360361d52617612b2919862b1 100644 (file)
@@ -7,6 +7,12 @@
  */
 package org.opendaylight.restconf.restful.utils;
 
+/**
+ * Future data factory.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ */
+@Deprecated
 class FutureDataFactory<T> {
 
     protected T result;
index f7cc9af18cc1b32f16e29bf7dd0de78e6e3eac62..578e44720152dada43aa8d72ccba0249c564df9f 100644 (file)
@@ -11,6 +11,12 @@ import com.google.common.base.Optional;
 import org.apache.commons.lang3.builder.Builder;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
+/**
+ * NormalizedNode factory.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ */
+@Deprecated
 class NormalizedNodeFactory extends FutureDataFactory<Optional<NormalizedNode<?, ?>>>
         implements Builder<NormalizedNode<?, ?>> {
 
index 9f3a9a6815a5a4a89f078783b44040903faef702..6f229c7b3635fb54bb108c1027f1ca60ce5a8faf 100644 (file)
@@ -17,6 +17,12 @@ import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
 
+/**
+ * URI parameters.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ */
+@Deprecated
 class ParametersUtil {
 
     private ParametersUtil() {
index cceeb8b2302162bf65e3effbc589cb91bb973736..e4df9c901d7cb764a9c531b86064c7381a7352b8 100644 (file)
@@ -18,15 +18,15 @@ import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 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.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEntity;
-import org.opendaylight.netconf.sal.restconf.impl.PatchStatusContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchStatusEntity;
 import org.opendaylight.restconf.RestConnectorProvider;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchEntity;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.common.patch.PatchStatusEntity;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
 import org.opendaylight.restconf.restful.utils.RestconfDataServiceConstant.PatchData;
@@ -39,6 +39,12 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * PatchDataTransaction util.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ */
+@Deprecated
 public final class PatchDataTransactionUtil {
     private static final Logger LOG = LoggerFactory.getLogger(PatchDataTransactionUtil.class);
 
index c407d852c8b51e31304a180249b8295e776d2b74..445e9fbc2f5ca5fd5c9ffcd99f4f647616727ff7 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.restconf.restful.utils;
 
+import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
 import java.net.URI;
 import javax.ws.rs.core.Response;
@@ -16,7 +17,6 @@ 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.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
@@ -40,7 +40,9 @@ import org.slf4j.LoggerFactory;
 /**
  * Util class to post data to DS.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class PostDataTransactionUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(PostDataTransactionUtil.class);
@@ -216,7 +218,7 @@ public final class PostDataTransactionUtil {
             final boolean before, final DOMTransactionChain domTransactionChain) {
         rwTransaction.delete(datastore, path.getParent().getParent());
         final InstanceIdentifierContext<?> instanceIdentifier =
-                ControllerContext.getInstance().toInstanceIdentifier(point);
+                ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent());
         int lastItemPosition = 0;
         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
             if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
@@ -251,7 +253,7 @@ public final class PostDataTransactionUtil {
             final DOMTransactionChain domTransactionChain) {
         rwTransaction.delete(datastore, path.getParent().getParent());
         final InstanceIdentifierContext<?> instanceIdentifier =
-                ControllerContext.getInstance().toInstanceIdentifier(point);
+                ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent());
         int lastItemPosition = 0;
         for (final MapEntryNode mapEntryNode : readList.getValue()) {
             if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
index 4b1b0aa0a58316bb08038a1afb25229eb4c84e6c..f8636cf2a71f030dafa4c296cce5be94a0db78ca 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.restconf.restful.utils;
 
+import com.google.common.base.Optional;
 import com.google.common.collect.Maps;
 import com.google.common.util.concurrent.CheckedFuture;
 import java.util.List;
@@ -17,15 +18,15 @@ import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFaile
 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.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
+import org.opendaylight.restconf.common.validation.RestconfValidationUtils;
 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
+import org.opendaylight.restconf.utils.parser.ParserIdentifier;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
@@ -49,7 +50,9 @@ import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 /**
  * Util class for put data to DS.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class PutDataTransactionUtil {
 
     /**
@@ -153,9 +156,11 @@ public final class PutDataTransactionUtil {
     public static Response putData(final NormalizedNodeContext payload, final SchemaContextRef schemaCtxRef,
                                final TransactionVarsWrapper transactionNode, final String insert, final String point) {
         final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
+        final SchemaContext schemaContext = schemaCtxRef.get();
         final ResponseFactory responseFactory = new ResponseFactory(
-                ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode));
-        final CheckedFuture<Void, TransactionCommitFailedException> submitData = submitData(path, schemaCtxRef.get(),
+                ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode,
+                        schemaContext));
+        final CheckedFuture<Void, TransactionCommitFailedException> submitData = submitData(path, schemaContext,
                 transactionNode.getTransactionChain(), payload.getData(), insert, point);
         FutureCallbackTx.addCallback(submitData, RestconfDataServiceConstant.PutData.PUT_TX_TYPE, responseFactory);
         return responseFactory.build();
@@ -285,7 +290,7 @@ public final class PutDataTransactionUtil {
         final TransactionVarsWrapper transactionNode =
                 new TransactionVarsWrapper(iid, null, domTransactionChain);
         final NormalizedNode<?, ?> readData = ReadDataTransactionUtil
-                .readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode);
+                .readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode, schemaContext);
         return readData;
     }
 
@@ -295,7 +300,7 @@ public final class PutDataTransactionUtil {
             final OrderedLeafSetNode<?> readLeafList, final boolean before) {
         rwTransaction.delete(datastore, path.getParent());
         final InstanceIdentifierContext<?> instanceIdentifier =
-                ControllerContext.getInstance().toInstanceIdentifier(point);
+                ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent());
         int lastItemPosition = 0;
         for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
             if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
@@ -325,7 +330,7 @@ public final class PutDataTransactionUtil {
             final OrderedMapNode readList, final boolean before) {
         writeTx.delete(datastore, path.getParent());
         final InstanceIdentifierContext<?> instanceIdentifier =
-                ControllerContext.getInstance().toInstanceIdentifier(point);
+                ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent());
         int lastItemPosition = 0;
         for (final MapEntryNode mapEntryNode : readList.getValue()) {
             if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
index 57c1edf0b78cb9dc425c233361f38a9e33befd2d..8bfa442e91cc922d54447b490c5f091a216dd4c4 100644 (file)
@@ -27,7 +27,6 @@ import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
-import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.WriterParameters;
@@ -78,7 +77,9 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
  * <li>all (config + state)
  * </ul>
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class ReadDataTransactionUtil {
 
     private ReadDataTransactionUtil() {
@@ -106,7 +107,6 @@ public final class ReadDataTransactionUtil {
     /**
      * Parse parameters from URI request and check their types and values.
      *
-     *
      * @param identifier
      *             {@link InstanceIdentifierContext}
      * @param uriInfo
@@ -195,31 +195,35 @@ public final class ReadDataTransactionUtil {
      * Read specific type of data from data store via transaction.
      *
      * @param valueOfContent
-     *             type of data to read (config, state, all)
+     *            type of data to read (config, state, all)
      * @param transactionNode
-     *             {@link TransactionVarsWrapper} - wrapper for variables
+     *            {@link TransactionVarsWrapper} - wrapper for variables
+     * @param ctx
+     *            schema context
      * @return {@link NormalizedNode}
      */
     @Nullable
     public static NormalizedNode<?, ?> readData(@Nonnull final String valueOfContent,
-            @Nonnull final TransactionVarsWrapper transactionNode) {
-        return readData(valueOfContent, transactionNode, null);
+            @Nonnull final TransactionVarsWrapper transactionNode, final SchemaContext ctx) {
+        return readData(valueOfContent, transactionNode, null, ctx);
     }
 
     /**
      * Read specific type of data from data store via transaction.
      *
      * @param valueOfContent
-     *             type of data to read (config, state, all)
+     *            type of data to read (config, state, all)
      * @param transactionNode
-     *             {@link TransactionVarsWrapper} - wrapper for variables
+     *            {@link TransactionVarsWrapper} - wrapper for variables
      * @param withDefa
-     *             vaule of with-defaults parameter
+     *            vaule of with-defaults parameter
+     * @param ctx
+     *            schema context
      * @return {@link NormalizedNode}
      */
     @Nullable
     public static NormalizedNode<?, ?> readData(@Nonnull final String valueOfContent,
-            @Nonnull final TransactionVarsWrapper transactionNode, final String withDefa) {
+            @Nonnull final TransactionVarsWrapper transactionNode, final String withDefa, final SchemaContext ctx) {
         switch (valueOfContent) {
             case RestconfDataServiceConstant.ReadData.CONFIG:
                 transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
@@ -227,14 +231,14 @@ public final class ReadDataTransactionUtil {
                     return readDataViaTransaction(transactionNode);
                 } else {
                     return prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
-                            transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa);
+                            transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa, ctx);
                 }
             case RestconfDataServiceConstant.ReadData.NONCONFIG:
                 transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
                 return readDataViaTransaction(transactionNode);
 
             case RestconfDataServiceConstant.ReadData.ALL:
-                return readAllData(transactionNode, withDefa);
+                return readAllData(transactionNode, withDefa, ctx);
 
             default:
                 throw new RestconfDocumentedException(
@@ -265,9 +269,9 @@ public final class ReadDataTransactionUtil {
     public static NormalizedNode<?, ?> readData(final String identifier, final String content,
                                                 final TransactionVarsWrapper transactionNode, final String withDefa,
                                                 final SchemaContextRef schemaContextRef, final UriInfo uriInfo) {
+        final SchemaContext schemaContext = schemaContextRef.get();
         if (identifier.contains(STREAMS_PATH) && !identifier.contains(STREAM_PATH_PART)) {
             final DOMDataReadWriteTransaction wTx = transactionNode.getTransactionChain().newReadWriteTransaction();
-            final SchemaContext schemaContext = schemaContextRef.get();
             final boolean exist = SubscribeToStreamUtil.checkExist(schemaContext, wTx);
 
             for (final NotificationDefinition notificationDefinition : schemaContextRef.get().getNotifications()) {
@@ -293,11 +297,11 @@ public final class ReadDataTransactionUtil {
             }
             SubscribeToStreamUtil.submitData(wTx);
         }
-        return readData(content, transactionNode, withDefa);
+        return readData(content, transactionNode, withDefa, schemaContext);
     }
 
     private static NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
-            final YangInstanceIdentifier path, final String withDefa) {
+            final YangInstanceIdentifier path, final String withDefa, final SchemaContext ctx) {
         boolean trim;
         switch (withDefa) {
             case "trim":
@@ -310,7 +314,6 @@ public final class ReadDataTransactionUtil {
                 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) {
@@ -447,13 +450,16 @@ public final class ReadDataTransactionUtil {
      * Read config and state data, then map them.
      *
      * @param transactionNode
-     *             {@link TransactionVarsWrapper} - wrapper for variables
-     * @param withDefa with-defaults parameter
+     *            {@link TransactionVarsWrapper} - wrapper for variables
+     * @param withDefa
+     *            with-defaults parameter
+     * @param ctx
+     *            schema context
      * @return {@link NormalizedNode}
      */
     @Nullable
     private static NormalizedNode<?, ?> readAllData(@Nonnull final TransactionVarsWrapper transactionNode,
-            final String withDefa) {
+            final String withDefa, final SchemaContext ctx) {
         // PREPARE STATE DATA NODE
         transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
         final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode);
@@ -465,7 +471,7 @@ public final class ReadDataTransactionUtil {
             configDataNode = readDataViaTransaction(transactionNode);
         } else {
             configDataNode = prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
-                    transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa);
+                    transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa, ctx);
         }
 
         // if no data exists
index 7d1f7cbf04d83aec60452db83f1c42dc0504dcd9..dcf7fd6b71230964bb4612b1f177bf9a402c1031 100644 (file)
@@ -10,7 +10,9 @@ package org.opendaylight.restconf.restful.utils;
 /**
  * Common util class for resolve enum from String.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class ResolveEnumUtil {
 
     private ResolveEnumUtil() {
index 1c165322966100f0e9c2660cd7ef9e21e7e80ed9..297027b6f17a0939deeb6fa0c66081b50adda6c5 100644 (file)
@@ -14,6 +14,12 @@ import javax.ws.rs.core.Response.Status;
 import org.apache.commons.lang3.builder.Builder;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
+/**
+ * Response factory.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ */
+@Deprecated
 final class ResponseFactory extends FutureDataFactory<Void> implements Builder<Response> {
 
     private ResponseBuilder responseBuilder;
index 1b5cdf31f086f093d1cf7f176df71c041b045c5c..7c2b1c198f5a1070e135e26ae765e19e1f4b1c7c 100644 (file)
@@ -18,7 +18,9 @@ import org.opendaylight.yangtools.yang.common.QNameModule;
 /**
  * Constants for RestconfDataService.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class RestconfDataServiceConstant {
 
     public static final QName NETCONF_BASE_QNAME;
index f31f5aac004912510a0f16777c47cd31c351aa1b..25594a778235df92c4480a548472aade462ea8be 100644 (file)
@@ -27,7 +27,9 @@ import org.slf4j.LoggerFactory;
 /**
  * Util class for rpc.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public class RestconfInvokeOperationsUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(RestconfInvokeOperationsUtil.class);
index 875994551d5382c1810b52fab445bf117e3b0968..10f4acdd6d1718ec8fb77c12e4b5fb219dc98313 100644 (file)
@@ -28,7 +28,9 @@ import org.slf4j.LoggerFactory;
 /**
  * Constants for streams.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class RestconfStreamsConstants {
 
     private static final Logger LOG = LoggerFactory.getLogger(RestconfStreamsConstants.class);
index 313748b37f16f7c9b84cffd1a4f91076d14a116a..59ed477837870b8c7499ef62e23719f335e966e2 100644 (file)
@@ -10,6 +10,12 @@ package org.opendaylight.restconf.restful.utils;
 import org.apache.commons.lang3.builder.Builder;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 
+/**
+ * Rpc result factory.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ */
+@Deprecated
 public class RpcResultFactory extends FutureDataFactory<DOMRpcResult> implements Builder<DOMRpcResult> {
 
     @Override
index 9cdeaa020026ef894f09513cd27f9f5ff328687c..50c4e3314960e67a19014539333a60aa018d3e4d 100644 (file)
@@ -67,7 +67,9 @@ import org.slf4j.LoggerFactory;
 /**
  * Subscribe to stream util class.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class SubscribeToStreamUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(SubscribeToStreamUtil.class);
index 626677880d09349b6c5730d9b63591fd6ffb224e..e67a5d5b56976b4a3b2e261bf3723e34b4aed844 100644 (file)
@@ -33,7 +33,9 @@ import org.slf4j.LoggerFactory;
 /**
  * Util class for common methods of transactions.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class TransactionUtil {
 
     private static final Logger LOG = LoggerFactory.getLogger(TransactionUtil.class);
index 5f9489bbf7ea43a8296495b99cf5f6545d6575c1..2a8b414fdc6b426405ca81f65c8bfde4939759bb 100644 (file)
@@ -10,8 +10,9 @@ package org.opendaylight.restconf.utils;
 import com.google.common.base.Splitter;
 
 /**
- * {@link Deprecated} move to splitted module restconf-nb-rfc8040. Util class for Restconf constants.
+ * Util class for Restconf constants.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
 @Deprecated
 public final class RestconfConstants {
@@ -22,7 +23,7 @@ public final class RestconfConstants {
     public static final String IDENTIFIER = "identifier";
     public static final char SLASH = '/';
     public static final Splitter SLASH_SPLITTER = Splitter.on(SLASH);
-    public static final String DRAFT_PATTERN = "restconf/17";
+    public static final String DRAFT_PATTERN = "restconf/18";
 
     private RestconfConstants() {
         throw new UnsupportedOperationException("Util class");
index 3ed913782a705c62ddf37e0c568fa28bbd8479af..3a48ed397784b02a63bbfa6ea113b29284a1ffc4 100644 (file)
@@ -10,7 +10,9 @@ package org.opendaylight.restconf.utils.mapping;
 /**
  * Util class for constants of mapping node.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class RestconfMappingNodeConstants {
 
     public static final String NAME = "name";
index af4a01b97c242e459b75af904fb7f55eafcd1476..6673dae1dc9b1cddd399fe11675e4d57e433e34b 100644 (file)
@@ -53,7 +53,9 @@ import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 /**
  * Util class for mapping nodes.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class RestconfMappingNodeUtil {
 
     private RestconfMappingNodeUtil() {
index bb957f6f553d38da3e40b488509ee07f7cb79c16..b1b4f1d5e03e0a1a5898070604b2fba801c31bd9 100644 (file)
@@ -10,7 +10,9 @@ package org.opendaylight.restconf.utils.mapping;
 /**
  * Util class for mapping entry stream.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class RestconfMappingStreamConstants {
 
     public static final String DESCRIPTION = "DESCRIPTION_PLACEHOLDER";
index a4b9217d2f768022828ac7e862b21f28eb59b693..2ba3f045615748d6c6522d2f712bace95ebfabfc 100644 (file)
@@ -25,6 +25,12 @@ import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
+/**
+ * Parameters parser.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
+ */
+@Deprecated
 public class ParserFieldsParameter {
     private static final char COLON = ':';
     private static final char SEMICOLON = ';';
@@ -112,7 +118,7 @@ public class ParserFieldsParameter {
                             currentNode,
                             input.substring(startPosition, currentPosition), currentQNameModule, currentLevel);
                     // call with child node as new start node for one level down
-                    int closingParenthesis = currentPosition
+                    final int closingParenthesis = currentPosition
                             + findClosingParenthesis(input.substring(currentPosition + 1));
                     parseInput(
                             input.substring(currentPosition + 1, closingParenthesis),
index 876583e83756a9b35696dbebad5e5af8c136c8c8..6d22412507bc471d7e8c6c15284f86889316dc98 100644 (file)
@@ -38,7 +38,10 @@ import org.slf4j.LoggerFactory;
 
 /**
  * Util class for parsing identifier.
+ *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class ParserIdentifier {
 
     private static final Logger LOG = LoggerFactory.getLogger(ParserIdentifier.class);
index e18a2ed1c8f6503932a4f8f25c1a6214f2b7cf60..9faecc9532a5d4aaf8ac1e8b0f05a75f7bc928bb 100644 (file)
@@ -13,10 +13,12 @@ import org.opendaylight.restconf.parser.builder.YangInstanceIdentifierDeserializ
 import org.opendaylight.restconf.parser.builder.YangInstanceIdentifierSerializer;
 
 /**
- * Util class of constants of {@link YangInstanceIdentifierSerializer}
- * and {@link YangInstanceIdentifierDeserializer}.
+ * Util class of constants of {@link YangInstanceIdentifierSerializer} and
+ * {@link YangInstanceIdentifierDeserializer}.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class ParserBuilderConstants {
 
     private ParserBuilderConstants() {
index ad988dc08b078c49fe1f9b12eb2de92f2d267289..0152873f0c1178072920252ae535ddb9db98825d 100644 (file)
@@ -10,17 +10,19 @@ package org.opendaylight.restconf.utils.validation;
 import java.text.ParseException;
 import java.util.Date;
 import java.util.Iterator;
-import org.opendaylight.netconf.md.sal.rest.common.RestconfValidationUtils;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.validation.RestconfValidationUtils;
 import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
 
 /**
  * Util class for validations.
  *
+ * @deprecated move to splitted module restconf-nb-rfc8040
  */
+@Deprecated
 public final class RestconfValidation {
 
     private RestconfValidation() {
index 8f46c6ddbc8c9a2f1450af543fb77de598537e69..0912ce245a4db4af442bf41eb8fa21e5dc742102 100644 (file)
@@ -24,8 +24,8 @@ import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
 import org.opendaylight.netconf.sal.rest.api.RestconfConstants;
 import org.opendaylight.netconf.sal.rest.impl.AbstractIdentifierAwareJaxRsProvider;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public abstract class AbstractBodyReaderTest {
index 7aa7d7ff038742f50de54ccd87f67b72664c780a..4f6c7e9b072b656288c717543b1c41909e096776 100644 (file)
@@ -17,8 +17,8 @@ import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.netconf.sal.rest.impl.JsonToPatchBodyReader;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public class TestJsonPatchBodyReader extends AbstractBodyReaderTest {
index c659cd36aaebfe4729658b5f657e18a0d5dc0af7..de3c0a0891b42a240e2c132d036145182d1e3462 100644 (file)
@@ -23,8 +23,8 @@ import org.junit.Test;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.netconf.sal.rest.impl.JsonToPatchBodyReader;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
index af4fee6d55d5f9a478f829281db58e06a8307712..fea1b6f017135b64d84d0ce1cc4ea0521d27382f 100644 (file)
@@ -16,8 +16,8 @@ import javax.ws.rs.core.MediaType;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.netconf.sal.rest.impl.XmlToPatchBodyReader;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public class TestXmlPatchBodyReader extends AbstractBodyReaderTest {
index 64184e0a655b5a8b609e5f8cac3f078fa2733199..6da8154b19dfeabb1737b3d53e67607e8d886ea0 100644 (file)
@@ -22,8 +22,8 @@ import org.junit.Test;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.netconf.sal.rest.impl.XmlToPatchBodyReader;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
index 41969c4f888d21ea6ee41e46954c49bafad4770b..86a1a11682573ea199e85acc05915a0305516c5e 100644 (file)
@@ -52,8 +52,6 @@ import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchStatusContext;
 import org.opendaylight.netconf.sal.restconf.impl.PutResult;
 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
@@ -63,6 +61,8 @@ import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
 import org.opendaylight.restconf.common.errors.RestconfError;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
 import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.handlers.TransactionChainHandler;
 import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType;
index 207dac862ad2193bd29fe245f6b63101fb7d4309..0c7ab15b533e4e9d5a03b6a9da696ea331d0826b 100644 (file)
@@ -15,6 +15,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
 import org.opendaylight.netconf.sal.restconf.impl.RestCodec.InstanceIdentifierCodecImpl;
+import org.opendaylight.restconf.common.util.IdentityValuesDTO;
 import org.opendaylight.yangtools.concepts.Codec;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
index 598d37be477668739c8b05dacfb719d3a50fb5b0..899991743ad13ae813266f66c334432d15c18920 100644 (file)
@@ -22,9 +22,9 @@ import javax.ws.rs.core.UriInfo;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
 import org.opendaylight.netconf.sal.rest.api.RestconfConstants;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
 import org.opendaylight.restconf.RestConnectorProvider;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
index cfee8ef98b19ccdf7ef6a440d3090382236ae6ab..a2387f115bb1066abe6223bdf56ce128668b1f4f 100644 (file)
@@ -23,8 +23,8 @@ import org.junit.Test;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.sal.rest.impl.test.providers.TestJsonBodyReader;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
index f36856c2f8f4a5b070bf0ff445bee6d8790cbd21..dbee32eb0e14e4350b9a5bc0a912da2778f2c5a9 100644 (file)
@@ -20,8 +20,8 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.sal.rest.impl.test.providers.TestJsonBodyReader;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public class JsonPatchBodyReaderTest extends AbstractBodyReaderTest {
index ffad2424101d147c6544a88fed9703dde8e3294c..0a7b130e8460f375adcda84e67ae00a1514bbd6b 100644 (file)
@@ -22,8 +22,8 @@ import org.junit.Test;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.sal.rest.impl.test.providers.TestXmlBodyReader;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
index 6c07ca82ff51edf1a62c6078857c69074d805494..51e28761e444310a4c50c6ba1d3ed5fdafda35b9 100644 (file)
@@ -19,8 +19,8 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.sal.rest.impl.test.providers.TestXmlBodyReader;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.patch.PatchContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public class XmlPatchBodyReaderTest extends AbstractBodyReaderTest {
index 08317bdbf17f54be7e29d1ae2711710462e208ce..cac315093c683c6de836515373744488d243b67b 100644 (file)
@@ -16,10 +16,10 @@ 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 static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.CREATE;
-import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.DELETE;
-import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.REMOVE;
-import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.REPLACE;
+import static org.opendaylight.restconf.common.patch.PatchEditOperation.CREATE;
+import static org.opendaylight.restconf.common.patch.PatchEditOperation.DELETE;
+import static org.opendaylight.restconf.common.patch.PatchEditOperation.REMOVE;
+import static org.opendaylight.restconf.common.patch.PatchEditOperation.REPLACE;
 
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
@@ -50,13 +50,13 @@ import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEntity;
-import org.opendaylight.netconf.sal.restconf.impl.PatchStatusContext;
 import org.opendaylight.restconf.RestConnectorProvider;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchEntity;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
index fd77baa603c8564db824985d2f235566106cf50b..9bfde6e12437b2be8b596c547b2d0fe190606bd6 100644 (file)
@@ -14,11 +14,11 @@ import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.MockitoAnnotations.initMocks;
-import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.CREATE;
-import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.DELETE;
-import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.MERGE;
-import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.REMOVE;
-import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.REPLACE;
+import static org.opendaylight.restconf.common.patch.PatchEditOperation.CREATE;
+import static org.opendaylight.restconf.common.patch.PatchEditOperation.DELETE;
+import static org.opendaylight.restconf.common.patch.PatchEditOperation.MERGE;
+import static org.opendaylight.restconf.common.patch.PatchEditOperation.REMOVE;
+import static org.opendaylight.restconf.common.patch.PatchEditOperation.REPLACE;
 
 import com.google.common.util.concurrent.Futures;
 import java.lang.reflect.Field;
@@ -32,13 +32,13 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
-import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchEntity;
-import org.opendaylight.netconf.sal.restconf.impl.PatchStatusContext;
-import org.opendaylight.netconf.sal.restconf.impl.PatchStatusEntity;
 import org.opendaylight.restconf.RestConnectorProvider;
 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
 import org.opendaylight.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchEntity;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.common.patch.PatchStatusEntity;
 import org.opendaylight.restconf.common.references.SchemaContextRef;
 import org.opendaylight.restconf.handlers.TransactionChainHandler;
 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
index 45b216420e93e297e9eb41a37eaa419ee40d6fdb..9ba6b2215b97c670da0adac1c849fe2bdff2dd3f 100644 (file)
@@ -87,7 +87,8 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path);
         doReturn(DATA.path).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
-        final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
+        final NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
         assertEquals(DATA.data3, normalizedNode);
     }
 
@@ -99,7 +100,8 @@ public class ReadDataTransactionUtilTest {
                 .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 NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
         assertEquals(DATA.data3, normalizedNode);
     }
 
@@ -111,7 +113,8 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
         doReturn(DATA.path2).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.ALL;
-        final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
+        final NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
         assertEquals(DATA.data2, normalizedNode);
     }
 
@@ -121,7 +124,8 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path2);
         doReturn(DATA.path2).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.NONCONFIG;
-        final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
+        final NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
         assertEquals(DATA.data2, normalizedNode);
     }
 
@@ -133,7 +137,8 @@ public class ReadDataTransactionUtilTest {
                 .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 NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
         final ContainerNode checkingData = Builders
                 .containerBuilder()
                 .withNodeIdentifier(NODE_IDENTIFIER)
@@ -151,7 +156,7 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.OPERATIONAL, DATA.path);
         doReturn(DATA.path).when(context).getInstanceIdentifier();
         final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
-                RestconfDataServiceConstant.ReadData.ALL, wrapper);
+                RestconfDataServiceConstant.ReadData.ALL, wrapper, schemaContext);
         final ContainerNode checkingData = Builders
                 .containerBuilder()
                 .withNodeIdentifier(NODE_IDENTIFIER)
@@ -169,7 +174,8 @@ public class ReadDataTransactionUtilTest {
                 .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 NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
         final MapNode checkingData = Builders
                 .mapBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create("ns", "2016-02-28", "list")))
@@ -184,7 +190,8 @@ public class ReadDataTransactionUtilTest {
                 .read(LogicalDatastoreType.CONFIGURATION, DATA.path2);
         doReturn(DATA.path2).when(context).getInstanceIdentifier();
         final String valueOfContent = RestconfDataServiceConstant.ReadData.CONFIG;
-        final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(valueOfContent, wrapper);
+        final NormalizedNode<?, ?> normalizedNode =
+                ReadDataTransactionUtil.readData(valueOfContent, wrapper, schemaContext);
         assertNull(normalizedNode);
     }
 
@@ -192,7 +199,7 @@ public class ReadDataTransactionUtilTest {
     public void readDataFailTest() {
         final String valueOfContent = RestconfDataServiceConstant.ReadData.READ_TYPE_TX;
         final NormalizedNode<?, ?> normalizedNode = ReadDataTransactionUtil.readData(
-                valueOfContent, wrapper);
+                valueOfContent, wrapper, schemaContext);
         assertNull(normalizedNode);
     }
 
index 615532039a4fda0407bd8a73c52995c4e38b65ff..a240ef1693872b739461c2b3d5e74b5555878d8a 100644 (file)
@@ -15,6 +15,7 @@ import org.junit.Assert;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.util.RestconfSchemaUtil;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
index a2c8fa51918d411360293bba8ca93594cb091e8b..d2acf7bc3717f7181527195a4f0a7f6d0bdc357b 100644 (file)
   <version>1.7.0-SNAPSHOT</version>
   <packaging>bundle</packaging>
 
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.opendaylight.netconf</groupId>
+        <artifactId>netconf-parent</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.opendaylight.aaa</groupId>
+        <artifactId>aaa-artifacts</artifactId>
+        <version>0.7.0-SNAPSHOT</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>config-artifacts</artifactId>
+        <version>0.8.0-SNAPSHOT</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>mdsal-artifacts</artifactId>
+        <version>1.7.0-SNAPSHOT</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
   <dependencies>
     <dependency>
       <groupId>org.opendaylight.netconf</groupId>
       <artifactId>restconf-common</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.netconf</groupId>
+      <artifactId>ietf-yang-library</artifactId>
+      <version>${project.version}</version>
+    </dependency>
 
     <dependency>
       <groupId>org.opendaylight.yangtools</groupId>
       <artifactId>yang-data-api</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-model-util</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-codec-xml</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.yangtools</groupId>
+      <artifactId>yang-data-codec-gson</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.mdsal.model</groupId>
+      <artifactId>ietf-yang-types-20130715</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-core-spi</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-util</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>io.netty</groupId>
+      <artifactId>netty-codec-http</artifactId>
+    </dependency>
 
     <dependency>
       <groupId>org.jboss.resteasy</groupId>
       <artifactId>jaxrs-api</artifactId>
       <scope>provided</scope>
     </dependency>
+
+    <dependency>
+      <groupId>com.fasterxml.jackson.dataformat</groupId>
+      <artifactId>jackson-dataformat-xml</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>3.0</version>
+    </dependency>
   </dependencies>
 </project>
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/RestConnectorProvider.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/RestConnectorProvider.java
new file mode 100644 (file)
index 0000000..24f2adb
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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.nb.rfc8040;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.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.model.SchemaService;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.RpcServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
+import org.opendaylight.restconf.nb.rfc8040.services.wrapper.ServicesWrapperImpl;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provider for restconf draft18.
+ *
+ */
+public class RestConnectorProvider implements RestconfConnector, AutoCloseable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestConnectorProvider.class);
+
+    public static final TransactionChainListener TRANSACTION_CHAIN_LISTENER = new TransactionChainListener() {
+        @Override
+        public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
+                final AsyncTransaction<?, ?> transaction, final Throwable cause) {
+            LOG.warn("TransactionChain({}) {} FAILED!", chain, transaction.getIdentifier(), cause);
+            resetTransactionChainForAdapaters(chain);
+            throw new RestconfDocumentedException("TransactionChain(" + chain + ") not committed correctly", cause);
+        }
+
+        @Override
+        public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
+            LOG.trace("TransactionChain({}) {} SUCCESSFUL", chain);
+        }
+    };
+
+    private static TransactionChainHandler transactionChainHandler;
+    private static DOMDataBroker dataBroker;
+    private static DOMMountPointServiceHandler mountPointServiceHandler;
+
+    private final SchemaService schemaService;
+    private final DOMRpcService rpcService;
+    private final DOMNotificationService notificationService;
+    private final DOMMountPointService mountPointService;
+    private ListenerRegistration<SchemaContextListener> listenerRegistration;
+
+    private SchemaContextHandler schemaCtxHandler;
+
+    public RestConnectorProvider(final DOMDataBroker domDataBroker, final SchemaService schemaService, final DOMRpcService rpcService,
+            final DOMNotificationService notificationService, final DOMMountPointService mountPointService) {
+        this.schemaService = Preconditions.checkNotNull(schemaService);
+        this.rpcService = Preconditions.checkNotNull(rpcService);
+        this.notificationService = Preconditions.checkNotNull(notificationService);
+        this.mountPointService = Preconditions.checkNotNull(mountPointService);
+
+        RestConnectorProvider.dataBroker = Preconditions.checkNotNull(domDataBroker);
+    }
+
+    public void start() {
+        final ServicesWrapperImpl wrapperServices = ServicesWrapperImpl.getInstance();
+
+        mountPointServiceHandler = new DOMMountPointServiceHandler(mountPointService);
+
+        final DOMDataBrokerHandler brokerHandler = new DOMDataBrokerHandler(dataBroker);
+
+        RestConnectorProvider.transactionChainHandler = new TransactionChainHandler(dataBroker
+                .createTransactionChain(RestConnectorProvider.TRANSACTION_CHAIN_LISTENER));
+
+        this.schemaCtxHandler = new SchemaContextHandler(transactionChainHandler);
+        this.listenerRegistration = schemaService.registerSchemaContextListener(this.schemaCtxHandler);
+
+        final RpcServiceHandler rpcServiceHandler = new RpcServiceHandler(rpcService);
+
+        final NotificationServiceHandler notificationServiceHandler =
+                new NotificationServiceHandler(notificationService);
+
+        wrapperServices.setHandlers(this.schemaCtxHandler, RestConnectorProvider.mountPointServiceHandler,
+                RestConnectorProvider.transactionChainHandler, brokerHandler, rpcServiceHandler,
+                notificationServiceHandler);
+    }
+
+    public DOMMountPointServiceHandler getMountPointServiceHandler() {
+        return mountPointServiceHandler;
+    }
+
+    /**
+     * After {@link TransactionChain} failed, this updates {@link TransactionChainHandler} with new transaction chain.
+     *
+     * @param chain
+     *             old {@link TransactionChain}
+     */
+    public static void resetTransactionChainForAdapaters(final TransactionChain<?, ?> chain) {
+        LOG.trace("Resetting TransactionChain({})", chain);
+        chain.close();
+        RestConnectorProvider.transactionChainHandler.update(
+                Preconditions.checkNotNull(dataBroker).createTransactionChain(
+                        RestConnectorProvider.TRANSACTION_CHAIN_LISTENER)
+        );
+    }
+
+    /**
+     * Get current {@link DOMMountPointService} from {@link DOMMountPointServiceHandler}.
+     * @return {@link DOMMountPointService}
+     */
+    public static DOMMountPointService getMountPointService() {
+        return mountPointServiceHandler.get();
+    }
+
+    @Override
+    public void close() throws Exception {
+        // close registration
+        if (this.listenerRegistration != null) {
+            this.listenerRegistration.close();
+        }
+
+        // close transaction chain
+        if (transactionChainHandler != null && transactionChainHandler.get() != null) {
+            transactionChainHandler.get().close();
+        }
+
+        transactionChainHandler = null;
+        mountPointServiceHandler = null;
+        dataBroker = null;
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/RestconfConnector.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/RestconfConnector.java
new file mode 100644 (file)
index 0000000..26a53cd
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2014, 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.restconf.nb.rfc8040;
+
+/*
+ * This is a simple dummy interface to allow us to create instances of RestconfProvider
+ * via the config subsystem.
+ */
+public interface RestconfConnector {
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/codecs/RestCodec.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/codecs/RestCodec.java
new file mode 100644 (file)
index 0000000..7c92625
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * 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.restconf.nb.rfc8040.codecs;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.restconf.common.util.IdentityValuesDTO;
+import org.opendaylight.restconf.common.util.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.restconf.common.util.IdentityValuesDTO.Predicate;
+import org.opendaylight.restconf.common.util.RestUtil;
+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.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.codec.IdentityrefCodec;
+import org.opendaylight.yangtools.yang.data.api.codec.InstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.data.api.codec.LeafrefCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.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.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RestCodec {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestCodec.class);
+
+    private RestCodec() {
+    }
+
+    public static final Codec<Object, Object> from(final TypeDefinition<?> typeDefinition,
+            final DOMMountPoint mountPoint, final SchemaContext schemaContext) {
+        return new ObjectCodec(typeDefinition, mountPoint, schemaContext);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static final class ObjectCodec implements Codec<Object, Object> {
+
+        private static final Logger LOG = LoggerFactory.getLogger(ObjectCodec.class);
+
+        public static final Codec LEAFREF_DEFAULT_CODEC = new LeafrefCodecImpl();
+        private final Codec instanceIdentifier;
+        private final Codec identityrefCodec;
+
+        private final TypeDefinition<?> type;
+
+        private final SchemaContext schemaContext;
+
+        private ObjectCodec(final TypeDefinition<?> typeDefinition, final DOMMountPoint mountPoint,
+                final SchemaContext schemaContext) {
+            this.schemaContext = schemaContext;
+            this.type = RestUtil.resolveBaseTypeFrom(typeDefinition);
+            if (this.type instanceof IdentityrefTypeDefinition) {
+                this.identityrefCodec = new IdentityrefCodecImpl(mountPoint, schemaContext);
+            } else {
+                this.identityrefCodec = null;
+            }
+            if (this.type instanceof InstanceIdentifierTypeDefinition) {
+                this.instanceIdentifier = new InstanceIdentifierCodecImpl(mountPoint, schemaContext);
+            } else {
+                this.instanceIdentifier = null;
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public Object deserialize(final Object input) {
+            try {
+                if (this.type instanceof IdentityrefTypeDefinition) {
+                    if (input instanceof IdentityValuesDTO) {
+                        return this.identityrefCodec.deserialize(input);
+                    }
+                    if (LOG.isDebugEnabled()) {
+                        LOG.debug(
+                            "Value is not instance of IdentityrefTypeDefinition but is {}. "
+                                    + "Therefore NULL is used as translation of  - {}",
+                            input == null ? "null" : input.getClass(), String.valueOf(input));
+                    }
+                    return null;
+                } else if (this.type instanceof InstanceIdentifierTypeDefinition) {
+                    if (input instanceof IdentityValuesDTO) {
+                        return this.instanceIdentifier.deserialize(input);
+                    } else {
+                        final StringModuleInstanceIdentifierCodec codec =
+                                new StringModuleInstanceIdentifierCodec(schemaContext);
+                        return codec.deserialize((String) input);
+                    }
+                } else {
+                    final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec =
+                            TypeDefinitionAwareCodec.from(this.type);
+                    if (typeAwarecodec != null) {
+                        if (input instanceof IdentityValuesDTO) {
+                            return typeAwarecodec.deserialize(((IdentityValuesDTO) input).getOriginValue());
+                        }
+                        return typeAwarecodec.deserialize(String.valueOf(input));
+                    } else {
+                        LOG.debug("Codec for type \"" + this.type.getQName().getLocalName()
+                                + "\" is not implemented yet.");
+                        return null;
+                    }
+                }
+            } catch (final ClassCastException e) { // TODO remove this catch when everyone use codecs
+                LOG.error(
+                        "ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input),
+                        e);
+                return null;
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public Object serialize(final Object input) {
+            try {
+                if (this.type instanceof IdentityrefTypeDefinition) {
+                    return this.identityrefCodec.serialize(input);
+                } else if (this.type instanceof LeafrefTypeDefinition) {
+                    return LEAFREF_DEFAULT_CODEC.serialize(input);
+                } else if (this.type instanceof InstanceIdentifierTypeDefinition) {
+                    return this.instanceIdentifier.serialize(input);
+                } else {
+                    final TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec =
+                            TypeDefinitionAwareCodec.from(this.type);
+                    if (typeAwarecodec != null) {
+                        return typeAwarecodec.serialize(input);
+                    } else {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("Codec for type \"" + this.type.getQName().getLocalName()
+                                + "\" is not implemented yet.");
+                        }
+                        return null;
+                    }
+                }
+            } catch (final ClassCastException e) { // TODO remove this catch when everyone use codecs
+                LOG.error(
+                        "ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input),
+                        e);
+                return input;
+            }
+        }
+
+    }
+
+    public static class IdentityrefCodecImpl implements IdentityrefCodec<IdentityValuesDTO> {
+
+        private static final Logger LOG = LoggerFactory.getLogger(IdentityrefCodecImpl.class);
+
+        private final DOMMountPoint mountPoint;
+
+        private final SchemaContext schemaContext;
+
+        public IdentityrefCodecImpl(final DOMMountPoint mountPoint, final SchemaContext schemaContext) {
+            this.mountPoint = mountPoint;
+            this.schemaContext = schemaContext;
+        }
+
+        @Override
+        public IdentityValuesDTO serialize(final QName data) {
+            return new IdentityValuesDTO(data.getNamespace().toString(), data.getLocalName(), null, null);
+        }
+
+        @Override
+        public QName deserialize(final IdentityValuesDTO data) {
+            final IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
+            final Module module =
+                    getModuleByNamespace(valueWithNamespace.getNamespace(), this.mountPoint, schemaContext);
+            if (module == null) {
+                LOG.info("Module was not found for namespace {}", valueWithNamespace.getNamespace());
+                LOG.info("Idenetityref will be translated as NULL for data - {}", String.valueOf(valueWithNamespace));
+                return null;
+            }
+
+            return QName.create(module.getNamespace(), module.getRevision(), valueWithNamespace.getValue());
+        }
+
+    }
+
+    public static class LeafrefCodecImpl implements LeafrefCodec<String> {
+
+        @Override
+        public String serialize(final Object data) {
+            return String.valueOf(data);
+        }
+
+        @Override
+        public Object deserialize(final String data) {
+            return data;
+        }
+
+    }
+
+    public static class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec<IdentityValuesDTO> {
+        private static final Logger LOG = LoggerFactory.getLogger(InstanceIdentifierCodecImpl.class);
+        private final DOMMountPoint mountPoint;
+        private final SchemaContext schemaContext;
+
+        public InstanceIdentifierCodecImpl(final DOMMountPoint mountPoint, final SchemaContext schemaContext) {
+            this.mountPoint = mountPoint;
+            this.schemaContext = schemaContext;
+        }
+
+        @Override
+        public IdentityValuesDTO serialize(final YangInstanceIdentifier data) {
+            final IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO();
+            for (final PathArgument pathArgument : data.getPathArguments()) {
+                final IdentityValue identityValue = qNameToIdentityValue(pathArgument.getNodeType());
+                if (pathArgument instanceof NodeIdentifierWithPredicates && identityValue != null) {
+                    final List<Predicate> predicates =
+                            keyValuesToPredicateList(((NodeIdentifierWithPredicates) pathArgument).getKeyValues());
+                    identityValue.setPredicates(predicates);
+                } else if (pathArgument instanceof NodeWithValue && identityValue != null) {
+                    final List<Predicate> predicates = new ArrayList<>();
+                    final String value = String.valueOf(((NodeWithValue) pathArgument).getValue());
+                    predicates.add(new Predicate(null, value));
+                    identityValue.setPredicates(predicates);
+                }
+                identityValuesDTO.add(identityValue);
+            }
+            return identityValuesDTO;
+        }
+
+        @Override
+        public YangInstanceIdentifier deserialize(final IdentityValuesDTO data) {
+            final List<PathArgument> result = new ArrayList<>();
+            final IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
+            final Module module =
+                    getModuleByNamespace(valueWithNamespace.getNamespace(), this.mountPoint, schemaContext);
+            if (module == null) {
+                LOG.info("Module by namespace '{}' of first node in instance-identifier was not found.",
+                        valueWithNamespace.getNamespace());
+                LOG.info("Instance-identifier will be translated as NULL for data - {}",
+                        String.valueOf(valueWithNamespace.getValue()));
+                return null;
+            }
+
+            DataNodeContainer parentContainer = module;
+            final List<IdentityValue> identities = data.getValuesWithNamespaces();
+            for (int i = 0; i < identities.size(); i++) {
+                final IdentityValue identityValue = identities.get(i);
+                URI validNamespace =
+                        resolveValidNamespace(identityValue.getNamespace(), this.mountPoint, schemaContext);
+                final DataSchemaNode node = findInstanceDataChildByNameAndNamespace(
+                        parentContainer, identityValue.getValue(), validNamespace);
+                if (node == null) {
+                    LOG.info("'{}' node was not found in {}", identityValue, parentContainer.getChildNodes());
+                    LOG.info("Instance-identifier will be translated as NULL for data - {}",
+                            String.valueOf(identityValue.getValue()));
+                    return null;
+                }
+                final QName qName = node.getQName();
+                PathArgument pathArgument = null;
+                if (identityValue.getPredicates().isEmpty()) {
+                    pathArgument = new NodeIdentifier(qName);
+                } else {
+                    if (node instanceof LeafListSchemaNode) { // predicate is value of leaf-list entry
+                        final Predicate leafListPredicate = identityValue.getPredicates().get(0);
+                        if (!leafListPredicate.isLeafList()) {
+                            LOG.info("Predicate's data is not type of leaf-list. It should be in format \".='value'\"");
+                            LOG.info("Instance-identifier will be translated as NULL for data - {}",
+                                    String.valueOf(identityValue.getValue()));
+                            return null;
+                        }
+                        pathArgument = new NodeWithValue<>(qName, leafListPredicate.getValue());
+                    } else if (node instanceof ListSchemaNode) { // predicates are keys of list
+                        final DataNodeContainer listNode = (DataNodeContainer) node;
+                        final Map<QName, Object> predicatesMap = new HashMap<>();
+                        for (final Predicate predicate : identityValue.getPredicates()) {
+                            validNamespace = resolveValidNamespace(predicate.getName().getNamespace(), this.mountPoint,
+                                    schemaContext);
+                            final DataSchemaNode listKey = findInstanceDataChildByNameAndNamespace(listNode,
+                                    predicate.getName().getValue(), validNamespace);
+                            predicatesMap.put(listKey.getQName(), predicate.getValue());
+                        }
+                        pathArgument = new NodeIdentifierWithPredicates(qName, predicatesMap);
+                    } else {
+                        LOG.info("Node {} is not List or Leaf-list.", node);
+                        LOG.info("Instance-identifier will be translated as NULL for data - {}",
+                                String.valueOf(identityValue.getValue()));
+                        return null;
+                    }
+                }
+                result.add(pathArgument);
+                if (i < identities.size() - 1) { // last element in instance-identifier can be other than
+                    // DataNodeContainer
+                    if (node instanceof DataNodeContainer) {
+                        parentContainer = (DataNodeContainer) node;
+                    } else {
+                        LOG.info("Node {} isn't instance of DataNodeContainer", node);
+                        LOG.info("Instance-identifier will be translated as NULL for data - {}",
+                                String.valueOf(identityValue.getValue()));
+                        return null;
+                    }
+                }
+            }
+
+            return result.isEmpty() ? null : YangInstanceIdentifier.create(result);
+        }
+
+        private static List<Predicate> keyValuesToPredicateList(final Map<QName, Object> keyValues) {
+            final List<Predicate> result = new ArrayList<>();
+            for (final QName qualifiedName : keyValues.keySet()) {
+                final Object value = keyValues.get(qualifiedName);
+                result.add(new Predicate(qNameToIdentityValue(qualifiedName), String.valueOf(value)));
+            }
+            return result;
+        }
+
+        private static IdentityValue qNameToIdentityValue(final QName qualifiedName) {
+            if (qualifiedName != null) {
+                return new IdentityValue(qualifiedName.getNamespace().toString(), qualifiedName.getLocalName());
+            }
+            return null;
+        }
+    }
+
+    private static Module getModuleByNamespace(final String namespace, final DOMMountPoint mountPoint,
+            final SchemaContext schemaContext) {
+        final URI validNamespace = resolveValidNamespace(namespace, mountPoint, schemaContext);
+        Module module = null;
+        if (mountPoint != null) {
+            module = mountPoint.getSchemaContext().findModuleByNamespace(validNamespace).iterator().next();
+        } else {
+            module = schemaContext.findModuleByNamespace(validNamespace).iterator().next();
+        }
+        if (module == null) {
+            LOG.info("Module for namespace " + validNamespace + " wasn't found.");
+            return null;
+        }
+        return module;
+    }
+
+    private static URI resolveValidNamespace(final String namespace, final DOMMountPoint mountPoint, final SchemaContext schemaContext) {
+        URI validNamespace;
+        if (mountPoint != null) {
+            validNamespace = findFirstModuleByName(schemaContext, namespace);
+        } else {
+            validNamespace = findFirstModuleByName(schemaContext, namespace);
+        }
+        if (validNamespace == null) {
+            validNamespace = URI.create(namespace);
+        }
+
+        return validNamespace;
+    }
+
+    private static URI findFirstModuleByName(final SchemaContext schemaContext, final String name) {
+        for (final Module module : schemaContext.getModules()) {
+            if (module.getName().equals(name)) {
+                return module.getNamespace();
+            }
+        }
+        return null;
+    }
+
+    private static DataSchemaNode findInstanceDataChildByNameAndNamespace(final DataNodeContainer container,
+            final String name, final URI namespace) {
+        Preconditions.checkNotNull(namespace);
+
+        final Iterable<DataSchemaNode> result = Iterables.filter(findInstanceDataChildrenByName(container, name),
+                node -> namespace.equals(node.getQName().getNamespace()));
+        return Iterables.getFirst(result, null);
+    }
+
+    private static List<DataSchemaNode> findInstanceDataChildrenByName(final DataNodeContainer container,
+            final String name) {
+        Preconditions.checkNotNull(container);
+        Preconditions.checkNotNull(name);
+
+        final List<DataSchemaNode> instantiatedDataNodeContainers = new ArrayList<>();
+        collectInstanceDataNodeContainers(instantiatedDataNodeContainers, container, name);
+        return instantiatedDataNodeContainers;
+    }
+
+    private static void collectInstanceDataNodeContainers(final List<DataSchemaNode> potentialSchemaNodes,
+            final DataNodeContainer container, final String name) {
+
+        final Iterable<DataSchemaNode> nodes =
+                Iterables.filter(container.getChildNodes(), node -> name.equals(node.getQName().getLocalName()));
+
+        // Can't combine this loop with the filter above because the filter is
+        // lazily-applied by Iterables.filter.
+        for (final DataSchemaNode potentialNode : nodes) {
+            if (isInstantiatedDataSchema(potentialNode)) {
+                potentialSchemaNodes.add(potentialNode);
+            }
+        }
+
+        final Iterable<ChoiceSchemaNode> choiceNodes =
+                Iterables.filter(container.getChildNodes(), ChoiceSchemaNode.class);
+        final Iterable<Set<ChoiceCaseNode>> map = Iterables.transform(choiceNodes, ChoiceSchemaNode::getCases);
+        for (final ChoiceCaseNode caze : Iterables.concat(map)) {
+            collectInstanceDataNodeContainers(potentialSchemaNodes, caze, name);
+        }
+    }
+
+    private static boolean isInstantiatedDataSchema(final DataSchemaNode node) {
+        return (node instanceof LeafSchemaNode) || (node instanceof LeafListSchemaNode)
+                || (node instanceof ContainerSchemaNode) || (node instanceof ListSchemaNode)
+                || (node instanceof AnyXmlSchemaNode);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/codecs/StringModuleInstanceIdentifierCodec.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/codecs/StringModuleInstanceIdentifierCodec.java
new file mode 100644 (file)
index 0000000..1fc2d83
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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.nb.rfc8040.codecs;
+
+import com.google.common.base.Preconditions;
+import java.net.URI;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public final class StringModuleInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec {
+
+    private final DataSchemaContextTree dataContextTree;
+    private final SchemaContext context;
+    private final String defaultPrefix;
+
+    public StringModuleInstanceIdentifierCodec(SchemaContext context) {
+        this.context = Preconditions.checkNotNull(context);
+        this.dataContextTree = DataSchemaContextTree.from(context);
+        this.defaultPrefix = "";
+    }
+
+    StringModuleInstanceIdentifierCodec(final SchemaContext context, @Nonnull final String defaultPrefix) {
+        this.context = Preconditions.checkNotNull(context);
+        this.dataContextTree = DataSchemaContextTree.from(context);
+        this.defaultPrefix = defaultPrefix;
+    }
+
+    @Override
+    protected Module moduleForPrefix(@Nonnull final String prefix) {
+        if (prefix.isEmpty() && !this.defaultPrefix.isEmpty()) {
+            return this.context.findModuleByName(this.defaultPrefix, null);
+        } else {
+            return this.context.findModuleByName(prefix, null);
+        }
+    }
+
+    @Nonnull
+    @Override
+    protected DataSchemaContextTree getDataContextTree() {
+        return this.dataContextTree;
+    }
+
+    @Nullable
+    @Override
+    protected String prefixForNamespace(@Nonnull final URI namespace) {
+        final Module module = this.context.findModuleByNamespaceAndRevision(namespace, null);
+        return module == null ? null : module.getName();
+    }
+}
\ No newline at end of file
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/DOMDataBrokerHandler.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/DOMDataBrokerHandler.java
new file mode 100644 (file)
index 0000000..99a9ba9
--- /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.restconf.nb.rfc8040.handlers;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+
+/**
+ * Implementation of {@link DOMDataBrokerHandler}.
+ *
+ */
+public class DOMDataBrokerHandler implements Handler<DOMDataBroker> {
+
+    private final DOMDataBroker broker;
+
+    public DOMDataBrokerHandler(final DOMDataBroker broker) {
+        this.broker = broker;
+    }
+
+    @Override
+    public DOMDataBroker get() {
+        return this.broker;
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/DOMMountPointServiceHandler.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/DOMMountPointServiceHandler.java
new file mode 100644 (file)
index 0000000..5aa930c
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.nb.rfc8040.handlers;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+
+/**
+ * Implementation of {@link DOMMountPointServiceHandler}.
+ *
+ */
+public class DOMMountPointServiceHandler implements Handler<DOMMountPointService> {
+
+    private final DOMMountPointService domMountPointService;
+
+    /**
+     * Prepare mount point service for Restconf services.
+     *
+     * @param domMountPointService
+     *             mount point service
+     */
+    public DOMMountPointServiceHandler(final DOMMountPointService domMountPointService) {
+        Preconditions.checkNotNull(domMountPointService);
+        this.domMountPointService = domMountPointService;
+    }
+
+    @Override
+    public DOMMountPointService get() {
+        return this.domMountPointService;
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/Handler.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/Handler.java
new file mode 100644 (file)
index 0000000..31bfaeb
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.nb.rfc8040.handlers;
+
+/**
+ * Handler for handling object prepared by provider for Restconf services.
+ *
+ * @param <T>
+ *             specific type go object for handling it
+ */
+interface Handler<T> {
+
+    /**
+     * Get prepared object.
+     *
+     * @return T
+     */
+    T get();
+
+    /**
+     * Update object.
+     *
+     * @param object
+     *             new object to update old object
+     */
+    default void update(T object) {}
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/NotificationServiceHandler.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/NotificationServiceHandler.java
new file mode 100644 (file)
index 0000000..30e5ced
--- /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.nb.rfc8040.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;
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/RpcServiceHandler.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/RpcServiceHandler.java
new file mode 100644 (file)
index 0000000..b9aa1e2
--- /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.restconf.nb.rfc8040.handlers;
+
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+
+/**
+ * Implementation of {@link RpcServiceHandler}.
+ *
+ */
+public class RpcServiceHandler implements Handler<DOMRpcService> {
+
+    private final DOMRpcService rpcService;
+
+    public RpcServiceHandler(final DOMRpcService rpcService) {
+        this.rpcService = rpcService;
+    }
+
+    @Override
+    public DOMRpcService get() {
+        return this.rpcService;
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/SchemaContextHandler.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/SchemaContextHandler.java
new file mode 100644 (file)
index 0000000..0db960f
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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.nb.rfc8040.handlers;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+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.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.nb.rfc8040.RestConnectorProvider;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040.IetfYangLibrary;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040.MonitoringModule;
+import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil;
+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.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of {@link SchemaContextHandler}.
+ *
+ */
+public class SchemaContextHandler implements SchemaContextListenerHandler {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SchemaContextHandler.class);
+
+    private final TransactionChainHandler transactionChainHandler;
+    private SchemaContext context;
+
+    private int moduleSetId;
+
+    /**
+     * Set module-set-id on initial value - 0.
+     *
+     * @param transactionChainHandler Transaction chain handler
+     */
+    public SchemaContextHandler(final TransactionChainHandler transactionChainHandler) {
+        this.transactionChainHandler = transactionChainHandler;
+        this.moduleSetId = 0;
+    }
+
+    @Override
+    public void onGlobalContextUpdated(final SchemaContext context) {
+        Preconditions.checkNotNull(context);
+        this.context = null;
+        this.context = context;
+        this.moduleSetId++;
+        final Module ietfYangLibraryModule =
+                context.findModuleByNamespaceAndRevision(IetfYangLibrary.URI_MODULE, IetfYangLibrary.DATE);
+        NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> normNode =
+                RestconfMappingNodeUtil.mapModulesByIetfYangLibraryYang(context.getModules(), ietfYangLibraryModule,
+                        context, String.valueOf(this.moduleSetId));
+        putData(normNode);
+
+        final Module monitoringModule =
+                this.context.findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE);
+        normNode = RestconfMappingNodeUtil.mapCapabilites(monitoringModule);
+        putData(normNode);
+    }
+
+    @Override
+    public SchemaContext get() {
+        return this.context;
+    }
+
+    private void putData(
+            final NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> normNode) {
+        final DOMDataWriteTransaction wTx = this.transactionChainHandler.get().newWriteOnlyTransaction();
+        wTx.put(LogicalDatastoreType.OPERATIONAL,
+                YangInstanceIdentifier.create(NodeIdentifier.create(normNode.getNodeType())), normNode);
+        try {
+            wTx.submit().checkedGet();
+        } catch (final TransactionCommitFailedException e) {
+            if (!(e.getCause() instanceof ConflictingModificationAppliedException)) {
+                throw new RestconfDocumentedException("Problem occurred while putting data to DS.", e);
+            }
+
+            /*
+              Ignore error when another cluster node is already putting the same data to DS.
+              We expect that cluster is homogeneous and that node was going to write the same data
+              (that means no retry is needed). Transaction chain reset must be invoked to be able
+              to continue writing data with another transaction after failed transaction.
+              This is workaround for bug:
+              https://bugs.opendaylight.org/show_bug.cgi?id=7728
+            */
+            LOG.warn("Ignoring that another cluster node is already putting the same data to DS.", e);
+            RestConnectorProvider.resetTransactionChainForAdapaters(this.transactionChainHandler.get());
+        }
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/SchemaContextListenerHandler.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/SchemaContextListenerHandler.java
new file mode 100644 (file)
index 0000000..4af1ba8
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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.nb.rfc8040.handlers;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+
+interface SchemaContextListenerHandler extends Handler<SchemaContext>, SchemaContextListener {
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/TransactionChainHandler.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/handlers/TransactionChainHandler.java
new file mode 100644 (file)
index 0000000..c949b15
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.nb.rfc8040.handlers;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+
+
+/**
+ * Implementation of {@link TransactionChainHandler}.
+ *
+ */
+public class TransactionChainHandler implements Handler<DOMTransactionChain> {
+
+    private DOMTransactionChain transactionChain;
+
+    /**
+     * Prepare transaction chain service for Restconf services.
+     *
+     * @param transactionChain Transaction chain
+     */
+    public TransactionChainHandler(final DOMTransactionChain transactionChain) {
+        Preconditions.checkNotNull(transactionChain);
+        this.transactionChain = transactionChain;
+    }
+
+    @Override
+    public void update(final DOMTransactionChain transactionChain) {
+        Preconditions.checkNotNull(transactionChain);
+        this.transactionChain = transactionChain;
+    }
+
+    @Override
+    public DOMTransactionChain get() {
+        return this.transactionChain;
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/references/SchemaContextRef.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/references/SchemaContextRef.java
new file mode 100644 (file)
index 0000000..64623c6
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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.nb.rfc8040.references;
+
+import java.lang.ref.SoftReference;
+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.nb.rfc8040.Rfc8040;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * This class creates {@link SoftReference} of actual {@link SchemaContext}
+ * object and even if the {@link SchemaContext} changes, this will be sticks
+ * reference to the old {@link SchemaContext} and provides work with the old
+ * {@link SchemaContext}.
+ *
+ */
+public final class SchemaContextRef {
+
+    private final SoftReference<SchemaContext> schemaContextRef;
+
+    /**
+     * Create {@link SoftReference} of actual {@link SchemaContext}.
+     *
+     * @param schemaContext
+     *             actual {@link SchemaContext}
+     */
+    public SchemaContextRef(final SchemaContext schemaContext) {
+        this.schemaContextRef = new SoftReference<SchemaContext>(schemaContext);
+    }
+
+    /**
+     * Get {@link SchemaContext} from reference.
+     *
+     * @return {@link SchemaContext}
+     */
+    public SchemaContext get() {
+        return this.schemaContextRef.get();
+    }
+
+    /**
+     * Get all modules like {@link Set} of {@link Module} from
+     * {@link SchemaContext}.
+     *
+     * @return {@link Set} of {@link Module}
+     */
+    public Set<Module> getModules() {
+        return get().getModules();
+    }
+
+    /**
+     * Get all modules like {@link Set} of {@link Module} from
+     * {@link SchemaContext} of {@link DOMMountPoint}.
+     *
+     * @param mountPoint
+     *             mount point
+     *
+     * @return {@link Set} of {@link Module}
+     */
+    public Set<Module> getModules(final DOMMountPoint mountPoint) {
+        final SchemaContext schemaContext = mountPoint == null ? null : mountPoint.getSchemaContext();
+        return schemaContext == null ? null : schemaContext.getModules();
+    }
+
+    /**
+     * Get {@link Module} by ietf-restconf qname from
+     * {@link Rfc8040.RestconfModule}.
+     *
+     * @return {@link Module}
+     */
+    public Module getRestconfModule() {
+        return this.findModuleByNamespaceAndRevision(Rfc8040.RestconfModule.IETF_RESTCONF_QNAME.getNamespace(),
+                Rfc8040.RestconfModule.IETF_RESTCONF_QNAME.getRevision());
+    }
+
+    /**
+     * Find {@link Module} in {@link SchemaContext} by {@link URI} and
+     * {@link Date}.
+     *
+     * @param namespace
+     *             namespace of module
+     * @param revision
+     *             revision of module
+     * @return {@link Module}
+     */
+    public Module findModuleByNamespaceAndRevision(final URI namespace, final Date revision) {
+        return this.get().findModuleByNamespaceAndRevision(namespace, revision);
+    }
+
+
+    /**
+     * Find {@link Module} in {@link SchemaContext} of {@link DOMMountPoint} by
+     * {@link QName} of {@link Module}.
+     *
+     * @param mountPoint
+     *             mount point
+     * @param moduleQname
+     *             {@link QName} of module
+     * @return {@link Module}
+     */
+    public Module findModuleInMountPointByQName(final DOMMountPoint mountPoint, final QName moduleQname) {
+        final SchemaContext schemaContext = mountPoint == null ? null : mountPoint.getSchemaContext();
+        return schemaContext == null ? null
+                : schemaContext.findModuleByName(moduleQname.getLocalName(), moduleQname.getRevision());
+    }
+
+    /**
+     * Find {@link Module} in {@link SchemaContext} by {@link QName}.
+     *
+     * @param moduleQname
+     *             {@link QName} of module
+     * @return {@link Module}
+     */
+    public Module findModuleByQName(final QName moduleQname) {
+        return this.findModuleByNameAndRevision(moduleQname.getLocalName(), moduleQname.getRevision());
+    }
+
+    /**
+     * Find {@link Module} in {@link SchemaContext} by {@link String} localName
+     * and {@link Date} revision.
+     *
+     * @param localName
+     *             local name of module
+     * @param revision
+     *             revision of module
+     * @return {@link Module}
+     */
+    public Module findModuleByNameAndRevision(final String localName, final Date revision) {
+        return this.get().findModuleByName(localName, revision);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/RestconfDataService.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/RestconfDataService.java
new file mode 100644 (file)
index 0000000..46314c5
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * 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.nb.rfc8040.rests.services.api;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.patch.Patch;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040;
+import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
+
+/**
+ * The "{+restconf}/data" subtree represents the datastore resource type, which
+ * is a collection of configuration data and state data nodes.
+ *
+ */
+public interface RestconfDataService {
+
+    /**
+     * Get target data resource.
+     *
+     * @param identifier
+     *            path to target
+     * @param uriInfo
+     *            URI info
+     * @return {@link NormalizedNodeContext}
+     */
+    @GET
+    @Path("/data/{identifier:.+}")
+    @Produces({ Rfc8040.MediaTypes.DATA + RestconfConstants.JSON, Rfc8040.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+            MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+    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({ Rfc8040.MediaTypes.DATA + RestconfConstants.JSON, Rfc8040.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+            MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+    Response readData(@Context UriInfo uriInfo);
+
+    /**
+     * Create or replace the target data resource.
+     *
+     * @param identifier
+     *            path to target
+     * @param payload
+     *            data node for put to config DS
+     * @return {@link Response}
+     */
+    @PUT
+    @Path("/data/{identifier:.+}")
+    @Consumes({ Rfc8040.MediaTypes.DATA + RestconfConstants.JSON, Rfc8040.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+            MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+    Response putData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload,
+            @Context UriInfo uriInfo);
+
+    /**
+     * Create a data resource in target.
+     *
+     * @param identifier
+     *            path to target
+     * @param payload
+     *            new data
+     * @param uriInfo
+     *            URI info
+     * @return {@link Response}
+     */
+    @POST
+    @Path("/data/{identifier:.+}")
+    @Consumes({ Rfc8040.MediaTypes.DATA + RestconfConstants.JSON, Rfc8040.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+            MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+    Response postData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload,
+            @Context UriInfo uriInfo);
+
+    /**
+     * Create a data resource.
+     *
+     * @param payload
+     *            new data
+     * @param uriInfo
+     *            URI info
+     * @return {@link Response}
+     */
+    @POST
+    @Path("/data")
+    @Consumes({ Rfc8040.MediaTypes.DATA + RestconfConstants.JSON, Rfc8040.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+            MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+    Response postData(NormalizedNodeContext payload, @Context UriInfo uriInfo);
+
+    /**
+     * Delete the target data resource.
+     *
+     * @param identifier
+     *            path to target
+     * @return {@link Response}
+     */
+    @DELETE
+    @Path("/data/{identifier:.+}")
+    Response deleteData(@Encoded @PathParam("identifier") String identifier);
+
+    /**
+     * Ordered list of edits that are applied to the target datastore by the
+     * server.
+     *
+     * @param identifier
+     *            path to target
+     * @param context
+     *            edits
+     * @param uriInfo
+     *            URI info
+     * @return {@link PatchStatusContext}
+     */
+    @Patch
+    @Path("/data/{identifier:.+}")
+    @Consumes({ Rfc8040.MediaTypes.PATCH + RestconfConstants.JSON, Rfc8040.MediaTypes.PATCH + RestconfConstants.XML })
+    @Produces({ Rfc8040.MediaTypes.PATCH_STATUS + RestconfConstants.JSON,
+            Rfc8040.MediaTypes.PATCH_STATUS + RestconfConstants.XML })
+    PatchStatusContext patchData(@Encoded @PathParam("identifier") String identifier, PatchContext context,
+                                 @Context UriInfo uriInfo);
+
+    /**
+     * Ordered list of edits that are applied to the datastore by the server.
+     *
+     * @param context
+     *            edits
+     * @param uriInfo
+     *            URI info
+     * @return {@link PatchStatusContext}
+     */
+    @Patch
+    @Path("/data")
+    @Consumes({ Rfc8040.MediaTypes.PATCH + RestconfConstants.JSON, Rfc8040.MediaTypes.PATCH + RestconfConstants.XML })
+    @Produces({ Rfc8040.MediaTypes.PATCH_STATUS + RestconfConstants.JSON,
+            Rfc8040.MediaTypes.PATCH_STATUS + RestconfConstants.XML })
+    PatchStatusContext patchData(PatchContext context, @Context UriInfo uriInfo);
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/RestconfInvokeOperationsService.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/RestconfInvokeOperationsService.java
new file mode 100644 (file)
index 0000000..1de502b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.nb.rfc8040.rests.services.api;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040;
+import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
+
+/**
+ * An operation resource represents a protocol operation defined with the YANG
+ * "rpc" statement. It is invoked using a POST method on the operation resource.
+ *
+ */
+public interface RestconfInvokeOperationsService {
+
+    /**
+     * Invoke RPC operation.
+     *
+     * @param identifier
+     *             module name and rpc identifier string for the desired
+     *            operation
+     * @param payload
+     *             {@link NormalizedNodeContext} - the body of the operation
+     * @param uriInfo
+     *             URI info
+     * @return {@link NormalizedNodeContext}
+     */
+    @POST
+    @Path("/operations/{identifier:.+}")
+    @Produces({ Rfc8040.MediaTypes.DATA + RestconfConstants.JSON, Rfc8040.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+            MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+    @Consumes({ Rfc8040.MediaTypes.DATA + RestconfConstants.JSON, Rfc8040.MediaTypes.DATA, MediaType.APPLICATION_JSON,
+            MediaType.APPLICATION_XML, MediaType.TEXT_XML })
+    NormalizedNodeContext invokeRpc(@Encoded @PathParam("identifier") String identifier,
+            NormalizedNodeContext payload, @Context UriInfo uriInfo);
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/RestconfStreamsSubscriptionService.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/RestconfStreamsSubscriptionService.java
new file mode 100644 (file)
index 0000000..d131c2c
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.nb.rfc8040.rests.services.api;
+
+import javax.ws.rs.Encoded;
+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.UriInfo;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+
+/**
+ * Subscribing to streams.
+ *
+ */
+public interface RestconfStreamsSubscriptionService {
+
+    /**
+     * Subscribing to receive notification from stream support.
+     *
+     * @param identifier
+     *             name of stream
+     * @param uriInfo
+     *             URI info
+     * @return {@link NormalizedNodeContext}
+     */
+    @GET
+    @Path("data/ietf-restconf-monitoring:restconf-state/streams/stream/{identifier:.+}")
+    NormalizedNodeContext subscribeToStream(@Encoded @PathParam("identifier") String identifier,
+            @Context UriInfo uriInfo);
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/TransactionServicesWrapper.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/api/TransactionServicesWrapper.java
new file mode 100644 (file)
index 0000000..1855db9
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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.nb.rfc8040.rests.services.api;
+
+/**
+ * Wrapper for all transaction services.
+ * <ul>
+ * <li>{@link RestconfDataService}
+ * <li>{@link RestconfInvokeOperationsService}
+ * <li>{@link RestconfStreamsSubscriptionService}
+ * </ul>
+ *
+ */
+public interface TransactionServicesWrapper
+        extends RestconfDataService, RestconfInvokeOperationsService, RestconfStreamsSubscriptionService {
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfDataServiceImpl.java
new file mode 100644 (file)
index 0000000..f2c47d2
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * 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.nb.rfc8040.rests.services.impl;
+
+import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.CREATE_NOTIFICATION_STREAM;
+import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_ACCESS_PATH_PART;
+import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_LOCATION_PATH_PART;
+import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Map.Entry;
+import javax.annotation.Nonnull;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.context.WriterParameters;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.nb.rfc8040.RestConnectorProvider;
+import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
+import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfDataService;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.DeleteDataTransactionUtil;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.PatchDataTransactionUtil;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.PostDataTransactionUtil;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.PutDataTransactionUtil;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.ReadDataTransactionUtil;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant;
+import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of {@link RestconfDataService}.
+ */
+public class RestconfDataServiceImpl implements RestconfDataService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class);
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss");
+
+    private final SchemaContextHandler schemaContextHandler;
+    private final TransactionChainHandler transactionChainHandler;
+    private final DOMMountPointServiceHandler mountPointServiceHandler;
+
+    private final RestconfStreamsSubscriptionService delegRestconfSubscrService;
+
+    public RestconfDataServiceImpl(final SchemaContextHandler schemaContextHandler,
+                                   final TransactionChainHandler transactionChainHandler,
+            final DOMMountPointServiceHandler mountPointServiceHandler,
+            final RestconfStreamsSubscriptionService delegRestconfSubscrService) {
+        this.schemaContextHandler = schemaContextHandler;
+        this.transactionChainHandler = transactionChainHandler;
+        this.mountPointServiceHandler = mountPointServiceHandler;
+        this.delegRestconfSubscrService = delegRestconfSubscrService;
+    }
+
+    @Override
+    public Response readData(final UriInfo uriInfo) {
+        return readData(null, uriInfo);
+    }
+
+    @Override
+    public Response readData(final String identifier, final UriInfo uriInfo) {
+        final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
+        final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
+                identifier, schemaContextRef.get(), Optional.of(this.mountPointServiceHandler.get()));
+
+        boolean withDefaUsed = false;
+        String withDefa = null;
+
+        for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            switch (entry.getKey()) {
+                case "with-defaults":
+                    if (!withDefaUsed) {
+                        withDefaUsed = true;
+                        withDefa = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("With-defaults parameter can be used only once.");
+                    }
+                    break;
+                default:
+                    LOG.info("Unknown key : {}.", entry.getKey());
+                    break;
+            }
+        }
+        boolean tagged = false;
+        if (withDefaUsed) {
+            if ("report-all-tagged".equals(withDefa)) {
+                tagged = true;
+                withDefa = null;
+            }
+            if ("report-all".equals(withDefa)) {
+                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();
+        } else {
+            transactionChain = transactionChainOfMountPoint(mountPoint);
+        }
+
+        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
+                instanceIdentifier, mountPoint, transactionChain);
+        final NormalizedNode<?, ?> node =
+                ReadDataTransactionUtil.readData(identifier, parameters.getContent(), transactionNode, withDefa,
+                        schemaContextRef, uriInfo);
+        if (identifier.contains(STREAM_PATH) && identifier.contains(STREAM_ACCESS_PATH_PART)
+                && identifier.contains(STREAM_LOCATION_PATH_PART)) {
+            final String value = (String) node.getValue();
+            final String streamName = value.substring(
+                    value.indexOf(CREATE_NOTIFICATION_STREAM.toString() + RestconfConstants.SLASH),
+                    value.length());
+            this.delegRestconfSubscrService.subscribeToStream(streamName, uriInfo);
+        }
+        if (node == null) {
+            throw new RestconfDocumentedException(
+                    "Request could not be completed because the relevant data model content does not exist",
+                    RestconfError.ErrorType.PROTOCOL,
+                    RestconfError.ErrorTag.DATA_MISSING);
+        }
+
+        if ((parameters.getContent().equals(RestconfDataServiceConstant.ReadData.ALL))
+                    || parameters.getContent().equals(RestconfDataServiceConstant.ReadData.CONFIG)) {
+            return Response.status(200)
+                    .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters))
+                    .header("ETag", '"' + node.getNodeType().getModule().getFormattedRevision()
+                        + node.getNodeType().getLocalName() + '"')
+                    .header("Last-Modified", FORMATTER.format(LocalDateTime.now(Clock.systemUTC())))
+                    .build();
+        }
+
+        return Response.status(200).entity(new NormalizedNodeContext(instanceIdentifier, node, parameters)).build();
+    }
+
+    @Override
+    public Response putData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+        Preconditions.checkNotNull(payload);
+
+        boolean insertUsed = false;
+        boolean pointUsed = false;
+        String insert = null;
+        String point = null;
+
+        for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            switch (entry.getKey()) {
+                case "insert":
+                    if (!insertUsed) {
+                        insertUsed = true;
+                        insert = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("Insert parameter can be used only once.");
+                    }
+                    break;
+                case "point":
+                    if (!pointUsed) {
+                        pointUsed = 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(insertUsed, pointUsed, insert);
+
+        final InstanceIdentifierContext<? extends SchemaNode> iid = payload
+                .getInstanceIdentifierContext();
+
+        PutDataTransactionUtil.validInputData(iid.getSchemaNode(), payload);
+        PutDataTransactionUtil.validTopLevelNodeName(iid.getInstanceIdentifier(), payload);
+        PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
+
+        final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
+        final DOMTransactionChain transactionChain;
+        final SchemaContextRef ref;
+        if (mountPoint == null) {
+            transactionChain = this.transactionChainHandler.get();
+            ref = new SchemaContextRef(this.schemaContextHandler.get());
+        } else {
+            transactionChain = transactionChainOfMountPoint(mountPoint);
+            ref = new SchemaContextRef(mountPoint.getSchemaContext());
+        }
+
+        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
+                payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
+        return PutDataTransactionUtil.putData(payload, ref, transactionNode, insert, point);
+    }
+
+    private static void checkQueryParams(final boolean insertUsed, final boolean pointUsed, final String insert) {
+        if (pointUsed && !insertUsed) {
+            throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.");
+        }
+        if (pointUsed && (insert.equals("first") || insert.equals("last"))) {
+            throw new RestconfDocumentedException(
+                    "Point parameter can be used only with 'after' or 'before' values of Insert parameter.");
+        }
+    }
+
+    @Override
+    public Response postData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+        return postData(payload, uriInfo);
+    }
+
+    @Override
+    public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
+        Preconditions.checkNotNull(payload);
+
+        boolean insertUsed = false;
+        boolean pointUsed = false;
+        String insert = null;
+        String point = null;
+
+        for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+            switch (entry.getKey()) {
+                case "insert":
+                    if (!insertUsed) {
+                        insertUsed = true;
+                        insert = entry.getValue().iterator().next();
+                    } else {
+                        throw new RestconfDocumentedException("Insert parameter can be used only once.");
+                    }
+                    break;
+                case "point":
+                    if (!pointUsed) {
+                        pointUsed = 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(insertUsed, pointUsed, insert);
+
+        final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
+        final DOMTransactionChain transactionChain;
+        final SchemaContextRef ref;
+        if (mountPoint == null) {
+            transactionChain = this.transactionChainHandler.get();
+            ref = new SchemaContextRef(this.schemaContextHandler.get());
+        } else {
+            transactionChain = transactionChainOfMountPoint(mountPoint);
+            ref = new SchemaContextRef(mountPoint.getSchemaContext());
+        }
+        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
+                payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
+        return PostDataTransactionUtil.postData(uriInfo, payload, transactionNode, ref, insert, point);
+    }
+
+    @Override
+    public Response deleteData(final String 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 DOMTransactionChain transactionChain;
+        if (mountPoint == null) {
+            transactionChain = this.transactionChainHandler.get();
+        } else {
+            transactionChain = transactionChainOfMountPoint(mountPoint);
+        }
+
+        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(instanceIdentifier, mountPoint,
+                transactionChain);
+        return DeleteDataTransactionUtil.deleteData(transactionNode);
+    }
+
+    @Override
+    public PatchStatusContext patchData(final String identifier, final PatchContext context, final UriInfo uriInfo) {
+        return patchData(context, uriInfo);
+    }
+
+    @Override
+    public PatchStatusContext patchData(final PatchContext context, final UriInfo uriInfo) {
+        Preconditions.checkNotNull(context);
+        final DOMMountPoint mountPoint = context.getInstanceIdentifierContext().getMountPoint();
+
+        final DOMTransactionChain transactionChain;
+        final SchemaContextRef ref;
+        if (mountPoint == null) {
+            transactionChain = this.transactionChainHandler.get();
+            ref = new SchemaContextRef(this.schemaContextHandler.get());
+        } else {
+            transactionChain = transactionChainOfMountPoint(mountPoint);
+            ref = new SchemaContextRef(mountPoint.getSchemaContext());
+        }
+
+        final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
+                context.getInstanceIdentifierContext(), mountPoint, transactionChain);
+
+        return PatchDataTransactionUtil.patchData(context, transactionNode, ref);
+    }
+
+    /**
+     * Prepare transaction chain to access data of mount point.
+     * @param mountPoint
+     *            mount point reference
+     * @return {@link DOMTransactionChain}
+     */
+    private static DOMTransactionChain transactionChainOfMountPoint(@Nonnull final DOMMountPoint mountPoint) {
+        final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+        if (domDataBrokerService.isPresent()) {
+            return domDataBrokerService.get().createTransactionChain(RestConnectorProvider.TRANSACTION_CHAIN_LISTENER);
+        }
+
+        final String errMsg = "DOM data broker service isn't available for mount point " + mountPoint.getIdentifier();
+        LOG.warn(errMsg);
+        throw new RestconfDocumentedException(errMsg);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfInvokeOperationsServiceImpl.java
new file mode 100644 (file)
index 0000000..71c6c6c
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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.nb.rfc8040.rests.services.impl;
+
+import java.net.URI;
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.nb.rfc8040.handlers.RpcServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfInvokeOperationsService;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.CreateStreamUtil;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfInvokeOperationsUtil;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants;
+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;
+
+/**
+ * Implementation of {@link RestconfInvokeOperationsService}.
+ *
+ */
+public class RestconfInvokeOperationsServiceImpl implements RestconfInvokeOperationsService {
+
+    private final RpcServiceHandler rpcServiceHandler;
+    private final SchemaContextHandler schemaContextHandler;
+
+    public RestconfInvokeOperationsServiceImpl(final RpcServiceHandler rpcServiceHandler,
+            final SchemaContextHandler schemaContextHandler) {
+        this.rpcServiceHandler = rpcServiceHandler;
+        this.schemaContextHandler = schemaContextHandler;
+    }
+
+    @Override
+    public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
+                                           final UriInfo uriInfo) {
+        final SchemaContextRef refSchemaCtx = new SchemaContextRef(this.schemaContextHandler.get());
+        final SchemaPath schemaPath = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
+        final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
+        final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace();
+        DOMRpcResult response;
+
+        SchemaContextRef schemaContextRef;
+
+        if (mountPoint == null) {
+            if (namespace.toString().equals(RestconfStreamsConstants.SAL_REMOTE_NAMESPACE)) {
+                if (identifier.contains(RestconfStreamsConstants.CREATE_DATA_SUBSCR)) {
+                    response = CreateStreamUtil.createDataChangeNotifiStream(payload, refSchemaCtx);
+                } else {
+                    throw new RestconfDocumentedException("Not supported operation", ErrorType.RPC,
+                            ErrorTag.OPERATION_NOT_SUPPORTED);
+                }
+            } else {
+                response = RestconfInvokeOperationsUtil.invokeRpc(payload.getData(), schemaPath,
+                        this.rpcServiceHandler);
+            }
+            schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
+        } else {
+            response = RestconfInvokeOperationsUtil.invokeRpcViaMountPoint(mountPoint, payload.getData(), schemaPath);
+            schemaContextRef = new SchemaContextRef(mountPoint.getSchemaContext());
+        }
+
+        final DOMRpcResult result = RestconfInvokeOperationsUtil.checkResponse(response);
+
+        RpcDefinition resultNodeSchema = null;
+        final NormalizedNode<?, ?> resultData = result.getResult();
+        if ((result != null) && (result.getResult() != null)) {
+            resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode();
+        }
+        return new NormalizedNodeContext(new InstanceIdentifierContext<RpcDefinition>(null, resultNodeSchema,
+                mountPoint, schemaContextRef.get()), resultData);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfStreamsSubscriptionServiceImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/services/impl/RestconfStreamsSubscriptionServiceImpl.java
new file mode 100644 (file)
index 0000000..547c318
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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.nb.rfc8040.rests.services.impl;
+
+import java.net.URI;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import javax.annotation.Nonnull;
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants;
+import org.opendaylight.restconf.nb.rfc8040.rests.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.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of {@link RestconfStreamsSubscriptionService}.
+ *
+ */
+public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSubscriptionService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestconfStreamsSubscriptionServiceImpl.class);
+
+    private final HandlersHolder handlersHolder;
+
+    /**
+     * Initialize holder of handlers with holders as parameters.
+     *
+     * @param domDataBrokerHandler
+     *             handler of {@link DOMDataBroker}
+     * @param notificationServiceHandler
+     *             handler of {@link DOMNotificationService}
+     * @param schemaHandler
+     *             handler of {@link SchemaContext}
+     * @param transactionChainHandler
+     *             handler of {@link DOMTransactionChain}
+     */
+    public RestconfStreamsSubscriptionServiceImpl(final DOMDataBrokerHandler domDataBrokerHandler,
+            final NotificationServiceHandler notificationServiceHandler, final SchemaContextHandler schemaHandler,
+            final TransactionChainHandler transactionChainHandler) {
+        this.handlersHolder = new HandlersHolder(domDataBrokerHandler, notificationServiceHandler,
+                transactionChainHandler, schemaHandler);
+    }
+
+    @Override
+    public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
+        final NotificationQueryParams notificationQueryParams = NotificationQueryParams.fromUriInfo(uriInfo);
+
+        URI response = null;
+        if (identifier.contains(RestconfStreamsConstants.DATA_SUBSCR)) {
+            response = SubscribeToStreamUtil.notifiDataStream(identifier, uriInfo, notificationQueryParams,
+                    this.handlersHolder);
+        } else if (identifier.contains(RestconfStreamsConstants.NOTIFICATION_STREAM)) {
+            response = SubscribeToStreamUtil.notifYangStream(identifier, uriInfo, notificationQueryParams,
+                    this.handlersHolder);
+        }
+
+        if (response != null) {
+            // prepare node with value of location
+            final InstanceIdentifierContext<?> iid =
+                    SubscribeToStreamUtil.prepareIIDSubsStreamOutput(this.handlersHolder.getSchemaHandler());
+            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);
+    }
+
+    /**
+     * Holder of all handlers for notifications.
+     */
+    public final class HandlersHolder {
+
+        private final DOMDataBrokerHandler domDataBrokerHandler;
+        private final NotificationServiceHandler notificationServiceHandler;
+        private final TransactionChainHandler transactionChainHandler;
+        private final SchemaContextHandler schemaHandler;
+
+        private HandlersHolder(final DOMDataBrokerHandler domDataBrokerHandler,
+                final NotificationServiceHandler notificationServiceHandler,
+                final TransactionChainHandler transactionChainHandler, final SchemaContextHandler schemaHandler) {
+            this.domDataBrokerHandler = domDataBrokerHandler;
+            this.notificationServiceHandler = notificationServiceHandler;
+            this.transactionChainHandler = transactionChainHandler;
+            this.schemaHandler = schemaHandler;
+        }
+
+        /**
+         * Get {@link DOMDataBrokerHandler}.
+         *
+         * @return the domDataBrokerHandler
+         */
+        public DOMDataBrokerHandler getDomDataBrokerHandler() {
+            return this.domDataBrokerHandler;
+        }
+
+        /**
+         * Get {@link NotificationServiceHandler}.
+         *
+         * @return the notificationServiceHandler
+         */
+        public NotificationServiceHandler getNotificationServiceHandler() {
+            return this.notificationServiceHandler;
+        }
+
+        /**
+         * Get {@link TransactionChainHandler}.
+         *
+         * @return the transactionChainHandler
+         */
+        public TransactionChainHandler getTransactionChainHandler() {
+            return this.transactionChainHandler;
+        }
+
+        /**
+         * Get {@link SchemaContextHandler}.
+         *
+         * @return the schemaHandler
+         */
+        public SchemaContextHandler getSchemaHandler() {
+            return this.schemaHandler;
+        }
+    }
+
+    /**
+     * Parser and holder of query paramteres from uriInfo for notifications.
+     *
+     */
+    public static final class NotificationQueryParams {
+
+        private final Instant start;
+        private final Instant stop;
+        private final String filter;
+
+        private NotificationQueryParams(final Instant start, final Instant stop, final String filter) {
+            this.start = start == null ? Instant.now() : start;
+            this.stop = stop;
+            this.filter = filter;
+        }
+
+        static NotificationQueryParams fromUriInfo(final UriInfo uriInfo) {
+            Instant start = null;
+            boolean startTimeUsed = false;
+            Instant stop = null;
+            boolean stopTimeUsed = false;
+            String filter = null;
+            boolean filterUsed = false;
+
+            for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
+                switch (entry.getKey()) {
+                    case "start-time":
+                        if (!startTimeUsed) {
+                            startTimeUsed = true;
+                            start = SubscribeToStreamUtil.parseDateFromQueryParam(entry);
+                        } else {
+                            throw new RestconfDocumentedException("Start-time parameter can be used only once.");
+                        }
+                        break;
+                    case "stop-time":
+                        if (!stopTimeUsed) {
+                            stopTimeUsed = true;
+                            stop = SubscribeToStreamUtil.parseDateFromQueryParam(entry);
+                        } else {
+                            throw new RestconfDocumentedException("Stop-time parameter can be used only once.");
+                        }
+                        break;
+                    case "filter":
+                        if (!filterUsed) {
+                            filterUsed = true;
+                            filter = entry.getValue().iterator().next();
+                        }
+                        break;
+                    default:
+                        throw new RestconfDocumentedException(
+                                "Bad parameter used with notifications: " + entry.getKey());
+                }
+            }
+            if (!startTimeUsed && stopTimeUsed) {
+                throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter.");
+            }
+
+            return new NotificationQueryParams(start, stop, filter);
+        }
+
+        /**
+         * Get start-time query parameter.
+         *
+         * @return start-time
+         */
+        @Nonnull
+        public Instant getStart() {
+            return start;
+        }
+
+        /**
+         * Get stop-time query parameter.
+         *
+         * @return stop-time
+         */
+        public Optional<Instant> getStop() {
+            return Optional.ofNullable(stop);
+        }
+
+        /**
+         * Get filter query parameter.
+         *
+         * @return filter
+         */
+        public Optional<String> getFilter() {
+            return Optional.ofNullable(filter);
+        }
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/TransactionVarsWrapper.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/transactions/TransactionVarsWrapper.java
new file mode 100644 (file)
index 0000000..fb7ef5c
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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.nb.rfc8040.rests.transactions;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+
+/**
+ * This class represent delegation wrapper for transaction variables.
+ *
+ */
+public final class TransactionVarsWrapper {
+
+    private final InstanceIdentifierContext<?> instanceIdentifier;
+    private final DOMMountPoint mountPoint;
+    private LogicalDatastoreType configuration = null;
+    private final DOMTransactionChain transactionChain;
+
+    /**
+     * Set base type of variables, which ones we need for transaction.
+     * {@link LogicalDatastoreType} is default set to null (to read all data
+     * from DS - config + state).
+     *
+     * @param instanceIdentifier
+     *             {@link InstanceIdentifierContext} of data for transaction
+     * @param mountPoint
+     *             mount point if is present
+     * @param transactionChain
+     *             transaction chain for creating specific type of transaction
+     *            in specific operation
+     */
+    public TransactionVarsWrapper(final InstanceIdentifierContext<?> instanceIdentifier, final DOMMountPoint mountPoint,
+            final DOMTransactionChain transactionChain) {
+        this.instanceIdentifier = instanceIdentifier;
+        this.mountPoint = mountPoint;
+        this.transactionChain = transactionChain;
+    }
+
+    /**
+     * Get instance identifier of data.
+     *
+     * @return {@link InstanceIdentifierContext}
+     */
+    public InstanceIdentifierContext<?> getInstanceIdentifier() {
+        return this.instanceIdentifier;
+    }
+
+    /**
+     * Get mount point.
+     *
+     * @return {@link DOMMountPoint}
+     */
+    public DOMMountPoint getMountPoint() {
+        return this.mountPoint;
+    }
+
+    /**
+     * Set {@link LogicalDatastoreType} of data for transaction.
+     *
+     * @param configuration
+     *             {@link LogicalDatastoreType}
+     */
+    public void setLogicalDatastoreType(final LogicalDatastoreType configuration) {
+        this.configuration = configuration;
+
+    }
+
+    /**
+     * Get type of data.
+     *
+     * @return {@link LogicalDatastoreType}
+     */
+    public LogicalDatastoreType getLogicalDatastoreType() {
+        return this.configuration;
+    }
+
+    /**
+     * Get transaction chain for creating specific transaction for specific
+     * operation.
+     *
+     * @return transaction chain
+     */
+    public DOMTransactionChain getTransactionChain() {
+        return this.transactionChain;
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/CreateStreamUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/CreateStreamUtil.java
new file mode 100644 (file)
index 0000000..3459036
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * 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.nb.rfc8040.rests.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.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;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
+import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
+import org.opendaylight.restconf.nb.rfc8040.streams.listeners.Notificator;
+import org.opendaylight.restconf.nb.rfc8040.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;
+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.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;
+
+/**
+ * Util class for streams.
+ *
+ * <ul>
+ * <li>create stream
+ * <li>subscribe
+ * </ul>
+ *
+ */
+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");
+    }
+
+    /**
+     * Create stream with POST operation via RPC.
+     *
+     * @param payload
+     *             input of rpc - example in JSON:
+     *
+     *            <pre>
+     *            {@code
+     *            {
+     *                "input": {
+     *                    "path": "/toaster:toaster/toaster:toasterStatus",
+     *                    "sal-remote-augment:datastore": "OPERATIONAL",
+     *                    "sal-remote-augment:scope": "ONE"
+     *                }
+     *            }
+     *            }
+     *            </pre>
+     *
+     * @param refSchemaCtx
+     *             reference to {@link SchemaContext} -
+     *            {@link SchemaContextRef}
+     * @return {@link CheckedFuture} with {@link DOMRpcResult} - This mean
+     *         output of RPC - example in JSON:
+     *
+     *         <pre>
+     *         {@code
+     *         {
+     *             "output": {
+     *                 "stream-name": "toaster:toaster/toaster:toasterStatus/datastore=OPERATIONAL/scope=ONE"
+     *             }
+     *         }
+     *         }
+     *         </pre>
+     *
+     */
+    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);
+        String streamName = prepareDataChangeNotifiStreamName(path, refSchemaCtx.get(), data);
+
+        final QName outputQname = QName.create(qname, "output");
+        final QName streamNameQname = QName.create(qname, "stream-name");
+
+        final NotificationOutputType outputType = prepareOutputType(data);
+        if (outputType.equals(NotificationOutputType.JSON)) {
+            streamName = streamName + "/JSON";
+        }
+
+        if (!Notificator.existListenerFor(streamName)) {
+            Notificator.createListener(path, streamName, outputType);
+        }
+
+        final ContainerNode output =
+                ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
+                        .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
+        return new DefaultDOMRpcResult(output);
+    }
+
+    /**
+     * Prepare {@code NotificationOutputType}.
+     *
+     * @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);
+        ds = ds == null ? RestconfStreamsConstants.DEFAULT_DS : ds;
+
+        DataChangeScope scope = parseEnum(data, DataChangeScope.class, RestconfStreamsConstants.SCOPE_PARAM_NAME);
+        scope = scope == null ? RestconfStreamsConstants.DEFAULT_SCOPE : scope;
+
+        final String streamName = RestconfStreamsConstants.DATA_SUBSCR + "/"
+                + Notificator
+                .createStreamNameFromUri(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext)
+                + RestconfStreamsConstants.DS_URI + ds + RestconfStreamsConstants.SCOPE_URI + scope);
+        return streamName;
+    }
+
+    private static <T> T parseEnum(final ContainerNode data, final Class<T> clazz, final String paramName) {
+        final Optional<DataContainerChild<? extends PathArgument, ?>> augNode = data
+                .getChild(RestconfStreamsConstants.SAL_REMOTE_AUG_IDENTIFIER);
+        if (!augNode.isPresent() && !(augNode instanceof AugmentationNode)) {
+            return null;
+        }
+        final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode =
+                ((AugmentationNode) augNode.get()).getChild(
+                        new NodeIdentifier(QName.create(RestconfStreamsConstants.SAL_REMOTE_AUGMENT, paramName)));
+        if (!enumNode.isPresent()) {
+            return null;
+        }
+        final Object value = enumNode.get().getValue();
+        if (!(value instanceof String)) {
+            return null;
+        }
+
+        return ResolveEnumUtil.resolveEnum(clazz, (String) value);
+    }
+
+    private static YangInstanceIdentifier preparePath(final ContainerNode data, final QName qualifiedName) {
+        final Optional<DataContainerChild<? extends PathArgument, ?>> path = data
+                .getChild(new YangInstanceIdentifier.NodeIdentifier(QName.create(qualifiedName, "path")));
+        Object pathValue = null;
+        if (path.isPresent()) {
+            pathValue = path.get().getValue();
+        }
+        if (!(pathValue instanceof YangInstanceIdentifier)) {
+            final String errMsg = "Instance identifier was not normalized correctly ";
+            LOG.debug(errMsg + qualifiedName);
+            throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
+        }
+        return (YangInstanceIdentifier) pathValue;
+    }
+
+    /**
+     * Create stream with POST operation via RPC.
+     *
+     * @param notificatinoDefinition
+     *             input of RPC
+     * @param refSchemaCtx
+     *             schemaContext
+     * @param outputType
+     *              output type
+     * @return {@link DOMRpcResult}
+     */
+    public static List<NotificationListenerAdapter> createYangNotifiStream(
+            final NotificationDefinition notificatinoDefinition, final SchemaContextRef refSchemaCtx,
+            final String outputType) {
+        final List<SchemaPath> paths = new ArrayList<>();
+        final QName notificatinoDefinitionQName = notificatinoDefinition.getQName();
+        final Module module =
+                refSchemaCtx.findModuleByNamespaceAndRevision(notificatinoDefinitionQName.getModule().getNamespace(),
+                        notificatinoDefinitionQName.getModule().getRevision());
+        Preconditions.checkNotNull(module,
+                "Module for namespace " + notificatinoDefinitionQName.getModule().getNamespace() + " does not exist");
+        NotificationDefinition notifiDef = null;
+        for (final NotificationDefinition notification : module.getNotifications()) {
+            if (notification.getQName().equals(notificatinoDefinitionQName)) {
+                notifiDef = notification;
+                break;
+            }
+        }
+        final String moduleName = module.getName();
+        Preconditions.checkNotNull(notifiDef,
+                "Notification " + notificatinoDefinitionQName + "doesn't exist in module " + moduleName);
+        paths.add(notifiDef.getPath());
+        String streamName = RestconfStreamsConstants.CREATE_NOTIFICATION_STREAM + "/";
+        streamName = streamName + moduleName + ":" + notificatinoDefinitionQName.getLocalName();
+        if (outputType.equals("JSON")) {
+            streamName = streamName + "/JSON";
+        }
+
+        if (Notificator.existNotificationListenerFor(streamName)) {
+            final List<NotificationListenerAdapter> notificationListenerFor =
+                    Notificator.getNotificationListenerFor(streamName);
+            return SubscribeToStreamUtil.pickSpecificListenerByOutput(notificationListenerFor, outputType);
+        }
+
+        return Notificator.createNotificationListener(paths, streamName, outputType);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/DeleteDataTransactionUtil.java
new file mode 100644 (file)
index 0000000..241245f
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import com.google.common.util.concurrent.CheckedFuture;
+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.DOMTransactionChain;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+/**
+ * Util class for delete specific data in config DS.
+ *
+ */
+public final class DeleteDataTransactionUtil {
+
+    private DeleteDataTransactionUtil() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+    /**
+     * Delete data from DS via transaction.
+     *
+     * @param transactionNode
+     *             Wrapper for data of transaction
+     * @return {@link Response}
+     */
+    public static Response deleteData(final TransactionVarsWrapper transactionNode) {
+        final CheckedFuture<Void, TransactionCommitFailedException> future = submitData(
+                transactionNode.getTransactionChain(), transactionNode.getTransactionChain().newReadWriteTransaction(),
+                transactionNode.getInstanceIdentifier().getInstanceIdentifier());
+        final ResponseFactory response = new ResponseFactory();
+        FutureCallbackTx.addCallback(future, RestconfDataServiceConstant.DeleteData.DELETE_TX_TYPE, response);
+        return response.build();
+    }
+
+    /**
+     * Delete data via transaction. Return error if data to delete does not exist.
+     *
+     * @param transactionChain
+     *             transaction chain
+     * @param readWriteTx
+     *             read and write transaction
+     * @param path
+     *             path of data to delete
+     * @return {@link CheckedFuture}
+     */
+    private static CheckedFuture<Void, TransactionCommitFailedException> submitData(
+            final DOMTransactionChain transactionChain, final DOMDataReadWriteTransaction readWriteTx,
+            final YangInstanceIdentifier path) {
+        TransactionUtil.checkItemExists(transactionChain, readWriteTx, LogicalDatastoreType.CONFIGURATION, path,
+                RestconfDataServiceConstant.DeleteData.DELETE_TX_TYPE);
+        readWriteTx.delete(LogicalDatastoreType.CONFIGURATION, path);
+        return readWriteTx.submit();
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureCallbackTx.java
new file mode 100644 (file)
index 0000000..4969942
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Add callback for future objects and result set to the data factory.
+ *
+ */
+final class FutureCallbackTx {
+
+    private static final Logger LOG = LoggerFactory.getLogger(FutureCallbackTx.class);
+
+    private FutureCallbackTx() {
+        throw new UnsupportedOperationException("Util class");
+    }
+
+    /**
+     * Add callback to the future object.
+     *
+     * @param listenableFuture
+     *             future object
+     * @param txType
+     *             type of operation (READ, POST, PUT, DELETE)
+     * @param dataFactory
+     *             factory setting result
+     * @throws RestconfDocumentedException
+     *             if the Future throws an exception
+     */
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    static <T, X extends Exception> void addCallback(final CheckedFuture<T, X> listenableFuture, final String txType,
+            final FutureDataFactory<T> dataFactory) throws RestconfDocumentedException {
+
+        try {
+            final T result = listenableFuture.checkedGet();
+            dataFactory.setResult(result);
+            LOG.trace("Transaction({}) SUCCESSFUL", txType);
+        } catch (Exception e) {
+            dataFactory.setFailureStatus();
+            LOG.warn("Transaction({}) FAILED!", txType, e);
+            if (e instanceof DOMRpcException) {
+                final List<RpcError> rpcErrorList = new ArrayList<>();
+                rpcErrorList.add(
+                        RpcResultBuilder.newError(RpcError.ErrorType.RPC, "operation-failed", e.getMessage()));
+                dataFactory.setResult((T) new DefaultDOMRpcResult(rpcErrorList));
+            } else {
+                throw new RestconfDocumentedException(
+                        "Transaction(" + txType + ") not committed correctly", e);
+            }
+        }
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureDataFactory.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/FutureDataFactory.java
new file mode 100644 (file)
index 0000000..70ec0a6
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+class FutureDataFactory<T> {
+
+    protected T result;
+    private boolean statusFail = false;
+
+    void setResult(final T result) {
+        this.result = result;
+    }
+
+    void setFailureStatus() {
+        this.statusFail = true;
+    }
+
+    boolean getFailureStatus() {
+        return statusFail;
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/NormalizedNodeFactory.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/NormalizedNodeFactory.java
new file mode 100644 (file)
index 0000000..2001f73
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import com.google.common.base.Optional;
+import org.apache.commons.lang3.builder.Builder;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+class NormalizedNodeFactory extends FutureDataFactory<Optional<NormalizedNode<?, ?>>>
+        implements Builder<NormalizedNode<?, ?>> {
+
+    @Override
+    public NormalizedNode<?, ?> build() {
+        if (this.result.isPresent()) {
+            return this.result.get();
+        }
+        return null;
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ParametersUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ParametersUtil.java
new file mode 100644 (file)
index 0000000..919446f
--- /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.nb.rfc8040.rests.utils;
+
+import com.google.common.collect.Sets;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.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);
+        }
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PatchDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PatchDataTransactionUtil.java
new file mode 100644 (file)
index 0000000..bee24e9
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+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.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchEntity;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.common.patch.PatchStatusEntity;
+import org.opendaylight.restconf.nb.rfc8040.RestConnectorProvider;
+import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfDataServiceConstant.PatchData;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+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.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class PatchDataTransactionUtil {
+    private static final Logger LOG = LoggerFactory.getLogger(PatchDataTransactionUtil.class);
+
+    private PatchDataTransactionUtil() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+    /**
+     * Process edit operations of one {@link PatchContext}.
+     * @param context Patch context to be processed
+     * @param transactionNode Wrapper for transaction
+     * @param schemaContextRef Soft reference for global schema context
+     * @return {@link PatchStatusContext}
+     */
+    public static PatchStatusContext patchData(final PatchContext context, final TransactionVarsWrapper transactionNode,
+                                               final SchemaContextRef schemaContextRef) {
+        final List<PatchStatusEntity> editCollection = new ArrayList<>();
+        boolean noError = true;
+        final DOMDataReadWriteTransaction tx = transactionNode.getTransactionChain().newReadWriteTransaction();
+
+        for (final PatchEntity patchEntity : context.getData()) {
+            if (noError) {
+                switch (patchEntity.getOperation()) {
+                    case CREATE:
+                        try {
+                            createDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
+                                    patchEntity.getTargetNode(), patchEntity.getNode(), tx, schemaContextRef);
+                            editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
+                        } catch (final RestconfDocumentedException e) {
+                            editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
+                                    false, Lists.newArrayList(e.getErrors())));
+                            noError = false;
+                        }
+                        break;
+                    case DELETE:
+                        try {
+                            deleteDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
+                                    tx);
+                            editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
+                        } catch (final RestconfDocumentedException e) {
+                            editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
+                                    false, Lists.newArrayList(e.getErrors())));
+                            noError = false;
+                        }
+                        break;
+                    case MERGE:
+                        try {
+                            mergeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
+                                    patchEntity.getTargetNode(), patchEntity.getNode(), tx, schemaContextRef);
+                            editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
+                        } catch (final RestconfDocumentedException e) {
+                            editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
+                                    false, Lists.newArrayList(e.getErrors())));
+                            noError = false;
+                        }
+                        break;
+                    case REPLACE:
+                        try {
+                            replaceDataWithinTransaction(LogicalDatastoreType.CONFIGURATION,
+                                    patchEntity.getTargetNode(), patchEntity.getNode(), schemaContextRef, tx);
+                            editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
+                        } catch (final RestconfDocumentedException e) {
+                            editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
+                                    false, Lists.newArrayList(e.getErrors())));
+                            noError = false;
+                        }
+                        break;
+                    case REMOVE:
+                        try {
+                            removeDataWithinTransaction(LogicalDatastoreType.CONFIGURATION, patchEntity.getTargetNode(),
+                                    tx);
+                            editCollection.add(new PatchStatusEntity(patchEntity.getEditId(), true, null));
+                        } catch (final RestconfDocumentedException e) {
+                            editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
+                                    false, Lists.newArrayList(e.getErrors())));
+                            noError = false;
+                        }
+                        break;
+                    default:
+                        editCollection.add(new PatchStatusEntity(patchEntity.getEditId(),
+                                false, Lists.newArrayList(new RestconfError(ErrorType.PROTOCOL,
+                                ErrorTag.OPERATION_NOT_SUPPORTED, "Not supported Yang Patch operation"))));
+                        noError = false;
+                        break;
+                }
+            } else {
+                break;
+            }
+        }
+
+        // if no errors then submit transaction, otherwise cancel
+        if (noError) {
+            final ResponseFactory response = new ResponseFactory();
+            final CheckedFuture<Void, TransactionCommitFailedException> future = tx.submit();
+
+            try {
+                FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
+            } catch (final RestconfDocumentedException e) {
+                // if errors occurred during transaction commit then patch failed and global errors are reported
+                return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false,
+                        Lists.newArrayList(e.getErrors()));
+            }
+
+            return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), true, null);
+        } else {
+            tx.cancel();
+            RestConnectorProvider.resetTransactionChainForAdapaters(transactionNode.getTransactionChain());
+            return new PatchStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
+                    false, null);
+        }
+    }
+
+    /**
+     * Create data within one transaction, return error if already exists.
+     * @param dataStore Datastore to write data to
+     * @param path Path for data to be created
+     * @param payload Data to be created
+     * @param rWTransaction Transaction
+     * @param schemaContextRef Soft reference for global schema context
+     */
+    private static void createDataWithinTransaction(final LogicalDatastoreType dataStore,
+                                                    final YangInstanceIdentifier path,
+                                                    final NormalizedNode<?, ?> payload,
+                                                    final DOMDataReadWriteTransaction rwTransaction,
+                                                    final SchemaContextRef schemaContextRef) {
+        LOG.trace("POST {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
+        createData(payload, schemaContextRef.get(), path, rwTransaction, dataStore, true);
+    }
+
+    /**
+     * Check if data exists and remove it within one transaction.
+     * @param dataStore Datastore to delete data from
+     * @param path Path for data to be deleted
+     * @param readWriteTransaction Transaction
+     */
+    private static void deleteDataWithinTransaction(final LogicalDatastoreType dataStore,
+                                                    final YangInstanceIdentifier path,
+                                                    final DOMDataReadWriteTransaction readWriteTransaction) {
+        LOG.trace("Delete {} within Restconf Patch: {}", dataStore.name(), path);
+        checkItemExistsWithinTransaction(readWriteTransaction, dataStore, path);
+        readWriteTransaction.delete(dataStore, path);
+    }
+
+    /**
+     * Merge data within one transaction.
+     * @param dataStore Datastore to merge data to
+     * @param path Path for data to be merged
+     * @param payload Data to be merged
+     * @param writeTransaction Transaction
+     * @param schemaContextRef Soft reference for global schema context
+     */
+    private static void mergeDataWithinTransaction(final LogicalDatastoreType dataStore,
+                                                   final YangInstanceIdentifier path,
+                                                   final NormalizedNode<?, ?> payload,
+                                                   final DOMDataReadWriteTransaction writeTransaction,
+                                                   final SchemaContextRef schemaContextRef) {
+        LOG.trace("Merge {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
+        TransactionUtil.ensureParentsByMerge(path, schemaContextRef.get(), writeTransaction);
+        writeTransaction.merge(dataStore, path, payload);
+    }
+
+    /**
+     * Do NOT check if data exists and remove it within one transaction.
+     * @param dataStore Datastore to delete data from
+     * @param path Path for data to be deleted
+     * @param writeTransaction Transaction
+     */
+    private static void removeDataWithinTransaction(final LogicalDatastoreType dataStore,
+                                                    final YangInstanceIdentifier path,
+                                                    final DOMDataWriteTransaction writeTransaction) {
+        LOG.trace("Remove {} within Restconf Patch: {}", dataStore.name(), path);
+        writeTransaction.delete(dataStore, path);
+    }
+
+    /**
+     * Create data within one transaction, replace if already exists.
+     * @param dataStore Datastore to write data to
+     * @param path Path for data to be created
+     * @param payload Data to be created
+     * @param schemaContextRef Soft reference for global schema context
+     * @param rwTransaction Transaction
+     */
+    private static void replaceDataWithinTransaction(final LogicalDatastoreType dataStore,
+                                                     final YangInstanceIdentifier path,
+                                                     final NormalizedNode<?, ?> payload,
+                                                     final SchemaContextRef schemaContextRef,
+                                                     final DOMDataReadWriteTransaction rwTransaction) {
+        LOG.trace("PUT {} within Restconf Patch: {} with payload {}", dataStore.name(), path, payload);
+        createData(payload, schemaContextRef.get(), path, rwTransaction, dataStore, false);
+    }
+
+    /**
+     * Create data within one transaction. If {@code errorIfExists} is set to {@code true} then data will be checked
+     * for existence before created, otherwise they will be overwritten.
+     * @param payload Data to be created
+     * @param schemaContext Global schema context
+     * @param path Path for data to be created
+     * @param rwTransaction Transaction
+     * @param dataStore Datastore to write data to
+     * @param errorIfExists Enable checking for existence of data (throws error if already exists)
+     */
+    private static void createData(final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+                                   final YangInstanceIdentifier path, final DOMDataReadWriteTransaction rwTransaction,
+                                   final LogicalDatastoreType dataStore, final boolean errorIfExists) {
+        if (payload instanceof MapNode) {
+            final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+            rwTransaction.merge(dataStore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+            TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
+            for (final MapEntryNode child : ((MapNode) payload).getValue()) {
+                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+
+                if (errorIfExists) {
+                    checkItemDoesNotExistsWithinTransaction(rwTransaction, dataStore, childPath);
+                }
+
+                rwTransaction.put(dataStore, childPath, child);
+            }
+        } else {
+            if (errorIfExists) {
+                checkItemDoesNotExistsWithinTransaction(rwTransaction, dataStore, path);
+            }
+
+            TransactionUtil.ensureParentsByMerge(path, schemaContext, rwTransaction);
+            rwTransaction.put(dataStore, path, payload);
+        }
+    }
+
+    /**
+     * Check if items already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
+     * data does NOT already exists.
+     * @param rwTransaction Transaction
+     * @param store Datastore
+     * @param path Path to be checked
+     */
+    public static void checkItemExistsWithinTransaction(final DOMDataReadWriteTransaction rwTransaction,
+                                                final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        final CheckedFuture<Boolean, ReadFailedException> future = rwTransaction.exists(store, path);
+        final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
+
+        FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
+
+        if (!response.result) {
+            final String errMsg = "Operation via Restconf was not executed because data does not exist";
+            LOG.trace("{}:{}", errMsg, path);
+            throw new RestconfDocumentedException(
+                    "Data does not exist", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, path);
+        }
+    }
+
+    /**
+     * Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
+     * data already exists.
+     * @param rwTransaction Transaction
+     * @param store Datastore
+     * @param path Path to be checked
+     */
+    public static void checkItemDoesNotExistsWithinTransaction(final DOMDataReadWriteTransaction rwTransaction,
+                                               final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+        final CheckedFuture<Boolean, ReadFailedException> future = rwTransaction.exists(store, path);
+        final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
+
+        FutureCallbackTx.addCallback(future, PatchData.PATCH_TX_TYPE, response);
+
+        if (response.result) {
+            final String errMsg = "Operation via Restconf was not executed because data already exists";
+            LOG.trace("{}:{}", errMsg, path);
+            throw new RestconfDocumentedException(
+                    "Data already exists", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, path);
+        }
+    }
+}
\ No newline at end of file
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PostDataTransactionUtil.java
new file mode 100644 (file)
index 0000000..1e41297
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.net.URI;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+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.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.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;
+
+/**
+ * Util class to post data to DS.
+ *
+ */
+public final class PostDataTransactionUtil {
+
+    private static final Logger LOG = LoggerFactory.getLogger(PostDataTransactionUtil.class);
+
+    private PostDataTransactionUtil() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+    /**
+     * Check mount point and prepare variables for post data.
+     *
+     * @param uriInfo
+     *
+     * @param payload
+     *             data
+     * @param transactionNode
+     *             wrapper for transaction data
+     * @param schemaContextRef
+     *             reference to actual {@link SchemaContext}
+     * @param point
+     *             point
+     * @param insert
+     *             insert
+     * @return {@link CheckedFuture}
+     */
+    public static Response postData(final UriInfo uriInfo, final NormalizedNodeContext payload,
+            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(), 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);
+        return dataFactory.build();
+    }
+
+    /**
+     * Post data by type.
+     *
+     * @param path
+     *             path
+     * @param data
+     *             data
+     * @param transactionNode
+     *             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 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 =
+                ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent());
+        int lastItemPosition = 0;
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            lastItemPosition++;
+        }
+        if (!before) {
+            lastItemPosition++;
+        }
+        int lastInsertedPosition = 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 (lastInsertedPosition == lastItemPosition) {
+                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);
+            lastInsertedPosition++;
+        }
+    }
+
+    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 =
+                ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent());
+        int lastItemPosition = 0;
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            lastItemPosition++;
+        }
+        if (!before) {
+            lastItemPosition++;
+        }
+        int lastInsertedPosition = 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 (lastInsertedPosition == lastItemPosition) {
+                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);
+            lastInsertedPosition++;
+        }
+    }
+
+    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()) {
+                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+                TransactionUtil.checkItemDoesNotExists(
+                        transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, childPath,
+                        RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+                if (!merge) {
+                    merge = true;
+                    TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
+                    final NormalizedNode<?, ?> emptySubTree = ImmutableNodes.fromInstanceId(schemaContext, path);
+                    transaction.merge(LogicalDatastoreType.CONFIGURATION,
+                            YangInstanceIdentifier.create(emptySubTree.getIdentifier()), emptySubTree);
+                }
+                transaction.put(LogicalDatastoreType.CONFIGURATION, childPath, child);
+            }
+        } else {
+            TransactionUtil.checkItemDoesNotExists(
+                    transactionChain, transaction, LogicalDatastoreType.CONFIGURATION, path,
+                    RestconfDataServiceConstant.PostData.POST_TX_TYPE);
+
+            TransactionUtil.ensureParentsByMerge(path, schemaContext, transaction);
+            transaction.put(LogicalDatastoreType.CONFIGURATION, path, data);
+        }
+    }
+
+    /**
+     * Get location from {@link YangInstanceIdentifier} and {@link UriInfo}.
+     *
+     * @param uriInfo
+     *             uri info
+     * @param transactionNode
+     *             wrapper for data of transaction
+     * @param schemaContextRef
+     *            reference to {@link SchemaContext}
+     * @return {@link URI}
+     */
+    private static URI resolveLocation(final UriInfo uriInfo, final TransactionVarsWrapper transactionNode,
+            final SchemaContextRef schemaContextRef) {
+        if (uriInfo == null) {
+            return null;
+        }
+
+        final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+        uriBuilder.path("data");
+        uriBuilder.path(ParserIdentifier
+                .stringFromYangInstanceIdentifier(transactionNode.getInstanceIdentifier().getInstanceIdentifier(),
+                schemaContextRef.get()));
+
+        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);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/PutDataTransactionUtil.java
new file mode 100644 (file)
index 0000000..fe772b2
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.List;
+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.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.validation.RestconfValidationUtils;
+import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
+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;
+
+/**
+ * Util class for put data to DS.
+ *
+ */
+public final class PutDataTransactionUtil {
+
+    /**
+     * Valid input data with {@link SchemaNode}.
+     *
+     * @param schemaNode
+     *             {@link SchemaNode}
+     * @param payload
+     *             input data
+     */
+    public static void validInputData(final SchemaNode schemaNode, final NormalizedNodeContext payload) {
+        if ((schemaNode != null) && (payload.getData() == null)) {
+            throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+        } else if ((schemaNode == null) && (payload.getData() != null)) {
+            throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+        }
+    }
+
+    /**
+     * Valid top level node name.
+     *
+     * @param path
+     *             path of node
+     * @param payload
+     *             data
+     */
+    public static void validTopLevelNodeName(final YangInstanceIdentifier path, final NormalizedNodeContext payload) {
+        final String payloadName = payload.getData().getNodeType().getLocalName();
+
+        if (path.isEmpty()) {
+            if (!payload.getData().getNodeType().equals(RestconfDataServiceConstant.NETCONF_BASE_QNAME)) {
+                throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
+                        ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+            }
+        } else {
+            final String identifierName = path.getLastPathArgument().getNodeType().getLocalName();
+            if (!payloadName.equals(identifierName)) {
+                throw new RestconfDocumentedException(
+                        "Payload name (" + payloadName + ") is different from identifier name (" + identifierName + ")",
+                        ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+            }
+        }
+    }
+
+    /**
+     * Validates whether keys in {@code payload} are equal to values of keys in
+     * {@code iiWithData} for list schema node.
+     *
+     * @throws RestconfDocumentedException
+     *             if key values or key count in payload and URI isn't equal
+     */
+    public static void validateListKeysEqualityInPayloadAndUri(final NormalizedNodeContext payload) {
+        final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
+        final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
+        final SchemaNode schemaNode = iiWithData.getSchemaNode();
+        final NormalizedNode<?, ?> data = payload.getData();
+        if (schemaNode instanceof ListSchemaNode) {
+            final List<QName> keyDefinitions = ((ListSchemaNode) schemaNode).getKeyDefinition();
+            if ((lastPathArgument instanceof NodeIdentifierWithPredicates) && (data instanceof MapEntryNode)) {
+                final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
+                        .getKeyValues();
+                isEqualUriAndPayloadKeyValues(uriKeyValues, (MapEntryNode) data, keyDefinitions);
+            }
+        }
+    }
+
+    private static void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final MapEntryNode payload,
+            final List<QName> keyDefinitions) {
+        final Map<QName, Object> mutableCopyUriKeyValues = Maps.newHashMap(uriKeyValues);
+        for (final QName keyDefinition : keyDefinitions) {
+            final Object uriKeyValue = mutableCopyUriKeyValues.remove(keyDefinition);
+            RestconfValidationUtils.checkDocumentedError(uriKeyValue != null, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING,
+                    "Missing key " + keyDefinition + " in URI.");
+
+            final Object dataKeyValue = payload.getIdentifier().getKeyValues().get(keyDefinition);
+
+            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. ";
+                throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+            }
+        }
+    }
+
+    /**
+     * Check mount point and prepare variables for put data to DS.
+     *
+     * @param payload
+     *             data to put
+     * @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 String insert, final String point) {
+        final YangInstanceIdentifier path = payload.getInstanceIdentifierContext().getInstanceIdentifier();
+        final SchemaContext schemaContext = schemaCtxRef.get();
+        final ResponseFactory responseFactory = new ResponseFactory(
+                ReadDataTransactionUtil.readData(RestconfDataServiceConstant.ReadData.CONFIG, transactionNode,
+                        schemaContext));
+        final CheckedFuture<Void, TransactionCommitFailedException> submitData = submitData(path, schemaContext,
+                transactionNode.getTransactionChain(), payload.getData(), insert, point);
+        FutureCallbackTx.addCallback(submitData, RestconfDataServiceConstant.PutData.PUT_TX_TYPE, responseFactory);
+        return responseFactory.build();
+    }
+
+    /**
+     * Put data to DS.
+     *
+     * @param path
+     *             path of data
+     * @param schemaContext
+     *             {@link SchemaContext}
+     * @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 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, schemaContext);
+        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 =
+                ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent());
+        int lastItemPosition = 0;
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            lastItemPosition++;
+        }
+        if (!before) {
+            lastItemPosition++;
+        }
+        int lastInsertedPosition = 0;
+        final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+        rwTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (lastInsertedPosition == lastItemPosition) {
+                simplePut(datastore, path, rwTransaction, schemaContext, data);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
+            rwTransaction.put(datastore, childPath, nodeChild);
+            lastInsertedPosition++;
+        }
+    }
+
+    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 =
+                ParserIdentifier.toInstanceIdentifier(point, schemaContext, Optional.absent());
+        int lastItemPosition = 0;
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            lastItemPosition++;
+        }
+        if (!before) {
+            lastItemPosition++;
+        }
+        int lastInsertedPosition = 0;
+        final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+        writeTx.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (lastInsertedPosition == lastItemPosition) {
+                simplePut(datastore, path, writeTx, schemaContext, data);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
+            writeTx.put(datastore, childPath, mapEntryNode);
+            lastInsertedPosition++;
+        }
+    }
+
+    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");
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ReadDataTransactionUtil.java
new file mode 100644 (file)
index 0000000..b973d26
--- /dev/null
@@ -0,0 +1,692 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAMS_PATH;
+import static org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants.STREAM_PATH_PART;
+import com.google.common.base.Optional;
+import com.google.common.primitives.Ints;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.net.URI;
+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.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.context.WriterParameters;
+import org.opendaylight.restconf.common.context.WriterParameters.WriterParametersBuilder;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
+import org.opendaylight.restconf.nb.rfc8040.rests.transactions.TransactionVarsWrapper;
+import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
+import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserFieldsParameter;
+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;
+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.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;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+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.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.NotificationDefinition;
+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.
+ * <ul>
+ * <li>config
+ * <li>state
+ * <li>all (config + state)
+ * </ul>
+ *
+ */
+public final class ReadDataTransactionUtil {
+
+    private 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}
+     */
+    @Nonnull
+    public static 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}
+     */
+    @Nonnull
+    public static 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.
+     *
+     * @param valueOfContent
+     *            type of data to read (config, state, all)
+     * @param transactionNode
+     *            {@link TransactionVarsWrapper} - wrapper for variables
+     * @param schemaContext
+     * @return {@link NormalizedNode}
+     */
+    @Nullable
+    public static NormalizedNode<?, ?> readData(@Nonnull final String valueOfContent,
+            @Nonnull final TransactionVarsWrapper transactionNode, final SchemaContext schemaContext) {
+        return readData(valueOfContent, transactionNode, null, schemaContext);
+    }
+
+    /**
+     * 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
+     * @param ctx
+     * @return {@link NormalizedNode}
+     */
+    @Nullable
+    public static NormalizedNode<?, ?> readData(@Nonnull final String valueOfContent,
+            @Nonnull final TransactionVarsWrapper transactionNode, final String withDefa, final SchemaContext ctx) {
+        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, ctx);
+                }
+            case RestconfDataServiceConstant.ReadData.NONCONFIG:
+                transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
+                return readDataViaTransaction(transactionNode);
+
+            case RestconfDataServiceConstant.ReadData.ALL:
+                return readAllData(transactionNode, withDefa, ctx);
+
+            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)"));
+        }
+    }
+
+    /**
+     * Read specific type of data from data store via transaction and if identifier read data from
+     * streams then put streams from actual schema context to datastore.
+     *
+     * @param identifier
+     *             identifier of data to read
+     * @param content
+     *             type of data to read (config, state, all)
+     * @param transactionNode
+     *             {@link TransactionVarsWrapper} - wrapper for variables
+     * @param withDefa
+     *             vaule of with-defaults parameter
+     * @param schemaContextRef
+     *             schema context
+     * @param uriInfo
+     *             uri info
+     * @return {@link NormalizedNode}
+     */
+    public static NormalizedNode<?, ?> readData(final String identifier, final String content,
+                                                final TransactionVarsWrapper transactionNode, final String withDefa,
+                                                final SchemaContextRef schemaContextRef, final UriInfo uriInfo) {
+        final SchemaContext schemaContext = schemaContextRef.get();
+        if (identifier.contains(STREAMS_PATH) && !identifier.contains(STREAM_PATH_PART)) {
+            final DOMDataReadWriteTransaction wTx = transactionNode.getTransactionChain().newReadWriteTransaction();
+            final boolean exist = SubscribeToStreamUtil.checkExist(schemaContext, wTx);
+
+            for (final NotificationDefinition notificationDefinition : schemaContextRef.get().getNotifications()) {
+                final List<NotificationListenerAdapter> notifiStreamXML =
+                        CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContextRef,
+                                NotificationOutputType.XML.getName());
+                final List<NotificationListenerAdapter> notifiStreamJSON =
+                        CreateStreamUtil.createYangNotifiStream(notificationDefinition, schemaContextRef,
+                                NotificationOutputType.JSON.getName());
+                notifiStreamJSON.addAll(notifiStreamXML);
+
+                for (final NotificationListenerAdapter listener : notifiStreamJSON) {
+                    final URI uri = SubscribeToStreamUtil.prepareUriByStreamName(uriInfo, listener.getStreamName());
+                    final NormalizedNode mapToStreams =
+                            RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(
+                                    listener.getSchemaPath().getLastComponent(), schemaContext.getNotifications(),
+                                    null, listener.getOutputType(), uri,
+                                    SubscribeToStreamUtil.getMonitoringModule(schemaContext), exist);
+                    SubscribeToStreamUtil.writeDataToDS(schemaContext,
+                            listener.getSchemaPath().getLastComponent().getLocalName(), wTx, exist,
+                            mapToStreams);
+                }
+            }
+            SubscribeToStreamUtil.submitData(wTx);
+        }
+        return readData(content, transactionNode, withDefa, schemaContext);
+    }
+
+    private static NormalizedNode<?, ?> prepareDataByParamWithDef(final NormalizedNode<?, ?> result,
+            final YangInstanceIdentifier path, final String withDefa, final SchemaContext ctx) {
+        boolean trim;
+        switch (withDefa) {
+            case "trim":
+                trim = true;
+                break;
+            case "explicit":
+                trim = false;
+                break;
+            default:
+                throw new RestconfDocumentedException("");
+        }
+
+        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 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());
+        }
+    }
+
+    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());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * If is set specific {@link LogicalDatastoreType} in
+     * {@link TransactionVarsWrapper}, then read this type of data from DS. If
+     * don't, we have to read all data from DS (state + config)
+     *
+     * @param transactionNode
+     *             {@link TransactionVarsWrapper} - wrapper for variables
+     * @return {@link NormalizedNode}
+     */
+    @Nullable
+    private static NormalizedNode<?, ?> readDataViaTransaction(
+            @Nonnull final TransactionVarsWrapper transactionNode) {
+        final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory();
+        try (DOMDataReadOnlyTransaction tx = transactionNode.getTransactionChain().newReadOnlyTransaction()) {
+            final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> listenableFuture = tx.read(
+                transactionNode.getLogicalDatastoreType(),
+                transactionNode.getInstanceIdentifier().getInstanceIdentifier());
+            FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX,
+                dataFactory);
+        }
+        return dataFactory.build();
+    }
+
+    /**
+     * Read config and state data, then map them.
+     *
+     * @param transactionNode
+     *            {@link TransactionVarsWrapper} - wrapper for variables
+     * @param withDefa
+     *            with-defaults parameter
+     * @param ctx
+     * @return {@link NormalizedNode}
+     */
+    @Nullable
+    private static NormalizedNode<?, ?> readAllData(@Nonnull final TransactionVarsWrapper transactionNode,
+            final String withDefa, final SchemaContext ctx) {
+        // PREPARE STATE DATA NODE
+        transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL);
+        final NormalizedNode<?, ?> stateDataNode = readDataViaTransaction(transactionNode);
+
+        // PREPARE CONFIG DATA NODE
+        transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION);
+        final NormalizedNode<?, ?> configDataNode;
+        if (withDefa == null) {
+            configDataNode = readDataViaTransaction(transactionNode);
+        } else {
+            configDataNode = prepareDataByParamWithDef(readDataViaTransaction(transactionNode),
+                    transactionNode.getInstanceIdentifier().getInstanceIdentifier(), withDefa, ctx);
+        }
+
+        // if no data exists
+        if ((stateDataNode == null) && (configDataNode == null)) {
+            return null;
+        }
+
+        // return config data
+        if (stateDataNode == null) {
+            return configDataNode;
+        }
+
+        // return state data
+        if (configDataNode == null) {
+            return stateDataNode;
+        }
+
+        // merge data from config and state
+        return mapNode(stateDataNode, configDataNode);
+    }
+
+    /**
+     * Map data by type of read node.
+     *
+     * @param stateDataNode
+     *             data node of state data
+     * @param configDataNode
+     *             data node of config data
+     * @return {@link NormalizedNode}
+     */
+    @Nonnull
+    private static NormalizedNode<?, ?> mapNode(@Nonnull final NormalizedNode<?, ?> stateDataNode,
+                                                         @Nonnull final NormalizedNode<?, ?> configDataNode) {
+        validPossibilityOfMergeNodes(stateDataNode, configDataNode);
+        if (configDataNode instanceof RpcDefinition) {
+            return prepareRpcData(configDataNode, stateDataNode);
+        } else {
+            return prepareData(configDataNode, stateDataNode);
+        }
+    }
+
+    /**
+     * Valid of can be data merged together.
+     *
+     * @param stateDataNode
+     *             data node of state data
+     * @param configDataNode
+     *             data node of config data
+     */
+    private static void validPossibilityOfMergeNodes(@Nonnull final NormalizedNode<?, ?> stateDataNode,
+                                                     @Nonnull final NormalizedNode<?, ?> configDataNode) {
+        final QNameModule moduleOfStateData = stateDataNode.getIdentifier().getNodeType().getModule();
+        final QNameModule moduleOfConfigData = configDataNode.getIdentifier().getNodeType().getModule();
+        if (moduleOfStateData != moduleOfConfigData) {
+            throw new RestconfDocumentedException("It is not possible to merge ");
+        }
+    }
+
+    /**
+     * Prepare and map data for rpc.
+     *
+     * @param configDataNode
+     *             data node of config data
+     * @param stateDataNode
+     *             data node of state data
+     * @return {@link NormalizedNode}
+     */
+    @Nonnull
+    private static NormalizedNode<?, ?> prepareRpcData(@Nonnull final NormalizedNode<?, ?> configDataNode,
+                                                                @Nonnull final NormalizedNode<?, ?> stateDataNode) {
+        final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = ImmutableNodes
+                .mapEntryBuilder();
+        mapEntryBuilder.withNodeIdentifier((NodeIdentifierWithPredicates) configDataNode.getIdentifier());
+
+        // MAP CONFIG DATA
+        mapRpcDataNode(configDataNode, mapEntryBuilder);
+        // MAP STATE DATA
+        mapRpcDataNode(stateDataNode, mapEntryBuilder);
+
+        return ImmutableNodes.mapNodeBuilder(configDataNode.getNodeType()).addChild(mapEntryBuilder.build()).build();
+    }
+
+    /**
+     * Map node to map entry builder.
+     *
+     * @param dataNode
+     *             data node
+     * @param mapEntryBuilder
+     *             builder for mapping data
+     */
+    private static void mapRpcDataNode(@Nonnull final NormalizedNode<?, ?> dataNode,
+                                       @Nonnull final DataContainerNodeBuilder<
+                                               NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder) {
+        ((ContainerNode) dataNode).getValue().forEach(mapEntryBuilder::addChild);
+    }
+
+    /**
+     * Prepare and map all data from DS.
+     *
+     * @param configDataNode
+     *             data node of config data
+     * @param stateDataNode
+     *             data node of state data
+     * @return {@link NormalizedNode}
+     */
+    @Nonnull
+    private static NormalizedNode<?, ?> prepareData(@Nonnull final NormalizedNode<?, ?> configDataNode,
+                                                             @Nonnull final NormalizedNode<?, ?> stateDataNode) {
+        if (configDataNode instanceof MapNode) {
+            final CollectionNodeBuilder<MapEntryNode, MapNode> builder = ImmutableNodes
+                    .mapNodeBuilder().withNodeIdentifier(((MapNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(
+                    ((MapNode) configDataNode).getValue(), ((MapNode) stateDataNode).getValue(), builder);
+
+            return builder.build();
+        } else if (configDataNode instanceof MapEntryNode) {
+            final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = ImmutableNodes
+                    .mapEntryBuilder().withNodeIdentifier(((MapEntryNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(
+                    ((MapEntryNode) configDataNode).getValue(), ((MapEntryNode) stateDataNode).getValue(), builder);
+
+            return builder.build();
+        } else if (configDataNode instanceof ContainerNode) {
+            final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> builder = Builders
+                    .containerBuilder().withNodeIdentifier(((ContainerNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(
+                    ((ContainerNode) configDataNode).getValue(), ((ContainerNode) stateDataNode).getValue(), builder);
+
+            return builder.build();
+        } else if (configDataNode instanceof AugmentationNode) {
+            final DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> builder = Builders
+                    .augmentationBuilder().withNodeIdentifier(((AugmentationNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(((AugmentationNode) configDataNode).getValue(),
+                    ((AugmentationNode) stateDataNode).getValue(), builder);
+
+            return builder.build();
+        } else if (configDataNode instanceof ChoiceNode) {
+            final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> builder = Builders
+                    .choiceBuilder().withNodeIdentifier(((ChoiceNode) configDataNode).getIdentifier());
+
+            mapValueToBuilder(
+                    ((ChoiceNode) configDataNode).getValue(), ((ChoiceNode) stateDataNode).getValue(), builder);
+
+            return builder.build();
+        } else if (configDataNode instanceof LeafNode) {
+            return ImmutableNodes.leafNode(configDataNode.getNodeType(), configDataNode.getValue());
+        } else {
+            throw new RestconfDocumentedException("Bad type of node.");
+        }
+    }
+
+    /**
+     * Map value from container node to builder.
+     *
+     * @param configData
+     *             collection of config data nodes
+     * @param stateData
+     *             collection of state data nodes
+     * @param builder
+     *             builder
+     */
+    private static <T extends NormalizedNode<? extends PathArgument, ?>> void mapValueToBuilder(
+            @Nonnull final Collection<T> configData,
+            @Nonnull final Collection<T> stateData,
+            @Nonnull final NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
+        final Map<PathArgument, T> configMap = configData.stream().collect(
+                Collectors.toMap(NormalizedNode::getIdentifier, Function.identity()));
+        final Map<PathArgument, T> stateMap = stateData.stream().collect(
+                Collectors.toMap(NormalizedNode::getIdentifier, Function.identity()));
+
+        // merge config and state data of children with different identifiers
+        mapDataToBuilder(configMap, stateMap, builder);
+
+        // merge config and state data of children with the same identifiers
+        mergeDataToBuilder(configMap, stateMap, builder);
+    }
+
+    /**
+     * Map data with different identifiers to builder. Data with different identifiers can be just added
+     * as childs to parent node.
+     *
+     * @param configMap
+     *             map of config data nodes
+     * @param stateMap
+     *             map of state data nodes
+     * @param builder
+     *           - builder
+     */
+    private static <T extends NormalizedNode<? extends PathArgument, ?>> void mapDataToBuilder(
+            @Nonnull final Map<PathArgument, T> configMap,
+            @Nonnull final Map<PathArgument, T> stateMap,
+            @Nonnull final NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
+        configMap.entrySet().stream().filter(x -> !stateMap.containsKey(x.getKey())).forEach(
+            y -> builder.addChild(y.getValue()));
+        stateMap.entrySet().stream().filter(x -> !configMap.containsKey(x.getKey())).forEach(
+            y -> builder.addChild(y.getValue()));
+    }
+
+    /**
+     * Map data with the same identifiers to builder. Data with the same identifiers cannot be just added but we need to
+     * go one level down with {@code prepareData} method.
+     *
+     * @param configMap
+     *             immutable config data
+     * @param stateMap
+     *             immutable state data
+     * @param builder
+     *           - builder
+     */
+    @SuppressWarnings("unchecked")
+    private static <T extends NormalizedNode<? extends PathArgument, ?>> void mergeDataToBuilder(
+            @Nonnull final Map<PathArgument, T> configMap,
+            @Nonnull final Map<PathArgument, T> stateMap,
+            @Nonnull final NormalizedNodeContainerBuilder<?, PathArgument, T, ?> builder) {
+        // it is enough to process only config data because operational contains the same data
+        configMap.entrySet().stream().filter(x -> stateMap.containsKey(x.getKey())).forEach(
+            y -> builder.addChild((T) prepareData(y.getValue(), stateMap.get(y.getKey()))));
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ResolveEnumUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ResolveEnumUtil.java
new file mode 100644 (file)
index 0000000..d997fbd
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+/**
+ * Common util class for resolve enum from String.
+ *
+ */
+public final class ResolveEnumUtil {
+
+    private ResolveEnumUtil() {
+        throw new UnsupportedOperationException("Util class");
+    }
+
+    /**
+     * Resolve specific type of enum by value.
+     *
+     * @param clazz
+     *             enum
+     * @param value
+     *             string of enum
+     * @return - enum
+     */
+    public static <T> T resolveEnum(final Class<T> clazz, final String value) {
+        for (final T t : clazz.getEnumConstants()) {
+            if (((Enum<?>) t).name().equals(value)) {
+                return t;
+            }
+        }
+        return null;
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ResponseFactory.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/ResponseFactory.java
new file mode 100644 (file)
index 0000000..3447664
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import java.net.URI;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.Response.Status;
+import org.apache.commons.lang3.builder.Builder;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+final class ResponseFactory extends FutureDataFactory<Void> implements Builder<Response> {
+
+    private ResponseBuilder responseBuilder;
+
+    ResponseFactory(final NormalizedNode<?, ?> readData) {
+        final Status status = prepareStatus(readData);
+        this.responseBuilder = Response.status(status);
+    }
+
+    ResponseFactory(final NormalizedNode<?, ?> readData, final URI location) {
+        final Status status = prepareStatus(readData);
+        this.responseBuilder = Response.status(status);
+        this.responseBuilder.location(location);
+    }
+
+    ResponseFactory() {
+        this.responseBuilder = Response.status(Status.OK);
+    }
+
+    @Override
+    public Response build() {
+        if (getFailureStatus()) {
+            responseBuilder = responseBuilder.status(Response.Status.INTERNAL_SERVER_ERROR);
+        }
+        return this.responseBuilder.build();
+    }
+
+    private static Status prepareStatus(final NormalizedNode<?, ?> readData) {
+        return readData != null ? Status.OK : Status.CREATED;
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfDataServiceConstant.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfDataServiceConstant.java
new file mode 100644 (file)
index 0000000..c0acdcb
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+/**
+ * Constants for RestconfDataService.
+ *
+ */
+public final class RestconfDataServiceConstant {
+
+    public static final QName NETCONF_BASE_QNAME;
+
+    static {
+        try {
+            NETCONF_BASE_QNAME = QName.create(
+                    QNameModule.create(new URI(PutData.NETCONF_BASE), null), PutData.NETCONF_BASE_PAYLOAD_NAME);
+        } catch (final URISyntaxException e) {
+            final String errMsg = "It wasn't possible to create instance of URI class with " + PutData.NETCONF_BASE
+                    + " URI";
+            throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
+        }
+    }
+
+    private RestconfDataServiceConstant() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+    /**
+     * Constants for read data.
+     *
+     */
+    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 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.");
+        }
+    }
+
+    /**
+     * Constants for data to put.
+     *
+     */
+    public final class PutData {
+        public static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
+        public static final String NETCONF_BASE_PAYLOAD_NAME = "data";
+        public static final String PUT_TX_TYPE = "PUT";
+
+        private PutData() {
+            throw new UnsupportedOperationException("Util class.");
+        }
+    }
+
+    /**
+     * Constants for data to post.
+     *
+     */
+    public final class PostData {
+        public static final String POST_TX_TYPE = "POST";
+
+        private PostData() {
+            throw new UnsupportedOperationException("Util class.");
+        }
+    }
+
+    /**
+     * Constants for data to delete.
+     *
+     */
+    public final class DeleteData {
+        public static final String DELETE_TX_TYPE = "DELETE";
+
+        private DeleteData() {
+            throw new UnsupportedOperationException("Util class.");
+        }
+    }
+
+    /**
+     * Constants for data to yang patch.
+     *
+     */
+    public final class PatchData {
+        public static final String PATCH_TX_TYPE = "Patch";
+
+        private PatchData() {
+            throw new UnsupportedOperationException("Util class.");
+        }
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfInvokeOperationsUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfInvokeOperationsUtil.java
new file mode 100644 (file)
index 0000000..c76c79d
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.concurrent.CancellationException;
+import javax.ws.rs.core.Response.Status;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+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.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.nb.rfc8040.handlers.RpcServiceHandler;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Util class for rpc.
+ *
+ */
+public class RestconfInvokeOperationsUtil {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestconfInvokeOperationsUtil.class);
+
+    private RestconfInvokeOperationsUtil() {
+        throw new UnsupportedOperationException("Util class");
+    }
+
+    /**
+     * Invoking rpc via mount point.
+     *
+     * @param mountPoint
+     *             mount point
+     * @param data
+     *             input data
+     * @param schemaPath
+     *             schema path of data
+     * @return {@link CheckedFuture}
+     */
+    public static DOMRpcResult invokeRpcViaMountPoint(final DOMMountPoint mountPoint, final NormalizedNode<?, ?> data,
+            final SchemaPath schemaPath) {
+        final Optional<DOMRpcService> mountPointService = mountPoint.getService(DOMRpcService.class);
+        if (mountPointService.isPresent()) {
+            final CheckedFuture<DOMRpcResult, DOMRpcException> rpc = mountPointService.get().invokeRpc(schemaPath,
+                    data);
+            return prepareResult(rpc);
+        }
+        final String errmsg = "RPC service is missing.";
+        LOG.debug(errmsg);
+        throw new RestconfDocumentedException(errmsg);
+    }
+
+    /**
+     * Invoke rpc.
+     *
+     * @param data
+     *             input data
+     * @param schemaPath
+     *             schema path of data
+     * @param rpcServiceHandler
+     *             rpc service handler to invoke rpc
+     * @return {@link CheckedFuture}
+     */
+    public static DOMRpcResult invokeRpc(final NormalizedNode<?, ?> data, final SchemaPath schemaPath,
+            final RpcServiceHandler rpcServiceHandler) {
+        final DOMRpcService rpcService = rpcServiceHandler.get();
+        if (rpcService == null) {
+            throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
+        }
+
+        final CheckedFuture<DOMRpcResult, DOMRpcException> rpc = rpcService.invokeRpc(schemaPath, data);
+        return prepareResult(rpc);
+    }
+
+    /**
+     * Check the validity of the result.
+     *
+     * @param response
+     *             response of rpc
+     * @return {@link DOMRpcResult} result
+     */
+    public static DOMRpcResult checkResponse(final DOMRpcResult response) {
+        if (response == null) {
+            return null;
+        }
+        try {
+            if (response.getErrors().isEmpty()) {
+                return response;
+            }
+            LOG.debug("RpcError message", response.getErrors());
+            throw new RestconfDocumentedException("RPCerror message ", null, response.getErrors());
+        } catch (final CancellationException e) {
+            final String errMsg = "The operation was cancelled while executing.";
+            LOG.debug("Cancel RpcExecution: " + errMsg, e);
+            throw new RestconfDocumentedException(errMsg, ErrorType.RPC, ErrorTag.PARTIAL_OPERATION);
+        }
+    }
+
+    private static DOMRpcResult prepareResult(final CheckedFuture<DOMRpcResult, DOMRpcException> rpc) {
+        final RpcResultFactory dataFactory = new RpcResultFactory();
+        FutureCallbackTx.addCallback(rpc, RestconfDataServiceConstant.PostData.POST_TX_TYPE, dataFactory);
+        return dataFactory.build();
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfStreamsConstants.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RestconfStreamsConstants.java
new file mode 100644 (file)
index 0000000..c68cb49
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import com.google.common.collect.ImmutableSet;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.Date;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.builder.ParserBuilderConstants;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Constants for streams.
+ *
+ */
+public final class RestconfStreamsConstants {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestconfStreamsConstants.class);
+
+    public static final String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
+
+    public static final String DATASTORE_PARAM_NAME = "datastore";
+
+    private static final URI NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = URI.create("urn:sal:restconf:event:subscription");
+
+    public static final QNameModule SAL_REMOTE_AUGMENT;
+
+    static {
+        final Date eventSubscriptionAugRevision;
+        try {
+            eventSubscriptionAugRevision = SimpleDateFormatUtil.getRevisionFormat().parse("2014-07-08");
+        } catch (final ParseException e) {
+            final String errMsg = "It wasn't possible to convert revision date of sal-remote-augment to date";
+            LOG.debug(errMsg);
+            throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
+        }
+        SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, eventSubscriptionAugRevision)
+                .intern();
+    }
+
+    public static final AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER = new AugmentationIdentifier(
+        ImmutableSet.of(QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore"),
+            QName.create(SAL_REMOTE_AUGMENT, "notification-output-type")));
+
+    public static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE;
+
+    public static final LogicalDatastoreType DEFAULT_DS = LogicalDatastoreType.CONFIGURATION;
+
+    public static final String SCOPE_PARAM_NAME = "scope";
+
+    public static final char EQUAL = ParserBuilderConstants.Deserializer.EQUAL;
+
+    public static final String DS_URI = RestconfConstants.SLASH + DATASTORE_PARAM_NAME + EQUAL;
+
+    public static final String SCOPE_URI = RestconfConstants.SLASH + SCOPE_PARAM_NAME + EQUAL;
+
+    public static final int NOTIFICATION_PORT = 8181;
+
+    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;
+
+    public static final String STREAMS_PATH = "ietf-restconf-monitoring:restconf-state/streams";
+    public static final String STREAM_PATH_PART = "/stream=";
+    public static final String STREAM_PATH = STREAMS_PATH + STREAM_PATH_PART;
+    public static final String STREAM_ACCESS_PATH_PART = "/access=";
+    public static final String STREAM_LOCATION_PATH_PART = "/location";
+
+    private RestconfStreamsConstants() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RpcResultFactory.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/RpcResultFactory.java
new file mode 100644 (file)
index 0000000..2af90da
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import org.apache.commons.lang3.builder.Builder;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+
+public class RpcResultFactory extends FutureDataFactory<DOMRpcResult> implements Builder<DOMRpcResult> {
+
+    @Override
+    public DOMRpcResult build() {
+        return this.result;
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/SubscribeToStreamUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/SubscribeToStreamUtil.java
new file mode 100644 (file)
index 0000000..b90dd4b
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import java.net.URI;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.util.ArrayList;
+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.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040.MonitoringModule;
+import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfStreamsSubscriptionServiceImpl.HandlersHolder;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfStreamsSubscriptionServiceImpl.NotificationQueryParams;
+import org.opendaylight.restconf.nb.rfc8040.streams.listeners.ListenerAdapter;
+import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
+import org.opendaylight.restconf.nb.rfc8040.streams.listeners.Notificator;
+import org.opendaylight.restconf.nb.rfc8040.streams.websockets.WebSocketServer;
+import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
+import org.opendaylight.restconf.nb.rfc8040.utils.mapping.RestconfMappingNodeUtil;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec;
+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.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.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+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.
+ *
+ */
+public final class SubscribeToStreamUtil {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SubscribeToStreamUtil.class);
+    private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
+            .appendValue(ChronoField.YEAR, 4).appendLiteral('-')
+            .appendValue(ChronoField.MONTH_OF_YEAR, 2).appendLiteral('-')
+            .appendValue(ChronoField.DAY_OF_MONTH, 2).appendLiteral('T')
+            .appendValue(ChronoField.HOUR_OF_DAY, 2).appendLiteral(':')
+            .appendValue(ChronoField.MINUTE_OF_HOUR, 2).appendLiteral(':')
+            .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
+            .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true)
+            .appendOffset("+HH:MM", "Z").toFormatter();
+
+    private SubscribeToStreamUtil() {
+        throw new UnsupportedOperationException("Util class");
+    }
+
+    /**
+     * Register listeners by streamName in identifier to listen to yang
+     * notifications, put or delete info about listener to DS according to
+     * ietf-restconf-monitoring.
+     *
+     * @param identifier
+     *             identifier as stream name
+     * @param uriInfo
+     *             for getting base URI information
+     * @param notificationQueryParams
+     *             query parameters of notification
+     * @param handlersHolder
+     *             holder of handlers for notifications
+     * @return location for listening
+     */
+    @SuppressWarnings("rawtypes")
+    public static URI notifYangStream(final String identifier, final UriInfo uriInfo,
+            final NotificationQueryParams notificationQueryParams, final HandlersHolder handlersHolder) {
+        final String streamName = Notificator.createStreamNameFromUri(identifier);
+        if (Strings.isNullOrEmpty(streamName)) {
+            throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+        }
+        List<NotificationListenerAdapter> listeners = Notificator.getNotificationListenerFor(streamName);
+        if (identifier.contains(RestconfConstants.SLASH + NotificationOutputType.JSON.getName())) {
+            listeners = pickSpecificListenerByOutput(listeners, NotificationOutputType.JSON.getName());
+        } else {
+            listeners = pickSpecificListenerByOutput(listeners, NotificationOutputType.XML.getName());
+        }
+        if ((listeners == null) || listeners.isEmpty()) {
+            throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL,
+                    ErrorTag.UNKNOWN_ELEMENT);
+        }
+
+        final DOMDataReadWriteTransaction wTx =
+                handlersHolder.getTransactionChainHandler().get().newReadWriteTransaction();
+        final SchemaContext schemaContext = handlersHolder.getSchemaHandler().get();
+        final boolean exist = checkExist(schemaContext, wTx);
+
+        final URI uri = prepareUriByStreamName(uriInfo, streamName);
+        for (final NotificationListenerAdapter listener : listeners) {
+            registerToListenNotification(listener, handlersHolder.getNotificationServiceHandler());
+            listener.setQueryParams(notificationQueryParams.getStart(), notificationQueryParams.getStop(),
+                    notificationQueryParams.getFilter(), false);
+            listener.setCloseVars(handlersHolder.getTransactionChainHandler(), handlersHolder.getSchemaHandler());
+            final NormalizedNode mapToStreams = RestconfMappingNodeUtil
+                    .mapYangNotificationStreamByIetfRestconfMonitoring(listener.getSchemaPath().getLastComponent(),
+                            schemaContext.getNotifications(), notificationQueryParams.getStart(),
+                            listener.getOutputType(), uri, getMonitoringModule(schemaContext), exist);
+            writeDataToDS(schemaContext, listener.getSchemaPath().getLastComponent().getLocalName(), wTx, exist,
+                    mapToStreams);
+        }
+        submitData(wTx);
+
+        return uri;
+    }
+
+    static List<NotificationListenerAdapter>
+            pickSpecificListenerByOutput(final List<NotificationListenerAdapter> listeners, final String outputType) {
+        for (final NotificationListenerAdapter notificationListenerAdapter : listeners) {
+            if (notificationListenerAdapter.getOutputType().equals(outputType)) {
+                final List<NotificationListenerAdapter> list = new ArrayList<>();
+                list.add(notificationListenerAdapter);
+                return list;
+            }
+        }
+        return listeners;
+    }
+
+    /**
+     * 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 data change
+     * notifications, put or delete info about listener to DS according to
+     * ietf-restconf-monitoring.
+     *
+     * @param identifier
+     *             identifier as stream name
+     * @param uriInfo
+     *             for getting base URI information
+     * @param notificationQueryParams
+     *             query parameters of notification
+     * @param handlersHolder
+     *             holder of handlers for notifications
+     * @return location for listening
+     */
+    @SuppressWarnings("rawtypes")
+    public static URI notifiDataStream(final String identifier, final UriInfo uriInfo,
+            final NotificationQueryParams notificationQueryParams, final HandlersHolder handlersHolder) {
+        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(notificationQueryParams.getStart(), notificationQueryParams.getStop(),
+                notificationQueryParams.getFilter(), false);
+        listener.setCloseVars(handlersHolder.getTransactionChainHandler(), handlersHolder.getSchemaHandler());
+
+        registration(ds, scope, listener, handlersHolder.getDomDataBrokerHandler().get());
+
+        final URI uri = prepareUriByStreamName(uriInfo, streamName);
+
+        final DOMDataReadWriteTransaction wTx =
+                handlersHolder.getTransactionChainHandler().get().newReadWriteTransaction();
+        final SchemaContext schemaContext = handlersHolder.getSchemaHandler().get();
+        final boolean exist = checkExist(schemaContext, wTx);
+
+        final NormalizedNode mapToStreams = RestconfMappingNodeUtil
+                .mapDataChangeNotificationStreamByIetfRestconfMonitoring(listener.getPath(),
+                        notificationQueryParams.getStart(), listener.getOutputType(), uri,
+                        getMonitoringModule(schemaContext), exist, schemaContext);
+        writeDataToDS(schemaContext, listener.getPath().getLastPathArgument().getNodeType().getLocalName(), wTx, exist,
+                mapToStreams);
+        submitData(wTx);
+        return uri;
+    }
+
+    public static Module getMonitoringModule(final SchemaContext schemaContext) {
+        final Module monitoringModule =
+                schemaContext.findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE);
+        return monitoringModule;
+    }
+
+    /**
+     * Parse input of query parameters - start-time or stop-time - from
+     * {@link DateAndTime} format to {@link Instant} format.
+     *
+     * @param entry
+     *             start-time or stop-time as string in {@link DateAndTime}
+     *            format
+     * @return parsed {@link Instant} by entry
+     */
+    public static Instant parseDateFromQueryParam(final Entry<String, List<String>> entry) {
+        final DateAndTime event = new DateAndTime(entry.getValue().iterator().next());
+        final String value = event.getValue();
+        final TemporalAccessor p;
+        try {
+            p = FORMATTER.parse(value);
+        } catch (final DateTimeParseException e) {
+            throw new RestconfDocumentedException("Cannot parse of value in date: " + value, e);
+        }
+        return Instant.from(p);
+
+    }
+
+    @SuppressWarnings("rawtypes")
+    static void writeDataToDS(final SchemaContext schemaContext,
+                              final String name, final DOMDataReadWriteTransaction readWriteTransaction,
+                              final boolean exist, final NormalizedNode mapToStreams) {
+        String pathId = "";
+        if (exist) {
+            pathId = MonitoringModule.PATH_TO_STREAM_WITHOUT_KEY + name;
+        } else {
+            pathId = MonitoringModule.PATH_TO_STREAMS;
+        }
+        readWriteTransaction.merge(LogicalDatastoreType.OPERATIONAL, IdentifierCodec.deserialize(pathId, schemaContext),
+                mapToStreams);
+    }
+
+    static void submitData(final DOMDataReadWriteTransaction readWriteTransaction) {
+        try {
+            readWriteTransaction.submit().checkedGet();
+        } catch (final TransactionCommitFailedException e) {
+            throw new RestconfDocumentedException("Problem while putting data to DS.", e);
+        }
+    }
+
+    /**
+     * Prepare map of values from URI.
+     *
+     * @param identifier
+     *             URI
+     * @return {@link Map}
+     */
+    public static Map<String, String> mapValuesFromUri(final String identifier) {
+        final HashMap<String, String> result = new HashMap<>();
+        for (final String token : RestconfConstants.SLASH_SPLITTER.split(identifier)) {
+            final String[] paramToken = token.split(String.valueOf(RestconfStreamsConstants.EQUAL));
+            if (paramToken.length == 2) {
+                result.put(paramToken[0], paramToken[1]);
+            }
+        }
+        return result;
+    }
+
+    static URI prepareUriByStreamName(final UriInfo uriInfo, final String streamName) {
+        final int port = SubscribeToStreamUtil.prepareNotificationPort();
+
+        final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
+        final UriBuilder uriToWebSocketServer =
+                uriBuilder.port(port).scheme(RestconfStreamsConstants.SCHEMA_SUBSCIBRE_URI);
+        final URI uri = uriToWebSocketServer.replacePath(streamName).build();
+        return uri;
+    }
+
+    /**
+     * Register data change listener in dom data broker and set it to listener
+     * on stream.
+     *
+     * @param ds
+     *             {@link LogicalDatastoreType}
+     * @param scope
+     *             {@link DataChangeScope}
+     * @param listener
+     *             listener on specific stream
+     * @param domDataBroker
+     *             data broker for register data change listener
+     */
+    @SuppressWarnings("deprecation")
+    private static void registration(final LogicalDatastoreType ds, final DataChangeScope scope,
+            final ListenerAdapter listener, final DOMDataBroker domDataBroker) {
+        if (listener.isListening()) {
+            return;
+        }
+
+        final YangInstanceIdentifier path = listener.getPath();
+        final ListenerRegistration<DOMDataChangeListener> registration =
+                domDataBroker.registerDataChangeListener(ds, path, listener, scope);
+
+        listener.setRegistration(registration);
+    }
+
+    /**
+     * Get port from web socket server. If doesn't exit, create it.
+     *
+     * @return port
+     */
+    private static int prepareNotificationPort() {
+        int port = RestconfStreamsConstants.NOTIFICATION_PORT;
+        try {
+            final WebSocketServer webSocketServer = WebSocketServer.getInstance();
+            port = webSocketServer.getPort();
+        } catch (final NullPointerException e) {
+            WebSocketServer.createInstance(RestconfStreamsConstants.NOTIFICATION_PORT);
+        }
+        return port;
+    }
+
+    static boolean checkExist(final SchemaContext schemaContext,
+                              final DOMDataReadWriteTransaction readWriteTransaction) {
+        boolean exist;
+        try {
+            exist = readWriteTransaction.exists(LogicalDatastoreType.OPERATIONAL,
+                    IdentifierCodec.deserialize(MonitoringModule.PATH_TO_STREAMS, schemaContext)).checkedGet();
+        } catch (final ReadFailedException e1) {
+            throw new RestconfDocumentedException("Problem while checking data if exists", e1);
+        }
+        return exist;
+    }
+
+    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);
+    }
+
+    /**
+     * Parse enum from URI.
+     *
+     * @param clazz
+     *             enum type
+     * @param value
+     *             string of enum value
+     * @return enum
+     */
+    private static <T> T parseURIEnum(final Class<T> clazz, final String value) {
+        if ((value == null) || value.equals("")) {
+            return null;
+        }
+        return ResolveEnumUtil.resolveEnum(clazz, value);
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/TransactionUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/rests/utils/TransactionUtil.java
new file mode 100644 (file)
index 0000000..a4452b6
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * 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.nb.rfc8040.rests.utils;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+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.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.nb.rfc8040.RestConnectorProvider;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Util class for common methods of transactions.
+ *
+ */
+public final class TransactionUtil {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TransactionUtil.class);
+
+    private TransactionUtil() {
+        throw new UnsupportedOperationException("Util class");
+    }
+
+    /**
+     * Merged parents of data.
+     *
+     * @param path
+     *             path of data
+     * @param schemaContext
+     *             {@link SchemaContext}
+     * @param writeTx
+     *             write transaction
+     */
+    public static void ensureParentsByMerge(final YangInstanceIdentifier path, final SchemaContext schemaContext,
+            final DOMDataWriteTransaction writeTx) {
+        final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
+        YangInstanceIdentifier rootNormalizedPath = null;
+
+        final Iterator<PathArgument> it = path.getPathArguments().iterator();
+
+        while (it.hasNext()) {
+            final PathArgument pathArgument = it.next();
+            if (rootNormalizedPath == null) {
+                rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
+            }
+
+            if (it.hasNext()) {
+                normalizedPathWithoutChildArgs.add(pathArgument);
+            }
+        }
+
+        if (normalizedPathWithoutChildArgs.isEmpty()) {
+            return;
+        }
+
+        Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
+
+        final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
+                YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
+        writeTx.merge(LogicalDatastoreType.CONFIGURATION, rootNormalizedPath, parentStructure);
+    }
+
+    /**
+     * Check if items already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
+     * data does NOT already exists.
+     * @param transactionChain Transaction chain
+     * @param rwTransaction Transaction
+     * @param store Datastore
+     * @param path Path to be checked
+     * @param operationType Type of operation (READ, POST, PUT, DELETE...)
+     */
+    public static void checkItemExists(final DOMTransactionChain transactionChain,
+                                       final DOMDataReadWriteTransaction rwTransaction,
+                                       final LogicalDatastoreType store, final YangInstanceIdentifier path,
+                                       final String operationType) {
+        final CheckedFuture<Boolean, ReadFailedException> future = rwTransaction.exists(store, path);
+        final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
+
+        FutureCallbackTx.addCallback(future, operationType, response);
+
+        if (!response.result) {
+            // close transaction and reset transaction chain
+            rwTransaction.cancel();
+            RestConnectorProvider.resetTransactionChainForAdapaters(transactionChain);
+
+            // throw error
+            final String errMsg = "Operation via Restconf was not executed because data does not exist";
+            LOG.trace("{}:{}", errMsg, path);
+            throw new RestconfDocumentedException(
+                    "Data does not exist", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, path);
+        }
+    }
+
+    /**
+     * Check if items do NOT already exists at specified {@code path}. Throws {@link RestconfDocumentedException} if
+     * data already exists.
+     * @param transactionChain Transaction chain
+     * @param rwTransaction Transaction
+     * @param store Datastore
+     * @param path Path to be checked
+     * @param operationType Type of operation (READ, POST, PUT, DELETE...)
+     */
+    public static void checkItemDoesNotExists(final DOMTransactionChain transactionChain,
+                                              final DOMDataReadWriteTransaction rwTransaction,
+                                              final LogicalDatastoreType store, final YangInstanceIdentifier path,
+                                              final String operationType) {
+        final CheckedFuture<Boolean, ReadFailedException> future = rwTransaction.exists(store, path);
+        final FutureDataFactory<Boolean> response = new FutureDataFactory<>();
+
+        FutureCallbackTx.addCallback(future, operationType, response);
+
+        if (response.result) {
+            // close transaction and reset transaction chain
+            rwTransaction.cancel();
+            RestConnectorProvider.resetTransactionChainForAdapaters(transactionChain);
+
+            // throw error
+            final String errMsg = "Operation via Restconf was not executed because data already exists";
+            LOG.trace("{}:{}", errMsg, path);
+            throw new RestconfDocumentedException(
+                    "Data already exists", ErrorType.PROTOCOL, ErrorTag.DATA_EXISTS, path);
+        }
+    }
+}
@@ -5,7 +5,7 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-package org.opendaylight.restconf.nb.rfc8040.simple.services.api;
+package org.opendaylight.restconf.nb.rfc8040.services.simple.api;
 
 /**
  * Wrapper for all base services.
@@ -5,7 +5,7 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-package org.opendaylight.restconf.nb.rfc8040.simple.services.api;
+package org.opendaylight.restconf.nb.rfc8040.services.simple.api;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
@@ -5,7 +5,7 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-package org.opendaylight.restconf.nb.rfc8040.simple.services.api;
+package org.opendaylight.restconf.nb.rfc8040.services.simple.api;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
@@ -5,7 +5,7 @@
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
-package org.opendaylight.restconf.nb.rfc8040.simple.services.api;
+package org.opendaylight.restconf.nb.rfc8040.services.simple.api;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeContainerSchemaNode.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeContainerSchemaNode.java
new file mode 100644 (file)
index 0000000..8604f57
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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.nb.rfc8040.services.simple.impl;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+/**
+ * Special case only use by GET restconf/operations (since moment of old Yang
+ * parser and old yang model API removal) to build and use fake container for
+ * module.
+ */
+class FakeContainerSchemaNode implements ContainerSchemaNode {
+    static final SchemaPath PATH =
+            SchemaPath.create(true, QName.create(FakeRestconfModule.QNAME, "operations").intern());
+
+    private final Collection<DataSchemaNode> children;
+
+    FakeContainerSchemaNode(final Collection<LeafSchemaNode> children) {
+        this.children = ImmutableList.copyOf(children);
+    }
+
+    @Override
+    public Set<TypeDefinition<?>> getTypeDefinitions() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public Collection<DataSchemaNode> getChildNodes() {
+        return this.children;
+    }
+
+    @Override
+    public Set<GroupingDefinition> getGroupings() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public DataSchemaNode getDataChildByName(final QName name) {
+        for (final DataSchemaNode node : this.children) {
+            if (node.getQName().equals(name)) {
+                return node;
+            }
+        }
+        throw new RestconfDocumentedException(name + " is not in child of " + PATH.getLastComponent());
+    }
+
+    @Override
+    public Set<UsesNode> getUses() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public Set<AugmentationSchema> getAvailableAugmentations() {
+        return new HashSet<>();
+    }
+
+    @Override
+    public boolean isAugmenting() {
+        return false;
+    }
+
+    @Override
+    public boolean isAddedByUses() {
+        return false;
+    }
+
+    @Override
+    public boolean isConfiguration() {
+        return false;
+    }
+
+    @Override
+    public ConstraintDefinition getConstraints() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public QName getQName() {
+        return PATH.getLastComponent();
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return PATH;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public String getDescription() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public String getReference() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public Status getStatus() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public boolean isPresenceContainer() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeImportedModule.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeImportedModule.java
new file mode 100644 (file)
index 0000000..5bcab1a
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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.nb.rfc8040.services.simple.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ForwardingObject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+final class FakeImportedModule extends ForwardingObject implements Module {
+
+    private final Module delegate;
+
+    FakeImportedModule(final Module delegate) {
+        this.delegate = Preconditions.checkNotNull(delegate);
+    }
+
+    @Override
+    protected Module delegate() {
+        return delegate;
+    }
+
+    @Override
+    public Set<TypeDefinition<?>> getTypeDefinitions() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Collection<DataSchemaNode> getChildNodes() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public Set<GroupingDefinition> getGroupings() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public DataSchemaNode getDataChildByName(final QName name) {
+        return null;
+    }
+
+    @Override
+    public Set<UsesNode> getUses() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public String getModuleSourcePath() {
+        return null;
+    }
+
+    @Override
+    public QNameModule getQNameModule() {
+        return delegate.getQNameModule();
+    }
+
+    @Override
+    public String getName() {
+        return delegate.getName();
+    }
+
+    @Override
+    public URI getNamespace() {
+        return delegate.getNamespace();
+    }
+
+    @Override
+    public Date getRevision() {
+        return delegate.getRevision();
+    }
+
+    @Override
+    public String getPrefix() {
+        return delegate.getPrefix();
+    }
+
+    @Override
+    public String getYangVersion() {
+        return delegate.getYangVersion();
+    }
+
+    @Override
+    public String getDescription() {
+        return delegate.getDescription();
+    }
+
+    @Override
+    public String getReference() {
+        return delegate.getReference();
+    }
+
+    @Override
+    public String getOrganization() {
+        return delegate.getOrganization();
+    }
+
+    @Override
+    public String getContact() {
+        return delegate.getContact();
+    }
+
+    @Override
+    public Set<ModuleImport> getImports() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Set<Module> getSubmodules() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Set<FeatureDefinition> getFeatures() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Set<AugmentationSchema> getAugmentations() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Set<RpcDefinition> getRpcs() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Set<Deviation> getDeviations() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Set<IdentitySchemaNode> getIdentities() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public List<ExtensionDefinition> getExtensionSchemaNodes() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public String getSource() {
+        return null;
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeLeafSchemaNode.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeLeafSchemaNode.java
new file mode 100644 (file)
index 0000000..053fd33
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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.nb.rfc8040.services.simple.impl;
+
+import java.util.List;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
+
+/**
+ * Special case only use by GET restconf/operations (since moment of old Yang
+ * parser and old yang model API removal) to build and use fake leaf like child
+ * in container.
+ */
+final class FakeLeafSchemaNode implements LeafSchemaNode {
+
+    private final SchemaPath path;
+
+    /**
+     * Base values for fake leaf schema node.
+     *
+     * @param qname
+     *             qname
+     */
+    FakeLeafSchemaNode(final QName qname) {
+        this.path = FakeContainerSchemaNode.PATH.createChild(qname);
+    }
+
+    @Override
+    public boolean isAugmenting() {
+        return true;
+    }
+
+    @Override
+    public boolean isAddedByUses() {
+        return false;
+    }
+
+    @Override
+    public boolean isConfiguration() {
+        return false;
+    }
+
+    @Override
+    public ConstraintDefinition getConstraints() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public QName getQName() {
+        return path.getLastComponent();
+    }
+
+    @Override
+    public SchemaPath getPath() {
+        return path;
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public String getDescription() {
+        throw new UnsupportedOperationException("Not supported operations.");
+    }
+
+    @Override
+    public String getReference() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public Status getStatus() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public TypeDefinition<?> getType() {
+        return BaseTypes.emptyType();
+    }
+
+    @Override
+    public String getDefault() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public String getUnits() {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeModuleImport.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeModuleImport.java
new file mode 100644 (file)
index 0000000..8ce1b39
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.nb.rfc8040.services.simple.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Date;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+
+/**
+ * Fake {@link ModuleImport} implementation used to attach corrent prefix mapping to fake RPCs.
+ *
+ * @author Robert Varga
+ */
+final class FakeModuleImport implements ModuleImport {
+    private final Module module;
+
+    FakeModuleImport(final Module module) {
+        this.module = Preconditions.checkNotNull(module);
+    }
+
+    @Override
+    public String getModuleName() {
+        return module.getName();
+    }
+
+    @Override
+    public Date getRevision() {
+        return module.getRevision();
+    }
+
+    @Override
+    public String getPrefix() {
+        return module.getName();
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeRestconfModule.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/FakeRestconfModule.java
new file mode 100644 (file)
index 0000000..73ed275
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * 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.nb.rfc8040.services.simple.impl;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.common.YangVersion;
+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.Deviation;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.ModuleImport;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+/**
+ * Special case only use by GET restconf/operations (since moment of old Yang
+ * parser and old yang model API removal) to build and use fake module to create
+ * new schema context.
+ */
+final class FakeRestconfModule implements Module {
+
+    static final QNameModule QNAME;
+
+    static {
+        Date date;
+        try {
+            date = SimpleDateFormatUtil.getRevisionFormat().parse("2016-06-28");
+        } catch (final ParseException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+        QNAME = QNameModule.create(URI.create("urn:ietf:params:xml:ns:yang:ietf-restconf"), date).intern();
+    }
+
+    private final Collection<DataSchemaNode> children;
+    private final ImmutableSet<ModuleImport> imports;
+
+    /**
+     * Instantiate a new fake module.
+     *
+     * @param neededModules needed import statements
+     * @param child fake child container
+     */
+    FakeRestconfModule(final Collection<Module> neededModules, final ContainerSchemaNode child) {
+        this.children = ImmutableList.of(child);
+        this.imports = ImmutableSet.copyOf(Collections2.transform(neededModules, FakeModuleImport::new));
+    }
+
+    @Override
+    public Set<TypeDefinition<?>> getTypeDefinitions() {
+        throw new UnsupportedOperationException("Not supported operations.");
+    }
+
+    @Override
+    public Collection<DataSchemaNode> getChildNodes() {
+        return this.children;
+    }
+
+    @Override
+    public Set<GroupingDefinition> getGroupings() {
+        throw new UnsupportedOperationException("Not supported operations.");
+    }
+
+    @Override
+    public DataSchemaNode getDataChildByName(final QName name) {
+        for (final DataSchemaNode node : this.children) {
+            if (node.getQName().equals(name)) {
+                return node;
+            }
+        }
+        throw new RestconfDocumentedException(name + " is not in child of " + FakeRestconfModule.QNAME);
+    }
+
+    @Override
+    public Set<UsesNode> getUses() {
+        throw new UnsupportedOperationException("Not supported operations.");
+    }
+
+    @Override
+    public String getModuleSourcePath() {
+        throw new UnsupportedOperationException("Not supported operations.");
+    }
+
+    @Override
+    public QNameModule getQNameModule() {
+        return QNAME;
+    }
+
+    @Override
+    public String getName() {
+        return "ietf-restconf";
+    }
+
+    @Override
+    public URI getNamespace() {
+        return QNAME.getNamespace();
+    }
+
+    @Override
+    public Date getRevision() {
+        return QNAME.getRevision();
+    }
+
+    @Override
+    public String getPrefix() {
+        return "restconf";
+    }
+
+    @Override
+    public String getYangVersion() {
+        return YangVersion.VERSION_1.toString();
+    }
+
+    @Override
+    public String getDescription() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public String getReference() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public String getOrganization() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public String getContact() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public Set<ModuleImport> getImports() {
+        return imports;
+    }
+
+    @Override
+    public Set<Module> getSubmodules() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Set<FeatureDefinition> getFeatures() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public Set<NotificationDefinition> getNotifications() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public Set<AugmentationSchema> getAugmentations() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public Set<RpcDefinition> getRpcs() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public Set<Deviation> getDeviations() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public Set<IdentitySchemaNode> getIdentities() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public List<ExtensionDefinition> getExtensionSchemaNodes() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+
+    @Override
+    public String getSource() {
+        throw new UnsupportedOperationException("Operation not implemented.");
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/RestconfImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/RestconfImpl.java
new file mode 100644 (file)
index 0000000..bb84a09
--- /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.restconf.nb.rfc8040.services.simple.impl;
+
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040.IetfYangLibrary;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040.RestconfModule;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.services.simple.api.RestconfService;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+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.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+public class RestconfImpl implements RestconfService {
+
+    private final SchemaContextHandler schemaContextHandler;
+
+    public RestconfImpl(final SchemaContextHandler schemaContextHandler) {
+        this.schemaContextHandler = schemaContextHandler;
+    }
+
+    @Override
+    public NormalizedNodeContext getLibraryVersion() {
+        final SchemaContext context = this.schemaContextHandler.get();
+        SchemaNode schemaNode = null;
+        for (final GroupingDefinition groupingDefinition : context
+                .findModuleByNamespaceAndRevision(RestconfModule.URI_MODULE, RestconfModule.DATE).getGroupings()) {
+            if (groupingDefinition.getQName().equals(RestconfModule.RESTCONF_GROUPING_QNAME)) {
+                schemaNode = ((ContainerSchemaNode) groupingDefinition
+                        .getDataChildByName(RestconfModule.RESTCONF_CONTAINER_QNAME))
+                                .getDataChildByName(RestconfModule.LIB_VER_LEAF_QNAME);
+            }
+        }
+        final YangInstanceIdentifier yangIId = YangInstanceIdentifier.of(
+                QName.create(RestconfModule.NAME, RestconfModule.REVISION, RestconfModule.LIB_VER_LEAF_SCHEMA_NODE));
+        final InstanceIdentifierContext<? extends SchemaNode> iid =
+                new InstanceIdentifierContext<SchemaNode>(yangIId, schemaNode, null, context);
+        final NormalizedNode<?, ?> data =
+                Builders.leafBuilder((LeafSchemaNode) schemaNode).withValue(IetfYangLibrary.REVISION).build();
+        return new NormalizedNodeContext(iid, data);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/RestconfOperationsServiceImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/RestconfOperationsServiceImpl.java
new file mode 100644 (file)
index 0000000..d2b383b
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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.nb.rfc8040.services.simple.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
+import org.opendaylight.restconf.nb.rfc8040.services.simple.api.RestconfOperationsService;
+import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+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.model.api.ContainerSchemaNode;
+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.util.SimpleSchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of {@link RestconfOperationsService}.
+ *
+ */
+public class RestconfOperationsServiceImpl implements RestconfOperationsService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestconfOperationsServiceImpl.class);
+
+    private final SchemaContextHandler schemaContextHandler;
+    private final DOMMountPointServiceHandler domMountPointServiceHandler;
+
+    /**
+     * Set {@link SchemaContextHandler} for getting actual {@link SchemaContext}.
+     *
+     * @param schemaContextHandler
+     *             handling schema context
+     * @param domMountPointServiceHandler
+     *             handling dom mount point service
+     */
+    public RestconfOperationsServiceImpl(final SchemaContextHandler schemaContextHandler,
+            final DOMMountPointServiceHandler domMountPointServiceHandler) {
+        this.schemaContextHandler = schemaContextHandler;
+        this.domMountPointServiceHandler = domMountPointServiceHandler;
+    }
+
+    @Override
+    public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
+        final SchemaContextRef ref = new SchemaContextRef(this.schemaContextHandler.get());
+        return getOperations(ref.getModules(), null);
+    }
+
+    @Override
+    public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
+        final Set<Module> modules;
+        final DOMMountPoint mountPoint;
+        final SchemaContextRef ref = new SchemaContextRef(this.schemaContextHandler.get());
+        if (identifier.contains(RestconfConstants.MOUNT)) {
+            final InstanceIdentifierContext<?> mountPointIdentifier = ParserIdentifier.toInstanceIdentifier(identifier,
+                    ref.get(), Optional.of(this.domMountPointServiceHandler.get()));
+            mountPoint = mountPointIdentifier.getMountPoint();
+            modules = ref.getModules(mountPoint);
+        } else {
+            final String errMsg =
+                    "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
+            LOG.debug(errMsg + RestconfConstants.MOUNT + " for " + identifier);
+            throw new RestconfDocumentedException(errMsg + RestconfConstants.MOUNT, ErrorType.PROTOCOL,
+                    ErrorTag.INVALID_VALUE);
+        }
+
+        return getOperations(modules, mountPoint);
+    }
+
+    /**
+     * Special case only for GET restconf/operations use (since moment of old
+     * Yang parser and old Yang model API removal). The method is creating fake
+     * schema context with fake module and fake data by use own implementations
+     * of schema nodes and module.
+     *
+     * @param modules
+     *             set of modules for get RPCs from every module
+     * @param mountPoint
+     *             mount point, if in use otherwise null
+     * @return {@link NormalizedNodeContext}
+     */
+    private static NormalizedNodeContext getOperations(final Set<Module> modules, final DOMMountPoint mountPoint) {
+        final Collection<Module> neededModules = new ArrayList<>(modules.size());
+        final ArrayList<LeafSchemaNode> fakeRpcSchema = new ArrayList<>();
+
+        for (final Module m : modules) {
+            final Set<RpcDefinition> rpcs = m.getRpcs();
+            if (!rpcs.isEmpty()) {
+                neededModules.add(m);
+
+                fakeRpcSchema.ensureCapacity(fakeRpcSchema.size() + rpcs.size());
+                rpcs.forEach(rpc -> fakeRpcSchema.add(new FakeLeafSchemaNode(rpc.getQName())));
+            }
+        }
+
+        final ContainerSchemaNode fakeCont = new FakeContainerSchemaNode(fakeRpcSchema);
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder =
+                Builders.containerBuilder(fakeCont);
+
+        for (final LeafSchemaNode leaf : fakeRpcSchema) {
+            containerBuilder.withChild(Builders.leafBuilder(leaf).build());
+        }
+
+        final Collection<Module> fakeModules = new ArrayList<>(neededModules.size() + 1);
+        neededModules.forEach(imp -> fakeModules.add(new FakeImportedModule(imp)));
+        fakeModules.add(new FakeRestconfModule(neededModules, fakeCont));
+
+        final SchemaContext fakeSchemaCtx = SimpleSchemaContext.forModules(ImmutableSet.copyOf(fakeModules));
+        final InstanceIdentifierContext<ContainerSchemaNode> instanceIdentifierContext =
+                new InstanceIdentifierContext<>(null, fakeCont, mountPoint, fakeSchemaCtx);
+        return new NormalizedNodeContext(instanceIdentifierContext, containerBuilder.build());
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/RestconfSchemaServiceImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/simple/impl/RestconfSchemaServiceImpl.java
new file mode 100644 (file)
index 0000000..e19e9db
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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.nb.rfc8040.services.simple.impl;
+
+import org.opendaylight.restconf.common.schema.SchemaExportContext;
+import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.references.SchemaContextRef;
+import org.opendaylight.restconf.nb.rfc8040.services.simple.api.RestconfSchemaService;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Implementation of {@link RestconfSchemaService}.
+ *
+ */
+public class RestconfSchemaServiceImpl implements RestconfSchemaService {
+
+    private final SchemaContextHandler schemaContextHandler;
+    private final DOMMountPointServiceHandler domMountPointServiceHandler;
+
+    /**
+     * Set {@link SchemaContextHandler} for getting actual {@link SchemaContext}
+     * .
+     *
+     * @param schemaContextHandler
+     *             handling schema context
+     * @param domMountPointServiceHandler
+     *             handling dom mount point service
+     */
+    public RestconfSchemaServiceImpl(final SchemaContextHandler schemaContextHandler,
+            final DOMMountPointServiceHandler domMountPointServiceHandler) {
+        this.schemaContextHandler = schemaContextHandler;
+        this.domMountPointServiceHandler = domMountPointServiceHandler;
+    }
+
+    @Override
+    public SchemaExportContext getSchema(final String identifier) {
+        final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
+        return ParserIdentifier.toSchemaExportContextFromIdentifier(schemaContextRef.get(), identifier,
+                this.domMountPointServiceHandler.get());
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/wrapper/ServicesWrapperImpl.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/services/wrapper/ServicesWrapperImpl.java
new file mode 100644 (file)
index 0000000..6bef197
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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.nb.rfc8040.services.wrapper;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.restconf.common.context.NormalizedNodeContext;
+import org.opendaylight.restconf.common.patch.PatchContext;
+import org.opendaylight.restconf.common.patch.PatchStatusContext;
+import org.opendaylight.restconf.common.schema.SchemaExportContext;
+import org.opendaylight.restconf.nb.rfc8040.handlers.DOMDataBrokerHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.DOMMountPointServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.NotificationServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.RpcServiceHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfDataService;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfInvokeOperationsService;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.api.TransactionServicesWrapper;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfDataServiceImpl;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfInvokeOperationsServiceImpl;
+import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfStreamsSubscriptionServiceImpl;
+import org.opendaylight.restconf.nb.rfc8040.services.simple.api.BaseServicesWrapper;
+import org.opendaylight.restconf.nb.rfc8040.services.simple.api.RestconfOperationsService;
+import org.opendaylight.restconf.nb.rfc8040.services.simple.api.RestconfSchemaService;
+import org.opendaylight.restconf.nb.rfc8040.services.simple.api.RestconfService;
+import org.opendaylight.restconf.nb.rfc8040.services.simple.impl.RestconfImpl;
+import org.opendaylight.restconf.nb.rfc8040.services.simple.impl.RestconfOperationsServiceImpl;
+import org.opendaylight.restconf.nb.rfc8040.services.simple.impl.RestconfSchemaServiceImpl;
+
+/**
+ * Wrapper for services.
+ * <ul>
+ * <li>{@link BaseServicesWrapper}
+ * <li>{@link TransactionServicesWrapper}
+ * </ul>
+ *
+ */
+@Path("/")
+public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServicesWrapper {
+
+    private RestconfDataService delegRestconfDataService;
+    private RestconfInvokeOperationsService delegRestconfInvokeOpsService;
+    private RestconfStreamsSubscriptionService delegRestconfSubscrService;
+    private RestconfOperationsService delegRestOpsService;
+    private RestconfSchemaService delegRestSchService;
+    private RestconfService delegRestService;
+
+    private ServicesWrapperImpl() {
+    }
+
+    private static class InstanceHolder {
+        public static final ServicesWrapperImpl INSTANCE = new ServicesWrapperImpl();
+    }
+
+    public static ServicesWrapperImpl getInstance() {
+        return InstanceHolder.INSTANCE;
+    }
+
+    @Override
+    public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
+        return this.delegRestOpsService.getOperations(uriInfo);
+    }
+
+    @Override
+    public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
+        return this.delegRestOpsService.getOperations(identifier, uriInfo);
+    }
+
+    @Override
+    public SchemaExportContext getSchema(final String mountAndModuleId) {
+        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, final UriInfo uriInfo) {
+        return this.delegRestconfDataService.putData(identifier, payload, uriInfo);
+    }
+
+    @Override
+    public Response postData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+        return this.delegRestconfDataService.postData(identifier, payload, uriInfo);
+    }
+
+    @Override
+    public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
+        return this.delegRestconfDataService.postData(payload, uriInfo);
+    }
+
+    @Override
+    public Response deleteData(final String identifier) {
+        return this.delegRestconfDataService.deleteData(identifier);
+    }
+
+    @Override
+    public PatchStatusContext patchData(final String identifier, final PatchContext context, final UriInfo uriInfo) {
+        return this.delegRestconfDataService.patchData(identifier, context, uriInfo);
+    }
+
+    @Override
+    public PatchStatusContext patchData(final PatchContext context, final UriInfo uriInfo) {
+        return this.delegRestconfDataService.patchData(context, uriInfo);
+    }
+
+    @Override
+    public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload,
+            final UriInfo uriInfo) {
+        return this.delegRestconfInvokeOpsService.invokeRpc(identifier, payload, uriInfo);
+    }
+
+    @Override
+    public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
+        return this.delegRestconfSubscrService.subscribeToStream(identifier, uriInfo);
+    }
+
+    @Override
+    public NormalizedNodeContext getLibraryVersion() {
+        return this.delegRestService.getLibraryVersion();
+    }
+
+    public void setHandlers(final SchemaContextHandler schemaCtxHandler,
+            final DOMMountPointServiceHandler domMountPointServiceHandler,
+            final TransactionChainHandler transactionChainHandler, final DOMDataBrokerHandler domDataBrokerHandler,
+            final RpcServiceHandler rpcServiceHandler, final NotificationServiceHandler notificationServiceHandler) {
+        this.delegRestOpsService = new RestconfOperationsServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
+        this.delegRestSchService = new RestconfSchemaServiceImpl(schemaCtxHandler, domMountPointServiceHandler);
+        this.delegRestconfSubscrService = new RestconfStreamsSubscriptionServiceImpl(domDataBrokerHandler,
+                notificationServiceHandler, schemaCtxHandler, transactionChainHandler);
+        this.delegRestconfDataService =
+                new RestconfDataServiceImpl(schemaCtxHandler, transactionChainHandler, domMountPointServiceHandler,
+                        this.delegRestconfSubscrService);
+        this.delegRestconfInvokeOpsService =
+                new RestconfInvokeOperationsServiceImpl(rpcServiceHandler, schemaCtxHandler);
+        this.delegRestService = new RestconfImpl(schemaCtxHandler);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractCommonSubscriber.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractCommonSubscriber.java
new file mode 100644 (file)
index 0000000..ea3ad27
--- /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.nb.rfc8040.streams.listeners;
+
+import com.google.common.eventbus.AsyncEventBus;
+import com.google.common.eventbus.EventBus;
+import io.netty.channel.Channel;
+import io.netty.util.internal.ConcurrentSet;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Features of subscribing part of both notifications.
+ */
+abstract class AbstractCommonSubscriber extends AbstractQueryParams implements BaseListenerInterface {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractCommonSubscriber.class);
+
+    private final Set<Channel> subscribers = new ConcurrentSet<>();
+    private final EventBus eventBus;
+
+    @SuppressWarnings("rawtypes")
+    private EventBusChangeRecorder eventBusChangeRecorder;
+    @SuppressWarnings("rawtypes")
+    private ListenerRegistration registration;
+
+    /**
+     * Creating {@link EventBus}.
+     */
+    protected AbstractCommonSubscriber() {
+        this.eventBus = new AsyncEventBus(Executors.newSingleThreadExecutor());
+    }
+
+    @Override
+    public final boolean hasSubscribers() {
+        return !this.subscribers.isEmpty();
+    }
+
+    @Override
+    public final Set<Channel> getSubscribers() {
+        return this.subscribers;
+    }
+
+    @Override
+    public final void close() throws Exception {
+        this.registration.close();
+        this.registration = null;
+
+        deleteDataInDS();
+        unregister();
+    }
+
+    /**
+     * Creates event of type {@link EventType#REGISTER}, set {@link Channel}
+     * subscriber to the event and post event into event bus.
+     *
+     * @param subscriber
+     *            Channel
+     */
+    public void addSubscriber(final Channel subscriber) {
+        if (!subscriber.isActive()) {
+            LOG.debug("Channel is not active between websocket server and subscriber {}" + subscriber.remoteAddress());
+        }
+        final Event event = new Event(EventType.REGISTER);
+        event.setSubscriber(subscriber);
+        this.eventBus.post(event);
+    }
+
+    /**
+     * Creates event of type {@link EventType#DEREGISTER}, sets {@link Channel}
+     * subscriber to the event and posts event into event bus.
+     *
+     * @param subscriber subscriber channel
+     */
+    public void removeSubscriber(final Channel subscriber) {
+        LOG.debug("Subscriber {} is removed.", subscriber.remoteAddress());
+        final Event event = new Event(EventType.DEREGISTER);
+        event.setSubscriber(subscriber);
+        this.eventBus.post(event);
+    }
+
+    /**
+     * Sets {@link ListenerRegistration} registration.
+     *
+     * @param registration
+     *            DOMDataChangeListener registration
+     */
+    @SuppressWarnings("rawtypes")
+    public void setRegistration(final ListenerRegistration registration) {
+        this.registration = registration;
+    }
+
+    /**
+     * Checks if {@link ListenerRegistration} registration exist.
+     *
+     * @return True if exist, false otherwise.
+     */
+    public boolean isListening() {
+        return this.registration != null;
+    }
+
+    /**
+     * Creating and registering {@link EventBusChangeRecorder} of specific
+     * listener on {@link EventBus}.
+     *
+     * @param listener
+     *            specific listener of notifications
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    protected <T extends BaseListenerInterface> void register(final T listener) {
+        this.eventBusChangeRecorder = new EventBusChangeRecorder(listener);
+        this.eventBus.register(this.eventBusChangeRecorder);
+    }
+
+    /**
+     * Post event to event bus.
+     *
+     * @param event
+     *            data of incoming notifications
+     */
+    protected void post(final Event event) {
+        this.eventBus.post(event);
+    }
+
+    /**
+     * Removes all subscribers and unregisters event bus change recorder form
+     * event bus.
+     */
+    protected void unregister() {
+        this.subscribers.clear();
+        this.eventBus.unregister(this.eventBusChangeRecorder);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractNotificationsData.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractNotificationsData.java
new file mode 100644 (file)
index 0000000..78f6dd1
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * 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.nb.rfc8040.streams.listeners;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040.MonitoringModule;
+import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
+import org.opendaylight.restconf.nb.rfc8040.handlers.TransactionChainHandler;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.IdentifierCodec;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+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.xml.XMLStreamNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Abstract class for processing and preparing data.
+ *
+ */
+abstract class AbstractNotificationsData {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractNotificationsData.class);
+    private static final TransformerFactory TF = TransformerFactory.newInstance();
+    private static final XMLOutputFactory OF = XMLOutputFactory.newFactory();
+
+    private TransactionChainHandler transactionChainHandler;
+    protected SchemaContextHandler schemaHandler;
+    private String localName;
+
+    /**
+     * Transaction chain for delete data in DS on close().
+     *
+     * @param transactionChainHandler
+     *            creating new write transaction for delete data on close
+     * @param schemaHandler
+     *            for getting schema to deserialize
+     *            {@link MonitoringModule#PATH_TO_STREAM_WITHOUT_KEY} to
+     *            {@link YangInstanceIdentifier}
+     */
+    public void setCloseVars(final TransactionChainHandler transactionChainHandler,
+            final SchemaContextHandler schemaHandler) {
+        this.transactionChainHandler = transactionChainHandler;
+        this.schemaHandler = schemaHandler;
+    }
+
+    /**
+     * Delete data in DS.
+     */
+    protected void deleteDataInDS() throws Exception {
+        final DOMDataWriteTransaction wTx = this.transactionChainHandler.get().newWriteOnlyTransaction();
+        wTx.delete(LogicalDatastoreType.OPERATIONAL, IdentifierCodec
+                .deserialize(MonitoringModule.PATH_TO_STREAM_WITHOUT_KEY + this.localName, this.schemaHandler.get()));
+        wTx.submit().checkedGet();
+    }
+
+    /**
+     * Set localName of last path element of specific listener.
+     *
+     * @param localName
+     *            local name
+     */
+    protected void setLocalNameOfPath(final String localName) {
+        this.localName = localName;
+    }
+
+    /**
+     * Formats data specified by RFC3339.
+     *
+     * @param now time stamp
+     * @return Data specified by RFC3339.
+     */
+    protected static String toRFC3339(final Instant now) {
+        return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(now, ZoneId.systemDefault()));
+    }
+
+    /**
+     * Creates {@link Document} document.
+     *
+     * @return {@link Document} document.
+     */
+    protected static Document createDocument() {
+        return UntrustedXML.newDocumentBuilder().newDocument();
+    }
+
+    /**
+     * Write normalized node to {@link DOMResult}.
+     *
+     * @param normalized
+     *            data
+     * @param context
+     *            actual schema context
+     * @param schemaPath
+     *            schema path of data
+     * @return {@link DOMResult}
+     */
+    protected DOMResult writeNormalizedNode(final NormalizedNode<?, ?> normalized, final SchemaContext context,
+            final SchemaPath schemaPath) throws IOException, XMLStreamException {
+        final Document doc = UntrustedXML.newDocumentBuilder().newDocument();
+        final DOMResult result = new DOMResult(doc);
+        NormalizedNodeWriter normalizedNodeWriter = null;
+        NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
+        XMLStreamWriter writer = null;
+
+        try {
+            writer = OF.createXMLStreamWriter(result);
+            normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context, schemaPath);
+            normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
+
+            normalizedNodeWriter.write(normalized);
+
+            normalizedNodeWriter.flush();
+        } finally {
+            if (normalizedNodeWriter != null) {
+                normalizedNodeWriter.close();
+            }
+            if (normalizedNodeStreamWriter != null) {
+                normalizedNodeStreamWriter.close();
+            }
+            if (writer != null) {
+                writer.close();
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Generating base element of every notification.
+     *
+     * @param doc
+     *            base {@link Document}
+     * @return element of {@link Document}
+     */
+    protected Element basePartDoc(final Document doc) {
+        final Element notificationElement =
+                doc.createElementNS("urn:ietf:params:xml:ns:netconf:notification:1.0", "notification");
+
+        doc.appendChild(notificationElement);
+
+        final Element eventTimeElement = doc.createElement("eventTime");
+        eventTimeElement.setTextContent(toRFC3339(Instant.now()));
+        notificationElement.appendChild(eventTimeElement);
+
+        return notificationElement;
+    }
+
+    /**
+     * Generating of {@link Document} transforming to string.
+     *
+     * @param doc
+     *            {@link Document} with data
+     * @return - string from {@link Document}
+     */
+    protected String transformDoc(final Document doc) {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        try {
+            final Transformer transformer = TF.newTransformer();
+            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
+            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+            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(out));
+        } catch (final TransformerException e) {
+            // FIXME: this should raise an exception
+            final String msg = "Error during transformation of Document into String";
+            LOG.error(msg, e);
+            return msg;
+        }
+
+        return new String(out.toByteArray(), StandardCharsets.UTF_8);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractQueryParams.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractQueryParams.java
new file mode 100644 (file)
index 0000000..e9a412d
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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.nb.rfc8040.streams.listeners;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import java.io.StringReader;
+import java.time.Instant;
+import java.util.Optional;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+
+/**
+ * Features of query parameters part of both notifications.
+ *
+ */
+abstract class AbstractQueryParams extends AbstractNotificationsData {
+    // FIXME: BUG-7956: switch to using UntrustedXML
+    private static final DocumentBuilderFactory DBF;
+
+    static {
+        final DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
+        f.setCoalescing(true);
+        f.setExpandEntityReferences(false);
+        f.setIgnoringElementContentWhitespace(true);
+        f.setIgnoringComments(true);
+        f.setXIncludeAware(false);
+        try {
+            f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+            f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+            f.setFeature("http://xml.org/sax/features/external-general-entities", false);
+            f.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+        } catch (final ParserConfigurationException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+        DBF = f;
+    }
+
+    // FIXME: these should be final
+    private Instant start = null;
+    private Instant stop = null;
+    private String filter = null;
+    private boolean leafNodesOnly = false;
+
+    @VisibleForTesting
+    public final Instant getStart() {
+        return start;
+    }
+
+    /**
+     * 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
+     * @param leafNodesOnly
+     *            if true, notifications will contain changes to leaf nodes only
+     */
+    public void setQueryParams(final Instant start, final Optional<Instant> stop, final Optional<String> filter,
+                               final boolean leafNodesOnly) {
+        this.start = Preconditions.checkNotNull(start);
+        this.stop = stop.orElse(null);
+        this.filter = filter.orElse(null);
+        this.leafNodesOnly = leafNodesOnly;
+    }
+
+    /**
+     * Check whether this query should only notify about leaf node changes.
+     *
+     * @return true if this query should only notify about leaf node changes
+     */
+    public boolean getLeafNodesOnly() {
+        return leafNodesOnly;
+    }
+
+    /**
+     * Checking query parameters on specific notification.
+     *
+     * @param xml       data of notification
+     * @param listener  listener of notification
+     * @return true if notification meets the requirements of query parameters,
+     *         false otherwise
+     */
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    protected <T extends BaseListenerInterface> boolean checkQueryParams(final String xml, final T listener) {
+        final Instant now = Instant.now();
+        if (this.stop != null) {
+            if ((this.start.compareTo(now) < 0) && (this.stop.compareTo(now) > 0)) {
+                return checkFilter(xml);
+            }
+            if (this.stop.compareTo(now) < 0) {
+                try {
+                    listener.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;
+                return checkFilter(xml);
+            }
+        } else {
+            return checkFilter(xml);
+        }
+        return false;
+    }
+
+    /**
+     * Check if is filter used and then prepare and post data do client.
+     *
+     * @param xml   data of notification
+     */
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    private boolean checkFilter(final String xml) {
+        if (this.filter == null) {
+            return true;
+        }
+
+        try {
+            return parseFilterParam(xml);
+        } catch (final Exception e) {
+            throw new RestconfDocumentedException("Problem while parsing filter.", e);
+        }
+    }
+
+    /**
+     * Parse and evaluate filter value by xml.
+     *
+     * @return true or false - depends on filter expression and data of
+     *         notifiaction
+     * @throws Exception if operation fails
+     */
+    private boolean parseFilterParam(final String xml) throws Exception {
+        final Document docOfXml = DBF.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
+        final XPath xPath = XPathFactory.newInstance().newXPath();
+        // FIXME: BUG-7956: xPath.setNamespaceContext(nsContext);
+        return (boolean) xPath.compile(this.filter).evaluate(docOfXml, XPathConstants.BOOLEAN);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/BaseListenerInterface.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/BaseListenerInterface.java
new file mode 100644 (file)
index 0000000..fdecd7d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.nb.rfc8040.streams.listeners;
+
+import io.netty.channel.Channel;
+import java.util.Set;
+
+/**
+ * Base interface for both listeners({@link ListenerAdapter},
+ * {@link NotificationListenerAdapter}).
+ */
+interface BaseListenerInterface extends AutoCloseable {
+
+    /**
+     * Return all subscribers of listener.
+     *
+     * @return set of subscribers
+     */
+    Set<Channel> getSubscribers();
+
+    /**
+     * Checks if exists at least one {@link Channel} subscriber.
+     *
+     * @return True if exist at least one {@link Channel} subscriber, false
+     *         otherwise.
+     */
+    boolean hasSubscribers();
+
+    /**
+     * Get name of stream.
+     *
+     * @return stream name
+     */
+    String getStreamName();
+
+    /**
+     * Get output type.
+     *
+     * @return outputType
+     */
+    String getOutputType();
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/Event.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/Event.java
new file mode 100644 (file)
index 0000000..1e77174
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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.nb.rfc8040.streams.listeners;
+
+import io.netty.channel.Channel;
+
+/**
+ * Represents event of specific {@link EventType} type, holds data and
+ * {@link Channel} subscriber.
+ */
+class Event {
+    private final EventType type;
+    private Channel subscriber;
+    private String data;
+
+    /**
+     * Creates new event specified by {@link EventType} type.
+     *
+     * @param type
+     *            EventType
+     */
+    Event(final EventType type) {
+        this.type = type;
+    }
+
+    /**
+     * Gets the {@link Channel} subscriber.
+     *
+     * @return Channel
+     */
+    public Channel getSubscriber() {
+        return this.subscriber;
+    }
+
+    /**
+     * Sets subscriber for event.
+     *
+     * @param subscriber
+     *            Channel
+     */
+    public void setSubscriber(final Channel subscriber) {
+        this.subscriber = subscriber;
+    }
+
+    /**
+     * Gets event String.
+     *
+     * @return String representation of event data.
+     */
+    public String getData() {
+        return this.data;
+    }
+
+    /**
+     * Sets event data.
+     *
+     * @param data
+     *            String.
+     */
+    public void setData(final String data) {
+        this.data = data;
+    }
+
+    /**
+     * Gets event type.
+     *
+     * @return The type of the event.
+     */
+    public EventType getType() {
+        return this.type;
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/EventBusChangeRecorder.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/EventBusChangeRecorder.java
new file mode 100644 (file)
index 0000000..c43acae
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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.nb.rfc8040.streams.listeners;
+
+import com.google.common.eventbus.Subscribe;
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class EventBusChangeRecorder<T extends BaseListenerInterface> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(EventBusChangeRecorder.class);
+    private final T listener;
+
+    /**
+     * Event bus change recorder of specific listener of notifications.
+     *
+     * @param listener
+     *             specific listener
+     */
+    EventBusChangeRecorder(final T listener) {
+        this.listener = listener;
+    }
+
+    @Subscribe
+    public void recordCustomerChange(final Event event) {
+        if (event.getType() == EventType.REGISTER) {
+            final Channel subscriber = event.getSubscriber();
+            if (!this.listener.getSubscribers().contains(subscriber)) {
+                this.listener.getSubscribers().add(subscriber);
+            }
+        } else if (event.getType() == EventType.DEREGISTER) {
+            this.listener.getSubscribers().remove(event.getSubscriber());
+            Notificator.removeListenerIfNoSubscriberExists(this.listener);
+        } else if (event.getType() == EventType.NOTIFY) {
+            for (final Channel subscriber : this.listener.getSubscribers()) {
+                if (subscriber.isActive()) {
+                    LOG.debug("Data are sent to subscriber {}:", subscriber.remoteAddress());
+                    subscriber.writeAndFlush(new TextWebSocketFrame(event.getData()));
+                } else {
+                    LOG.debug("Subscriber {} is removed - channel is not active yet.", subscriber.remoteAddress());
+                    this.listener.getSubscribers().remove(subscriber);
+                }
+            }
+        }
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/EventType.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/EventType.java
new file mode 100644 (file)
index 0000000..fd97152
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * 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.nb.rfc8040.streams.listeners;
+
+/**
+ * Type of the event.
+ */
+enum EventType {
+    REGISTER, DEREGISTER, NOTIFY;
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenerAdapter.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenerAdapter.java
new file mode 100644 (file)
index 0000000..1208b6b
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2014, 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.nb.rfc8040.streams.listeners;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.dom.DOMResult;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+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.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+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.UnkeyedListEntryNode;
+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;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * {@link ListenerAdapter} is responsible to track events, which occurred by
+ * changing data in data source.
+ */
+public class ListenerAdapter extends AbstractCommonSubscriber implements DOMDataChangeListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ListenerAdapter.class);
+
+    private final YangInstanceIdentifier path;
+    private final String streamName;
+    private final NotificationOutputType outputType;
+
+    private AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change;
+
+    /**
+     * Creates new {@link ListenerAdapter} listener specified by path and stream
+     * name and register for subscribing.
+     *
+     * @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,
+            final NotificationOutputType outputType) {
+        super();
+        register(this);
+        setLocalNameOfPath(path.getLastPathArgument().getNodeType().getLocalName());
+
+        this.outputType = Preconditions.checkNotNull(outputType);
+        this.path = Preconditions.checkNotNull(path);
+        Preconditions.checkArgument((streamName != null) && !streamName.isEmpty());
+        this.streamName = streamName;
+    }
+
+    @Override
+    public void onDataChanged(final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change) {
+        this.change = change;
+        final String xml = prepareXml();
+        if (checkQueryParams(xml, this)) {
+            prepareAndPostData(xml);
+        }
+    }
+
+    /**
+     * Gets the name of the stream.
+     *
+     * @return The name of the stream.
+     */
+    @Override
+    public String getStreamName() {
+        return this.streamName;
+    }
+
+    @Override
+    public String getOutputType() {
+        return this.outputType.getName();
+    }
+
+    /**
+     * Get path pointed to data in data store.
+     *
+     * @return Path pointed to data in data store.
+     */
+    public YangInstanceIdentifier getPath() {
+        return this.path;
+    }
+
+    /**
+     * Prepare data of notification and data to client.
+     *
+     * @param xml   data
+     */
+    private void prepareAndPostData(final String xml) {
+        final Event event = new Event(EventType.NOTIFY);
+        if (this.outputType.equals(NotificationOutputType.JSON)) {
+            try {
+                final JsonNode node = new XmlMapper().readTree(xml.getBytes());
+                event.setData(node.toString());
+            } catch (final IOException e) {
+                LOG.error("Error parsing XML {}", xml, e);
+                Throwables.propagate(e);
+            }
+        } else {
+            event.setData(xml);
+        }
+        post(event);
+    }
+
+    /**
+     * Tracks events of data change by customer.
+     */
+
+    /**
+     * Prepare data in printable form and transform it to String.
+     *
+     * @return Data in printable form.
+     */
+    private String prepareXml() {
+        final SchemaContext schemaContext = schemaHandler.get();
+        final DataSchemaContextTree dataContextTree = DataSchemaContextTree.from(schemaContext);
+        final Document doc = createDocument();
+        final Element notificationElement = basePartDoc(doc);
+
+        final Element dataChangedNotificationEventElement = doc.createElementNS(
+                "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "data-changed-notification");
+
+        addValuesToDataChangedNotificationEventElement(doc, dataChangedNotificationEventElement, this.change,
+                schemaContext, dataContextTree);
+        notificationElement.appendChild(dataChangedNotificationEventElement);
+        return transformDoc(doc);
+    }
+
+    /**
+     * Adds values to data changed notification event element.
+     *
+     * @param doc
+     *            {@link Document}
+     * @param dataChangedNotificationEventElement
+     *            {@link Element}
+     * @param change
+     *            {@link AsyncDataChangeEvent}
+     */
+    private void addValuesToDataChangedNotificationEventElement(final Document doc,
+            final Element dataChangedNotificationEventElement,
+            final AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change,
+            final SchemaContext schemaContext, final DataSchemaContextTree dataSchemaContextTree) {
+
+        addCreatedChangedValuesFromDataToElement(doc, change.getCreatedData().entrySet(),
+                dataChangedNotificationEventElement, Operation.CREATED, schemaContext, dataSchemaContextTree);
+
+        addCreatedChangedValuesFromDataToElement(doc, change.getUpdatedData().entrySet(),
+                dataChangedNotificationEventElement, Operation.UPDATED, schemaContext, dataSchemaContextTree);
+
+        addValuesFromDataToElement(doc, change.getRemovedPaths(), dataChangedNotificationEventElement,
+                Operation.DELETED, schemaContext, dataSchemaContextTree);
+    }
+
+    /**
+     * Adds values from data to element.
+     *
+     * @param doc
+     *            {@link Document}
+     * @param data
+     *            Set of {@link YangInstanceIdentifier}.
+     * @param element
+     *            {@link Element}
+     * @param operation
+     *            {@link Operation}
+     * @param schemaContext
+     * @param dataSchemaContextTree
+     */
+    private void addValuesFromDataToElement(final Document doc, final Set<YangInstanceIdentifier> data,
+            final Element element, final Operation operation, final SchemaContext schemaContext,
+            final DataSchemaContextTree dataSchemaContextTree) {
+        if ((data == null) || data.isEmpty()) {
+            return;
+        }
+        for (final YangInstanceIdentifier path : data) {
+            if (!dataSchemaContextTree.getChild(path).isMixin()) {
+                final Node node = createDataChangeEventElement(doc, path, operation, schemaContext);
+                element.appendChild(node);
+            }
+        }
+    }
+
+    private void addCreatedChangedValuesFromDataToElement(final Document doc,
+            final Set<Entry<YangInstanceIdentifier, NormalizedNode<?, ?>>> data, final Element element,
+            final Operation operation, final SchemaContext schemaContext,
+            final DataSchemaContextTree dataSchemaContextTree) {
+        if ((data == null) || data.isEmpty()) {
+            return;
+        }
+        for (final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry : data) {
+            if (!dataSchemaContextTree.getChild(entry.getKey()).isMixin()
+                    && (!getLeafNodesOnly() || entry.getValue() instanceof LeafNode)) {
+                final Node node = createCreatedChangedDataChangeEventElement(doc, entry, operation, schemaContext,
+                        dataSchemaContextTree);
+                element.appendChild(node);
+            }
+        }
+    }
+
+    /**
+     * Creates changed event element from data.
+     *
+     * @param doc
+     *            {@link Document}
+     * @param path
+     *            Path to data in data store.
+     * @param operation
+     *            {@link Operation}
+     * @param schemaContext
+     * @return {@link Node} node represented by changed event element.
+     */
+    private Node createDataChangeEventElement(final Document doc, final YangInstanceIdentifier path,
+            final Operation operation, final SchemaContext schemaContext) {
+        final Element dataChangeEventElement = doc.createElement("data-change-event");
+        final Element pathElement = doc.createElement("path");
+        addPathAsValueToElement(path, pathElement, schemaContext);
+        dataChangeEventElement.appendChild(pathElement);
+
+        final Element operationElement = doc.createElement("operation");
+        operationElement.setTextContent(operation.value);
+        dataChangeEventElement.appendChild(operationElement);
+
+        return dataChangeEventElement;
+    }
+
+    private Node createCreatedChangedDataChangeEventElement(final Document doc,
+            final Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry, final Operation operation,
+            final SchemaContext schemaContext, final DataSchemaContextTree dataSchemaContextTree) {
+        final Element dataChangeEventElement = doc.createElement("data-change-event");
+        final Element pathElement = doc.createElement("path");
+        final YangInstanceIdentifier path = entry.getKey();
+        addPathAsValueToElement(path, pathElement, schemaContext);
+        dataChangeEventElement.appendChild(pathElement);
+
+        final Element operationElement = doc.createElement("operation");
+        operationElement.setTextContent(operation.value);
+        dataChangeEventElement.appendChild(operationElement);
+
+        try {
+            SchemaPath nodePath;
+            final NormalizedNode<?, ?> normalized = entry.getValue();
+            if ((normalized instanceof MapEntryNode) || (normalized instanceof UnkeyedListEntryNode)) {
+                nodePath = dataSchemaContextTree.getChild(path).getDataSchemaNode().getPath();
+            } else {
+                nodePath = dataSchemaContextTree.getChild(path).getDataSchemaNode().getPath().getParent();
+            }
+            final DOMResult domResult = writeNormalizedNode(normalized, schemaContext, nodePath);
+            final Node result = doc.importNode(domResult.getNode().getFirstChild(), true);
+            final Element dataElement = doc.createElement("data");
+            dataElement.appendChild(result);
+            dataChangeEventElement.appendChild(dataElement);
+        } catch (final IOException e) {
+            LOG.error("Error in writer ", e);
+        } catch (final XMLStreamException e) {
+            LOG.error("Error processing stream", e);
+        }
+
+        return dataChangeEventElement;
+    }
+
+    /**
+     * Adds path as value to element.
+     *
+     * @param path
+     *            Path to data in data store.
+     * @param element
+     *            {@link Element}
+     * @param schemaContext
+     */
+    @SuppressWarnings("rawtypes")
+    private void addPathAsValueToElement(final YangInstanceIdentifier path, final Element element,
+            final SchemaContext schemaContext) {
+        final StringBuilder textContent = new StringBuilder();
+
+        for (final PathArgument pathArgument : path.getPathArguments()) {
+            if (pathArgument instanceof YangInstanceIdentifier.AugmentationIdentifier) {
+                continue;
+            }
+            textContent.append("/");
+            writeIdentifierWithNamespacePrefix(element, textContent, pathArgument.getNodeType(), schemaContext);
+            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, schemaContext);
+                    textContent.append("='");
+                    textContent.append(predicateValue);
+                    textContent.append("'");
+                    textContent.append("]");
+                }
+            } else if (pathArgument instanceof NodeWithValue) {
+                textContent.append("[.='");
+                textContent.append(((NodeWithValue) pathArgument).getValue());
+                textContent.append("'");
+                textContent.append("]");
+            }
+        }
+        element.setTextContent(textContent.toString());
+    }
+
+    /**
+     * Writes identifier that consists of prefix and QName.
+     *
+     * @param element
+     *            {@link Element}
+     * @param textContent
+     *            StringBuilder
+     * @param qualifiedName
+     *            QName
+     * @param schemaContext
+     */
+    private static void writeIdentifierWithNamespacePrefix(final Element element, final StringBuilder textContent,
+            final QName qualifiedName, final SchemaContext schemaContext) {
+        final Module module = schemaContext.findModuleByNamespaceAndRevision(qualifiedName.getNamespace(),
+                qualifiedName.getRevision());
+
+        textContent.append(module.getName());
+        textContent.append(":");
+        textContent.append(qualifiedName.getLocalName());
+    }
+
+    /**
+     * Consists of three types {@link Operation#CREATED},
+     * {@link Operation#UPDATED} and {@link Operation#DELETED}.
+     */
+    private enum Operation {
+        CREATED("created"), UPDATED("updated"), DELETED("deleted");
+
+        private final String value;
+
+        Operation(final String value) {
+            this.value = value;
+        }
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/NotificationListenerAdapter.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/NotificationListenerAdapter.java
new file mode 100644 (file)
index 0000000..e3eb6fe
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * 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.nb.rfc8040.streams.listeners;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.time.Instant;
+import java.util.Collection;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.dom.DOMResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+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.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.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * {@link NotificationListenerAdapter} is responsible to track events on
+ * notifications.
+ *
+ */
+public class NotificationListenerAdapter extends AbstractCommonSubscriber implements DOMNotificationListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(NotificationListenerAdapter.class);
+
+    private final String streamName;
+    private final SchemaPath path;
+    private final String outputType;
+
+    private SchemaContext schemaContext;
+    private DOMNotification notification;
+
+    /**
+     * Set path of listener and stream name, register event bus.
+     *
+     * @param path
+     *             path of notification
+     * @param streamName
+     *             stream name of listener
+     * @param outputType
+     *             type of output on notification (JSON, XML)
+     */
+    NotificationListenerAdapter(final SchemaPath path, final String streamName, final String outputType) {
+        super();
+        register(this);
+        setLocalNameOfPath(path.getLastComponent().getLocalName());
+
+        this.outputType = Preconditions.checkNotNull(outputType);
+        this.path = Preconditions.checkNotNull(path);
+        Preconditions.checkArgument(streamName != null && !streamName.isEmpty());
+        this.streamName = streamName;
+    }
+
+    /**
+     * Get outputType of listener.
+     *
+     * @return the outputType
+     */
+    @Override
+    public String getOutputType() {
+        return this.outputType;
+    }
+
+    @Override
+    public void onNotification(final DOMNotification notification) {
+        this.schemaContext = schemaHandler.get();
+        this.notification = notification;
+
+        final String xml = prepareXml();
+        if (checkQueryParams(xml, this)) {
+            prepareAndPostData(xml);
+        }
+    }
+
+    /**
+     * Get stream name of this listener.
+     *
+     * @return {@link String}
+     */
+    @Override
+    public String getStreamName() {
+        return this.streamName;
+    }
+
+    /**
+     * Get schema path of notification.
+     *
+     * @return {@link SchemaPath}
+     */
+    public SchemaPath getSchemaPath() {
+        return this.path;
+    }
+
+    /**
+     * Prepare data of notification and data to client.
+     *
+     * @param xml   data
+     */
+    private void prepareAndPostData(final String xml) {
+        final Event event = new Event(EventType.NOTIFY);
+        if (this.outputType.equals("JSON")) {
+            event.setData(prepareJson());
+        } else {
+            event.setData(xml);
+        }
+        post(event);
+    }
+
+    /**
+     * Prepare json from notification data.
+     *
+     * @return json as {@link String}
+     */
+    @VisibleForTesting
+    String prepareJson() {
+        final JsonParser jsonParser = new JsonParser();
+        final JsonObject json = new JsonObject();
+        json.add("ietf-restconf:notification", jsonParser.parse(writeBodyToString()));
+        json.addProperty("event-time", ListenerAdapter.toRFC3339(Instant.now()));
+        return json.toString();
+    }
+
+    @VisibleForTesting
+    void setNotification(final DOMNotification notification) {
+        this.notification = Preconditions.checkNotNull(notification);
+    }
+
+    @VisibleForTesting
+    void setSchemaContext(final SchemaContext schemaContext) {
+        this.schemaContext = Preconditions.checkNotNull(schemaContext);
+    }
+
+    private String writeBodyToString() {
+        final Writer writer = new StringWriter();
+        final NormalizedNodeStreamWriter jsonStream =
+                JSONNormalizedNodeStreamWriter.createExclusiveWriter(JSONCodecFactory.getShared(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();
+    }
+
+    private String prepareXml() {
+        final Document doc = createDocument();
+        final Element notificationElement = basePartDoc(doc);
+
+        final Element notificationEventElement = doc.createElementNS(
+                "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote", "create-notification-stream");
+        addValuesToNotificationEventElement(doc, notificationEventElement, this.notification, this.schemaContext);
+        notificationElement.appendChild(notificationEventElement);
+
+        return transformDoc(doc);
+    }
+
+    private void addValuesToNotificationEventElement(final Document doc, final Element element,
+            final DOMNotification notification, final SchemaContext schemaContext) {
+        if (notification == null) {
+            return;
+        }
+
+        final NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>> body =
+                notification.getBody();
+        try {
+
+            final DOMResult domResult = writeNormalizedNode(body, schemaContext, this.path);
+            final Node result = doc.importNode(domResult.getNode().getFirstChild(), true);
+            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) {
+            LOG.error("Error processing stream", e);
+        }
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/Notificator.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/Notificator.java
new file mode 100644 (file)
index 0000000..cd4c74a
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * 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.restconf.nb.rfc8040.streams.listeners;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+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;
+
+/**
+ * {@link Notificator} is responsible to create, remove and find
+ * {@link ListenerAdapter} listener.
+ */
+public class Notificator {
+
+    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();
+
+    private Notificator() {
+    }
+
+    /**
+     * Returns list of all stream names.
+     */
+    public static Set<String> getStreamNames() {
+        return dataChangeListener.keySet();
+    }
+
+    /**
+     * Gets {@link ListenerAdapter} specified by stream name.
+     *
+     * @param streamName
+     *            The name of the stream.
+     * @return {@link ListenerAdapter} specified by stream name.
+     */
+    public static ListenerAdapter getListenerFor(final String streamName) {
+        return dataChangeListener.get(streamName);
+    }
+
+    /**
+     * Checks if the listener specified by {@link YangInstanceIdentifier} path exist.
+     *
+     * @param streamName    name of the stream
+     * @return True if the listener exist, false otherwise.
+     */
+    public static boolean existListenerFor(final String streamName) {
+        return dataChangeListener.containsKey(streamName);
+    }
+
+    /**
+     * 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.
+     * @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 NotificationOutputType outputType) {
+        final ListenerAdapter listener = new ListenerAdapter(path, streamName, outputType);
+        try {
+            LOCK.lock();
+            dataChangeListener.put(streamName, listener);
+        } finally {
+            LOCK.unlock();
+        }
+        return listener;
+    }
+
+    /**
+     * Looks for listener determined by {@link YangInstanceIdentifier} path and removes it.
+     * Creates String representation of stream name from URI. Removes slash from URI in start and end position.
+     *
+     * @param uri
+     *            URI for creation stream name.
+     * @return String representation of stream name.
+     */
+    public static String createStreamNameFromUri(final String uri) {
+        if (uri == null) {
+            return null;
+        }
+        String result = uri;
+        if (result.startsWith("/")) {
+            result = result.substring(1);
+        }
+        if (result.endsWith("/")) {
+            result = result.substring(0, result.length() - 1);
+        }
+        return result;
+    }
+
+    /**
+     * Removes all listeners.
+     */
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public static void removeAllListeners() {
+        for (final ListenerAdapter listener : dataChangeListener.values()) {
+            try {
+                listener.close();
+            } catch (final Exception e) {
+                LOG.error("Failed to close listener", e);
+            }
+        }
+        try {
+            LOCK.lock();
+            dataChangeListener = new ConcurrentHashMap<>();
+        } finally {
+            LOCK.unlock();
+        }
+    }
+
+    /**
+     * Delete {@link ListenerAdapter} listener specified in parameter.
+     *
+     * @param <T>
+     *
+     * @param listener
+     *            ListenerAdapter
+     */
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    private static <T extends BaseListenerInterface> void deleteListener(final T listener) {
+        if (listener != null) {
+            try {
+                listener.close();
+            } catch (final Exception e) {
+                LOG.error("Failed to close listener", e);
+            }
+            try {
+                LOCK.lock();
+                dataChangeListener.remove(listener.getStreamName());
+            } finally {
+                LOCK.unlock();
+            }
+        }
+    }
+
+    /**
+     * Check if the listener specified by qnames of request exist.
+     *
+     * @param streamName
+     *             name of stream
+     * @return True if the listener exist, false otherwise.
+     */
+    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<>();
+        for (final SchemaPath path : paths) {
+            final NotificationListenerAdapter listener = new NotificationListenerAdapter(path, streamName, outputType);
+            listListeners.add(listener);
+        }
+        try {
+            LOCK.lock();
+            notificationListenersByStreamName.put(streamName, listListeners);
+        } finally {
+            LOCK.unlock();
+        }
+        return listListeners;
+    }
+
+    public static <T extends BaseListenerInterface> void removeListenerIfNoSubscriberExists(final T listener) {
+        if (!listener.hasSubscribers()) {
+            if (listener instanceof NotificationListenerAdapter) {
+                deleteNotificationListener(listener);
+            } else {
+                deleteListener(listener);
+            }
+        }
+    }
+
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    private static <T extends BaseListenerInterface> void deleteNotificationListener(final T listener) {
+        if (listener != null) {
+            try {
+                listener.close();
+            } catch (final Exception e) {
+                LOG.error("Failed to close listener", e);
+            }
+            try {
+                LOCK.lock();
+                notificationListenersByStreamName.remove(listener.getStreamName());
+            } finally {
+                LOCK.unlock();
+            }
+        }
+    }
+
+    public static List<NotificationListenerAdapter> getNotificationListenerFor(final String streamName) {
+        return notificationListenersByStreamName.get(streamName);
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/websockets/WebSocketServer.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/websockets/WebSocketServer.java
new file mode 100644 (file)
index 0000000..b497f74
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2014, 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.restconf.nb.rfc8040.streams.websockets;
+
+import com.google.common.base.Preconditions;
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import org.opendaylight.restconf.nb.rfc8040.streams.listeners.Notificator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link WebSocketServer} is the singleton responsible for starting and stopping the
+ * web socket server.
+ */
+public class WebSocketServer implements Runnable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(WebSocketServer.class);
+
+    private static WebSocketServer instance = null;
+
+    private final int port;
+
+    private EventLoopGroup bossGroup;
+    private EventLoopGroup workerGroup;
+
+
+    private WebSocketServer(final int port) {
+        this.port = port;
+    }
+
+    /**
+     * Create singleton instance of {@link WebSocketServer}.
+     *
+     * @param port TCP port used for this server
+     * @return instance of {@link WebSocketServer}
+     */
+    public static WebSocketServer createInstance(final int port) {
+        Preconditions.checkState(instance == null, "createInstance() has already been called");
+        Preconditions.checkArgument(port >= 1024, "Privileged port (below 1024) is not allowed");
+
+        instance = new WebSocketServer(port);
+        return instance;
+    }
+
+    /**
+     * Get the websocket of TCP port.
+     *
+     * @return websocket TCP port
+     */
+    public int getPort() {
+        return port;
+    }
+
+    /**
+     * Get instance of {@link WebSocketServer} created by {@link #createInstance(int)}.
+     *
+     * @return instance of {@link WebSocketServer}
+     */
+    public static WebSocketServer getInstance() {
+        Preconditions.checkNotNull(instance, "createInstance() must be called prior to getInstance()");
+        return instance;
+    }
+
+    /**
+     * Destroy the existing instance.
+     */
+    public static void destroyInstance() {
+        Preconditions.checkState(instance != null, "createInstance() must be called prior to destroyInstance()");
+
+        instance.stop();
+        instance = null;
+    }
+
+    @Override
+    public void run() {
+        bossGroup = new NioEventLoopGroup();
+        workerGroup = new NioEventLoopGroup();
+        try {
+            final ServerBootstrap serverBootstrap = new ServerBootstrap();
+            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
+                    .childHandler(new WebSocketServerInitializer());
+
+            final Channel channel = serverBootstrap.bind(port).sync().channel();
+            LOG.info("Web socket server started at port {}.", port);
+
+            channel.closeFuture().sync();
+        } catch (final InterruptedException e) {
+            LOG.error("Web socket server encountered an error during startup attempt on port {}", port, e);
+        } finally {
+            stop();
+        }
+    }
+
+    /**
+     * Stops the web socket server and removes all listeners.
+     */
+    private void stop() {
+        LOG.debug("Stopping the web socket server instance on port {}", port);
+        Notificator.removeAllListeners();
+        if (bossGroup != null) {
+            bossGroup.shutdownGracefully();
+            bossGroup = null;
+        }
+        if (workerGroup != null) {
+            workerGroup.shutdownGracefully();
+            workerGroup = null;
+        }
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/websockets/WebSocketServerHandler.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/websockets/WebSocketServerHandler.java
new file mode 100644 (file)
index 0000000..e804c4c
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2014, 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.restconf.nb.rfc8040.streams.websockets;
+
+import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
+import static io.netty.handler.codec.http.HttpHeaders.setContentLength;
+import static io.netty.handler.codec.http.HttpHeaders.Names.HOST;
+import static io.netty.handler.codec.http.HttpMethod.GET;
+import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
+import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
+import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
+import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
+import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
+import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
+import io.netty.handler.codec.http.websocketx.WebSocketFrame;
+import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
+import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
+import io.netty.util.CharsetUtil;
+import java.io.IOException;
+import java.util.List;
+import org.opendaylight.restconf.nb.rfc8040.streams.listeners.ListenerAdapter;
+import org.opendaylight.restconf.nb.rfc8040.streams.listeners.NotificationListenerAdapter;
+import org.opendaylight.restconf.nb.rfc8040.streams.listeners.Notificator;
+import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link WebSocketServerHandler} is implementation of {@link SimpleChannelInboundHandler} which allow handle
+ * {@link FullHttpRequest} and {@link WebSocketFrame} messages.
+ */
+public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(WebSocketServerHandler.class);
+
+    private WebSocketServerHandshaker handshaker;
+
+    @Override
+    protected void channelRead0(final ChannelHandlerContext ctx, final Object msg) throws Exception {
+        if (msg instanceof FullHttpRequest) {
+            handleHttpRequest(ctx, (FullHttpRequest) msg);
+        } else if (msg instanceof WebSocketFrame) {
+            handleWebSocketFrame(ctx, (WebSocketFrame) msg);
+        }
+    }
+
+    /**
+     * Checks if HTTP request method is GET and if is possible to decode HTTP result of request.
+     *
+     * @param ctx
+     *            ChannelHandlerContext
+     * @param req
+     *            FullHttpRequest
+     */
+    private void handleHttpRequest(final ChannelHandlerContext ctx, final FullHttpRequest req) throws Exception {
+        // Handle a bad request.
+        if (!req.getDecoderResult().isSuccess()) {
+            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
+            return;
+        }
+
+        // Allow only GET methods.
+        if (req.getMethod() != GET) {
+            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
+            return;
+        }
+
+        final String streamName = Notificator.createStreamNameFromUri(req.getUri());
+        if (streamName.contains(RestconfConstants.DATA_SUBSCR)) {
+            final ListenerAdapter listener = Notificator.getListenerFor(streamName);
+            if (listener != null) {
+                listener.addSubscriber(ctx.channel());
+                LOG.debug("Subscriber successfully registered.");
+            } else {
+                LOG.error("Listener for stream with name '{}' was not found.", streamName);
+                sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, INTERNAL_SERVER_ERROR));
+            }
+        } else if (streamName.contains(RestconfConstants.NOTIFICATION_STREAM)) {
+            final List<NotificationListenerAdapter> listeners = Notificator.getNotificationListenerFor(streamName);
+            if (!listeners.isEmpty() && (listeners != null)) {
+                for (final NotificationListenerAdapter listener : listeners) {
+                    listener.addSubscriber(ctx.channel());
+                    LOG.debug("Subscriber successfully registered.");
+                }
+            } else {
+                LOG.error("Listener for stream with name '{}' was not found.", streamName);
+                sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, INTERNAL_SERVER_ERROR));
+            }
+        }
+
+        // Handshake
+        final WebSocketServerHandshakerFactory wsFactory =
+                new WebSocketServerHandshakerFactory(getWebSocketLocation(req),
+                null, false);
+        this.handshaker = wsFactory.newHandshaker(req);
+        if (this.handshaker == null) {
+            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
+        } else {
+            this.handshaker.handshake(ctx.channel(), req);
+        }
+
+    }
+
+    /**
+     * Checks response status, send response and close connection if necessary.
+     *
+     * @param ctx
+     *            ChannelHandlerContext
+     * @param req
+     *            HttpRequest
+     * @param res
+     *            FullHttpResponse
+     */
+    private static void sendHttpResponse(final ChannelHandlerContext ctx, final HttpRequest req,
+            final FullHttpResponse res) {
+        // Generate an error page if response getStatus code is not OK (200).
+        if (res.getStatus().code() != 200) {
+            final ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
+            res.content().writeBytes(buf);
+            buf.release();
+            setContentLength(res, res.content().readableBytes());
+        }
+
+        // Send the response and close the connection if necessary.
+        final ChannelFuture f = ctx.channel().writeAndFlush(res);
+        if (!isKeepAlive(req) || (res.getStatus().code() != 200)) {
+            f.addListener(ChannelFutureListener.CLOSE);
+        }
+    }
+
+    /**
+     * Handles web socket frame.
+     *
+     * @param ctx
+     *            {@link ChannelHandlerContext}
+     * @param frame
+     *            {@link WebSocketFrame}
+     */
+    private void handleWebSocketFrame(final ChannelHandlerContext ctx, final WebSocketFrame frame) throws IOException {
+        if (frame instanceof CloseWebSocketFrame) {
+            this.handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
+            final String streamName = Notificator.createStreamNameFromUri(((CloseWebSocketFrame) frame).reasonText());
+            if (streamName.contains(RestconfConstants.DATA_SUBSCR)) {
+                final ListenerAdapter listener = Notificator.getListenerFor(streamName);
+                if (listener != null) {
+                    listener.removeSubscriber(ctx.channel());
+                    LOG.debug("Subscriber successfully registered.");
+                }
+                Notificator.removeListenerIfNoSubscriberExists(listener);
+            } else if (streamName.contains(RestconfConstants.NOTIFICATION_STREAM)) {
+                final List<NotificationListenerAdapter> listeners = Notificator.getNotificationListenerFor(streamName);
+                if (!listeners.isEmpty() && (listeners != null)) {
+                    for (final NotificationListenerAdapter listener : listeners) {
+                        listener.removeSubscriber(ctx.channel());
+                    }
+                }
+            }
+            return;
+        } else if (frame instanceof PingWebSocketFrame) {
+            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
+            return;
+        }
+    }
+
+    @Override
+    public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
+        if (!(cause instanceof java.nio.channels.ClosedChannelException)) {
+            // LOG.info("Not expected error cause: ", cause.toString());
+        }
+        ctx.close();
+    }
+
+    /**
+     * Get web socket location from HTTP request.
+     *
+     * @param req
+     *            HTTP request from which the location will be returned
+     * @return String representation of web socket location.
+     */
+    private static String getWebSocketLocation(final HttpRequest req) {
+        return "ws://" + req.headers().get(HOST) + req.getUri();
+    }
+
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/websockets/WebSocketServerInitializer.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/websockets/WebSocketServerInitializer.java
new file mode 100644 (file)
index 0000000..2c768d8
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 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.restconf.nb.rfc8040.streams.websockets;
+
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpServerCodec;
+
+/**
+ * {@link WebSocketServerInitializer} is used to setup the {@link ChannelPipeline} of a {@link io.netty.channel.Channel}
+ * .
+ */
+public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> {
+
+    @Override
+    protected void initChannel(final SocketChannel ch) throws Exception {
+        ChannelPipeline pipeline = ch.pipeline();
+        pipeline.addLast("codec-http", new HttpServerCodec());
+        pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
+        pipeline.addLast("handler", new WebSocketServerHandler());
+    }
+
+}
index 4f5387a80710c86dbc02b135da71ffb4b66aac91..0237a862a1170d83a0feea592e8cd9e6296cbe44 100644 (file)
@@ -21,7 +21,10 @@ public final class RestconfConstants {
     public static final String IDENTIFIER = "identifier";
     public static final char SLASH = '/';
     public static final Splitter SLASH_SPLITTER = Splitter.on(SLASH);
-    public static final String DRAFT_PATTERN = "restconf/17";
+    public static final String DRAFT_PATTERN = "restconf/18";
+
+    public static final CharSequence DATA_SUBSCR = "data-change-event-subscription";
+    public static final CharSequence NOTIFICATION_STREAM = "notification-stream";
 
     private RestconfConstants() {
         throw new UnsupportedOperationException("Util class");
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/mapping/RestconfMappingNodeConstants.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/mapping/RestconfMappingNodeConstants.java
new file mode 100644 (file)
index 0000000..0e3a401
--- /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.restconf.nb.rfc8040.utils.mapping;
+
+/**
+ * Util class for constants of mapping node.
+ *
+ */
+public final class RestconfMappingNodeConstants {
+
+    public static final String NAME = "name";
+    public static final String REVISION = "revision";
+    public static final String NAMESPACE = "namespace";
+    public static final String FEATURE = "feature";
+    public static final String DESCRIPTION = "description";
+    public static final String REPLAY_SUPPORT = "replay-support";
+    public static final String REPLAY_LOG = "replay-log-creation-time";
+    public static final String EVENTS = "events";
+
+    private RestconfMappingNodeConstants() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/mapping/RestconfMappingNodeUtil.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/mapping/RestconfMappingNodeUtil.java
new file mode 100644 (file)
index 0000000..4f645ed
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+ * 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.nb.rfc8040.utils.mapping;
+
+import com.google.common.base.Optional;
+import java.net.URI;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Collection;
+import java.util.Set;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040.IetfYangLibrary;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040.MonitoringModule;
+import org.opendaylight.restconf.nb.rfc8040.Rfc8040.MonitoringModule.QueryParams;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160621.module.list.Module.ConformanceType;
+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.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.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+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.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Deviation;
+import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+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.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+/**
+ * Util class for mapping nodes.
+ *
+ */
+public final class RestconfMappingNodeUtil {
+
+    private RestconfMappingNodeUtil() {
+        throw new UnsupportedOperationException("Util class");
+    }
+
+    /**
+     * Map data from modules to {@link NormalizedNode}.
+     *
+     * @param modules
+     *             modules for mapping
+     * @param ietfYangLibraryModule
+     *             ietf-yang-library module
+     * @param context
+     *             schema context
+     * @param moduleSetId
+     *             module-set-id of actual set
+     * @return mapped data as {@link NormalizedNode}
+     */
+    public static NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>>
+            mapModulesByIetfYangLibraryYang(final Set<Module> modules, final Module ietfYangLibraryModule,
+                    final SchemaContext context, final String moduleSetId) {
+        final DataSchemaNode modulesStateSch =
+                ietfYangLibraryModule.getDataChildByName(IetfYangLibrary.MODUELS_STATE_CONT_QNAME);
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> modulesStateBuilder =
+                Builders.containerBuilder((ContainerSchemaNode) modulesStateSch);
+
+        final DataSchemaNode moduleSetIdSch =
+                ((ContainerSchemaNode) modulesStateSch).getDataChildByName(IetfYangLibrary.MODULE_SET_ID_LEAF_QNAME);
+        modulesStateBuilder
+                .withChild(Builders.leafBuilder((LeafSchemaNode) moduleSetIdSch).withValue(moduleSetId).build());
+
+        final DataSchemaNode moduleSch = findNodeInGroupings(IetfYangLibrary.MODULE_QNAME_LIST, ietfYangLibraryModule);
+        final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> mapBuilder =
+                Builders.orderedMapBuilder((ListSchemaNode) moduleSch);
+        for (final Module module : context.getModules()) {
+            fillMapByModules(mapBuilder, moduleSch, false, module, ietfYangLibraryModule, context);
+        }
+        return modulesStateBuilder.withChild(mapBuilder.build()).build();
+    }
+
+    /**
+     * Map data by the specific module or submodule.
+     *
+     * @param mapBuilder
+     *             ordered list builder for children
+     * @param moduleSch
+     *             schema of list for entryMapBuilder
+     * @param isSubmodule
+     *             true if module is specified as submodule, false otherwise
+     * @param module
+     *             specific module or submodule
+     * @param ietfYangLibraryModule
+     *             ietf-yang-library module
+     * @param context
+     *             schema context
+     */
+    private static void fillMapByModules(final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> mapBuilder,
+            final DataSchemaNode moduleSch, final boolean isSubmodule, final Module module,
+            final Module ietfYangLibraryModule, final SchemaContext context) {
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
+                Builders.mapEntryBuilder((ListSchemaNode) moduleSch);
+        addCommonLeafs(module, mapEntryBuilder, ietfYangLibraryModule);
+        addChildOfModuleBySpecificModuleInternal(
+                IetfYangLibrary.SPECIFIC_MODULE_SCHEMA_LEAF_QNAME, mapEntryBuilder, IetfYangLibrary.BASE_URI_OF_SCHEMA
+                        + module.getName() + "/" + module.getQNameModule().getFormattedRevision(),
+                ietfYangLibraryModule);
+        if (!isSubmodule) {
+            addChildOfModuleBySpecificModuleOfListChild(IetfYangLibrary.SPECIFIC_MODULE_NAMESPACE_LEAF_QNAME,
+                    mapEntryBuilder, module.getNamespace().toString(), ietfYangLibraryModule);
+
+            // features - not mandatory
+            if ((module.getFeatures() != null) && !module.getFeatures().isEmpty()) {
+                addFeatureLeafList(IetfYangLibrary.SPECIFIC_MODULE_FEATURE_LEAF_LIST_QNAME, mapEntryBuilder,
+                        module.getFeatures(), ietfYangLibraryModule);
+            }
+            // deviations - not mandatory
+            if ((module.getDeviations() != null) && !module.getDeviations().isEmpty()) {
+                addDeviationList(module, mapEntryBuilder, ietfYangLibraryModule, context);
+                addChildOfModuleBySpecificModuleOfListChild(IetfYangLibrary.SPECIFIC_MODULE_CONFORMANCE_LEAF_QNAME,
+                        mapEntryBuilder, ConformanceType.Implement.getName(), ietfYangLibraryModule);
+            } else {
+                addChildOfModuleBySpecificModuleOfListChild(IetfYangLibrary.SPECIFIC_MODULE_CONFORMANCE_LEAF_QNAME,
+                        mapEntryBuilder, ConformanceType.Import.getName(), ietfYangLibraryModule);
+            }
+            // submodules - not mandatory
+            if ((module.getSubmodules() != null) && !module.getSubmodules().isEmpty()) {
+                addSubmodules(module, mapEntryBuilder, ietfYangLibraryModule, context);
+            }
+        }
+        mapBuilder.withChild(mapEntryBuilder.build());
+    }
+
+    /**
+     * Mapping submodules of specific module.
+     *
+     * @param module
+     *             module with submodules
+     * @param mapEntryBuilder
+     *             mapEntryBuilder of parent for mapping children
+     * @param ietfYangLibraryModule
+     *             ietf-yang-library module
+     * @param context
+     *             schema context
+     */
+    private static void addSubmodules(final Module module,
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
+            final Module ietfYangLibraryModule, final SchemaContext context) {
+        final DataSchemaNode listSubm = findSchemaInListOfModulesSchema(
+                IetfYangLibrary.SPECIFIC_MODULE_SUBMODULE_LIST_QNAME, ietfYangLibraryModule);
+        final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> mapBuilder =
+                Builders.orderedMapBuilder((ListSchemaNode) listSubm);
+        for (final Module submodule : module.getSubmodules()) {
+            fillMapByModules(mapBuilder, listSubm, true, submodule, ietfYangLibraryModule, context);
+        }
+        mapEntryBuilder.withChild(mapBuilder.build());
+    }
+
+    /**
+     * Mapping deviations of specific module.
+     *
+     * @param module
+     *             module with deviations
+     * @param mapEntryBuilder
+     *             mapEntryBuilder of parent for mapping children
+     * @param ietfYangLibraryModule
+     *             ietf-yang-library module
+     * @param context
+     *             schema context
+     */
+    private static void addDeviationList(final Module module,
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
+            final Module ietfYangLibraryModule, final SchemaContext context) {
+        final DataSchemaNode deviationsSchema = findSchemaInListOfModulesSchema(
+                IetfYangLibrary.SPECIFIC_MODULE_DEVIATION_LIST_QNAME, ietfYangLibraryModule);
+        final CollectionNodeBuilder<MapEntryNode, MapNode> deviations =
+                Builders.mapBuilder((ListSchemaNode) deviationsSchema);
+        for (final Deviation deviation : module.getDeviations()) {
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> deviationEntryNode =
+                    Builders.mapEntryBuilder((ListSchemaNode) deviationsSchema);
+            final QName lastComponent = deviation.getTargetPath().getLastComponent();
+            addChildOfModuleBySpecificModule(IetfYangLibrary.SPECIFIC_MODULE_NAME_LEAF_QNAME, deviationEntryNode,
+                    context.findModuleByNamespaceAndRevision(lastComponent.getNamespace(), lastComponent.getRevision())
+                            .getName(),
+                    ietfYangLibraryModule);
+            addChildOfModuleBySpecificModule(IetfYangLibrary.SPECIFIC_MODULE_REVISION_LEAF_QNAME, deviationEntryNode,
+                    lastComponent.getRevision(), ietfYangLibraryModule);
+            deviations.withChild(deviationEntryNode.build());
+        }
+        mapEntryBuilder.withChild(deviations.build());
+    }
+
+    /**
+     * Mapping features of specific module.
+     *
+     * @param qnameOfFeaturesLeafList
+     *             qname of feature leaf-list in ietf-yang-library module
+     * @param mapEntryBuilder
+     *             mapEntryBuilder of parent for mapping children
+     * @param features
+     *             features of specific module
+     * @param ietfYangLibraryModule
+     *             ieat-yang-library module
+     */
+    private static void addFeatureLeafList(final QName qnameOfFeaturesLeafList,
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
+            final Set<FeatureDefinition> features, final Module ietfYangLibraryModule) {
+        final DataSchemaNode schemaNode =
+                findSchemaInListOfModulesSchema(qnameOfFeaturesLeafList, ietfYangLibraryModule);
+        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafSetBuilder =
+                Builders.leafSetBuilder((LeafListSchemaNode) schemaNode);
+        for (final FeatureDefinition feature : features) {
+            leafSetBuilder.withChild(Builders.leafSetEntryBuilder((LeafListSchemaNode) schemaNode)
+                    .withValue(feature.getQName().getLocalName()).build());
+        }
+        mapEntryBuilder.withChild(leafSetBuilder.build());
+    }
+
+    /**
+     * Mapping common leafs (grouping common-leafs in ietf-yang-library) of
+     * specific module.
+     *
+     * @param module
+     *             specific module for getting name and revision
+     * @param mapEntryBuilder
+     *             mapEntryBuilder of parent for mapping children
+     * @param ietfYangLibraryModule
+     *             ietf-yang-library module
+     */
+    private static void addCommonLeafs(final Module module,
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
+            final Module ietfYangLibraryModule) {
+        addChildOfModuleBySpecificModuleInternal(IetfYangLibrary.SPECIFIC_MODULE_NAME_LEAF_QNAME, mapEntryBuilder,
+                module.getName(), ietfYangLibraryModule);
+        addChildOfModuleBySpecificModuleInternal(IetfYangLibrary.SPECIFIC_MODULE_REVISION_LEAF_QNAME, mapEntryBuilder,
+                module.getQNameModule().getFormattedRevision(), ietfYangLibraryModule);
+    }
+
+    /**
+     * Mapping data child of grouping module-list by ietf-yang-library.
+     *
+     * @param specificQName
+     *             qname of leaf in module-list grouping
+     * @param mapEntryBuilder
+     *             mapEntryBuilder of parent for mapping children
+     * @param value
+     *             value of leaf
+     * @param ietfYangLibraryModule
+     *             ietf-yang-library module
+     */
+    private static void addChildOfModuleBySpecificModuleOfListChild(final QName specificQName,
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
+            final Object value, final Module ietfYangLibraryModule) {
+        final DataSchemaNode leafSch = findSchemaInListOfModulesSchema(specificQName, ietfYangLibraryModule);
+        mapEntryBuilder.withChild(Builders.leafBuilder((LeafSchemaNode) leafSch).withValue(value).build());
+    }
+
+    /**
+     * Find specific schema in gourping module-lsit.
+     *
+     * @param specificQName
+     *             qname of schema
+     * @param ietfYangLibraryModule
+     *             ietf-yang-library module
+     * @return schemaNode of specific child
+     */
+    private static DataSchemaNode findSchemaInListOfModulesSchema(final QName specificQName,
+            final Module ietfYangLibraryModule) {
+        for (final GroupingDefinition groupingDefinition : ietfYangLibraryModule.getGroupings()) {
+            if (groupingDefinition.getQName().equals(IetfYangLibrary.GROUPING_MODULE_LIST_QNAME)) {
+                final DataSchemaNode dataChildByName =
+                        groupingDefinition.getDataChildByName(IetfYangLibrary.MODULE_QNAME_LIST);
+                return ((ListSchemaNode) dataChildByName).getDataChildByName(specificQName);
+            }
+        }
+        throw new RestconfDocumentedException(specificQName.getLocalName() + " doesn't exist.");
+    }
+
+    /**
+     * Mapping data child of internal groupings in module-list grouping.
+     *
+     * @param specifiLeafQName
+     *             qnmae of leaf for mapping
+     * @param mapEntryBuilder
+     *             mapEntryBuilder of parent for mapping children
+     * @param value
+     *             value of leaf
+     * @param ietfYangLibraryModule
+     *             ietf-yang-library module
+     */
+    private static void addChildOfModuleBySpecificModuleInternal(final QName specifiLeafQName,
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
+            final Object value, final Module ietfYangLibraryModule) {
+        final DataSchemaNode nameLeaf = findNodeInInternGroupings(specifiLeafQName, ietfYangLibraryModule);
+        mapEntryBuilder.withChild(Builders.leafBuilder((LeafSchemaNode) nameLeaf).withValue(value).build());
+    }
+
+    /**
+     * Find schema node of leaf by qname in internal groupings of module-list.
+     * grouping
+     *
+     * @param qnameOfSchema
+     *             qname of leaf
+     * @param ietfYangLibraryModule
+     *             ietf-yang-library module
+     * @return schema node of specific leaf
+     */
+    private static DataSchemaNode findNodeInInternGroupings(final QName qnameOfSchema,
+            final Module ietfYangLibraryModule) {
+        for (final GroupingDefinition groupingDefinition : ietfYangLibraryModule.getGroupings()) {
+            if (groupingDefinition.getQName().equals(IetfYangLibrary.GROUPING_MODULE_LIST_QNAME)) {
+                for (final GroupingDefinition internalGrouping : groupingDefinition.getGroupings()) {
+                    if (internalGrouping.getDataChildByName(qnameOfSchema) != null) {
+                        return internalGrouping.getDataChildByName(qnameOfSchema);
+                    }
+                }
+            }
+        }
+        throw new RestconfDocumentedException(qnameOfSchema.getLocalName() + " doesn't exist.");
+    }
+
+    /**
+     * Mapping childrens of list-module.
+     *
+     * @param specifiLeafQName
+     *             qname of leaf
+     * @param mapEntryBuilder
+     *             maptEntryBuilder of parent for mapping children
+     * @param value
+     *             valeu of leaf
+     * @param ietfYangLibraryModule
+     *             ietf-yang-library module
+     */
+    private static void addChildOfModuleBySpecificModule(final QName specifiLeafQName,
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
+            final Object value, final Module ietfYangLibraryModule) {
+        final DataSchemaNode nameLeaf = findNodeInGroupings(specifiLeafQName, ietfYangLibraryModule);
+        mapEntryBuilder.withChild(Builders.leafBuilder((LeafSchemaNode) nameLeaf).withValue(value).build());
+    }
+
+    /**
+     * Find schema of specific leaf in list-module grouping.
+     *
+     * @param qnameOfSchema
+     *             qname of leaf
+     * @param ietfYangLibraryModule
+     *             ietf-yang-library module
+     * @return schemaNode of specific leaf
+     */
+    private static DataSchemaNode findNodeInGroupings(final QName qnameOfSchema, final Module ietfYangLibraryModule) {
+        for (final GroupingDefinition groupingDefinition : ietfYangLibraryModule.getGroupings()) {
+            if (groupingDefinition.getDataChildByName(qnameOfSchema) != null) {
+                return groupingDefinition.getDataChildByName(qnameOfSchema);
+            }
+        }
+        throw new RestconfDocumentedException(qnameOfSchema.getLocalName() + " doesn't exist.");
+    }
+
+    /**
+     * Map capabilites by ietf-restconf-monitoring.
+     *
+     * @param monitoringModule
+     *             ietf-restconf-monitoring module
+     * @return mapped capabilites
+     */
+    public static NormalizedNode<NodeIdentifier, Collection<DataContainerChild<? extends PathArgument, ?>>>
+            mapCapabilites(final Module monitoringModule) {
+        final DataSchemaNode restconfState =
+                monitoringModule.getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME);
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> restStateContBuilder =
+                Builders.containerBuilder((ContainerSchemaNode) restconfState);
+        final DataSchemaNode capabilitesContSchema =
+                getChildOfCont((ContainerSchemaNode) restconfState, MonitoringModule.CONT_CAPABILITES_QNAME);
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> capabilitesContBuilder =
+                Builders.containerBuilder((ContainerSchemaNode) capabilitesContSchema);
+        final DataSchemaNode leafListCapa = getChildOfCont((ContainerSchemaNode) capabilitesContSchema,
+                MonitoringModule.LEAF_LIST_CAPABILITY_QNAME);
+        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafListCapaBuilder =
+                Builders.orderedLeafSetBuilder((LeafListSchemaNode) leafListCapa);
+        fillLeafListCapa(leafListCapaBuilder, (LeafListSchemaNode) leafListCapa);
+
+        return restStateContBuilder.withChild(capabilitesContBuilder.withChild(leafListCapaBuilder.build()).build())
+                .build();
+    }
+
+    /**
+     * Map data to leaf-list.
+     *
+     * @param builder
+     *             builder of parent for children
+     * @param leafListSchema
+     *             leaf list schema
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private static void fillLeafListCapa(final ListNodeBuilder builder, final LeafListSchemaNode leafListSchema) {
+        builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.DEPTH));
+        builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.FIELDS));
+        builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.FILTER));
+        builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.REPLAY));
+        builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.WITH_DEFAULTS));
+    }
+
+    /**
+     * Map value to leaf list entry node.
+     *
+     * @param leafListSchema
+     *             leaf list schema of leaf list entry
+     * @param value
+     *             value of leaf entry
+     * @return entry node
+     */
+    @SuppressWarnings("rawtypes")
+    private static LeafSetEntryNode leafListEntryBuild(final LeafListSchemaNode leafListSchema, final String value) {
+        return Builders.leafSetEntryBuilder(leafListSchema).withValue(value).build();
+    }
+
+    /**
+     * Find specific schema node by qname in parent {@link ContainerSchemaNode}.
+     *
+     * @param parent
+     *             schemaNode
+     * @param childQName
+     *             specific qname of child
+     * @return schema node of child by qname
+     */
+    private static DataSchemaNode getChildOfCont(final ContainerSchemaNode parent, final QName childQName) {
+        for (final DataSchemaNode child : parent.getChildNodes()) {
+            if (child.getQName().equals(childQName)) {
+                return child;
+            }
+        }
+        throw new RestconfDocumentedException(
+                childQName.getLocalName() + " doesn't exist in container " + MonitoringModule.CONT_RESTCONF_STATE_NAME);
+    }
+
+    /**
+     * Map data of yang notification to normalized node according to
+     * ietf-restconf-monitoring.
+     *
+     * @param notifiQName
+     *             qname of notification from listener
+     * @param notifications
+     *             list of notifications for find schema of notification by
+     *            notifiQName
+     * @param start
+     *             start-time query parameter of notification
+     * @param outputType
+     *             output type of notification
+     * @param uri
+     *             location of registered listener for sending data of
+     *            notification
+     * @param monitoringModule
+     *             ietf-restconf-monitoring module
+     * @param existParent
+     *             true if data of parent -
+     *            ietf-restconf-monitoring:restconf-state/streams - exist in DS
+     * @return mapped data of notification - map entry node if parent exists,
+     *         container streams with list and map entry node if not
+     */
+    @SuppressWarnings("rawtypes")
+    public static NormalizedNode mapYangNotificationStreamByIetfRestconfMonitoring(final QName notifiQName,
+            final Set<NotificationDefinition> notifications, final Instant start, final String outputType,
+            final URI uri, final Module monitoringModule, final boolean existParent) {
+        for (final NotificationDefinition notificationDefinition : notifications) {
+            if (notificationDefinition.getQName().equals(notifiQName)) {
+                final DataSchemaNode streamListSchema = ((ContainerSchemaNode) ((ContainerSchemaNode) monitoringModule
+                        .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
+                                .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME))
+                                        .getDataChildByName(MonitoringModule.LIST_STREAM_QNAME);
+                final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry =
+                        Builders.mapEntryBuilder((ListSchemaNode) streamListSchema);
+
+                final ListSchemaNode listSchema = ((ListSchemaNode) streamListSchema);
+                prepareLeafAndFillEntryBuilder(streamEntry,
+                        listSchema.getDataChildByName(MonitoringModule.LEAF_NAME_STREAM_QNAME),
+                        notificationDefinition.getQName().getLocalName());
+                if ((notificationDefinition.getDescription() != null)
+                        && !notificationDefinition.getDescription().equals("")) {
+                    prepareLeafAndFillEntryBuilder(streamEntry,
+                            listSchema.getDataChildByName(MonitoringModule.LEAF_DESCR_STREAM_QNAME),
+                            notificationDefinition.getDescription());
+                }
+                prepareLeafAndFillEntryBuilder(streamEntry,
+                        listSchema.getDataChildByName(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME), true);
+                if (start != null) {
+                    prepareLeafAndFillEntryBuilder(streamEntry,
+                        listSchema.getDataChildByName(MonitoringModule.LEAF_START_TIME_STREAM_QNAME),
+                        DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(start,
+                            ZoneId.systemDefault())));
+                }
+                prepareListAndFillEntryBuilder(streamEntry,
+                        (ListSchemaNode) listSchema.getDataChildByName(MonitoringModule.LIST_ACCESS_STREAM_QNAME),
+                        outputType, uri);
+
+                if (!existParent) {
+                    final DataSchemaNode contStreamsSchema = ((ContainerSchemaNode) monitoringModule
+                            .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
+                                    .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME);
+                    return Builders.containerBuilder((ContainerSchemaNode) contStreamsSchema).withChild(Builders
+                            .mapBuilder((ListSchemaNode) streamListSchema).withChild(streamEntry.build()).build())
+                            .build();
+                }
+                return streamEntry.build();
+            }
+        }
+
+        throw new RestconfDocumentedException(notifiQName + " doesn't exist in any modul");
+    }
+
+    private static void prepareListAndFillEntryBuilder(
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry,
+            final ListSchemaNode listSchemaNode, final String outputType, final URI uriToWebsocketServer) {
+        final CollectionNodeBuilder<MapEntryNode, MapNode> accessListBuilder = Builders.mapBuilder(listSchemaNode);
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> entryAccessList =
+                Builders.mapEntryBuilder(listSchemaNode);
+        prepareLeafAndFillEntryBuilder(entryAccessList,
+                listSchemaNode.getDataChildByName(MonitoringModule.LEAF_ENCODING_ACCESS_QNAME), outputType);
+        prepareLeafAndFillEntryBuilder(entryAccessList,
+                listSchemaNode.getDataChildByName(MonitoringModule.LEAF_LOCATION_ACCESS_QNAME),
+                uriToWebsocketServer.toString());
+        streamEntry.withChild(accessListBuilder.withChild(entryAccessList.build()).build());
+    }
+
+    /**
+     * Prepare leaf and fill entry builder.
+     *
+     * @param streamEntry   Stream entry
+     * @param leafSchema    Leaf schema
+     * @param value         Value
+     */
+    private static void prepareLeafAndFillEntryBuilder(
+            final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry,
+            final DataSchemaNode leafSchema, final Object value) {
+        streamEntry.withChild(Builders.leafBuilder((LeafSchemaNode) leafSchema).withValue(value).build());
+    }
+
+    /**
+     * Map data of data change notification to normalized node according to
+     * ietf-restconf-monitoring.
+     *
+     * @param path
+     *             path of data to listen on
+     * @param start
+     *             start-time query parameter of notification
+     * @param outputType
+     *             output type of notification
+     * @param uri
+     *             location of registered listener for sending data of
+     *            notification
+     * @param monitoringModule
+     *             ietf-restconf-monitoring module
+     * @param existParent
+     *             true if data of parent -
+     *            ietf-restconf-monitoring:restconf-state/streams - exist in DS
+     * @param schemaContext
+     *             schemaContext for parsing instance identifier to get schema
+     *            node of data
+     * @return mapped data of notification - map entry node if parent exists,
+     *         container streams with list and map entry node if not
+     */
+    @SuppressWarnings("rawtypes")
+    public static NormalizedNode mapDataChangeNotificationStreamByIetfRestconfMonitoring(
+            final YangInstanceIdentifier path, final Instant start, final String outputType, final URI uri,
+            final Module monitoringModule, final boolean existParent, final SchemaContext schemaContext) {
+        final SchemaNode schemaNode = ParserIdentifier
+                .toInstanceIdentifier(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext),
+                        schemaContext, Optional.absent())
+                .getSchemaNode();
+        final DataSchemaNode streamListSchema = ((ContainerSchemaNode) ((ContainerSchemaNode) monitoringModule
+                .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
+                        .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME))
+                                .getDataChildByName(MonitoringModule.LIST_STREAM_QNAME);
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry =
+                Builders.mapEntryBuilder((ListSchemaNode) streamListSchema);
+
+        final ListSchemaNode listSchema = ((ListSchemaNode) streamListSchema);
+        prepareLeafAndFillEntryBuilder(streamEntry,
+                listSchema.getDataChildByName(MonitoringModule.LEAF_NAME_STREAM_QNAME),
+                schemaNode.getQName().getLocalName());
+        if ((schemaNode.getDescription() != null) && !schemaNode.getDescription().equals("")) {
+            prepareLeafAndFillEntryBuilder(streamEntry,
+                    listSchema.getDataChildByName(MonitoringModule.LEAF_DESCR_STREAM_QNAME),
+                    schemaNode.getDescription());
+        }
+        prepareLeafAndFillEntryBuilder(streamEntry,
+                listSchema.getDataChildByName(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME), true);
+        prepareLeafAndFillEntryBuilder(streamEntry,
+                listSchema.getDataChildByName(MonitoringModule.LEAF_START_TIME_STREAM_QNAME),
+                DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(start, ZoneId.systemDefault())));
+        prepareListAndFillEntryBuilder(streamEntry,
+                (ListSchemaNode) listSchema.getDataChildByName(MonitoringModule.LIST_ACCESS_STREAM_QNAME), outputType,
+                uri);
+
+        if (!existParent) {
+            final DataSchemaNode contStreamsSchema = ((ContainerSchemaNode) monitoringModule
+                    .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
+                            .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME);
+            return Builders
+                    .containerBuilder((ContainerSchemaNode) contStreamsSchema).withChild(Builders
+                            .mapBuilder((ListSchemaNode) streamListSchema).withChild(streamEntry.build()).build())
+                    .build();
+        }
+        return streamEntry.build();
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/mapping/RestconfMappingStreamConstants.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/mapping/RestconfMappingStreamConstants.java
new file mode 100644 (file)
index 0000000..2e7721c
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.nb.rfc8040.utils.mapping;
+
+/**
+ * Util class for mapping entry stream.
+ *
+ */
+public final class RestconfMappingStreamConstants {
+
+    public static final String DESCRIPTION = "DESCRIPTION_PLACEHOLDER";
+    public static final Boolean REPLAY_SUPPORT = Boolean.valueOf(true);
+    public static final String REPLAY_LOG = "";
+    public static final String EVENTS = "";
+
+    private RestconfMappingStreamConstants() {
+        throw new UnsupportedOperationException("Util class");
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/IdentifierCodec.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/IdentifierCodec.java
new file mode 100644 (file)
index 0000000..3d67b9b
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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.nb.rfc8040.utils.parser;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Codec for identifier to serialize {@link YangInstanceIdentifier} to
+ * {@link String} and deserialize {@link String} to
+ * {@link YangInstanceIdentifier}.
+ *
+ */
+public final class IdentifierCodec {
+
+    private IdentifierCodec() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+    public static String serialize(final YangInstanceIdentifier data, final SchemaContext schemaContext) {
+        return YangInstanceIdentifierSerializer.create(schemaContext, data);
+    }
+
+    public static YangInstanceIdentifier deserialize(final String data, final SchemaContext schemaContext) {
+        if (data == null) {
+            return YangInstanceIdentifier.builder().build();
+        }
+        return YangInstanceIdentifier.create(YangInstanceIdentifierDeserializer.create(schemaContext, data));
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserFieldsParameter.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserFieldsParameter.java
new file mode 100644 (file)
index 0000000..0ed8a48
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * 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.nb.rfc8040.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.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.nb.rfc8040.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}
+     */
+    @Nonnull
+    public static 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
+                    final 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}
+     */
+    @Nonnull
+    private static 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 qualifiedName qname of initial node
+     * @return {@link DataSchemaContextNode}
+     */
+    @Nullable
+    private static DataSchemaContextNode<?> resolveMixinNode(@Nullable final DataSchemaContextNode<?> node,
+                                                             @Nonnull final Set<QName> level,
+                                                             @Nonnull final QName qualifiedName) {
+        DataSchemaContextNode<?> currentNode = node;
+        while (currentNode != null && currentNode.isMixin()) {
+            level.add(qualifiedName);
+            currentNode = currentNode.getChild(qualifiedName);
+        }
+
+        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;
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserIdentifier.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/ParserIdentifier.java
new file mode 100644 (file)
index 0000000..875b5d8
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * 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.nb.rfc8040.utils.parser;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.schema.SchemaExportContext;
+import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
+import org.opendaylight.restconf.nb.rfc8040.utils.validations.RestconfValidation;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+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;
+
+/**
+ * Util class for parsing identifier.
+ */
+public final class ParserIdentifier {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ParserIdentifier.class);
+
+    private ParserIdentifier() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+    /**
+     * Make {@link InstanceIdentifierContext} from {@link String} identifier
+     * <br>
+     * For identifiers of data NOT behind mount points returned
+     * {@link InstanceIdentifierContext} is prepared with {@code null} reference of {@link DOMMountPoint} and with
+     * controller's {@link SchemaContext}.
+     * <br>
+     * For identifiers of data behind mount points returned
+     * {@link InstanceIdentifierContext} is prepared with reference of {@link DOMMountPoint} and its
+     * own {@link SchemaContext}.
+     *
+     * @param identifier
+     *           - path identifier
+     * @param schemaContext
+     *           - controller schema context
+     * @param mountPointService
+     *           - mount point service
+     * @return {@link InstanceIdentifierContext}
+     */
+    public static InstanceIdentifierContext<?> toInstanceIdentifier(
+            final String identifier,
+            final SchemaContext schemaContext,
+            final Optional<DOMMountPointService> mountPointService) {
+        if (identifier != null && identifier.contains(RestconfConstants.MOUNT)) {
+            if (!mountPointService.isPresent()) {
+                throw new RestconfDocumentedException("Mount point service is not available");
+            }
+
+            final Iterator<String> pathsIt = Splitter.on("/" + RestconfConstants.MOUNT).split(identifier).iterator();
+
+            final String mountPointId = pathsIt.next();
+            final YangInstanceIdentifier mountYangInstanceIdentifier = IdentifierCodec.deserialize(
+                    mountPointId, schemaContext);
+            final Optional<DOMMountPoint> mountPoint =
+                    mountPointService.get().getMountPoint(mountYangInstanceIdentifier);
+
+            if (!mountPoint.isPresent()) {
+                throw new RestconfDocumentedException(
+                        "Mount point does not exist.", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
+            }
+
+            final DOMMountPoint domMountPoint = mountPoint.get();
+            final SchemaContext mountSchemaContext = domMountPoint.getSchemaContext();
+
+            final String pathId = pathsIt.next().replaceFirst("/", "");
+            final YangInstanceIdentifier pathYangInstanceIdentifier = IdentifierCodec.deserialize(
+                    pathId, mountSchemaContext);
+
+            final DataSchemaContextNode<?> child = DataSchemaContextTree.from(mountSchemaContext)
+                .getChild(pathYangInstanceIdentifier);
+            if (child != null) {
+                return new InstanceIdentifierContext<SchemaNode>(pathYangInstanceIdentifier, child.getDataSchemaNode(),
+                        domMountPoint, mountSchemaContext);
+            }
+            final QName rpcQName = pathYangInstanceIdentifier.getLastPathArgument().getNodeType();
+            RpcDefinition def = null;
+            for (final RpcDefinition rpcDefinition : mountSchemaContext
+                    .findModuleByNamespaceAndRevision(rpcQName.getNamespace(), rpcQName.getRevision()).getRpcs()) {
+                if (rpcDefinition.getQName().getLocalName().equals(rpcQName.getLocalName())) {
+                    def = rpcDefinition;
+                    break;
+                }
+            }
+            return new InstanceIdentifierContext<>(pathYangInstanceIdentifier, def, domMountPoint, mountSchemaContext);
+        } else {
+            final YangInstanceIdentifier deserialize = IdentifierCodec.deserialize(identifier, schemaContext);
+            final DataSchemaContextNode<?> child = DataSchemaContextTree.from(schemaContext).getChild(deserialize);
+
+            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<>(deserialize, def, null, schemaContext);
+        }
+    }
+
+    /**
+     * Make {@link String} from {@link YangInstanceIdentifier}.
+     *
+     * @param instanceIdentifier    Instance identifier
+     * @param schemaContext         Schema context
+     * @return                      Yang instance identifier serialized to String
+     */
+    public static String stringFromYangInstanceIdentifier(final YangInstanceIdentifier instanceIdentifier,
+            final SchemaContext schemaContext) {
+        return IdentifierCodec.serialize(instanceIdentifier, schemaContext);
+    }
+
+    /**
+     * Make a {@link QName} from identifier.
+     *
+     * @param identifier
+     *             path parameter
+     * @return {@link QName}
+     */
+    public static QName makeQNameFromIdentifier(final String identifier) {
+        // check if more than one slash is not used as path separator
+        if (identifier.contains(
+                String.valueOf(RestconfConstants.SLASH).concat(String.valueOf(RestconfConstants.SLASH)))) {
+            LOG.debug("URI has bad format. It should be \'moduleName/yyyy-MM-dd\' " + identifier);
+            throw new RestconfDocumentedException(
+                    "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL,
+                    ErrorTag.INVALID_VALUE);
+        }
+
+        final int mountIndex = identifier.indexOf(RestconfConstants.MOUNT);
+        final String moduleNameAndRevision;
+        if (mountIndex >= 0) {
+            moduleNameAndRevision = identifier.substring(mountIndex + RestconfConstants.MOUNT.length())
+                    .replaceFirst(String.valueOf(RestconfConstants.SLASH), "");
+        } else {
+            moduleNameAndRevision = identifier;
+        }
+
+        final List<String> pathArgs = RestconfConstants.SLASH_SPLITTER.splitToList(moduleNameAndRevision);
+        if (pathArgs.size() != 2) {
+            LOG.debug("URI has bad format '{}'. It should be 'moduleName/yyyy-MM-dd'", identifier);
+            throw new RestconfDocumentedException(
+                    "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL,
+                    ErrorTag.INVALID_VALUE);
+        }
+
+        final Date moduleRevision;
+        try {
+            moduleRevision = SimpleDateFormatUtil.getRevisionFormat().parse(pathArgs.get(1));
+        } catch (final ParseException e) {
+            LOG.debug("URI has bad format: '{}'. It should be 'moduleName/yyyy-MM-dd'", identifier);
+            throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+        }
+
+        return QName.create(null, moduleRevision, pathArgs.get(0));
+    }
+
+    /**
+     * Parsing {@link Module} module by {@link String} module name and
+     * {@link Date} revision and from the parsed module create
+     * {@link SchemaExportContext}.
+     *
+     * @param schemaContext
+     *             {@link SchemaContext}
+     * @param identifier
+     *             path parameter
+     * @param domMountPointService
+     *             {@link DOMMountPointService}
+     * @return {@link SchemaExportContext}
+     */
+    public static SchemaExportContext toSchemaExportContextFromIdentifier(final SchemaContext schemaContext,
+            final String identifier, final DOMMountPointService domMountPointService) {
+        final Iterable<String> pathComponents = RestconfConstants.SLASH_SPLITTER.split(identifier);
+        final Iterator<String> componentIter = pathComponents.iterator();
+        if (!Iterables.contains(pathComponents, RestconfConstants.MOUNT)) {
+            final String moduleName = RestconfValidation.validateAndGetModulName(componentIter);
+            final Date revision = RestconfValidation.validateAndGetRevision(componentIter);
+            final Module module = schemaContext.findModuleByName(moduleName, revision);
+            return new SchemaExportContext(schemaContext, module);
+        } else {
+            final StringBuilder pathBuilder = new StringBuilder();
+            while (componentIter.hasNext()) {
+                final String current = componentIter.next();
+
+                if (RestconfConstants.MOUNT.equals(current)) {
+                    pathBuilder.append("/");
+                    pathBuilder.append(RestconfConstants.MOUNT);
+                    break;
+                }
+
+                if (pathBuilder.length() != 0) {
+                    pathBuilder.append("/");
+                }
+
+                pathBuilder.append(current);
+            }
+            final InstanceIdentifierContext<?> point = ParserIdentifier
+                    .toInstanceIdentifier(pathBuilder.toString(), schemaContext, Optional.of(domMountPointService));
+            final String moduleName = RestconfValidation.validateAndGetModulName(componentIter);
+            final Date revision = RestconfValidation.validateAndGetRevision(componentIter);
+            final Module module = point.getMountPoint().getSchemaContext().findModuleByName(moduleName, revision);
+            return new SchemaExportContext(point.getMountPoint().getSchemaContext(), module);
+        }
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializer.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierDeserializer.java
new file mode 100644 (file)
index 0000000..5c1bbf3
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+ * 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.nb.rfc8040.utils.parser;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.opendaylight.restconf.common.errors.RestconfError;
+import org.opendaylight.restconf.common.util.RestUtil;
+import org.opendaylight.restconf.common.util.RestconfSchemaUtil;
+import org.opendaylight.restconf.common.validation.RestconfValidationUtils;
+import org.opendaylight.restconf.nb.rfc8040.codecs.RestCodec;
+import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.builder.ParserBuilderConstants;
+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;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+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;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+
+/**
+ * Deserializer for {@link String} to {@link YangInstanceIdentifier} for
+ * restconf.
+ *
+ */
+public final class YangInstanceIdentifierDeserializer {
+
+    private YangInstanceIdentifierDeserializer() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+    /**
+     * Method to create {@link Iterable} from {@link PathArgument} which are
+     * parsing from data by {@link SchemaContext}.
+     *
+     * @param schemaContext
+     *             for validate of parsing path arguments
+     * @param data
+     *             path to data
+     * @return {@link Iterable} of {@link PathArgument}
+     */
+    public static Iterable<PathArgument> create(final SchemaContext schemaContext, final String data) {
+        final List<PathArgument> path = new LinkedList<>();
+        final MainVarsWrapper variables = new YangInstanceIdentifierDeserializer.MainVarsWrapper(
+                data, DataSchemaContextTree.from(schemaContext).getRoot(),
+                YangInstanceIdentifierDeserializer.MainVarsWrapper.STARTING_OFFSET, schemaContext);
+
+        while (!allCharsConsumed(variables)) {
+            validArg(variables);
+            final QName qname = prepareQName(variables);
+
+            // this is the last identifier (input is consumed) or end of identifier (slash)
+            if (allCharsConsumed(variables)
+                    || (currentChar(variables.getOffset(), variables.getData()) == RestconfConstants.SLASH)) {
+                prepareIdentifier(qname, path, variables);
+                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) {
+                    prepareNodeWithPredicates(qname, path, variables);
+                } else {
+                    prepareNodeWithValue(qname, path, variables);
+                }
+            } else {
+                throw new IllegalArgumentException(
+                        "Bad char " + currentChar(variables.getOffset(), variables.getData()) + " on position "
+                                + variables.getOffset() + ".");
+            }
+        }
+
+        return ImmutableList.copyOf(path);
+    }
+
+    private static void prepareNodeWithPredicates(final QName qname, final List<PathArgument> path,
+                                                  final MainVarsWrapper variables) {
+
+        final DataSchemaNode dataSchemaNode = variables.getCurrent().getDataSchemaNode();
+        checkValid((dataSchemaNode != null), "Data schema node is null", variables.getData(), variables.getOffset());
+
+        final Iterator<QName> keys = ((ListSchemaNode) dataSchemaNode).getKeyDefinition().iterator();
+        final ImmutableMap.Builder<QName, Object> values = ImmutableMap.builder();
+
+        // skip already expected equal sign
+        skipCurrentChar(variables);
+
+        // read key value separated by comma
+        while (keys.hasNext() && !allCharsConsumed(variables) && (currentChar(variables.getOffset(),
+                variables.getData()) != RestconfConstants.SLASH)) {
+
+            // empty key value
+            if (currentChar(variables.getOffset(), variables.getData()) == ParserBuilderConstants.Deserializer.COMMA) {
+                values.put(keys.next(), ParserBuilderConstants.Deserializer.EMPTY_STRING);
+                skipCurrentChar(variables);
+                continue;
+            }
+
+            // check if next value is parsable
+            RestconfValidationUtils.checkDocumentedError(
+                    ParserBuilderConstants.Deserializer.IDENTIFIER_PREDICATE
+                            .matches(currentChar(variables.getOffset(), variables.getData())),
+                    RestconfError.ErrorType.PROTOCOL,
+                    RestconfError.ErrorTag.MALFORMED_MESSAGE,
+                    ""
+            );
+
+            // parse value
+            final QName key = keys.next();
+            DataSchemaNode leafSchemaNode = null;
+            if (dataSchemaNode instanceof ListSchemaNode) {
+                leafSchemaNode = ((ListSchemaNode) dataSchemaNode).getDataChildByName(key);
+            } else if (dataSchemaNode instanceof LeafListSchemaNode) {
+                leafSchemaNode = dataSchemaNode;
+            }
+            final String value = findAndParsePercentEncoded(nextIdentifierFromNextSequence(
+                    ParserBuilderConstants.Deserializer.IDENTIFIER_PREDICATE, variables));
+            final Object valueByType = prepareValueByType(leafSchemaNode, value, variables);
+            values.put(key, valueByType);
+
+
+            // skip comma
+            if (keys.hasNext() && !allCharsConsumed(variables) && (currentChar(
+                    variables.getOffset(), variables.getData()) == ParserBuilderConstants.Deserializer.COMMA)) {
+                skipCurrentChar(variables);
+            }
+        }
+
+        // the last key is considered to be empty
+        if (keys.hasNext()) {
+            if (allCharsConsumed(variables)
+                    || (currentChar(variables.getOffset(), variables.getData()) == RestconfConstants.SLASH)) {
+                values.put(keys.next(), ParserBuilderConstants.Deserializer.EMPTY_STRING);
+            }
+
+            // there should be no more missing keys
+            RestconfValidationUtils.checkDocumentedError(
+                    !keys.hasNext(),
+                    RestconfError.ErrorType.PROTOCOL,
+                    RestconfError.ErrorTag.MISSING_ATTRIBUTE,
+                    "Key value missing for: " + qname
+            );
+        }
+
+        path.add(new YangInstanceIdentifier.NodeIdentifierWithPredicates(qname, values.build()));
+    }
+
+    private static Object prepareValueByType(final DataSchemaNode schemaNode, final String value,
+            final MainVarsWrapper vars) {
+        Object decoded = null;
+
+        TypeDefinition<? extends TypeDefinition<?>> typedef = null;
+        if (schemaNode instanceof LeafListSchemaNode) {
+            typedef = ((LeafListSchemaNode) schemaNode).getType();
+        } else {
+            typedef = ((LeafSchemaNode) schemaNode).getType();
+        }
+        final TypeDefinition<?> baseType = RestUtil.resolveBaseTypeFrom(typedef);
+        if (baseType instanceof LeafrefTypeDefinition) {
+            typedef = SchemaContextUtil.getBaseTypeForLeafRef((LeafrefTypeDefinition) baseType, vars.getSchemaContext(),
+                    schemaNode);
+        }
+        final Codec<Object, Object> codec = RestCodec.from(typedef, null, vars.getSchemaContext());
+        decoded = codec.deserialize(value);
+        if (decoded == null) {
+            if ((baseType instanceof IdentityrefTypeDefinition)) {
+                decoded = toQName(value, schemaNode, vars.getSchemaContext());
+            }
+        }
+        return decoded;
+    }
+
+    private static Object toQName(final String value, final DataSchemaNode schemaNode,
+            final SchemaContext schemaContext) {
+        final String moduleName = toModuleName(value);
+        final String nodeName = toNodeName(value);
+        final Module module = schemaContext.findModuleByName(moduleName, null);
+        for (final IdentitySchemaNode identitySchemaNode : module.getIdentities()) {
+            final QName qName = identitySchemaNode.getQName();
+            if (qName.getLocalName().equals(nodeName)) {
+                return qName;
+            }
+        }
+        return QName.create(schemaNode.getQName().getNamespace(), schemaNode.getQName().getRevision(), nodeName);
+    }
+
+    private static String toNodeName(final String str) {
+        final int idx = str.indexOf(':');
+        if (idx == -1) {
+            return str;
+        }
+
+        if (str.indexOf(':', idx + 1) != -1) {
+            return str;
+        }
+
+        return str.substring(idx + 1);
+    }
+
+    private static String toModuleName(final String str) {
+        final int idx = str.indexOf(':');
+        if (idx == -1) {
+            return null;
+        }
+
+        if (str.indexOf(':', idx + 1) != -1) {
+            return null;
+        }
+
+        return str.substring(0, idx);
+    }
+
+    private static QName prepareQName(final MainVarsWrapper variables) {
+        checkValid(
+                ParserBuilderConstants.Deserializer.IDENTIFIER_FIRST_CHAR
+                        .matches(currentChar(variables.getOffset(), variables.getData())),
+                "Identifier must start with character from set 'a-zA-Z_'", variables.getData(), variables.getOffset());
+        final String preparedPrefix = nextIdentifierFromNextSequence(
+                ParserBuilderConstants.Deserializer.IDENTIFIER, variables);
+        final String prefix;
+        final String localName;
+
+        if (allCharsConsumed(variables)) {
+            return getQNameOfDataSchemaNode(preparedPrefix, variables);
+        }
+
+        switch (currentChar(variables.getOffset(), variables.getData())) {
+            case RestconfConstants.SLASH:
+                prefix = preparedPrefix;
+                return getQNameOfDataSchemaNode(prefix, variables);
+            case ParserBuilderConstants.Deserializer.COLON:
+                prefix = preparedPrefix;
+                skipCurrentChar(variables);
+                checkValid(
+                        ParserBuilderConstants.Deserializer.IDENTIFIER_FIRST_CHAR
+                                .matches(currentChar(variables.getOffset(), variables.getData())),
+                        "Identifier must start with character from set 'a-zA-Z_'", variables.getData(),
+                        variables.getOffset());
+                localName = nextIdentifierFromNextSequence(ParserBuilderConstants.Deserializer.IDENTIFIER, variables);
+
+                if (!allCharsConsumed(variables) && (currentChar(
+                        variables.getOffset(), variables.getData()) == ParserBuilderConstants.Deserializer.EQUAL)) {
+                    return getQNameOfDataSchemaNode(localName, variables);
+                } else {
+                    final Module module = moduleForPrefix(prefix, variables.getSchemaContext());
+                    Preconditions.checkArgument(module != null, "Failed to lookup prefix %s", prefix);
+                    return QName.create(module.getQNameModule(), localName);
+                }
+            case ParserBuilderConstants.Deserializer.EQUAL:
+                prefix = preparedPrefix;
+                return getQNameOfDataSchemaNode(prefix, variables);
+            default:
+                throw new IllegalArgumentException("Failed build path.");
+        }
+    }
+
+    private static String nextIdentifierFromNextSequence(final CharMatcher matcher, final MainVarsWrapper variables) {
+        final int start = variables.getOffset();
+        nextSequenceEnd(matcher, variables);
+        return variables.getData().substring(start, variables.getOffset());
+    }
+
+    private static void nextSequenceEnd(final CharMatcher matcher, final MainVarsWrapper variables) {
+        while (!allCharsConsumed(variables)
+                && matcher.matches(variables.getData().charAt(variables.getOffset()))) {
+            variables.setOffset(variables.getOffset() + 1);
+        }
+    }
+
+    private static void prepareNodeWithValue(final QName qname, final List<PathArgument> path,
+            final MainVarsWrapper variables) {
+        skipCurrentChar(variables);
+        final String value = nextIdentifierFromNextSequence(
+                ParserBuilderConstants.Deserializer.IDENTIFIER_PREDICATE, variables);
+
+        // exception if value attribute is missing
+        RestconfValidationUtils.checkDocumentedError(
+                !value.isEmpty(),
+                RestconfError.ErrorType.PROTOCOL,
+                RestconfError.ErrorTag.MISSING_ATTRIBUTE,
+                "Value missing for: " + qname
+        );
+        final DataSchemaNode dataSchemaNode = variables.getCurrent().getDataSchemaNode();
+        final Object valueByType = prepareValueByType(dataSchemaNode, findAndParsePercentEncoded(value), variables);
+        path.add(new YangInstanceIdentifier.NodeWithValue<>(qname, valueByType));
+    }
+
+    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());
+    }
+
+    private static DataSchemaContextNode<?> nextContextNode(final QName qname, final List<PathArgument> path,
+            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()) {
+            path.add(current.getIdentifier());
+            current = current.getChild(qname);
+            variables.setCurrent(current);
+        }
+        return current;
+    }
+
+    private static String findAndParsePercentEncoded(final String preparedPrefix) {
+        if (!preparedPrefix.contains(String.valueOf(ParserBuilderConstants.Deserializer.PERCENT_ENCODING))) {
+            return preparedPrefix;
+        }
+
+        final StringBuilder parsedPrefix = new StringBuilder(preparedPrefix);
+        final CharMatcher matcher = CharMatcher.is(ParserBuilderConstants.Deserializer.PERCENT_ENCODING);
+
+        while (matcher.matchesAnyOf(parsedPrefix)) {
+            final int percentCharPosition = matcher.indexIn(parsedPrefix);
+            parsedPrefix.replace(
+                    percentCharPosition,
+                    percentCharPosition + ParserBuilderConstants.Deserializer.LAST_ENCODED_CHAR,
+                    String.valueOf((char) Integer.parseInt(parsedPrefix.substring(
+                            percentCharPosition + ParserBuilderConstants.Deserializer.FIRST_ENCODED_CHAR,
+                            percentCharPosition + ParserBuilderConstants.Deserializer.LAST_ENCODED_CHAR),
+                            ParserBuilderConstants.Deserializer.PERCENT_ENCODED_RADIX)));
+        }
+
+        return parsedPrefix.toString();
+    }
+
+    private static QName getQNameOfDataSchemaNode(final String nodeName, final MainVarsWrapper variables) {
+        final DataSchemaNode dataSchemaNode = variables.getCurrent().getDataSchemaNode();
+        if (dataSchemaNode instanceof ContainerSchemaNode) {
+            final ContainerSchemaNode contSchemaNode = (ContainerSchemaNode) dataSchemaNode;
+            final DataSchemaNode node = RestconfSchemaUtil.findSchemaNodeInCollection(contSchemaNode.getChildNodes(),
+                    nodeName);
+            return node.getQName();
+        } else if (dataSchemaNode instanceof ListSchemaNode) {
+            final ListSchemaNode listSchemaNode = (ListSchemaNode) dataSchemaNode;
+            final DataSchemaNode node = RestconfSchemaUtil.findSchemaNodeInCollection(listSchemaNode.getChildNodes(),
+                    nodeName);
+            return node.getQName();
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    private static Module moduleForPrefix(final String prefix, final SchemaContext schemaContext) {
+        return schemaContext.findModuleByName(prefix, null);
+    }
+
+    private static void validArg(final MainVarsWrapper variables) {
+        // every identifier except of the first MUST start with slash
+        if (variables.getOffset() != MainVarsWrapper.STARTING_OFFSET) {
+            checkValid(RestconfConstants.SLASH == currentChar(variables.getOffset(), variables.getData()),
+                    "Identifier must start with '/'.", variables.getData(), variables.getOffset());
+
+            // skip slash
+            skipCurrentChar(variables);
+
+            // check if slash is not also the last char in identifier
+            checkValid(!allCharsConsumed(variables), "Identifier cannot end with '/'.",
+                    variables.getData(), variables.getOffset());
+        }
+    }
+
+    private static void skipCurrentChar(final MainVarsWrapper variables) {
+        variables.setOffset(variables.getOffset() + 1);
+    }
+
+    private static char currentChar(final int offset, final String data) {
+        return data.charAt(offset);
+    }
+
+    private static void checkValid(final boolean condition, final String errorMsg, final String data,
+            final int offset) {
+        Preconditions.checkArgument(condition, "Could not parse Instance Identifier '%s'. Offset: %s : Reason: %s",
+                data, offset, errorMsg);
+    }
+
+    private static boolean allCharsConsumed(final MainVarsWrapper variables) {
+        return variables.getOffset() == variables.getData().length();
+    }
+
+    private static final class MainVarsWrapper {
+        private static final int STARTING_OFFSET = 0;
+
+        private final SchemaContext schemaContext;
+        private final String data;
+
+        private DataSchemaContextNode<?> current;
+        private int offset;
+
+        MainVarsWrapper(final String data, final DataSchemaContextNode<?> current, final int offset,
+                final SchemaContext schemaContext) {
+            this.data = data;
+            this.current = current;
+            this.offset = offset;
+            this.schemaContext = schemaContext;
+        }
+
+        public String getData() {
+            return this.data;
+        }
+
+        public DataSchemaContextNode<?> getCurrent() {
+            return this.current;
+        }
+
+        public void setCurrent(final DataSchemaContextNode<?> current) {
+            this.current = current;
+        }
+
+        public int getOffset() {
+            return this.offset;
+        }
+
+        public void setOffset(final int offset) {
+            this.offset = offset;
+        }
+
+        public SchemaContext getSchemaContext() {
+            return this.schemaContext;
+        }
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierSerializer.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/YangInstanceIdentifierSerializer.java
new file mode 100644 (file)
index 0000000..a8c28d7
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * 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.nb.rfc8040.utils.parser;
+
+import com.google.common.base.Preconditions;
+import java.net.URI;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import org.opendaylight.restconf.nb.rfc8040.utils.RestconfConstants;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.builder.ParserBuilderConstants;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.builder.ParserBuilderConstants.Serializer;
+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.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.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.SchemaContext;
+
+/**
+ * Serializer for {@link YangInstanceIdentifier} to {@link String} for restconf.
+ */
+public final class YangInstanceIdentifierSerializer {
+
+    private YangInstanceIdentifierSerializer() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+    /**
+     * Method to create String from {@link Iterable} of {@link PathArgument}
+     * which are parsing from data by {@link SchemaContext}.
+     *
+     * @param schemaContext
+     *             for validate of parsing path arguments
+     * @param data
+     *             path to data
+     * @return {@link String}
+     */
+    public static String create(final SchemaContext schemaContext, final YangInstanceIdentifier data) {
+        final DataSchemaContextNode<?> current = DataSchemaContextTree.from(schemaContext).getRoot();
+        final MainVarsWrapper variables = new MainVarsWrapper(current);
+        final StringBuilder path = new StringBuilder();
+
+        QNameModule parentModule = null;
+        for (int i = 0; i < data.getPathArguments().size(); i++) {
+            // get module of the parent
+            if (!variables.getCurrent().isMixin()) {
+                parentModule = variables.getCurrent().getDataSchemaNode().getQName().getModule();
+            }
+
+            final PathArgument arg = data.getPathArguments().get(i);
+            variables.setCurrent(variables.getCurrent().getChild(arg));
+
+            Preconditions.checkArgument(variables.getCurrent() != null,
+                    "Invalid input %s: schema for argument %s (after %s) not found", data, arg, path);
+
+            if (variables.getCurrent().isMixin()) {
+                continue;
+            }
+
+            // append namespace before every node which is defined in other module than its parent
+            // condition is satisfied also for the first path argument
+            if (!arg.getNodeType().getModule().equals(parentModule)) {
+                // append slash if it is not the first path argument
+                if (path.length() > 0) {
+                    path.append(RestconfConstants.SLASH);
+                }
+
+                path.append(prefixForNamespace(arg.getNodeType(), schemaContext));
+                path.append(ParserBuilderConstants.Deserializer.COLON);
+            } else {
+                path.append(RestconfConstants.SLASH);
+            }
+
+            if (arg instanceof NodeIdentifierWithPredicates) {
+                prepareNodeWithPredicates(path, arg);
+            } else if (arg instanceof NodeWithValue) {
+                prepareNodeWithValue(path, arg);
+            } else {
+                appendQName(path, arg.getNodeType());
+            }
+        }
+
+        return path.toString();
+    }
+
+    private static void prepareNodeWithValue(final StringBuilder path, final PathArgument arg) {
+        path.append(arg.getNodeType().getLocalName());
+        path.append(ParserBuilderConstants.Deserializer.EQUAL);
+
+        String value = String.valueOf(((NodeWithValue<String>) arg).getValue());
+        if (Serializer.PERCENT_ENCODE_CHARS.matchesAnyOf(value)) {
+            value = parsePercentEncodeChars(value);
+        }
+        path.append(value);
+    }
+
+    private static void prepareNodeWithPredicates(final StringBuilder path, final PathArgument arg) {
+        path.append(arg.getNodeType().getLocalName());
+
+        final Iterator<Entry<QName, Object>> iterator = ((NodeIdentifierWithPredicates) arg).getKeyValues()
+                .entrySet().iterator();
+
+        if (iterator.hasNext()) {
+            path.append(ParserBuilderConstants.Deserializer.EQUAL);
+        }
+
+        while (iterator.hasNext()) {
+            String valueOf = String.valueOf(iterator.next().getValue());
+            if (Serializer.PERCENT_ENCODE_CHARS.matchesAnyOf(valueOf)) {
+                valueOf = parsePercentEncodeChars(valueOf);
+            }
+            path.append(valueOf);
+            if (iterator.hasNext()) {
+                path.append(ParserBuilderConstants.Deserializer.COMMA);
+            }
+        }
+    }
+
+    /**
+     * Encode {@link Serializer#DISABLED_CHARS} chars to percent encoded chars.
+     *
+     * @param valueOf
+     *             string to encode
+     * @return encoded {@link String}
+     */
+    private static String parsePercentEncodeChars(final String valueOf) {
+        final StringBuilder sb = new StringBuilder();
+        int start = 0;
+        while (start < valueOf.length()) {
+            if (Serializer.PERCENT_ENCODE_CHARS.matches(valueOf.charAt(start))) {
+                final String format = String.format("%x", (int) valueOf.charAt(start));
+                final String upperCase = format.toUpperCase();
+                sb.append(ParserBuilderConstants.Deserializer.PERCENT_ENCODING + upperCase);
+            } else {
+                sb.append(valueOf.charAt(start));
+            }
+            start++;
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Add {@link QName} to the serialized string.
+     *
+     * @param path
+     *             {@link StringBuilder}
+     * @param qname
+     *             {@link QName} node
+     * @return {@link StringBuilder}
+     */
+    private static StringBuilder appendQName(final StringBuilder path, final QName qname) {
+        path.append(qname.getLocalName());
+        return path;
+    }
+
+    /**
+     * Create prefix of namespace from {@link QName}.
+     *
+     * @param qname
+     *             {@link QName}
+     * @return {@link String}
+     */
+    private static String prefixForNamespace(final QName qname, final SchemaContext schemaContext) {
+        final URI namespace = qname.getNamespace();
+        Preconditions.checkArgument(namespace != null, "Failed to map QName {}", qname);
+        final Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, qname.getRevision());
+        return module.getName();
+    }
+
+    private static final class MainVarsWrapper {
+
+        private DataSchemaContextNode<?> current;
+
+        MainVarsWrapper(final DataSchemaContextNode<?> current) {
+            this.setCurrent(current);
+        }
+
+        public DataSchemaContextNode<?> getCurrent() {
+            return this.current;
+        }
+
+        public void setCurrent(final DataSchemaContextNode<?> current) {
+            this.current = current;
+        }
+
+    }
+}
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/builder/ParserBuilderConstants.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/parser/builder/ParserBuilderConstants.java
new file mode 100644 (file)
index 0000000..74cc886
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.nb.rfc8040.utils.parser.builder;
+
+import com.google.common.base.CharMatcher;
+import java.util.Arrays;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.YangInstanceIdentifierDeserializer;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.YangInstanceIdentifierSerializer;
+
+/**
+ * Util class of constants of {@link YangInstanceIdentifierSerializer} and
+ * {@link YangInstanceIdentifierDeserializer}.
+ *
+ */
+public final class ParserBuilderConstants {
+
+    private ParserBuilderConstants() {
+        throw new UnsupportedOperationException("Util class");
+    }
+
+    /**
+     * Constants for {@link YangInstanceIdentifierSerializer}.
+     *
+     */
+    public static final class Serializer {
+
+        private Serializer() {
+            throw new UnsupportedOperationException("Util class");
+        }
+
+        public static final String DISABLED_CHARS = Arrays.toString(new char[] { ':', '/', '?', '#', '[', ']', '@' })
+                .concat(Arrays.toString(new char[] { '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=' }));
+
+        public static final CharMatcher PERCENT_ENCODE_CHARS = CharMatcher.anyOf(DISABLED_CHARS).precomputed();
+    }
+
+    /**
+     * Constants for {@link YangInstanceIdentifierSerializer}.
+     *
+     */
+    public static final class Deserializer {
+
+        private Deserializer() {
+            throw new UnsupportedOperationException("Util class");
+        }
+
+        public static final CharMatcher BASE = CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z'))
+                .precomputed();
+
+        public static final CharMatcher IDENTIFIER_FIRST_CHAR = BASE.or(CharMatcher.is('_')).precomputed();
+
+        public static final CharMatcher IDENTIFIER = IDENTIFIER_FIRST_CHAR.or(CharMatcher.inRange('0', '9'))
+                .or(CharMatcher.anyOf(".-")).precomputed();
+
+        public static final CharMatcher IDENTIFIER_HEXA = CharMatcher.inRange('a', 'f')
+                .or(CharMatcher.inRange('A', 'F')).or(CharMatcher.inRange('0', '9')).precomputed();
+
+        public static final char COLON = ':';
+        public static final char EQUAL = '=';
+        public static final char COMMA = ',';
+        public static final char HYPHEN = '-';
+        public static final char PERCENT_ENCODING = '%';
+
+        public static final CharMatcher IDENTIFIER_PREDICATE = CharMatcher.noneOf(
+                Serializer.DISABLED_CHARS).precomputed();
+
+        public static final String EMPTY_STRING = "";
+
+        // position of the first encoded char after percent sign in percent encoded string
+        public static final int FIRST_ENCODED_CHAR = 1;
+        // position of the last encoded char after percent sign in percent encoded string
+        public static final int LAST_ENCODED_CHAR = 3;
+        // percent encoded radix for parsing integers
+        public static final int PERCENT_ENCODED_RADIX = 16;
+    }
+}
\ No newline at end of file
diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/validations/RestconfValidation.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/utils/validations/RestconfValidation.java
new file mode 100644 (file)
index 0000000..74c5012
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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.nb.rfc8040.utils.validations;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Iterator;
+import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag;
+import org.opendaylight.restconf.common.errors.RestconfError.ErrorType;
+import org.opendaylight.restconf.common.validation.RestconfValidationUtils;
+import org.opendaylight.restconf.nb.rfc8040.utils.parser.builder.ParserBuilderConstants;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+
+/**
+ * Util class for validations.
+ *
+ */
+public final class RestconfValidation {
+
+    private RestconfValidation() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+    /**
+     * Validation and parsing of revision.
+     *
+     * @param revisionDate
+     *             iterator
+     * @return {@link Date}
+     */
+    public static Date validateAndGetRevision(final Iterator<String> revisionDate) {
+        RestconfValidationUtils.checkDocumentedError(revisionDate.hasNext(), ErrorType.PROTOCOL,
+                ErrorTag.INVALID_VALUE, "Revision date must be supplied.");
+        try {
+            return SimpleDateFormatUtil.getRevisionFormat().parse(revisionDate.next());
+        } catch (final ParseException e) {
+            throw new RestconfDocumentedException("Supplied revision is not in expected date format YYYY-mm-dd", e);
+        }
+    }
+
+    /**
+     * Validation of name.
+     *
+     * @param moduleName
+     *             iterator
+     * @return {@link String}
+     */
+    public static String validateAndGetModulName(final Iterator<String> moduleName) {
+        RestconfValidationUtils.checkDocumentedError(
+                moduleName.hasNext(),
+                ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+                "Module name must be supplied."
+        );
+
+        final String name = moduleName.next();
+
+        RestconfValidationUtils.checkDocumentedError(
+                !name.isEmpty() && ParserBuilderConstants.Deserializer.IDENTIFIER_FIRST_CHAR.matches(name.charAt(0)),
+                ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+                "Identifier must start with character from set 'a-zA-Z_"
+        );
+
+        RestconfValidationUtils.checkDocumentedError(
+                !name.toUpperCase().startsWith("XML"),
+                ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+                "Identifier must NOT start with XML ignore case."
+        );
+
+        RestconfValidationUtils.checkDocumentedError(
+                ParserBuilderConstants.Deserializer.IDENTIFIER.matchesAllOf(name.substring(1)),
+                ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+                "Supplied name has not expected identifier format."
+        );
+
+        return name;
+    }
+
+}