[DMAAP-BC] Consolidate bus controller repos 51/120651/4
authorefiacor <fiachra.corcoran@est.tech>
Thu, 8 Apr 2021 15:40:06 +0000 (16:40 +0100)
committerefiacor <fiachra.corcoran@est.tech>
Wed, 21 Apr 2021 12:49:22 +0000 (13:49 +0100)
Migrate BC CSITs to the repo
Fix documentation

Signed-off-by: efiacor <fiachra.corcoran@est.tech>
Change-Id: I874ed61be1b61187e233e9fd0937f6658f0ec0f0
Issue-ID: DMAAP-1544

276 files changed:
.gitignore
certs/README [deleted file]
certs/org.onap.dmaap-bc.cred.props [deleted file]
certs/org.onap.dmaap-bc.crontab.sh [deleted file]
certs/org.onap.dmaap-bc.jks [deleted file]
certs/org.onap.dmaap-bc.keyfile [deleted file]
certs/org.onap.dmaap-bc.p12 [deleted file]
certs/org.onap.dmaap-bc.props [deleted file]
certs/org.onap.dmaap-bc.showpass [deleted file]
certs/org.onap.dmaap-bc.trust.jks [deleted file]
csit/plans/full_suite/setup.sh [new file with mode: 0755]
csit/plans/full_suite/teardown.sh [new file with mode: 0755]
csit/plans/full_suite/testplan.txt [new file with mode: 0755]
csit/plans/with_dr/setup.sh [new file with mode: 0755]
csit/plans/with_dr/teardown.sh [new file with mode: 0755]
csit/plans/with_dr/testplan.txt [new file with mode: 0755]
csit/plans/with_mr/setup.sh [new file with mode: 0755]
csit/plans/with_mr/teardown.sh [new file with mode: 0644]
csit/plans/with_mr/testplan.txt [new file with mode: 0644]
csit/prepare-csit.sh [new file with mode: 0755]
csit/run-csit.sh [new file with mode: 0755]
csit/run-project-csit.sh [new file with mode: 0755]
csit/scripts/dmaap-buscontroller/dmaapbc-init.sh [new file with mode: 0755]
csit/scripts/dmaap-buscontroller/dmaapbc-launch.sh [new file with mode: 0755]
csit/scripts/dmaap-buscontroller/docker-compose/buscontroller.env [new file with mode: 0644]
csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/org.onap.dmaap-bc.cred.props [new file with mode: 0644]
csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/org.onap.dmaap-bc.jks [new file with mode: 0644]
csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/org.onap.dmaap-bc.location.props [moved from certs/org.onap.dmaap-bc.location.props with 64% similarity]
csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/org.onap.dmaap-bc.props [new file with mode: 0644]
csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/truststore.jks [new file with mode: 0644]
csit/scripts/dmaap-buscontroller/docker-compose/dmaapbc.properties [new file with mode: 0644]
csit/scripts/dmaap-buscontroller/docker-compose/docker-compose-bc.yml [new file with mode: 0644]
csit/scripts/dmaap-buscontroller/docker-compose/logback.xml [moved from dmaap-bc/misc/logback.xml with 98% similarity]
csit/scripts/dmaap-datarouter/datarouter-launch.sh [new file with mode: 0644]
csit/scripts/dmaap-datarouter/datarouter-teardown.sh [new file with mode: 0755]
csit/scripts/dmaap-datarouter/docker-compose/docker-compose.yml [new file with mode: 0644]
csit/scripts/dmaap-datarouter/docker-compose/node.properties [new file with mode: 0644]
csit/scripts/dmaap-datarouter/docker-compose/provserver.properties [new file with mode: 0755]
csit/scripts/dmaap-datarouter/docker-compose/subscriber.properties [new file with mode: 0644]
csit/scripts/dmaap-datarouter/dr_certs/dr_node/org.onap.dmaap-dr-node.p12 [new file with mode: 0644]
csit/scripts/dmaap-datarouter/dr_certs/dr_node/org.onap.dmaap-dr.cred.props [new file with mode: 0644]
csit/scripts/dmaap-datarouter/dr_certs/dr_node/truststore.jks [new file with mode: 0644]
csit/scripts/dmaap-datarouter/dr_certs/dr_prov/org.onap.dmaap-dr-prov.p12 [new file with mode: 0755]
csit/scripts/dmaap-datarouter/dr_certs/dr_prov/org.onap.dmaap-dr.cred.props [new file with mode: 0644]
csit/scripts/dmaap-datarouter/dr_certs/dr_prov/truststore.jks [new file with mode: 0644]
csit/scripts/dmaap-message-router/dmaap-mr-launch.sh [new file with mode: 0755]
csit/scripts/dmaap-message-router/dmaap-mr-teardown.sh [new file with mode: 0755]
csit/scripts/dmaap-message-router/docker-compose/docker-compose.yml [new file with mode: 0644]
csit/scripts/dmaap-message-router/docker-compose/kafka/zk_client_jaas.conf [new file with mode: 0644]
csit/scripts/dmaap-message-router/docker-compose/mr/MsgRtrApi.properties [new file with mode: 0644]
csit/scripts/dmaap-message-router/docker-compose/mr/cadi.properties [new file with mode: 0644]
csit/scripts/dmaap-message-router/docker-compose/mr/logback.xml [new file with mode: 0644]
csit/scripts/dmaap-message-router/docker-compose/zk/zk_server_jaas.conf [new file with mode: 0644]
csit/scripts/get-instance-ip.sh [new file with mode: 0755]
csit/scripts/kill-instance.sh [new file with mode: 0755]
csit/tests/with_dr/__init__.robot [new file with mode: 0644]
csit/tests/with_dr/test1.robot [new file with mode: 0644]
csit/tests/with_mr/__init__.robot [new file with mode: 0644]
csit/tests/with_mr/test1.robot [new file with mode: 0644]
dbc-client/certs/ca.pem [moved from certs/ca.pem with 100% similarity]
dbc-client/certs/client.pem [moved from certs/client.pem with 100% similarity]
dbc-client/certs/key.pem [moved from certs/key.pem with 100% similarity]
dbc-client/misc/aaf-ca.crt [moved from misc/aaf-ca.crt with 100% similarity]
dbc-client/misc/dbc-client
dbc-client/pom.xml
dbc-client/src/main/resources/Dockerfile
dmaap-bc/README.md [new file with mode: 0644]
dmaap-bc/misc/dbc-api.jks [deleted file]
dmaap-bc/misc/opensource.env [deleted file]
dmaap-bc/misc/policyLogger.properties [deleted file]
dmaap-bc/pom.xml
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafConnection.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafDecrypt.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafEmpty.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafLurService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafObject.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceFactory.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceImpl.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafUserRole.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/ClearDecrypt.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DecryptionInterface.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapGrant.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapPerm.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFish.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AllowAll.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiAuthorizationCheckInterface.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPerms.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPolicy.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AuthenticationErrorException.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/DrProvConnection.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrProvConnection.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrTopicConnection.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnWrapper.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnectionFactory.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBException.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBFieldHandler.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBMap.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBSingleton.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DatabaseClass.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/LoadSchema.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/TableHandler.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/BaseLoggingClass.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/DmaapbcLogMessageEnum.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/logmsg.properties [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ApiError.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/BrTopic.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Node.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Pub.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Sub.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DcaeLocation.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Dmaap.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DmaapObject.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Feed.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/FqtnType.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Client.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Cluster.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MirrorMaker.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ReplicationType.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilter.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilter.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/Authorization.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AuthorizationFilter.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/BridgeResource.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_NodeResource.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_PubResource.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_SubResource.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResource.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DmaapResource.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/InfoResource.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClientResource.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResource.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequestTimeLogFilter.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredChecker.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredFieldException.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/ResponseBuilder.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/TopicResource.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/ApplicationConfig.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CadiCertificateManager.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertficateManagerFactory.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertificateManager.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/JettyServer.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/LegacyCertificateManager.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/Main.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafPermissionService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafTopicSetupService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/ApiService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/Credentials.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/CredentialsParser.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_NodeService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_PubService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_SubService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DcaeLocationService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/FeedService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClientService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClusterService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MirrorMakerService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/TopicService.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapConfig.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapTimestamp.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Fqdn.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Graph.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/PermissionBuilder.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomInteger.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomString.java [new file with mode: 0644]
dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Singleton.java [new file with mode: 0644]
dmaap-bc/src/main/resources/docker-compose.yml [deleted file]
dmaap-bc/src/main/resources/docker-databus-controller.conf [deleted file]
dmaap-bc/src/main/resources/docker/Dockerfile [moved from dmaap-bc/src/main/resources/Dockerfile with 67% similarity]
dmaap-bc/src/main/resources/misc/LocalKey [new file with mode: 0644]
dmaap-bc/src/main/resources/misc/PolicyEngineApi.properties.tmpl [new file with mode: 0644]
dmaap-bc/src/main/resources/misc/dmaapbc [moved from dmaap-bc/misc/dmaapbc with 62% similarity]
dmaap-bc/src/main/resources/misc/dmaapbc.properties.tmpl [new file with mode: 0755]
dmaap-bc/src/main/resources/misc/havecert.tmpl [moved from dmaap-bc/misc/log4j.properties.tmpl with 72% similarity]
dmaap-bc/src/main/resources/misc/logback.xml [new file with mode: 0644]
dmaap-bc/src/main/resources/misc/schema_all.sql [new file with mode: 0644]
dmaap-bc/src/main/webapp/HelloJetty.html [deleted file]
dmaap-bc/src/main/webapp/WEB-INF/log4j2.xml [deleted file]
dmaap-bc/src/main/webapp/WEB-INF/web.xml [deleted file]
dmaap-bc/src/main/webapp/index.jsp [deleted file]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafRoleTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafServiceFactoryTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafServiceImplTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafUserRoleTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFishTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/AllowAllTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/ApiPermsTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/ApiPolicyTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/client/DrProvConnectionTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/client/MrProvConnectionTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/client/MrTopicConnectionTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/DBFieldHandlerTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/DBMapTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/DBSingletonTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/LoadSchemaTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/TableHandlerTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/BrTopicTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DRNodeTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DRPubTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DRSubTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DcaeLocationTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DmaapTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/FeedTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/JUnitTestSuite.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/MRClientTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/MR_ClusterTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/MirrorMakerTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/TestRunner.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/TopicTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilterTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilterTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DR_NodeResourceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DR_PubResourceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DR_SubResourceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResourceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DmaapResourceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/FastJerseyTestContainer.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/FeedResourceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/InfoResourceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/MR_ClientResourceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResourceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/RequestTimeLogFilterTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/RequiredCheckerTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/RequiredFieldExceptionTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/ResponseBuilderTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/TestFeedCreator.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/TopicResourceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/server/JettyServerTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/server/MainTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/AafPermissionServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/AafTopicSetupServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/ApiServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/CredentialsParserTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/DR_NodeServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/DcaeLocationServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/DmaapServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/Dr_PubServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/FeedServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MR_ClientServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MR_ClusterServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MirrorMakerServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MirrorMakerServiceTestMockito.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/TopicServiceTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/testframework/DmaapObjectFactory.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/testframework/ReflectionHarness.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/DmaapConfigTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/DmaapTimestampTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/FqdnTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/GraphTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/PermissionBuilderTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/RandomIntegerTest.java [new file with mode: 0644]
dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/RandomStringTest.java [new file with mode: 0644]
dmaap-bc/src/test/resources/cadi.properties [new file with mode: 0644]
dmaap-bc/src/test/resources/dmaapbc.properties [new file with mode: 0644]
docs/administration/administration.rst [deleted file]
docs/apis/api-table.rst [new file with mode: 0644]
docs/apis/api.rst [new file with mode: 0644]
docs/apis/swagger.json [new file with mode: 0644]
docs/architecture.rst [moved from docs/architecture/architecture.rst with 77% similarity]
docs/configuration.rst [moved from docs/configuration/configuration.rst with 100% similarity]
docs/consumedapis.rst [moved from docs/consumedapis/consumedapis.rst with 99% similarity]
docs/delivery.rst [moved from docs/delivery/delivery.rst with 100% similarity]
docs/humaninterfaces/humaninterfaces.rst [deleted file]
docs/index.rst
docs/installation.rst [moved from docs/Installation/Installation.rst with 96% similarity]
docs/logging.rst [new file with mode: 0644]
docs/logging/logging.rst [deleted file]
docs/offeredapis.rst [moved from docs/offeredapis/offeredapis.rst with 61% similarity]
docs/release-notes.rst [moved from docs/release-notes/release-notes.rst with 92% similarity]
docs/security.rst [moved from docs/security/security.rst with 99% similarity]
pom.xml

index 0483f20..778ba9c 100644 (file)
@@ -6,3 +6,7 @@ last*
 .settings/
 dependency-reduced-pom.xml
 /logs/
+.vscode
+/bin/
+/venv/*
+*/archives/
diff --git a/certs/README b/certs/README
deleted file mode 100644 (file)
index 2f7f410..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-Procedure to create pem files for curl use within dbc-client container.
-
-1. Download certificate artifacts from AAF
-1b.  Remember to run the showpass step to capture the cleartext passwords.  Assume this will be in a file with suffix .showpass.
-1c.  Add AAF artifacts to buscontroller project under buscontroller/certs
-
-2. Display passwords in showpass file
-
-       ubuntu@dgl-rancher:~/dublin/buscontroller/certs$ cat *showpass
-       cadi_truststore_password=8b&R5%l$l:@jSWz@FCs;rhY*
-       cadi_keystore_password_jks=Y@Y5f&gm?PAz,CVQL,lk[VAF
-       cadi_key_password=2U[iOZzMHI:.#tdCwlBqc;}S
-       cadi_keystore_password=2U[iOZzMHI:.#tdCwlBqc;}S
-       cadi_keystore_password_p12=2U[iOZzMHI:.#tdCwlBqc;}S
-       Challenge=9H83TErBrN!u?;]1iCK@&69?
-       2019-03-22T17:38:32.447+0000: Trans Info
-                REMOTE Show Password 2214.6292ms
-
-3. copy the value for cadi_keystore_password_p12 into clipboard
-
-4. Use openssl to create pem files.  NOTE: paste pwd from step 3 to all answers.
-
-       ubuntu@dgl-rancher:~/dublin/buscontroller/certs$ openssl pkcs12 -in ./org.onap.dmaap-bc.p12 -out ca.pem -cacerts -nokeys
-       Enter Import Password:
-       MAC verified OK
-
-       ubuntu@dgl-rancher:~/dublin/buscontroller/certs$ openssl pkcs12 -in ./org.onap.dmaap-bc.p12 -out client.pem -clcerts -nokeys
-       Enter Import Password:
-       MAC verified OK
-
-       ubuntu@dgl-rancher:~/dublin/buscontroller/certs$ openssl pkcs12 -in ./org.onap.dmaap-bc.p12 -out key.pem -nocerts
-       Enter Import Password:
-       MAC verified OK
-       Enter PEM pass phrase:
-       Verifying - Enter PEM pass phrase:
-
-5. Confirm new pem files are created: 
-
-       ubuntu@dgl-rancher:~/dublin/buscontroller/certs$ ls -l
-       total 52
-       -rw-rw-r-- 1 ubuntu ubuntu 1759 Apr  3 14:52 ca.pem
-       -rw-rw-r-- 1 ubuntu ubuntu 1791 Apr  3 14:53 client.pem
-       -rw-rw-r-- 1 ubuntu ubuntu 1997 Apr  3 14:55 key.pem
-       -rw-rw-r-- 1 ubuntu ubuntu 1159 Apr  3 11:59 org.onap.dmaap-bc.cred.props
-       -rw-rw-r-- 1 ubuntu ubuntu  751 Apr  3 11:59 org.onap.dmaap-bc.crontab.sh
-       -rw-rw-r-- 1 ubuntu ubuntu 3613 Apr  3 11:59 org.onap.dmaap-bc.jks
-       -rw-rw-r-- 1 ubuntu ubuntu 2074 Apr  3 11:59 org.onap.dmaap-bc.keyfile
-       -rw-rw-r-- 1 ubuntu ubuntu  289 Apr  3 11:59 org.onap.dmaap-bc.location.props
-       -rw-rw-r-- 1 ubuntu ubuntu 4151 Apr  3 11:59 org.onap.dmaap-bc.p12
-       -rw-rw-r-- 1 ubuntu ubuntu  629 Apr  3 11:59 org.onap.dmaap-bc.props
-       -rw-rw-r-- 1 ubuntu ubuntu  365 Apr  3 11:59 org.onap.dmaap-bc.showpass
-       -rw-rw-r-- 1 ubuntu ubuntu 1413 Apr  3 11:59 org.onap.dmaap-bc.trust.jks
-
-6. pem files can now be included in docker image
-
diff --git a/certs/org.onap.dmaap-bc.cred.props b/certs/org.onap.dmaap-bc.cred.props
deleted file mode 100644 (file)
index fe47e47..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-############################################################
-# Properties Generated by AT&T Certificate Manager
-#   by root
-#   on 2019-03-22T17:37:39.746+0000
-# @copyright 2016, AT&T
-############################################################
-Challenge=enc:0B6_pOsEETnPFnQpfT0cppU1iw8hQ4580YF98UfazICvACybbPP8e7bOCtu6I3j3
-cadi_alias=dmaap-bc@dmaap-bc.onap.org
-cadi_key_password=enc:xzyD6s2QtG6dnrpgXFd3cJvzQQP8cO-XuSYugfJsKiGeUjNla6Ty6LeiuAn6sD2o
-cadi_keyfile=/opt/app/osaaf/local/org.onap.dmaap-bc.keyfile
-cadi_keystore=/opt/app/osaaf/local/org.onap.dmaap-bc.p12
-cadi_keystore_password=enc:3rLfxd-_WPUHEzYIbt2Cb9PwWOFkIQWBf_2DuHUOGJ4h_-gQjtTsx-h7H8qfkxC9
-cadi_keystore_password_jks=enc:VWdS8tbPO6JcL0gwj9mbV0fKSzWIP6NXLWaz9L1O309Dho9qo7wyJxeF8wR-8_wi
-cadi_keystore_password_p12=enc:3rLfxd-_WPUHEzYIbt2Cb9PwWOFkIQWBf_2DuHUOGJ4h_-gQjtTsx-h7H8qfkxC9
-cadi_truststore=/opt/app/osaaf/local/org.onap.dmaap-bc.trust.jks
-cadi_truststore_password=enc:pVXSw1tBNOOFYnovbI1tHlS_d4dvY0eQyWkhBsRavMRv6DiI9i8WMN5wVrM4KnUY
-cadi_x509_issuers=CN=intermediateCA_1, OU=OSAAF, O=ONAP, C=US:CN=intermediateCA_7, OU=OSAAF, O=ONAP, C=US:CN=intermediateCA_9, OU=OSAAF, O=ONAP, C=US
diff --git a/certs/org.onap.dmaap-bc.crontab.sh b/certs/org.onap.dmaap-bc.crontab.sh
deleted file mode 100644 (file)
index 861223c..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash /opt/app/osaaf/local/org.onap.dmaap-bc.crontab.sh
-# Certificate Manager Crontab Loading Script
-# Add/Update a Crontab entry, that adds a check on Certificate Manager generated Certificate nightly.
-# Generated by Certificate Manager 2019-03-22T17:37:39.745Z
-TFILE="/tmp/cmcron$$.temp"
-DIR="/opt/app/osaaf/local"
-CF="org.onap.dmaap-bc Certificate Check Script"
-SCRIPT="/opt/app/osaaf/local/org.onap.dmaap-bc.check.sh"
-crontab -l | sed -n "/#### BEGIN $CF/,/END $CF ####/!p" > $TFILE
-# Note: Randomize Minutes (0-60) and hours (1-4)
-echo "#### BEGIN $CF ####" >> $TFILE
-echo "$(( $RANDOM % 60)) $(( $(( $RANDOM % 3 )) + 1 )) * * * /bin/bash $SCRIPT >> $DIR/cronlog 2>&1 " >> $TFILE
-echo "#### END $CF ####" >> $TFILE
-crontab $TFILE
-rm $TFILE
diff --git a/certs/org.onap.dmaap-bc.jks b/certs/org.onap.dmaap-bc.jks
deleted file mode 100644 (file)
index 5f34374..0000000
Binary files a/certs/org.onap.dmaap-bc.jks and /dev/null differ
diff --git a/certs/org.onap.dmaap-bc.keyfile b/certs/org.onap.dmaap-bc.keyfile
deleted file mode 100644 (file)
index a50f742..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-AAxU7-TUq8pEp4SVZwpl8rgDrfEjngBUATPivJjmkw65Lg9H2pAyegluPdIBaMiSwV0gGk9PQ8x3
-3k1jb9c0Kt1aSOQpEqHOE44IlqCv8tX5bglqKy0PucUPVppp30nmiMWMVCi5szRfzeG_Vppcv_2u
-rbeor-oKC2B5nsyIVMaVnsNMSCwbNAqVf_h4lqB0CtNu_6fXXAxu9xrRvahHHoEeOGXqZI9WdqFF
-ds3-gjW8jpyI38u1yckF3-PYGi344KUy--uM4KHNnTfsFMwSwnZnIzzC5vxLt7b-lXRmlk02G6LC
-vHwPVVZv0U7qFYwikZdS93k3OwQCVezKGARzdaoycLKaDjsUtqFNJSFSNEaIX9AU37PBRDsaApxe
-glG-jbvqMBhjcHaAkSFLA_GKKslvHfyeUdRz_8-WAykn7RfNo45uGlMuNlNz8yvwUZcYfYOTd6Kz
-TnPh4c1bxgJFMZrn2X2PK0SB5l-4iwNA5UvXD5QuMdVMSxHaXGi_TioJauSrHLMJvpdzjPK8l46O
-etcN8KwOjMuhJEgXv7asz2WwlFlJ81IOYi6ynIqgziymZK39dFCWjj1FBbc5ZmawGfm-uoICNNAf
-iPVIwYJjD1YF288tgxBP4--dmqyldpBtecUvck_5t3xdV-BH8CqsjfMnXnyg3nvxLDbI-tnzw8Hd
-dmZe3T1WSyofqhiZlmcIZEJk_sU7u-oZcseTO5qc1_dgi16sfyhzwlFZOmMqp4Bk2oZFyyDpk4Tr
-g9qJVJQIOLDSaU-CkZ9IW89-MgKEl_ESmYWPt1Q3wdOGBDeNnZvtj6YStpIVsPyofK1yimhAL2jk
-OIklv6m-ri2cmWFY9q8ZYmHVEA1iMnw_oLRMfp7s6-RRSQALPK51QEy9PXR9JQ9R7_RK_KU6wMq4
-LTQAzOqu8xVmFbLZxcAiwVs8fV8ukgesMosWXHT1fLDWRlOi9ePfiauHOkGl64M8e6p6IUo5T1dg
-fg-vwZCYoeJJ8NEnK0sU7z0OZGS8ECI9sXZ9lAweQqQQBnGa5md0XW25r8svJgFrtAq2_d2G-qla
-6Dk00zgzJ3iQNuBA9Tgxr7qP0wro8tRi_ejPC2_0C3DIohFbuGpabMeMFomFAhzMLLZQrdJB_rrs
-heAXW3vieVDQkrojelrpuCDST2vveH01ksxaC7ZTtVAk_jVsoLi7wgTHW7IseoJf0FnMB-rxd_qn
-umfug96KFtVgVens_8mzNUV1SNFXdfW0J5-qG17TaV_Ynm8S1Fuu1usYiuNZjtmkMuo0hRoVasFK
-C3n3qK4WlB0C0abmuR2HiqqrEKRKcZyG3X7cgkhdgiD85yqBVlABXfrgXk6XCbKAt6XVykFHbGhm
-X9W3jOltRgNNa_MPYARwqqWu_3CJ83FLJI1FxtP--O7rFj1pPLM38zmUf3Hjz2pfn6EnapEfQ_qq
-FNyXK-wxf_7OFKmiZM0UMRICbTA8BBuhfCsYhqMIDqGwd9zMXmnw1ylVj_WFbGCj7swkgI04KSrv
-mn64Ct33G13Nf6FDF0e4qOMkjSRrpo51HEE-4SY7MiKjCTAev9Oqf-gRgQ_m6KfzcmkfOsk3goYu
-6yvWL4vNPaWTnx49eVyzOMck-UdWBIGWUIALDuKFV6b-WwwSUWj-p6dJGL48lgnEzrCsaZsdcFTv
-UgYl1Up3v2NBCmQ6Hv7XWr-r2WE9YiI7wF6pfqhKQ_81A039iQjo6KM0O8tf6b35K40n9Ekudu6D
-tG5_GKwPKdd9DZ-VcDzN-g1yYganDHyMLVAX2xvj4rcG5BtFaZgoMZ75BfMRsZ1mpt5vr8aOP98V
-1CA-XLM68PPmS_0vj5iKx9Fnk7pw0aRfjLsOwTDuzt4q7Zikz7UE9-HOlSJTu3Apy4cVacqHHhdu
-U714tAhWxp0onTVOVC4lVGIQJU9qGSNvyF7bl-gouwFxTAgAr-AfDC0Ny6VL8KEspybFSH75ixBw
-a0GAScwtvEtbZnLKOsS7-MpqI0fSCd6XQzqqXuNWS-V17kX5zOy5CjiKPdZkWDX0vLc__bL-
\ No newline at end of file
diff --git a/certs/org.onap.dmaap-bc.p12 b/certs/org.onap.dmaap-bc.p12
deleted file mode 100644 (file)
index eae6c97..0000000
Binary files a/certs/org.onap.dmaap-bc.p12 and /dev/null differ
diff --git a/certs/org.onap.dmaap-bc.props b/certs/org.onap.dmaap-bc.props
deleted file mode 100644 (file)
index d8fe419..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-############################################################
-# Properties Generated by AT&T Certificate Manager
-#   by root
-#   on 2019-03-22T17:37:33.690+0000
-# @copyright 2016, AT&T
-############################################################
-aaf_env=DEV
-aaf_id=dmaap-bc@dmaap-bc.onap.org
-aaf_locate_url=https://aaf-onap-test.osaaf.org:8095
-aaf_url=https://AAF_LOCATE_URL/AAF_NS.service:2.1
-cadi_etc_dir=/opt/app/osaaf/local
-cadi_latitude=38.000
-cadi_longitude=-72.000
-cadi_prop_files=/opt/app/osaaf/local/org.onap.dmaap-bc.location.props:/opt/app/osaaf/local/org.onap.dmaap-bc.cred.props
-cm_url=https://AAF_LOCATE_URL/%CNS.%AAF_NS.cm:2.1
diff --git a/certs/org.onap.dmaap-bc.showpass b/certs/org.onap.dmaap-bc.showpass
deleted file mode 100644 (file)
index 7c93e96..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-cadi_truststore_password=8b&R5%l$l:@jSWz@FCs;rhY*
-cadi_keystore_password_jks=Y@Y5f&gm?PAz,CVQL,lk[VAF
-cadi_key_password=2U[iOZzMHI:.#tdCwlBqc;}S
-cadi_keystore_password=2U[iOZzMHI:.#tdCwlBqc;}S
-cadi_keystore_password_p12=2U[iOZzMHI:.#tdCwlBqc;}S
-Challenge=9H83TErBrN!u?;]1iCK@&69?
-2019-03-22T17:38:32.447+0000: Trans Info
-         REMOTE Show Password 2214.6292ms
diff --git a/certs/org.onap.dmaap-bc.trust.jks b/certs/org.onap.dmaap-bc.trust.jks
deleted file mode 100644 (file)
index 6e35eed..0000000
Binary files a/certs/org.onap.dmaap-bc.trust.jks and /dev/null differ
diff --git a/csit/plans/full_suite/setup.sh b/csit/plans/full_suite/setup.sh
new file mode 100755 (executable)
index 0000000..16253fe
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/bash
+# 
+# ============LICENSE_START=======================================================
+# org.onap.dmaap
+# ================================================================================
+# Copyright (C) 2021 Nordix Foundation..
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=========================================================
+# 
+#
+
+source ${WORKSPACE}/scripts/dmaap-datarouter/datarouter-launch.sh
+dmaap_dr_launch
+DRPS_IP=${DR_PROV_IP}
+
+source ${WORKSPACE}/scripts/dmaap-message-router/dmaap-mr-launch.sh
+dmaap_mr_launch
+MRC_IP=${IP}
+
+source ${WORKSPACE}/scripts/dmaap-buscontroller/dmaapbc-launch.sh
+dmaapbc_launch ${DRPS_IP} ${MRC_IP}
+DMAAPBC_IP=${DMAAP_BC_IP}
+
+echo "DRPS_IP=$DRPS_IP DMAAPBC_IP=$DMAAPBC_IP"
+
+# Pass any variables required by Robot test suites in ROBOT_VARIABLES
+ROBOT_VARIABLES="-v DRPS_IP:${DRPS_IP} -v MRC_IP:${MRC_IP} -v DMAAPBC_IP:${DMAAPBC_IP}"
+set -x
+${WORKSPACE}/scripts/dmaap-buscontroller/dmaapbc-init.sh ${DMAAPBC_IP}
+set +x
+
diff --git a/csit/plans/full_suite/teardown.sh b/csit/plans/full_suite/teardown.sh
new file mode 100755 (executable)
index 0000000..e2d03ea
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# ============LICENSE_START=======================================================
+# org.onap.dmaap
+# ================================================================================
+# Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+# Modifications copyright (C) 2021 Nordix Foundation..
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=========================================================
+
+source ${WORKSPACE}/scripts/dmaap-message-router/dmaap-mr-teardown.sh
+dmaap_mr_teardown
+source ${WORKSPACE}/scripts/dmaap-datarouter/datarouter-teardown.sh
+teardown_dmaap_dr
+docker cp dmaap-bc:/opt/app/dmaapbc/logs/ONAP ${WORKSPACE}/archives/dmaap/last_run_logs/bc_logs
+docker-compose -f ${WORKSPACE}/scripts/dmaap-buscontroller/docker-compose/docker-compose-bc.yml rm -sf
+docker system prune -f
diff --git a/csit/plans/full_suite/testplan.txt b/csit/plans/full_suite/testplan.txt
new file mode 100755 (executable)
index 0000000..25ccd87
--- /dev/null
@@ -0,0 +1,3 @@
+# Place the suites in run order.
+with_dr
+with_mr
diff --git a/csit/plans/with_dr/setup.sh b/csit/plans/with_dr/setup.sh
new file mode 100755 (executable)
index 0000000..31b1b6e
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/bash
+# 
+# ============LICENSE_START=======================================================
+# org.onap.dmaap
+# ================================================================================
+# Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+# Modifications copyright (C) 2021 Nordix Foundation..
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=========================================================
+# 
+#
+source ${SCRIPTS}/common_functions.sh
+
+source ${WORKSPACE}/scripts/dmaap-datarouter/datarouter-launch.sh
+dmaap_dr_launch
+DRPS_IP=${DR_PROV_IP}
+
+source ${WORKSPACE}/scripts/dmaap-buscontroller/dmaapbc-launch.sh
+dmaapbc_launch ${DRPS_IP}
+DMAAPBC_IP=${DMAAP_BC_IP}
+
+echo "DRPS_IP=$DRPS_IP DMAAPBC_IP=$DMAAPBC_IP"
+
+# Pass any variables required by Robot test suites in ROBOT_VARIABLES
+ROBOT_VARIABLES="-v DRPS_IP:${DRPS_IP} -v DMAAPBC_IP:${DMAAPBC_IP}"
+set -x
+${WORKSPACE}/scripts/dmaap-buscontroller/dmaapbc-init.sh ${DMAAPBC_IP}
+set +x
+
diff --git a/csit/plans/with_dr/teardown.sh b/csit/plans/with_dr/teardown.sh
new file mode 100755 (executable)
index 0000000..8a6e7b2
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# ============LICENSE_START=======================================================
+# org.onap.dmaap
+# ================================================================================
+# Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+# Modifications copyright (C) 2021 Nordix Foundation..
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=========================================================
+
+source ${WORKSPACE}/scripts/dmaap-datarouter/datarouter-teardown.sh
+teardown_dmaap_dr
+docker cp dmaap-bc:/opt/app/dmaapbc/logs/ONAP ${WORKSPACE}/archives/dmaap/last_run_logs/bc_logs
+docker-compose -f ${WORKSPACE}/scripts/dmaap-buscontroller/docker-compose/docker-compose-bc.yml rm -sf
+docker system prune -f
diff --git a/csit/plans/with_dr/testplan.txt b/csit/plans/with_dr/testplan.txt
new file mode 100755 (executable)
index 0000000..21a6ee8
--- /dev/null
@@ -0,0 +1,2 @@
+# Place the suites in run order.
+with_dr
diff --git a/csit/plans/with_mr/setup.sh b/csit/plans/with_mr/setup.sh
new file mode 100755 (executable)
index 0000000..8eaed73
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/bash
+# 
+# ============LICENSE_START=======================================================
+# org.onap.dmaap
+# ================================================================================
+# Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+# Modifications copyright (C) 2021 Nordix Foundation..
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=========================================================
+# 
+#
+# Place the scripts in run order:
+source ${WORKSPACE}/scripts/dmaap-message-router/dmaap-mr-launch.sh
+dmaap_mr_launch
+MRC_IP=${IP}
+
+source ${WORKSPACE}/scripts/dmaap-buscontroller/dmaapbc-launch.sh
+dmaapbc_launch $MRC_IP
+DMAAP_BC_IP=${DMAAP_BC_IP}
+
+echo "DMAAP_BC_IP=$DMAAP_BC_IP"
+
+# Pass any variables required by Robot test suites in ROBOT_VARIABLES
+ROBOT_VARIABLES=" -v DMAAP_BC_IP:${DMAAP_BC_IP}"
+set -x
+${WORKSPACE}/scripts/dmaap-buscontroller/dmaapbc-init.sh ${DMAAP_BC_IP}
+set +x
diff --git a/csit/plans/with_mr/teardown.sh b/csit/plans/with_mr/teardown.sh
new file mode 100644 (file)
index 0000000..15b8691
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+# 
+# ============LICENSE_START=======================================================
+# org.onap.dmaap
+# ================================================================================
+# Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+# Modifications copyright (C) 2021 Nordix Foundation..
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=========================================================
+# 
+#
+source ${WORKSPACE}/scripts/dmaap-message-router/dmaap-mr-teardown.sh
+dmaap_mr_teardown
+docker cp dmaap-bc:/opt/app/dmaapbc/logs/ONAP ${WORKSPACE}/archives/dmaap/last_run_logs/bc_logs
+docker-compose -f ${WORKSPACE}/scripts/dmaap-buscontroller/docker-compose/docker-compose-bc.yml rm -sf
+docker system prune -f
+
+
+
diff --git a/csit/plans/with_mr/testplan.txt b/csit/plans/with_mr/testplan.txt
new file mode 100644 (file)
index 0000000..104371a
--- /dev/null
@@ -0,0 +1,3 @@
+# Test suites are relative paths under [integration/csit.git]/tests/.
+# Place the suites in run order.
+with_mr
diff --git a/csit/prepare-csit.sh b/csit/prepare-csit.sh
new file mode 100755 (executable)
index 0000000..62b8714
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/bash -x
+#
+# Copyright 2019 Â© Samsung Electronics Co., Ltd.
+# Modifications copyright (C) 2021 Nordix Foundation..
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This script installs common libraries required by CSIT tests
+#
+
+if [ -z "$WORKSPACE" ]; then
+    export WORKSPACE=`git rev-parse --show-toplevel`
+fi
+
+TESTPLANDIR=${WORKSPACE}/${TESTPLAN}
+
+if [ -f ${WORKSPACE}/env.properties ]; then
+    source ${WORKSPACE}/env.properties
+fi
+if [ -f ${ROBOT_VENV}/bin/activate ]; then
+    source ${ROBOT_VENV}/bin/activate
+else
+    rm -rf /tmp/ci-management
+    rm -f ${WORKSPACE}/env.properties
+    cd /tmp
+    git clone "https://gerrit.onap.org/r/ci-management"
+    source /tmp/ci-management/jjb/integration/include-raw-integration-install-robotframework.sh
+fi
+
+pip freeze
+
diff --git a/csit/run-csit.sh b/csit/run-csit.sh
new file mode 100755 (executable)
index 0000000..61ac8e1
--- /dev/null
@@ -0,0 +1,196 @@
+#!/bin/bash -x
+#
+# Copyright 2016-2017 Huawei Technologies Co., Ltd.
+# Modification Copyright 2019 Â© Samsung Electronics Co., Ltd.
+# Modifications copyright (C) 2021 Nordix Foundation..
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# $1 project/functionality
+# $2 robot options
+
+#
+# functions
+#
+
+function on_exit(){
+    rc=$?
+    if [[ ${WORKSPACE} ]]; then
+        if [[ ${WORKDIR} ]]; then
+            rsync -av "$WORKDIR/" "$WORKSPACE/archives/$TESTPLAN"
+        fi
+        # Record list of active docker containers
+        docker ps --format "{{.Image}}" > "$WORKSPACE/archives/$TESTPLAN/_docker-images.log"
+
+        # show memory consumption after all docker instances initialized
+        docker_stats | tee "$WORKSPACE/archives/$TESTPLAN/_sysinfo-2-after-robot.txt"
+    fi
+    # Run teardown script plan if it exists
+    cd "${TESTPLANDIR}"
+    TEARDOWN="${TESTPLANDIR}/teardown.sh"
+    if [ -f "${TEARDOWN}" ]; then
+        echo "Running teardown script ${TEARDOWN}"
+        source_safely "${TEARDOWN}"
+    fi
+    # TODO: do something with the output
+     exit $rc
+}
+# ensure that teardown and other finalizing steps are always executed
+trap on_exit EXIT
+
+function docker_stats(){
+    #General memory details
+    echo "> top -bn1 | head -3"
+    top -bn1 | head -3
+    echo
+
+    echo "> free -h"
+    free -h
+    echo
+
+    #Memory details per Docker
+    echo "> docker ps"
+    docker ps
+    echo
+
+    echo "> docker stats --no-stream"
+    docker stats --no-stream
+    echo
+}
+
+# save current set options
+function save_set() {
+    RUN_CSIT_SAVE_SET="$-"
+    RUN_CSIT_SHELLOPTS="$SHELLOPTS"
+}
+
+# load the saved set options
+function load_set() {
+    _setopts="$-"
+
+    # bash shellopts
+    for i in $(echo "$SHELLOPTS" | tr ':' ' ') ; do
+        set +o ${i}
+    done
+    for i in $(echo "$RUN_CSIT_SHELLOPTS" | tr ':' ' ') ; do
+        set -o ${i}
+    done
+
+    # other options
+    for i in $(echo "$_setopts" | sed 's/./& /g') ; do
+        set +${i}
+    done
+    set -${RUN_CSIT_SAVE_SET}
+}
+
+# set options for quick bailout when error
+function harden_set() {
+    set -xeo pipefail
+    set +u # enabled it would probably fail too many often
+}
+
+# relax set options so the sourced file will not fail
+# the responsibility is shifted to the sourced file...
+function relax_set() {
+    set +e
+    set +o pipefail
+}
+
+# wrapper for sourcing a file
+function source_safely() {
+    [ -z "$1" ] && return 1
+    relax_set
+    . "$1"
+    load_set
+}
+
+#
+# main
+#
+
+# set and save options for quick failure
+harden_set && save_set
+
+if [ $# -eq 0 ]
+then
+    echo
+    echo "Usage: $0 plans/<project>/<functionality> [<robot-options>]"
+    echo
+    echo "    <project>, <functionality>, <robot-options>:  "
+    echo "        The same values as for the '{project}-csit-{functionality}' JJB job template."
+    echo
+    exit 1
+fi
+
+if [ -z "$WORKSPACE" ]; then
+    export WORKSPACE=$(git rev-parse --show-toplevel)
+fi
+
+if [ -f "${WORKSPACE}/${1}/testplan.txt" ]; then
+    export TESTPLAN="${1}"
+else
+    echo "testplan not found: ${WORKSPACE}/${TESTPLAN}/testplan.txt"
+    exit 2
+fi
+
+export TESTOPTIONS="${2}"
+
+rm -rf "$WORKSPACE/archives/$TESTPLAN"
+mkdir -p "$WORKSPACE/archives/$TESTPLAN"
+
+TESTPLANDIR="${WORKSPACE}/${TESTPLAN}"
+
+# Run installation of prerequired libraries
+source_safely "${WORKSPACE}/prepare-csit.sh"
+
+# Activate the virtualenv containing all the required libraries installed by prepare-csit.sh
+source_safely "${ROBOT_VENV}/bin/activate"
+
+WORKDIR=$(mktemp -d --suffix=-robot-workdir)
+cd "${WORKDIR}"
+
+# Add csit scripts to PATH
+export PATH="${PATH}:${WORKSPACE}/docker/scripts:${WORKSPACE}/scripts:${ROBOT_VENV}/bin"
+export SCRIPTS="${WORKSPACE}/scripts"
+export ROBOT_VARIABLES=
+
+# Sign in to nexus3 docker repo
+docker login -u docker -p docker nexus3.onap.org:10001
+
+# Run setup script plan if it exists
+cd "${TESTPLANDIR}"
+SETUP="${TESTPLANDIR}/setup.sh"
+if [ -f "${SETUP}" ]; then
+    echo "Running setup script ${SETUP}"
+    source_safely "${SETUP}"
+fi
+
+# show memory consumption after all docker instances initialized
+docker_stats | tee "$WORKSPACE/archives/$TESTPLAN/_sysinfo-1-after-setup.txt"
+
+# Run test plan
+cd "$WORKDIR"
+echo "Reading the testplan:"
+cat "${TESTPLANDIR}/testplan.txt" | egrep -v '(^[[:space:]]*#|^[[:space:]]*$)' | sed "s|^|${WORKSPACE}/tests/|" > testplan.txt
+cat testplan.txt
+SUITES=$( xargs -a testplan.txt )
+
+echo ROBOT_VARIABLES="${ROBOT_VARIABLES}"
+echo "Starting Robot test suites ${SUITES} ..."
+relax_set
+python -m robot.run -N ${TESTPLAN} -v WORKSPACE:/tmp ${ROBOT_VARIABLES} ${TESTOPTIONS} ${SUITES}
+RESULT=$?
+load_set
+echo "RESULT: $RESULT"
+# Note that the final steps are done in on_exit function after this exit!
+exit $RESULT
diff --git a/csit/run-project-csit.sh b/csit/run-project-csit.sh
new file mode 100755 (executable)
index 0000000..fedb716
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/bash -x
+#
+# Copyright 2020-2021 Â© Samsung Electronics Co., Ltd.
+# Modifications copyright (C) 2021 Nordix Foundation..
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+export WORKSPACE=$(git rev-parse --show-toplevel)/csit
+
+rm -rf ${WORKSPACE}/archives
+mkdir -p ${WORKSPACE}/archives
+cd ${WORKSPACE}
+
+if [ "${1}" == "with_dr" ]; then
+   ./run-csit.sh plans/with_dr
+elif [ "${1}" == "with_mr" ]; then
+    ./run-csit.sh plans/with_mr
+else
+  ./run-csit.sh plans/full_suite
+fi
+
+
diff --git a/csit/scripts/dmaap-buscontroller/dmaapbc-init.sh b/csit/scripts/dmaap-buscontroller/dmaapbc-init.sh
new file mode 100755 (executable)
index 0000000..5e9cbb1
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+# $1 is the IP address of the buscontroller
+
+# INITIALIZE: dmaap object
+JSON=/tmp/$$.dmaap
+cat << EOF > $JSON
+{
+    "version": "1",
+    "topicNsRoot": "org.onap.dmaap",
+    "drProvUrl": "https://dmaap-dr-prov:8443",
+    "dmaapName": "onapCSIT",
+    "bridgeAdminTopic": "MM_AGENT_PROV"
+
+}
+EOF
+
+echo "Initializing /dmaap endpoint"
+curl -v -k  -X POST -d @${JSON} -H "Content-Type: application/json" https://$1:8443/webapi/dmaap
+
+
+# INITIALIZE: dcaeLocation object
+JSON=/tmp/$$.loc
+cat << EOF > $JSON
+{
+    "dcaeLocationName": "csit-sanfrancisco",
+    "dcaeLayer": "central-cloud",
+    "clli": "CSIT12345",
+    "zone": "zoneA"
+
+}
+EOF
+
+echo "Initializing /dcaeLocations endpoint"
+curl -v -k  -X POST -d @${JSON} -H "Content-Type: application/json" https://$1:8443/webapi/dcaeLocations
+
+
+# INITIALIZE: MR object in 1 site
+# since MR is currently deployed via docker-compose, its IP doesn't seem
+# to be routable from DBCL. Fortunately, the MR port is mapped from the docker bridge IP address.
+# Found this article for how to deterine the docker bridge IP so using it as a workaround.
+# https://stackoverflow.com/questions/22944631/how-to-get-the-ip-address-of-the-docker-host-from-inside-a-docker-container
+# Used the following snippet found buried in a comment to an answer and then modified for only 1 value.
+DOCKER_HOST=$(ip -4 addr show docker0 | grep -Po 'inet \K[\d.]+' | head -1 )
+# Perhaps there is a better way...
+JSON=/tmp/$$.mrc
+cat << EOF > $JSON
+{
+    "dcaeLocationName": "csit-sanfrancisco",
+    "fqdn": "message-router",
+    "topicProtocol" : "http",
+    "topicPort": "3904"
+
+}
+EOF
+
+echo "Initializing /mr_clusters endpoint"
+curl -v -k  -X POST -d @${JSON} -H "Content-Type: application/json" https://$1:8443/webapi/mr_clusters
diff --git a/csit/scripts/dmaap-buscontroller/dmaapbc-launch.sh b/csit/scripts/dmaap-buscontroller/dmaapbc-launch.sh
new file mode 100755 (executable)
index 0000000..6f46845
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# script to launch DMaaP buscontroller docker container
+# sets global var IP with assigned IP address
+
+function dmaapbc_launch() {
+    if [ $# == 1 ]; then
+      export dmaap_prov_ip=$1
+      export dmaap_mr_ip=$2
+    else
+      export dmaap_prov_ip=$1
+      export dmaap_mr_ip=$1
+    fi
+
+    docker-compose -f ${WORKSPACE}/scripts/dmaap-buscontroller/docker-compose/docker-compose-bc.yml up -d
+
+    sleep 10
+
+    DMAAP_BC_IP=`get-instance-ip.sh dmaap-bc`
+
+    # Wait for initialization
+    for i in 1 2 3 4 5 6 7 8 9 10; do
+        curl -sS ${DMAAP_BC_IP}:8080 && break
+        echo sleep ${i}
+        sleep ${i}
+    done
+}
diff --git a/csit/scripts/dmaap-buscontroller/docker-compose/buscontroller.env b/csit/scripts/dmaap-buscontroller/docker-compose/buscontroller.env
new file mode 100644 (file)
index 0000000..50ce43f
--- /dev/null
@@ -0,0 +1,2 @@
+DMAAPBC_WAIT_TO_EXIT=Y
+DMAAPBC_KSTOREFILE=/opt/app/osaaf/local/org.onap.dmaap-bc.jks
\ No newline at end of file
diff --git a/csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/org.onap.dmaap-bc.cred.props b/csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/org.onap.dmaap-bc.cred.props
new file mode 100644 (file)
index 0000000..52536a1
--- /dev/null
@@ -0,0 +1,17 @@
+############################################################
+# Properties Generated by AT&T Certificate Manager
+#   by root
+#   on 2021-03-12T11:38:49.244+0000
+# @copyright 2019, AT&T
+############################################################
+Challenge=secret
+cadi_alias=dmaap-bc@dmaap-bc.onap.org
+cadi_key_password=secret
+#cadi_keyfile=/opt/app/osaaf/local/org.onap.dmaap-dr.keyfile
+cadi_keystore=/opt/app/osaaf/local/org.onap.dmaap-bc.jks
+cadi_keystore_password=secret
+cadi_keystore_password_jks=secret
+cadi_keystore_password_p12=secret
+cadi_truststore=/opt/app/osaaf/local/truststore.jks
+cadi_truststore_password=secret
+cadi_x509_issuers=CN=intermediateCA_1, OU=OSAAF, O=ONAP, C=US:CN=intermediateCA_7, OU=OSAAF, O=ONAP, C=US:CN=intermediateCA_9, OU=OSAAF, O=ONAP, C=US
diff --git a/csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/org.onap.dmaap-bc.jks b/csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/org.onap.dmaap-bc.jks
new file mode 100644 (file)
index 0000000..ca8301c
Binary files /dev/null and b/csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/org.onap.dmaap-bc.jks differ
@@ -1,8 +1,8 @@
 ############################################################
 # Properties Generated by AT&T Certificate Manager
 #   by root
-#   on 2019-03-22T17:37:33.688+0000
-# @copyright 2016, AT&T
+#   on 2021-03-12T11:25:54.608+0000
+# @copyright 2019, AT&T
 ############################################################
-cadi_latitude=38.000
-cadi_longitude=-72.000
+cadi_latitude=38.0
+cadi_longitude=-72.0
diff --git a/csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/org.onap.dmaap-bc.props b/csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/org.onap.dmaap-bc.props
new file mode 100644 (file)
index 0000000..f3747fa
--- /dev/null
@@ -0,0 +1,24 @@
+############################################################
+# Properties Generated by AT&T Certificate Manager
+#   by root
+#   on 2021-03-12T11:25:54.617+0000
+# @copyright 2019, AT&T
+############################################################
+aaf_env=DEV
+aaf_id=dmaap-bc@dmaap-bc.onap.org
+aaf_locate_url=https://aaf-locate.onap:8095
+aaf_locator_app_ns=org.osaaf.aaf
+aaf_locator_container=oom
+aaf_locator_container_ns=onap
+aaf_locator_fqdn=dmaap-bc
+aaf_locator_public_fqdn=aaf.osaaf.org
+aaf_oauth2_introspect_url=https://AAF_LOCATE_URL/%CNS.%AAF_NS.introspect:2.1/introspect
+aaf_oauth2_token_url=https://AAF_LOCATE_URL/%CNS.%AAF_NS.token:2.1/token
+aaf_url=https://AAF_LOCATE_URL/%CNS.%AAF_NS.service:2.1
+aaf_url_cm=https://AAF_LOCATE_URL/%CNS.%AAF_NS.cm:2.1
+aaf_url_fs=https://AAF_LOCATE_URL/%CNS.%AAF_NS.fs:2.1
+aaf_url_gui=https://AAF_LOCATE_URL/%CNS.%AAF_NS.gui:2.1
+aaf_url_hello=https://aaf-locate.onap:8095/locate/onap.org.osaaf.aaf.hello:2.1
+aaf_url_oauth=https://AAF_LOCATE_URL/%CNS.%AAF_NS.oauth:2.1
+cadi_prop_files=/opt/app/osaaf/local/org.onap.dmaap-bc.location.props:/opt/app/osaaf/local/org.onap.dmaap-bc.cred.props
+cadi_protocols=TLSv1.1,TLSv1.2
diff --git a/csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/truststore.jks b/csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/truststore.jks
new file mode 100644 (file)
index 0000000..91547c6
Binary files /dev/null and b/csit/scripts/dmaap-buscontroller/docker-compose/cadi_aaf/truststore.jks differ
diff --git a/csit/scripts/dmaap-buscontroller/docker-compose/dmaapbc.properties b/csit/scripts/dmaap-buscontroller/docker-compose/dmaapbc.properties
new file mode 100644 (file)
index 0000000..32c67fb
--- /dev/null
@@ -0,0 +1,150 @@
+
+#####################################################
+#
+# Hooks for specific environment configurations
+#
+#####################################################
+# Indicator for whether to use AAF for authentication
+UseAAF: false
+
+# Stub out southbound calls for Unit Test cases to run.  e.g. not timeout
+# Comment out in other environments to get default (No)
+#UnitTest: Yes
+
+
+#####################################################
+#
+# Settings for Southbound API: Datarouter
+#
+#####################################################
+
+# URI to retrieve dynamic DR configuration
+ProvisioningURI:  /internal/prov
+
+# indicator for handling feed delete:
+#  DeleteOnDR - means use the DR API to DELETE a feed.  (default for backwards compatibility)
+#  SimulateDelete - means preserve the feed on DR (after cleaning it up), and mark as DELETED in DBCL.  Better for cloudify environments.
+Feed.deleteHandling: SimulateDelete
+
+###########################################################
+# The following properties default to match ONAP DR instance.
+# However, there are some non-ONAP DR instances that require other values.
+# Sets the X-DR-ON-BEHALF-OF HTTP Header value
+#DR.onBehalfHeader:
+# Value for the Content-Type Header in DR Feed API
+#DR.feedContentType:
+# Value for the Content-Type Header in DR Subscription API
+#DR.subContentType:
+#
+# END OF properties helpful for non-ONAP DR instance.
+############################################################
+
+#####################################################
+#
+# Settings for Soutbound API: Postgresql
+#
+#####################################################
+# flag indicates if we are using postgresql
+UsePGSQL: true
+
+# postgres host name
+# Need to connect to PG primary service, designated by service.name2
+DB.host: dbc-pg
+
+# postgres schema name
+DB.schema: dmaapbc
+
+# postgres user name
+DB.user: dmaapbc
+
+# postgres user password
+DB.cred: secret
+
+#####################################################
+#
+# Settings for Soutbound API: Message Router
+#
+#####################################################
+# indicator for multi-site (locations) deployment.  Give clue to buscontroller whether
+# there is a need for message replication between edge and central.
+# ONAP Casablanca is a single site deployment
+MR.multisite: false
+
+# FQDN of primary message router.
+# In ONAP Casablanca, there is only 1 message router service, so use that.
+# In a multi-site, MR cluster deployment, use the CNAME DNS entry which resolves to the primary central MR
+MR.CentralCname: message-router
+
+# Indicator for whether we want hostname verification on SSL connection to MR
+MR.hostnameVerify: false
+
+
+# MR Client Delete Level thoroughness:
+#  0 = don't delete
+#  1 = delete from persistent store
+#  2 = delete from persistent store (DB) and authorization store (AAF)
+MR.ClientDeleteLevel: 1
+
+# namespace of MR Topic Factory
+MR.TopicFactoryNS: org.onap.dmaap.mr.topicFactory
+
+# AAF Role assigned to Topic Manager Identity
+MR.TopicMgrRole: org.onap.dmaap-bc-topic-mgr.client
+
+# MR topic ProjectID (used in certain topic name generation formats)
+MR.projectID:  mr
+
+# Use Basic Authentication when provisioning topics
+MR.authentication: basicAuth
+
+# MR topic name style (default is FQTN_LEGACY_FORMAT)
+#MR.topicStyle: FQTN_LEGACY_FORMAT
+#
+# end of MR Related Properties
+################################################################################
+
+#####################################################
+#
+# Settings for authorization of DBCAPI
+#
+#####################################################
+# Namespace for URI values for the API used to create AAF permissions
+# e.g. if ApiNamespace is X.Y.dmaapbc.api then for URI /mr_clients we create AAF perm X.Y.dmaapbc.api.mr_clients
+ApiNamespace: org.onap.dmaap-bc.api
+
+# If API authorization is required, then implement a class to enforce it.
+# This overrides the Class used for API permission check.
+ApiPermission.Class: org.onap.dmaap.dbcapi.authentication.AllowAll
+
+
+#####################################################
+#
+# Certificate Management
+#
+#####################################################
+
+# Indicates how we are expecting certificates to be provided:
+#  cadi - a set of artifacts will be downloaded from AAF at deployment time, and details will be in a cadi properties file
+#  legacy (default) - artifacts will be installed manually or some other way and details will be in this file
+CertificateManagement: cadi
+
+# When CertificateManagement is cadi, then this is where all the cadi properties will be.
+# Note that the cadi properties include where the cert is, and the encrypted passwords to read.
+cadi.properties: /opt/app/osaaf/local/org.onap.dmaap-bc.props
+
+
+#####################################################
+#
+# HTTP Server Configuration
+#
+#####################################################
+
+# Allow http access to dbcapi
+HttpAllowed: true
+
+# listen to http port within this container (server)
+IntHttpPort: 8080
+
+# listen to https port within this container (server)
+# set to 0 if no certificates are available.
+IntHttpsPort: 8443
diff --git a/csit/scripts/dmaap-buscontroller/docker-compose/docker-compose-bc.yml b/csit/scripts/dmaap-buscontroller/docker-compose/docker-compose-bc.yml
new file mode 100644 (file)
index 0000000..458d06f
--- /dev/null
@@ -0,0 +1,46 @@
+version: '3.3'
+services:
+  dbc-pg-primary:
+    container_name: dbc-pg
+    image: nexus3.onap.org:10001/crunchydata/crunchy-postgres:centos8-13.2-4.6.1
+    ports:
+      - "5432:5432"
+    environment:
+      - PGHOST=/tmp
+      - PG_MODE=primary
+      - MODE=postgres
+      - PG_PRIMARY_USER=dmaapbc
+      - PG_PRIMARY_PASSWORD=secret
+      - PG_DATABASE=dmaap
+      - PG_USER=dmaapbc
+      - PG_PASSWORD=secret
+      - PG_ROOT_PASSWORD=secret
+      - PG_PRIMARY_PORT=5432
+    networks:
+      docker-compose_net:
+        aliases:
+          - dbc-pg
+
+  dmaap-bc:
+    container_name: dmaap-bc
+    hostname: dmaap-bc
+    image: onap/dmaap/dmaap-bc
+    ports:
+    - "30241:8080"
+    - "30242:8443"
+    volumes:
+    - ./buscontroller.env:/opt/app/config/conf/buscontroller.env
+    - ./dmaapbc.properties:/opt/app/config/conf/dmaapbc.properties
+    - ./cadi_aaf:/opt/app/osaaf/local
+    - ./logback.xml:/opt/app/dmaapbc/etc/logback.xml
+    networks:
+      docker-compose_net:
+        aliases:
+          - dmaap-bc
+    extra_hosts:
+      - dmaap-dr-prov:$dmaap_prov_ip
+      - message-router:$dmaap_prov_ip
+
+networks:
+  docker-compose_net:
+    external: true
similarity index 98%
rename from dmaap-bc/misc/logback.xml
rename to csit/scripts/dmaap-buscontroller/docker-compose/logback.xml
index 2af00f1..f0ba2b1 100644 (file)
@@ -1,9 +1,9 @@
-
 <!--
   ============LICENSE_START==========================================
   org.onap.dmaap
   ===================================================================
   Copyright Â© 2018 AT&T Intellectual Property. All rights reserved.
+  Copyright (C) 2021 Nordix Foundation.
   ===================================================================
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
@@ -29,9 +29,8 @@
   <!-- directory path for debugging type logs -->
   <property name="debugDir" value="logs" />
   
-  <!--  specify the component name 
-    <ECOMP-component-name>::= "MSO" | "DCAE" | "ASDC " | "AAI" |"Policy" | "SDNC" | "AC"  -->
-  <property name="componentName" value="ONAP"></property>
+  <!--  specify the component name -->
+  <property name="componentName" value="ONAP"/>
   
   <!--  log file names -->
   <property name="generalLogName" value="application" />
   
 
   
-  <root level="INFO">
+  <root level="DEBUG">
     <appender-ref ref="asyncEELF" />
   </root>
 
 </configuration>
-
diff --git a/csit/scripts/dmaap-datarouter/datarouter-launch.sh b/csit/scripts/dmaap-datarouter/datarouter-launch.sh
new file mode 100644 (file)
index 0000000..94a4595
--- /dev/null
@@ -0,0 +1,76 @@
+#!/bin/bash
+#
+# ============LICENSE_START=======================================================
+#  Copyright (C) 2021 Nordix Foundation.
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+
+function dmaap_dr_launch() {
+
+    mkdir -p ${WORKSPACE}/archives/dmaap/last_run_logs
+
+    # start DMaaP DR containers with docker compose and configuration from docker-compose.yml
+    docker login -u docker -p docker nexus3.onap.org:10001
+    docker-compose -f ${WORKSPACE}/scripts/dmaap-datarouter/docker-compose/docker-compose.yml up -d
+
+    # Wait for initialization of Docker container for datarouter-node, datarouter-prov and mariadb
+    for i in 1 2 3 4 5 6 7 8 9 10; do
+        if [[ $(docker inspect --format '{{ .State.Running }}' datarouter-node) ]] && \
+            [[ $(docker inspect --format '{{ .State.Running }}' datarouter-prov) ]] && \
+            [[ $(docker inspect --format '{{ .State.Running }}' mariadb) ]]
+        then
+            echo "DR Service Running"
+            break
+        else
+            echo sleep ${i}
+            sleep ${i}
+        fi
+    done
+
+    # Wait for healthy container datarouter-prov
+    for i in 1 2 3 4 5 6 7 8 9 10; do
+        if [[ "$(docker inspect --format '{{ .State.Health.Status }}' datarouter-prov)" = 'healthy' ]]
+        then
+            echo datarouter-prov.State.Health.Status is $(docker inspect --format '{{ .State.Health.Status }}' datarouter-prov)
+            echo "DR Service Running, datarouter-prov container is healthy"
+            break
+        else
+            echo datarouter-prov.State.Health.Status is $(docker inspect --format '{{ .State.Health.Status }}' datarouter-prov)
+            echo sleep ${i}
+            sleep ${i}
+            if [[ ${i} = 10 ]]
+            then
+                echo datarouter-prov container is not in healthy state - the test is not made, teardown...
+                docker-compose rm -sf
+                exit 1
+            fi
+        fi
+    done
+
+    DR_PROV_IP=`get-instance-ip.sh datarouter-prov`
+    DR_NODE_IP=`get-instance-ip.sh datarouter-node`
+    #DR_GATEWAY_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}' datarouter-prov)
+    echo DR_PROV_IP=${DR_PROV_IP}
+    echo DR_NODE_IP=${DR_NODE_IP}
+    #echo DR_GATEWAY_IP=${DR_GATEWAY_IP}
+
+#    docker exec -i datarouter-prov sh -c "curl -k -X PUT https://$DR_PROV_IP:8443/internal/api/NODES?val=dmaap-dr-node\|$DR_GATEWAY_IP"
+#    docker exec -i datarouter-prov sh -c "curl -k -X PUT https://$DR_PROV_IP:8443/internal/api/PROV_AUTH_ADDRESSES?val=dmaap-dr-prov\|$DR_GATEWAY_IP"
+
+    #Pass any variables required by Robot test suites in ROBOT_VARIABLES
+    ROBOT_VARIABLES="-v DR_PROV_IP:${DR_PROV_IP} -v DR_NODE_IP:${DR_NODE_IP} -v DR_SUB_IP:${DR_SUB_IP} -v DR_SUB2_IP:${DR_SUB2_IP}"
+}
\ No newline at end of file
diff --git a/csit/scripts/dmaap-datarouter/datarouter-teardown.sh b/csit/scripts/dmaap-datarouter/datarouter-teardown.sh
new file mode 100755 (executable)
index 0000000..ad05194
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/bash
+# ============LICENSE_START===================================================
+#  Copyright (C) 2019-2021 Nordix Foundation.
+# ============================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=====================================================
+
+function teardown_dmaap_dr (){
+    docker cp datarouter-prov:/opt/app/datartr/logs ${WORKSPACE}/archives/dmaap/last_run_logs/prov_logs
+    docker cp datarouter-node:/opt/app/datartr/logs ${WORKSPACE}/archives/dmaap/last_run_logs/node_event_logs
+    docker cp datarouter-node:/var/log/onap/datarouter ${WORKSPACE}/archives/dmaap/last_run_logs/node_server_logs
+    docker-compose -f ${WORKSPACE}/scripts/dmaap-datarouter/docker-compose/docker-compose.yml rm -sf
+}
\ No newline at end of file
diff --git a/csit/scripts/dmaap-datarouter/docker-compose/docker-compose.yml b/csit/scripts/dmaap-datarouter/docker-compose/docker-compose.yml
new file mode 100644 (file)
index 0000000..2518db6
--- /dev/null
@@ -0,0 +1,92 @@
+#
+# ============LICENSE_START=======================================================
+#  Copyright (C) 2019-21 Nordix Foundation.
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+#
+version: '2.1'
+services:
+  datarouter-prov:
+    image: nexus3.onap.org:10001/onap/dmaap/datarouter-prov:2.1.8
+    container_name: datarouter-prov
+    hostname: dmaap-dr-prov
+    ports:
+    - "443:8443"
+    - "8443:8443"
+    - "8080:8080"
+    volumes:
+    - ./provserver.properties:/opt/app/datartr/etc/provserver.properties
+    - ../dr_certs/dr_prov/truststore.jks:/opt/app/osaaf/local/truststore.jks
+    - ../dr_certs/dr_prov/org.onap.dmaap-dr-prov.p12:/opt/app/osaaf/local/org.onap.dmaap-dr-prov.p12
+    - ../dr_certs/dr_prov/org.onap.dmaap-dr.cred.props:/opt/app/osaaf/local/org.onap.dmaap-dr.cred.props
+    depends_on:
+      mariadb:
+        condition: service_healthy
+    healthcheck:
+      test: ["CMD", "curl", "-f", "http://dmaap-dr-prov:8080/internal/prov"]
+      interval: 10s
+      timeout: 30s
+      retries: 5
+    networks:
+      net:
+        aliases:
+        - dmaap-dr-prov
+
+  datarouter-node:
+    image: nexus3.onap.org:10001/onap/dmaap/datarouter-node:2.1.8
+    container_name: datarouter-node
+    hostname: dmaap-dr-node
+    ports:
+    - "9443:8443"
+    - "9090:8080"
+    volumes:
+    - ./node.properties:/opt/app/datartr/etc/node.properties
+    - ../dr_certs/dr_node/truststore.jks:/opt/app/osaaf/local/truststore.jks
+    - ../dr_certs/dr_node/org.onap.dmaap-dr-node.p12:/opt/app/osaaf/local/org.onap.dmaap-dr-node.p12
+    - ../dr_certs/dr_node/org.onap.dmaap-dr.cred.props:/opt/app/osaaf/local/org.onap.dmaap-dr.cred.props
+    depends_on:
+      datarouter-prov:
+        condition: service_healthy
+    networks:
+      net:
+        aliases:
+        - dmaap-dr-node
+
+  mariadb:
+    image: nexus3.onap.org:10001/library/mariadb:10.2.14
+    container_name: mariadb
+    hostname: datarouter-mariadb
+    ports:
+    - "3306:3306"
+    environment:
+      MYSQL_ROOT_PASSWORD: datarouter
+      MYSQL_DATABASE: datarouter
+      MYSQL_USER: datarouter
+      MYSQL_PASSWORD: datarouter
+    healthcheck:
+      test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "-u", "datarouter", "-pdatarouter", "--silent"]
+      interval: 10s
+      timeout: 30s
+      retries: 5
+    networks:
+      net:
+        aliases:
+        - datarouter-mariadb
+
+networks:
+  net:
+    driver: bridge
diff --git a/csit/scripts/dmaap-datarouter/docker-compose/node.properties b/csit/scripts/dmaap-datarouter/docker-compose/node.properties
new file mode 100644 (file)
index 0000000..58639cf
--- /dev/null
@@ -0,0 +1,82 @@
+# ============LICENSE_START===================================================
+#  Copyright (C) 2019-2021 Nordix Foundation.
+# ============================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=====================================================
+#
+#    Configuration parameters set at startup for the DataRouter node
+#
+#    URL to retrieve dynamic configuration
+ProvisioningURL = https://dmaap-dr-prov:8443/internal/prov
+#
+#    URL to upload PUB/DEL/EXP logs
+LogUploadURL = https://dmaap-dr-prov:8443/internal/logs
+#
+#    The port number for http as seen within the server
+IntHttpPort = 8080
+#
+#    The port number for https as seen within the server
+IntHttpsPort = 8443
+#
+#    The external port number for https taking port mapping into account
+ExtHttpsPort = 443
+#
+#    The minimum interval between fetches of the dynamic configuration from the provisioning server
+MinProvFetchInterval = 10000
+#
+#    The minimum interval between saves of the redirection data file
+MinRedirSaveInterval = 10000
+#
+#    The path to the directory where log files are stored
+LogDir = /opt/app/datartr/logs
+#
+#    The retention interval (in days) for log files
+LogRetention = 30
+#
+#    The path to the directories where data and meta data files are stored
+SpoolDir = /opt/app/datartr/spool
+#
+#    The path to the redirection data file
+RedirectionFile = etc/redirections.dat
+#
+#    The type of keystore for https
+KeyStoreType = PKCS12
+#
+#    The type of truststore for https
+TrustStoreType = jks
+#
+#    The path to the file used to trigger an orderly shutdown
+QuiesceFile = etc/SHUTDOWN
+#
+#    The key used to generate passwords for node to node transfers
+NodeAuthKey = Node123!
+#
+#    DR_NODE DEFAULT ENABLED TLS PROTOCOLS
+NodeHttpsProtocols = TLSv1.1|TLSv1.2
+#
+#    AAF type to generate permission string
+AAFType = org.onap.dmaap-dr.feed
+#
+#    AAF default instance to generate permission string - default should be legacy
+AAFInstance = legacy
+#
+#    AAF action to generate permission string - default should be publish
+AAFAction = publish
+#
+#    AAF CADI enabled flag
+CadiEnabled = false
+#
+#    AAF Props file path
+AAFPropsFilePath = /opt/app/osaaf/local/org.onap.dmaap-dr.props
diff --git a/csit/scripts/dmaap-datarouter/docker-compose/provserver.properties b/csit/scripts/dmaap-datarouter/docker-compose/provserver.properties
new file mode 100755 (executable)
index 0000000..b54868e
--- /dev/null
@@ -0,0 +1,55 @@
+# ============LICENSE_START===================================================
+#  Copyright (C) 2019-2021 Nordix Foundation.
+# ============================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=====================================================
+
+#Jetty Server properties
+org.onap.dmaap.datarouter.provserver.http.port           = 8080
+org.onap.dmaap.datarouter.provserver.https.port          = 8443
+org.onap.dmaap.datarouter.provserver.https.relaxation    = true
+
+org.onap.dmaap.datarouter.provserver.aafprops.path       = /opt/app/osaaf/local/org.onap.dmaap-dr.props
+
+org.onap.dmaap.datarouter.provserver.accesslog.dir       = /opt/app/datartr/logs
+org.onap.dmaap.datarouter.provserver.spooldir            = /opt/app/datartr/spool
+org.onap.dmaap.datarouter.provserver.dbscripts           = /opt/app/datartr/etc/misc
+org.onap.dmaap.datarouter.provserver.logretention        = 30
+
+#DMAAP-597 (Tech Dept) REST request source IP auth
+# relaxation to accommodate OOM kubernetes deploy
+org.onap.dmaap.datarouter.provserver.isaddressauthenabled = false
+
+#Localhost address config
+org.onap.dmaap.datarouter.provserver.localhost = 127.0.0.1
+
+# Database access
+org.onap.dmaap.datarouter.db.driver   = org.mariadb.jdbc.Driver
+org.onap.dmaap.datarouter.db.url      = jdbc:mariadb://datarouter-mariadb:3306/datarouter
+org.onap.dmaap.datarouter.db.login    = datarouter
+org.onap.dmaap.datarouter.db.password = datarouter
+
+# PROV - DEFAULT ENABLED TLS PROTOCOLS
+org.onap.dmaap.datarouter.provserver.https.include.protocols = TLSv1.1|TLSv1.2
+
+# AAF config
+org.onap.dmaap.datarouter.provserver.cadi.enabled = false
+
+org.onap.dmaap.datarouter.provserver.passwordencryption   = PasswordEncryptionKey#@$%^&1234#
+org.onap.dmaap.datarouter.provserver.aaf.feed.type        = org.onap.dmaap-dr.feed
+org.onap.dmaap.datarouter.provserver.aaf.sub.type         = org.onap.dmaap-dr.sub
+org.onap.dmaap.datarouter.provserver.aaf.instance         = legacy
+org.onap.dmaap.datarouter.provserver.aaf.action.publish   = publish
+org.onap.dmaap.datarouter.provserver.aaf.action.subscribe = subscribe
\ No newline at end of file
diff --git a/csit/scripts/dmaap-datarouter/docker-compose/subscriber.properties b/csit/scripts/dmaap-datarouter/docker-compose/subscriber.properties
new file mode 100644 (file)
index 0000000..311bbe5
--- /dev/null
@@ -0,0 +1,35 @@
+# ============LICENSE_START===================================================
+#  Copyright (C) 2019-2021 Nordix Foundation.
+# ============================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=====================================================
+
+#Subscriber properties
+org.onap.dmaap.datarouter.subscriber.http.port           = 7070
+org.onap.dmaap.datarouter.subscriber.https.port          = 7443
+org.onap.dmaap.datarouter.subscriber.auth.user           = LOGIN
+org.onap.dmaap.datarouter.subscriber.auth.password       = PASSWORD
+org.onap.dmaap.datarouter.subscriber.delivery.dir        = /opt/app/subscriber/delivery
+
+org.onap.dmaap.datarouter.subscriber.https.relaxation    = true
+org.onap.dmaap.datarouter.subscriber.keystore.type       = jks
+org.onap.dmaap.datarouter.subscriber.keymanager.password = changeit
+org.onap.dmaap.datarouter.subscriber.keystore.path       = /opt/app/datartr/self_signed/keystore.jks
+org.onap.dmaap.datarouter.subscriber.keystore.password   = changeit
+org.onap.dmaap.datarouter.subscriber.truststore.path     = /opt/app/datartr/self_signed/cacerts.jks
+org.onap.dmaap.datarouter.subscriber.truststore.password = changeit
+
+
+
diff --git a/csit/scripts/dmaap-datarouter/dr_certs/dr_node/org.onap.dmaap-dr-node.p12 b/csit/scripts/dmaap-datarouter/dr_certs/dr_node/org.onap.dmaap-dr-node.p12
new file mode 100644 (file)
index 0000000..3793a9d
Binary files /dev/null and b/csit/scripts/dmaap-datarouter/dr_certs/dr_node/org.onap.dmaap-dr-node.p12 differ
diff --git a/csit/scripts/dmaap-datarouter/dr_certs/dr_node/org.onap.dmaap-dr.cred.props b/csit/scripts/dmaap-datarouter/dr_certs/dr_node/org.onap.dmaap-dr.cred.props
new file mode 100644 (file)
index 0000000..e32e728
--- /dev/null
@@ -0,0 +1,17 @@
+############################################################
+# Properties Generated by AT&T Certificate Manager
+#   by root
+#   on 2021-03-12T11:38:49.244+0000
+# @copyright 2019, AT&T
+############################################################
+Challenge=secret
+cadi_alias=dmaap-dr-node@dmaap-dr.onap.org
+cadi_key_password=secret
+#cadi_keyfile=/opt/app/osaaf/local/org.onap.dmaap-dr.keyfile
+cadi_keystore=/opt/app/osaaf/local/org.onap.dmaap-dr-node.p12
+cadi_keystore_password=secret
+cadi_keystore_password_jks=secret
+cadi_keystore_password_p12=secret
+cadi_truststore=/opt/app/osaaf/local/truststore.jks
+cadi_truststore_password=secret
+cadi_x509_issuers=CN=intermediateCA_1, OU=OSAAF, O=ONAP, C=US:CN=intermediateCA_7, OU=OSAAF, O=ONAP, C=US:CN=intermediateCA_9, OU=OSAAF, O=ONAP, C=US
diff --git a/csit/scripts/dmaap-datarouter/dr_certs/dr_node/truststore.jks b/csit/scripts/dmaap-datarouter/dr_certs/dr_node/truststore.jks
new file mode 100644 (file)
index 0000000..91547c6
Binary files /dev/null and b/csit/scripts/dmaap-datarouter/dr_certs/dr_node/truststore.jks differ
diff --git a/csit/scripts/dmaap-datarouter/dr_certs/dr_prov/org.onap.dmaap-dr-prov.p12 b/csit/scripts/dmaap-datarouter/dr_certs/dr_prov/org.onap.dmaap-dr-prov.p12
new file mode 100755 (executable)
index 0000000..1393fb0
Binary files /dev/null and b/csit/scripts/dmaap-datarouter/dr_certs/dr_prov/org.onap.dmaap-dr-prov.p12 differ
diff --git a/csit/scripts/dmaap-datarouter/dr_certs/dr_prov/org.onap.dmaap-dr.cred.props b/csit/scripts/dmaap-datarouter/dr_certs/dr_prov/org.onap.dmaap-dr.cred.props
new file mode 100644 (file)
index 0000000..18f91ba
--- /dev/null
@@ -0,0 +1,17 @@
+############################################################
+# Properties Generated by AT&T Certificate Manager
+#   by root
+#   on 2021-03-12T11:29:50.699+0000
+# @copyright 2019, AT&T
+############################################################
+Challenge=secret
+cadi_alias=dmaap-dr-prov@dmaap-dr.onap.org
+cadi_key_password=secret
+#cadi_keyfile=/opt/app/osaaf/local/org.onap.dmaap-dr.keyfile
+cadi_keystore=/opt/app/osaaf/local/org.onap.dmaap-dr-prov.p12
+cadi_keystore_password=secret
+cadi_keystore_password_jks=secret
+cadi_keystore_password_p12=secret
+cadi_truststore=/opt/app/osaaf/local/truststore.jks
+cadi_truststore_password=secret
+cadi_x509_issuers=CN=intermediateCA_1, OU=OSAAF, O=ONAP, C=US:CN=intermediateCA_7, OU=OSAAF, O=ONAP, C=US:CN=intermediateCA_9, OU=OSAAF, O=ONAP, C=US
diff --git a/csit/scripts/dmaap-datarouter/dr_certs/dr_prov/truststore.jks b/csit/scripts/dmaap-datarouter/dr_certs/dr_prov/truststore.jks
new file mode 100644 (file)
index 0000000..91547c6
Binary files /dev/null and b/csit/scripts/dmaap-datarouter/dr_certs/dr_prov/truststore.jks differ
diff --git a/csit/scripts/dmaap-message-router/dmaap-mr-launch.sh b/csit/scripts/dmaap-message-router/dmaap-mr-launch.sh
new file mode 100755 (executable)
index 0000000..ae91f5b
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/bash
+#
+# ============LICENSE_START=======================================================
+# ONAP DMAAP MR 
+# ================================================================================
+# Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+# Modifications copyright (C) 2021 Nordix Foundation..
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END============================================
+# ===================================================================
+# ECOMP is a trademark and service mark of AT&T Intellectual Property.
+#
+# This script is a copy of plans/dmaap/mrpubsub/setup.sh, placed in the scripts
+# dir, and edited to be a callable function from other plans. e.g. dmaap-buscontroller needs it.
+
+
+# function to launch DMaaP MR docker containers.
+# sets global var IP with assigned IP address of MR container.
+# (kafka and zk containers are not called externally)
+
+function dmaap_mr_launch() {
+
+  mkdir -p ${WORKSPACE}/archives/dmaap/last_run_logs
+
+  # start DMaaP MR containers with docker compose and configuration from docker-compose.yml
+  docker login -u docker -p docker nexus3.onap.org:10001
+  docker-compose -f "${WORKSPACE}"/scripts/dmaap-message-router/docker-compose/docker-compose.yml up -d
+  docker ps
+
+  # Wait for initialization of Docker containers for DMaaP MR, Kafka and Zookeeper
+  for i in {1..50}; do
+      if [ $(docker inspect --format '{{ .State.Running }}' dmaap-mr) ] && \
+        [ $(docker inspect --format '{{ .State.Running }}' zookeeper) ] && \
+        [ $(docker inspect --format '{{ .State.Running }}' kafka) ]
+      then
+          echo "DMaaP Service Running"
+          break
+      else
+          echo sleep $i
+          sleep $i
+      fi
+  done
+
+  DMAAP_MR_IP=$(get-instance-ip.sh dmaap-mr)
+  IP=${DMAAP_MR_IP}
+  KAFKA_IP=$(get-instance-ip.sh kafka)
+  ZOOKEEPER_IP=$(get-instance-ip.sh zookeeper)
+
+  echo DMAAP_MR_IP="${DMAAP_MR_IP}"
+  echo IP="${IP}"
+  echo KAFKA_IP="${KAFKA_IP}"
+  echo ZOOKEEPER_IP="${ZOOKEEPER_IP}"
+
+  # Wait for initialization of docker services
+  for i in {1..50}; do
+      curl -sS -m 1 "${DMAAP_MR_IP}":3904/events/TestTopic && break
+      echo sleep $i
+      sleep $i
+  done
+}
+
diff --git a/csit/scripts/dmaap-message-router/dmaap-mr-teardown.sh b/csit/scripts/dmaap-message-router/dmaap-mr-teardown.sh
new file mode 100755 (executable)
index 0000000..dae8e9f
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright 2016-2017 Huawei Technologies Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Modifications copyright (c) 2018 AT&T Intellectual Property
+# Modifications copyright (C) 2021 Nordix Foundation..
+#
+
+function dmaap_mr_teardown() {
+kill-instance.sh dmaap-mr
+kill-instance.sh kafka
+kill-instance.sh zookeeper
+docker-compose -f ${WORKSPACE}/scripts/dmaap-message-router/docker-compose/docker-compose.yml rm -sf
+}
diff --git a/csit/scripts/dmaap-message-router/docker-compose/docker-compose.yml b/csit/scripts/dmaap-message-router/docker-compose/docker-compose.yml
new file mode 100644 (file)
index 0000000..94765f4
--- /dev/null
@@ -0,0 +1,76 @@
+version: '2.1'
+services:
+  zookeeper:
+    image: nexus3.onap.org:10001/onap/dmaap/zookeeper:6.0.3
+    container_name: zookeeper
+    ports:
+      - "2181:2181"
+    environment:
+     ZOOKEEPER_REPLICAS: 1
+     ZOOKEEPER_TICK_TIME: 2000
+     ZOOKEEPER_SYNC_LIMIT: 5
+     ZOOKEEPER_INIT_LIMIT: 10
+     ZOOKEEPER_MAX_CLIENT_CNXNS: 200
+     ZOOKEEPER_AUTOPURGE_SNAP_RETAIN_COUNT: 3
+     ZOOKEEPER_AUTOPURGE_PURGE_INTERVAL: 24
+     ZOOKEEPER_CLIENT_PORT: 2181
+     KAFKA_OPTS: -Djava.security.auth.login.config=/etc/zookeeper/secrets/jaas/zk_server_jaas.conf -Dzookeeper.kerberos.removeHostFromPrincipal=true -Dzookeeper.kerberos.removeRealmFromPrincipal=true -Dzookeeper.authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider -Dzookeeper.requireClientAuthScheme=sasl
+     ZOOKEEPER_SERVER_ID: 1
+    volumes:
+      -  ./zk/zk_server_jaas.conf:/etc/zookeeper/secrets/jaas/zk_server_jaas.conf
+    networks:
+      net:
+        aliases:
+        - zookeeper
+
+  kafka:
+   image: nexus3.onap.org:10001/onap/dmaap/kafka111:1.0.5
+   container_name: kafka
+   ports:
+    - "9092:9092"
+   environment:
+    enableCadi: 'false'
+    KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+    KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS: 40000
+    KAFKA_ZOOKEEPER_SESSION_TIMEOUT_MS: 40000
+    KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL_PLAINTEXT:PLAINTEXT,EXTERNAL_PLAINTEXT:PLAINTEXT
+    KAFKA_ADVERTISED_LISTENERS: INTERNAL_PLAINTEXT://kafka:9092
+    KAFKA_LISTENERS: INTERNAL_PLAINTEXT://0.0.0.0:9092
+    KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL_PLAINTEXT
+    KAFKA_CONFLUENT_SUPPORT_METRICS_ENABLE: 'false'
+    KAFKA_OPTS: -Djava.security.auth.login.config=/etc/kafka/secrets/jaas/zk_client_jaas.conf
+    KAFKA_ZOOKEEPER_SET_ACL: 'true'
+    KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
+    # Reduced the number of partitions only to avoid the timeout error for the first subscribe call in slow environment
+    KAFKA_OFFSETS_TOPIC_NUM_PARTITIONS: 1
+   volumes:
+     -  ./kafka/zk_client_jaas.conf:/etc/kafka/secrets/jaas/zk_client_jaas.conf
+   networks:
+     net:
+       aliases:
+       - kafka
+   depends_on:
+    - zookeeper
+
+  dmaap:
+    image: nexus3.onap.org:10001/onap/dmaap/dmaap-mr:1.1.20
+    container_name: dmaap-mr
+    ports:
+      - "3904:3904"
+      - "3905:3905"
+    environment:
+     enableCadi: 'false'
+    volumes:
+      - ./mr/MsgRtrApi.properties:/appl/dmaapMR1/bundleconfig/etc/appprops/MsgRtrApi.properties
+      - ./mr/logback.xml:/appl/dmaapMR1/bundleconfig/etc/logback.xml
+      - ./mr/cadi.properties:/appl/dmaapMR1/etc/cadi.properties
+    networks:
+      net:
+        aliases:
+        - dmaap
+    depends_on:
+      - zookeeper
+      - kafka
+networks:
+  net:
+    driver: bridge
diff --git a/csit/scripts/dmaap-message-router/docker-compose/kafka/zk_client_jaas.conf b/csit/scripts/dmaap-message-router/docker-compose/kafka/zk_client_jaas.conf
new file mode 100644 (file)
index 0000000..d4ef1eb
--- /dev/null
@@ -0,0 +1,5 @@
+Client {
+   org.apache.zookeeper.server.auth.DigestLoginModule required
+   username="kafka"
+   password="kafka_secret";
+ };
\ No newline at end of file
diff --git a/csit/scripts/dmaap-message-router/docker-compose/mr/MsgRtrApi.properties b/csit/scripts/dmaap-message-router/docker-compose/mr/MsgRtrApi.properties
new file mode 100644 (file)
index 0000000..7aede4f
--- /dev/null
@@ -0,0 +1,166 @@
+###############################################################################
+#  ============LICENSE_START=======================================================
+#  org.onap.dmaap
+#  ================================================================================
+#  Copyright ï¿½ 2017 AT&T Intellectual Property. All rights reserved.
+#  ================================================================================
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#        http://www.apache.org/licenses/LICENSE-2.0
+#  
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#  ============LICENSE_END=========================================================
+#
+#  ECOMP is a trademark and service mark of AT&T Intellectual Property.
+#  
+###############################################################################
+###############################################################################
+##
+## Cambria API Server config
+##
+##     - Default values are shown as commented settings.
+##
+
+###############################################################################
+##
+## HTTP service
+##
+##             - 3904 is standard as of 7/29/14.
+#
+## Zookeeper Connection
+##
+##     Both Cambria and Kafka make use of Zookeeper.
+##
+config.zk.servers=zookeeper
+
+###############################################################################
+##
+## Kafka Connection
+##
+##     Items below are passed through to Kafka's producer and consumer
+##     configurations (after removing "kafka.")
+##     if you want to change request.required.acks it can take this one value
+kafka.metadata.broker.list=kafka:9092
+##kafka.request.required.acks=-1
+#kafka.client.zookeeper=${config.zk.servers}
+consumer.timeout.ms=100
+zookeeper.connection.timeout.ms=6000
+zookeeper.session.timeout.ms=20000
+zookeeper.sync.time.ms=2000
+auto.commit.interval.ms=1000
+fetch.message.max.bytes =1000000
+auto.commit.enable=false
+
+#(backoff*retries > zksessiontimeout)
+kafka.rebalance.backoff.ms=10000
+kafka.rebalance.max.retries=6
+
+
+###############################################################################
+##
+##     Secured Config
+##
+##     Some data stored in the config system is sensitive -- API keys and secrets,
+##     for example. to protect it, we use an encryption layer for this section
+##     of the config.
+##
+## The key is a base64 encode AES key. This must be created/configured for
+## each installation.
+#cambria.secureConfig.key=
+##
+## The initialization vector is a 16 byte value specific to the secured store.
+## This must be created/configured for each installation.
+#cambria.secureConfig.iv=
+
+## Southfield Sandbox
+cambria.secureConfig.key=b/7ouTn9FfEw2PQwL0ov/Q==
+cambria.secureConfig.iv=wR9xP5k5vbz/xD0LmtqQLw==
+authentication.adminSecret=fe3cCompound
+#cambria.secureConfig.key[pc569h]=YT3XPyxEmKCTLI2NK+Sjbw==
+#cambria.secureConfig.iv[pc569h]=rMm2jhR3yVnU+u2V9Ugu3Q==
+
+
+###############################################################################
+##
+## Consumer Caching
+##
+##     Kafka expects live connections from the consumer to the broker, which
+##     obviously doesn't work over connectionless HTTP requests. The Cambria
+##     server proxies HTTP requests into Kafka consumer sessions that are kept
+##     around for later re-use. Not doing so is costly for setup per request,
+##     which would substantially impact a high volume consumer's performance.
+##
+##     This complicates Cambria server failover, because we often need server
+##     A to close its connection before server B brings up the replacement.    
+##
+
+## The consumer cache is normally enabled.
+#cambria.consumer.cache.enabled=true
+
+## Cached consumers are cleaned up after a period of disuse. The server inspects
+## consumers every sweepFreqSeconds and will clean up any connections that are
+## dormant for touchFreqMs.
+#cambria.consumer.cache.sweepFreqSeconds=15
+cambria.consumer.cache.touchFreqMs=120000
+##stickforallconsumerrequests=false
+## The cache is managed through ZK. The default value for the ZK connection
+## string is the same as config.zk.servers.
+#cambria.consumer.cache.zkConnect=${config.zk.servers}
+
+##
+## Shared cache information is associated with this node's name. The default
+## name is the hostname plus the HTTP service port this host runs on. (The
+## hostname is determined via InetAddress.getLocalHost ().getCanonicalHostName(),
+## which is not always adequate.) You can set this value explicitly here.
+##
+#cambria.api.node.identifier=<use-something-unique-to-this-instance>
+
+#cambria.rateLimit.maxEmptyPollsPerMinute=30
+#cambria.rateLimitActual.delay.ms=10
+
+###############################################################################
+##
+## Metrics Reporting
+##
+##     This server can report its metrics periodically on a topic.
+##
+#metrics.send.cambria.enabled=true
+#metrics.send.cambria.topic=cambria.apinode.metrics
+#msgrtr.apinode.metrics.dmaap
+#metrics.send.cambria.sendEverySeconds=60
+
+cambria.consumer.cache.zkBasePath=/fe3c/cambria/consumerCache
+consumer.timeout=17
+
+##############################################################################
+#100mb
+maxcontentlength=10000
+
+
+##############################################################################
+#AAF Properties
+msgRtr.namespace.aaf=org.onap.dmaap.mr.topic
+msgRtr.topicfactory.aaf=org.onap.dmaap.mr.topicFactory|:org.onap.dmaap.mr.topic:
+enforced.topic.name.AAF=org.onap.dmaap.mr
+forceAAF=false
+transidUEBtopicreqd=false
+defaultNSforUEB=org.onap.dmaap.mr
+##############################################################################
+#Mirror Maker Agent
+msgRtr.mirrormakeradmin.aaf=org.onap.dmaap.mr.mirrormaker|*|admin
+msgRtr.mirrormakeruser.aaf=org.onap.dmaap.mr.mirrormaker|*|user
+msgRtr.mirrormakeruser.aaf.create=org.onap.dmaap.mr.topicFactory|:org.onap.dmaap.mr.topic:
+msgRtr.mirrormaker.timeout=15000
+msgRtr.mirrormaker.topic=org.onap.dmaap.mr.mmagent
+msgRtr.mirrormaker.consumergroup=mmagentserver
+msgRtr.mirrormaker.consumerid=1
+
+kafka.max.poll.interval.ms=300000
+kafka.heartbeat.interval.ms=60000
+kafka.session.timeout.ms=240000
+kafka.max.poll.records=1000
\ No newline at end of file
diff --git a/csit/scripts/dmaap-message-router/docker-compose/mr/cadi.properties b/csit/scripts/dmaap-message-router/docker-compose/mr/cadi.properties
new file mode 100644 (file)
index 0000000..e7d056b
--- /dev/null
@@ -0,0 +1,19 @@
+aaf_locate_url=https://aaf-onap-test.osaaf.org:8095\
+aaf_url=https://AAF_LOCATE_URL/onap.org.osaaf.aaf.service:2.1
+aaf_env=DEV
+aaf_lur=org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm
+
+cadi_truststore=/appl/dmaapMR1/etc/org.onap.dmaap.mr.trust.jks
+cadi_truststore_password=8FyfX+ar;0$uZQ0h9*oXchNX
+
+cadi_keyfile=/appl/dmaapMR1/etc/org.onap.dmaap.mr.keyfile
+
+cadi_alias=dmaapmr@mr.dmaap.onap.org
+cadi_keystore=/appl/dmaapMR1/etc/org.onap.dmaap.mr.p12
+cadi_keystore_password=GDQttV7)BlOvWMf6F7tz&cjy
+cadi_x509_issuers=CN=intermediateCA_1, OU=OSAAF, O=ONAP, C=US:CN=intermediateCA_7, OU=OSAAF, O=ONAP, C=US:CN=intermediateCA_9, OU=OSAAF, O=ONAP, C=US
+
+cadi_loglevel=INFO
+cadi_protocols=TLSv1.1,TLSv1.2
+cadi_latitude=37.78187
+cadi_longitude=-122.26147
\ No newline at end of file
diff --git a/csit/scripts/dmaap-message-router/docker-compose/mr/logback.xml b/csit/scripts/dmaap-message-router/docker-compose/mr/logback.xml
new file mode 100644 (file)
index 0000000..f02a2db
--- /dev/null
@@ -0,0 +1,208 @@
+<!--
+     ============LICENSE_START=======================================================
+     Copyright Â© 2019 AT&T Intellectual Property. All rights reserved.
+     ================================================================================
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+           http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+     ============LICENSE_END=========================================================
+ -->
+
+<configuration scan="true" scanPeriod="3 seconds" debug="false">
+  <contextName>${module.ajsc.namespace.name}</contextName>
+  <jmxConfigurator />
+  <property name="logDirectory" value="${AJSC_HOME}/log" />
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <filter class="ch.qos.logback.classic.filter.LevelFilter">
+      <level>ERROR</level>
+      <onMatch>ACCEPT</onMatch>
+      <onMismatch>DENY</onMismatch>
+    </filter>
+    <encoder>
+      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{1024} - %msg%n
+      </pattern>
+    </encoder>
+  </appender>
+
+  <appender name="INFO" class="ch.qos.logback.core.ConsoleAppender">
+    <filter class="ch.qos.logback.classic.filter.LevelFilter">
+      <level>INFO</level>
+      <onMatch>ACCEPT</onMatch>
+      <onMismatch>DENY</onMismatch>
+    </filter>
+  </appender>
+
+  <appender name="DEBUG" class="ch.qos.logback.core.ConsoleAppender">
+
+    <encoder>
+      <pattern>"%d [%thread] %-5level %logger{1024} - %msg%n"</pattern>
+    </encoder>
+  </appender>
+
+  <appender name="ERROR" class="ch.qos.logback.core.ConsoleAppender"> class="ch.qos.logback.core.ConsoleAppender">
+    <filter class="ch.qos.logback.classic.filter.LevelFilter">
+      <level>ERROR</level>
+      <onMatch>ACCEPT</onMatch>
+      <onMismatch>DENY</onMismatch>
+    </filter>
+    <encoder>
+      <pattern>"%d [%thread] %-5level %logger{1024} - %msg%n"</pattern>
+    </encoder>
+  </appender>
+
+
+  <!-- Msgrtr related loggers -->
+  <logger name="org.onap.dmaap.dmf.mr.service" level="INFO" />
+  <logger name="org.onap.dmaap.dmf.mr.service.impl" level="INFO" />
+
+  <logger name="org.onap.dmaap.dmf.mr.resources" level="INFO" />
+  <logger name="org.onap.dmaap.dmf.mr.resources.streamReaders" level="INFO" />
+
+  <logger name="org.onap.dmaap.dmf.mr.backends" level="INFO" />
+  <logger name="org.onap.dmaap.dmf.mr.backends.kafka" level="INFO" />
+  <logger name="org.onap.dmaap.dmf.mr.backends.memory" level="INFO" />
+
+  <logger name="org.onap.dmaap.dmf.mr.beans" level="INFO" />
+
+  <logger name="org.onap.dmaap.dmf.mr.constants" level="INFO" />
+
+  <logger name="org.onap.dmaap.dmf.mr.exception" level="INFO" />
+
+  <logger name="org.onap.dmaap.dmf.mr.listener" level="INFO" />
+
+  <logger name="org.onap.dmaap.dmf.mr.metabroker" level="INFO" />
+
+  <logger name="org.onap.dmaap.dmf.mr.metrics.publisher" level="INFO" />
+  <logger name="org.onap.dmaap.dmf.mr.metrics.publisher.impl" level="INFO" />
+
+
+
+  <logger name="org.onap.dmaap.dmf.mr.security" level="INFO" />
+  <logger name="org.onap.dmaap.dmf.mr.security.impl" level="INFO" />
+
+  <logger name="org.onap.dmaap.dmf.mr.transaction" level="INFO" />
+  <logger name="com.att.dmf.mr.transaction.impl" level="INFO" />
+
+  <logger name="org.onap.dmaap.dmf.mr.metabroker" level="INFO" />
+  <logger name="org.onap.dmaap.dmf.mr.metabroker" level="INFO" />
+
+  <logger name="org.onap.dmaap.dmf.mr.utils" level="INFO" />
+  <logger name="org.onap.dmaap.mr.filter" level="INFO" />
+
+  <!--<logger name="com.att.nsa.cambria.*" level="INFO" />-->
+
+  <!-- Msgrtr loggers in ajsc -->
+  <logger name="org.onap.dmaap.service" level="INFO" />
+  <logger name="org.onap.dmaap" level="INFO" />
+
+
+  <!-- Spring related loggers -->
+  <logger name="org.springframework" level="WARN" additivity="false"/>
+  <logger name="org.springframework.beans" level="WARN" additivity="false"/>
+  <logger name="org.springframework.web" level="WARN" additivity="false" />
+  <logger name="com.blog.spring.jms" level="WARN" additivity="false" />
+
+  <!-- AJSC Services (bootstrap services) -->
+  <logger name="ajsc" level="WARN" additivity="false"/>
+  <logger name="ajsc.RouteMgmtService" level="INFO" additivity="false"/>
+  <logger name="ajsc.ComputeService" level="INFO" additivity="false" />
+  <logger name="ajsc.VandelayService" level="WARN" additivity="false"/>
+  <logger name="ajsc.FilePersistenceService" level="WARN" additivity="false"/>
+  <logger name="ajsc.UserDefinedJarService" level="WARN" additivity="false" />
+  <logger name="ajsc.UserDefinedBeansDefService" level="WARN" additivity="false" />
+  <logger name="ajsc.LoggingConfigurationService" level="WARN" additivity="false" />
+
+  <!-- AJSC related loggers (DME2 Registration, csi logging, restlet, servlet
+    logging) -->
+  <logger name="ajsc.utils" level="WARN" additivity="false"/>
+  <logger name="ajsc.utils.DME2Helper" level="INFO" additivity="false" />
+  <logger name="ajsc.filters" level="DEBUG" additivity="false" />
+  <logger name="ajsc.beans.interceptors" level="DEBUG" additivity="false" />
+  <logger name="ajsc.restlet" level="DEBUG" additivity="false" />
+  <logger name="ajsc.servlet" level="DEBUG" additivity="false" />
+  <logger name="com.att" level="WARN" additivity="false" />
+  <logger name="com.att.ajsc.csi.logging" level="WARN" additivity="false" />
+  <logger name="com.att.ajsc.filemonitor" level="WARN" additivity="false"/>
+
+  <logger name="com.att.nsa.dmaap.util" level="INFO" additivity="false"/>
+  <logger name="com.att.cadi.filter" level="INFO" additivity="false" />
+
+
+  <!-- Other Loggers that may help troubleshoot -->
+  <logger name="net.sf" level="WARN" additivity="false" />
+  <logger name="org.apache.commons.httpclient" level="WARN" additivity="false"/>
+  <logger name="org.apache.commons" level="WARN" additivity="false" />
+  <logger name="org.apache.coyote" level="WARN" additivity="false"/>
+  <logger name="org.apache.jasper" level="WARN" additivity="false"/>
+
+  <!-- Camel Related Loggers (including restlet/servlet/jaxrs/cxf logging.
+    May aid in troubleshooting) -->
+  <logger name="org.apache.camel" level="WARN" additivity="false" />
+  <logger name="org.apache.cxf" level="WARN" additivity="false" />
+  <logger name="org.apache.camel.processor.interceptor" level="WARN" additivity="false"/>
+  <logger name="org.apache.cxf.jaxrs.interceptor" level="WARN" additivity="false" />
+  <logger name="org.apache.cxf.service" level="WARN" additivity="false" />
+  <logger name="org.restlet" level="DEBUG" additivity="false" />
+  <logger name="org.apache.camel.component.restlet" level="DEBUG" additivity="false" />
+  <logger name="org.apache.kafka" level="DEBUG" additivity="false" />
+  <logger name="org.apache.zookeeper" level="INFO" additivity="false" />
+  <logger name="org.I0Itec.zkclient" level="DEBUG" additivity="false" />
+
+  <!-- logback internals logging -->
+  <logger name="ch.qos.logback.classic" level="INFO" additivity="false"/>
+  <logger name="ch.qos.logback.core" level="INFO" additivity="false" />
+
+  <!-- logback jms appenders & loggers definition starts here -->
+  <!-- logback jms appenders & loggers definition starts here -->
+  <appender name="auditLogs" class="ch.qos.logback.core.ConsoleAppender">
+    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+    </filter>
+    <encoder>
+      <pattern>"%d [%thread] %-5level %logger{1024} - %msg%n"</pattern>
+    </encoder>
+  </appender>
+  <appender name="perfLogs" class="ch.qos.logback.core.ConsoleAppender">
+    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+    </filter>
+    <encoder>
+      <pattern>"%d [%thread] %-5level %logger{1024} - %msg%n"</pattern>
+    </encoder>
+  </appender>
+  <appender name="ASYNC-audit" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>1000</queueSize>
+    <discardingThreshold>0</discardingThreshold>
+    <appender-ref ref="Audit-Record-Queue" />
+  </appender>
+
+  <logger name="AuditRecord" level="INFO" additivity="FALSE">
+    <appender-ref ref="STDOUT" />
+  </logger>
+  <logger name="AuditRecord_DirectCall" level="INFO" additivity="FALSE">
+    <appender-ref ref="STDOUT" />
+  </logger>
+  <appender name="ASYNC-perf" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>1000</queueSize>
+    <discardingThreshold>0</discardingThreshold>
+    <appender-ref ref="Performance-Tracker-Queue" />
+  </appender>
+  <logger name="PerfTrackerRecord" level="INFO" additivity="FALSE">
+    <appender-ref ref="ASYNC-perf" />
+    <appender-ref ref="perfLogs" />
+  </logger>
+  <!-- logback jms appenders & loggers definition ends here -->
+
+  <root level="DEBUG">
+    <appender-ref ref="DEBUG" />
+    <appender-ref ref="ERROR" />
+    <appender-ref ref="INFO" />
+    <appender-ref ref="STDOUT" />
+  </root>
+
+</configuration>
diff --git a/csit/scripts/dmaap-message-router/docker-compose/zk/zk_server_jaas.conf b/csit/scripts/dmaap-message-router/docker-compose/zk/zk_server_jaas.conf
new file mode 100644 (file)
index 0000000..26bf460
--- /dev/null
@@ -0,0 +1,4 @@
+Server {
+       org.apache.zookeeper.server.auth.DigestLoginModule required
+       user_kafka=kafka_secret;
+};
\ No newline at end of file
diff --git a/csit/scripts/get-instance-ip.sh b/csit/scripts/get-instance-ip.sh
new file mode 100755 (executable)
index 0000000..a236c02
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016-2017 Huawei Technologies Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $1
diff --git a/csit/scripts/kill-instance.sh b/csit/scripts/kill-instance.sh
new file mode 100755 (executable)
index 0000000..5997098
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright 2016-2017 Huawei Technologies Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# $1 nickname for the instance
+
+mkdir -p $WORKSPACE/archives
+
+running_containers=$(docker ps --filter name=$1 -q)
+if [ -z "$running_containers" ]
+then
+    echo "$1 already terminated"
+else
+    echo "Stopping and removing containers"
+    docker logs $running_containers >> $WORKSPACE/archives/$1.log
+    docker stop $running_containers
+    docker rm $running_containers
+fi
+
diff --git a/csit/tests/with_dr/__init__.robot b/csit/tests/with_dr/__init__.robot
new file mode 100644 (file)
index 0000000..2d40412
--- /dev/null
@@ -0,0 +1,2 @@
+*** Settings ***
+Documentation    dmaap-buscontroller - dr
diff --git a/csit/tests/with_dr/test1.robot b/csit/tests/with_dr/test1.robot
new file mode 100644 (file)
index 0000000..119e06b
--- /dev/null
@@ -0,0 +1,69 @@
+*** Settings ***
+Library           Collections
+Library           OperatingSystem
+Library           RequestsLibrary
+
+*** Variables ***
+${DBC_URL}     http://${DMAAPBC_IP}:8080/webapi
+${LOC}          csit-sanfrancisco
+${FEED1_DATA}  { "feedName":"feed1", "feedVersion": "csit", "feedDescription":"generated for CSIT", "owner":"dgl", "asprClassification": "unclassified" }
+${FEED2_DATA}  { "feedName":"feed2", "feedVersion": "csit", "feedDescription":"generated for CSIT", "owner":"dgl", "asprClassification": "unclassified" }
+${PUB2_DATA}   { "dcaeLocationName": "${LOC}", "username": "pub2", "userpwd": "topSecret123", "feedId": "2" }
+${SUB2_DATA}   { "dcaeLocationName": "${LOC}", "username": "sub2", "userpwd": "someSecret123", "deliveryURL": "https://${DMAAPBC_IP}:8443/webapi/noURI", "feedId": "2" }
+
+
+*** Test Cases ***
+(DMAAP-441c1)
+    [Documentation]        Create Feed w no clients POST webapi/feeds endpoint
+    ${resp}=         PostCall     ${DBC_URL}/feeds    ${FEED1_DATA}
+    Should Be Equal As Integers   ${resp.status_code}  200
+
+(DMAAP-441c2)
+    [Documentation]        Create Feed w clients POST webapi/feeds endpoint
+    ${resp}=         PostCall     ${DBC_URL}/feeds    ${FEED2_DATA}
+    Should Be Equal As Integers   ${resp.status_code}  200
+
+(DMAAP-441c3)
+    [Documentation]        Add Publisher to existing feed
+    ${resp}=         PostCall      ${DBC_URL}/dr_pubs    ${PUB2_DATA}
+    Should Be Equal As Integers    ${resp.status_code}  201
+    ${JSON}=         Evaluate      json.loads(r"""${resp.content}""", strict=False)
+    ${result}=       Set Variable  ${JSON['pubId']}
+    Set Suite Variable             ${pubId}    ${result}
+
+(DMAAP-441c4)
+    [Documentation]        Add Subscriber to existing feed
+    ${resp}=         PostCall      ${DBC_URL}/dr_subs    ${SUB2_DATA}
+    Should Be Equal As Integers    ${resp.status_code}  201
+    ${JSON}=         Evaluate      json.loads(r"""${resp.content}""", strict=False)
+    ${result}=       Set Variable  ${JSON['subId']}
+    Set Suite Variable             ${subId}    ${result}
+
+(DMAAP-443)
+    [Documentation]        List existing feeds
+    ${resp}=       Evaluate    requests.get('${DBC_URL}/feeds', verify=False)    requests
+    Should Be Equal As Integers     ${resp.status_code}  200
+
+(DMAAP-444)
+    [Documentation]        Delete existing subscriber
+    ${resp}=         DelCall        ${DBC_URL}/dr_subs/${subId}
+    Should Be Equal As Integers     ${resp.status_code}  204
+
+(DMAAP-445)
+    [Documentation]        Delete existing publisher
+    ${resp}=         DelCall        ${DBC_URL}/dr_pubs/${pubId}
+    Should Be Equal As Integers     ${resp.status_code}  204
+
+
+*** Keywords ***
+PostCall
+    [Arguments]    ${url}           ${data}
+    ${headers}=    Create Dictionary    Accept=application/json    Content-Type=application/json
+    ${resp}=       Evaluate    requests.post('${url}',data='${data}', headers=${headers},verify=False)    requests
+    [Return]       ${resp}
+
+DelCall
+    [Arguments]    ${url}           
+    ${headers}=    Create Dictionary    Accept=application/json    Content-Type=application/json
+    ${resp}=       Evaluate    requests.delete('${url}', headers=${headers},verify=False)    requests
+    [Return]       ${resp}
diff --git a/csit/tests/with_mr/__init__.robot b/csit/tests/with_mr/__init__.robot
new file mode 100644 (file)
index 0000000..ffcec41
--- /dev/null
@@ -0,0 +1,3 @@
+*** Settings ***
+Documentation    dmaap-buscontroller - mr
+
diff --git a/csit/tests/with_mr/test1.robot b/csit/tests/with_mr/test1.robot
new file mode 100644 (file)
index 0000000..c56e725
--- /dev/null
@@ -0,0 +1,80 @@
+*** Settings ***
+Library           Collections
+Library           OperatingSystem
+Library           RequestsLibrary
+
+
+*** Variables ***
+${DBC_URI}      webapi
+${DBC_URL}      http://${DMAAP_BC_IP}:8080/${DBC_URI}
+${TOPIC_NS}     org.onap.dmaap.onapCSIT
+${LOC}          csit-sanfrancisco
+${PUB_CORE}     "dcaeLocationName": "${LOC}", "clientRole": "org.onap.dmaap.client.pub", "action": [ "pub", "view" ] 
+${SUB_CORE}     "dcaeLocationName": "${LOC}", "clientRole": "org.onap.dmaap.client.sub", "action": [ "sub", "view" ] 
+${PUB}          { ${PUB_CORE} }
+${SUB}          { ${SUB_CORE} }
+${TOPIC1_DATA}  { "topicName":"singleMRtopic1", "topicDescription":"generated for CSIT", "owner":"dgl"}
+${TOPIC2_DATA}  { "topicName":"singleMRtopic2", "topicDescription":"generated for CSIT", "owner":"dgl", "clients": [ ${PUB}, ${SUB}] }
+${TOPIC3_DATA}  { "topicName":"singleMRtopic3", "topicDescription":"generated for CSIT", "owner":"dgl"}
+${PUB3_DATA}    { "fqtn": "${TOPIC_NS}.singleMRtopic3", ${PUB_CORE} }
+${SUB3_DATA}    { "fqtn": "${TOPIC_NS}.singleMRtopic3", ${SUB_CORE} }
+
+
+*** Test Cases ***
+(DMAAP-293)
+    [Documentation]        Create Topic w no clients POST ${DBC_URI}/topics endpoint
+    ${resp}=         PostCall    ${DBC_URL}/topics    ${TOPIC1_DATA}
+    Should Be Equal As Integers  ${resp.status_code}  201   
+
+(DMAAP-294)
+    [Documentation]        Create Topic w pub and sub clients POST ${DBC_URI}/topics endpoint
+    ${resp}=         PostCall    ${DBC_URL}/topics    ${TOPIC2_DATA}
+    Should Be Equal As Integers  ${resp.status_code}  201
+
+(DMAAP-295)
+    [Documentation]        Create Topic w no clients and then add a client POST ${DBC_URI}/mr_clients endpoint
+    ${resp}=         PostCall    ${DBC_URL}/topics    ${TOPIC3_DATA}
+    Should Be Equal As Integers  ${resp.status_code}  201   
+    ${resp}=         PostCall    ${DBC_URL}/mr_clients    ${PUB3_DATA}
+    Should Be Equal As Integers  ${resp.status_code}  200   
+    ${resp}=         PostCall    ${DBC_URL}/mr_clients    ${SUB3_DATA}
+    Should Be Equal As Integers  ${resp.status_code}  200   
+
+(DMAAP-297)
+    [Documentation]    Query for all topics and specific topic
+    ${resp}=           Evaluate    requests.get('${DBC_URL}/topics', verify=False)    requests
+    Should Be Equal As Integers  ${resp.status_code}  200
+    ${resp}=       Evaluate    requests.get('${DBC_URL}/topics/${TOPIC_NS}.singleMRtopic3', verify=False)    requests
+    Should Be Equal As Integers  ${resp.status_code}  200
+
+(DMAAP-301)
+    [Documentation]    Delete a subscriber
+    ${resp}=           Evaluate    requests.get('${DBC_URL}/topics/${TOPIC_NS}.singleMRtopic3', verify=False)    requests
+    Should Be Equal As Integers  ${resp.status_code}  200
+    ${JSON}=           Evaluate      json.loads(r"""${resp.content}""", strict=False)
+    ${clientId}=       Set Variable  ${JSON['clients'][1]['mrClientId']}
+    ${resp}=           DelCall   ${DBC_URL}/mr_clients/${clientId}
+    Should Be Equal As Integers  ${resp.status_code}  204
+
+(DMAAP-302)
+    [Documentation]    Delete a publisher
+    ${resp}=           Evaluate    requests.get('${DBC_URL}/topics/${TOPIC_NS}.singleMRtopic3', verify=False)    requests
+    Should Be Equal As Integers  ${resp.status_code}  200
+    ${JSON}=           Evaluate      json.loads(r"""${resp.content}""", strict=False)
+    ${clientId}=       Set Variable  ${JSON['clients'][0]['mrClientId']}
+    ${resp}=           DelCall   ${DBC_URL}/mr_clients/${clientId}
+    Should Be Equal As Integers  ${resp.status_code}  204
+
+
+*** Keywords ***
+PostCall
+    [Arguments]    ${url}           ${data}
+    ${headers}=    Create Dictionary    Accept=application/json    Content-Type=application/json
+    ${resp}=       Evaluate    requests.post('${url}',data='${data}', headers=${headers},verify=False)    requests
+    [Return]       ${resp}
+
+DelCall
+    [Arguments]    ${url}           
+    ${headers}=    Create Dictionary    Accept=application/json    Content-Type=application/json
+    ${resp}=       Evaluate    requests.delete('${url}', headers=${headers},verify=False)    requests
+    [Return]       ${resp}
similarity index 100%
rename from certs/ca.pem
rename to dbc-client/certs/ca.pem
similarity index 100%
rename from certs/client.pem
rename to dbc-client/certs/client.pem
similarity index 100%
rename from certs/key.pem
rename to dbc-client/certs/key.pem
similarity index 100%
rename from misc/aaf-ca.crt
rename to dbc-client/misc/aaf-ca.crt
index d1de3e1..461a069 100644 (file)
@@ -44,9 +44,6 @@ AUTH_METHOD=${AUTH_METHOD:-basicAuth}
 BA_IDENTITY=${BA_IDENTITY:-dmaap-bc@dmaap-bc.onap.org}
 BA_PWD=${BA_PWD:-'demo123456!'}
 
-
-
-
 config() {
        echo "ENTER config"
        set -x
@@ -113,6 +110,7 @@ init() {
        done
        set +x
 }
+
 dopost() {
        set -x
        RETRY_TIME=60
@@ -136,6 +134,7 @@ dopost() {
        set +x
 
 }
+
 doprov() {
 
        set -x
@@ -155,6 +154,7 @@ doprov() {
        done
        set +x
 }
+
 delay() {
        echo "DELAY=$DELAY"
        if [ ! -z "$DELAY" ]
index 30940bb..b9a4abd 100644 (file)
   ============LICENSE_END============================================
   ECOMP is a trademark and service mark of AT&T Intellectual Property.
 -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>dbc-client</artifactId>
   <name>dbc-client</name>
+  <packaging>jar</packaging>
   <parent>
     <groupId>org.onap.dmaap.buscontroller</groupId>
     <artifactId>parent</artifactId>
   </parent>
   <description>Packaging Platform (DMaaP) HTTP Client for Bus Controller Provisioning.</description>
   <properties>
-    <multiproject.basedir>${basedir}/..</multiproject.basedir>
-    <docker.maven.plugin.version>1.0.0</docker.maven.plugin.version>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <!-- docker image -->
-    <docker.image>onap/dmaap/dbc-client</docker.image>
-    <nexusproxy>https://nexus.onap.org</nexusproxy>
-    <docker.push.registry>nexus3.onap.org:10003</docker.push.registry>
-    <!-- for Distribution Management -->
-    <sitePath>/content/sites/site/org/onap/dmaap/dbc-client/${revision}</sitePath>
-    <timestamp>${maven.build.timestamp}</timestamp>
-    <maven.build.timestamp.format>yyyyMMdd'T'HHmmss'Z'</maven.build.timestamp.format>
+
+    <docker.location>${basedir}/target/${project.artifactId}</docker.location>
+    <dmaapbcclient.image.name>${docker.image.root}${project.artifactId}</dmaapbcclient.image.name>
+    <sitePath>/content/sites/site/org/onap/dmaap/dbc-client/${project.version}</sitePath>
   </properties>
   <build>
     <finalName>dbc-client</finalName>
     <plugins>
-      <!-- for Distribution management -->
       <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-site-plugin</artifactId>
-        <dependencies>
-          <dependency>
-            <groupId>org.apache.maven.wagon</groupId>
-            <artifactId>wagon-webdav-jackrabbit</artifactId>
-            <version>2.10</version>
-          </dependency>
-        </dependencies>
+        <artifactId>maven-resources-plugin</artifactId>
+        <version>2.7</version>
+        <executions>
+          <execution>
+            <id>copy-jar</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${basedir}/target/docker-stage/opt/app/dmaapbc/lib</outputDirectory>
+              <resources>
+                <resource>
+                  <directory>${multiproject.basedir}/dmaap-bc/target</directory>
+                  <includes>
+                    <include>dbc-client.jar</include>
+                  </includes>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
       </plugin>
-
     </plugins>
     <pluginManagement>
       <plugins>
-        <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
-        <plugin>
-          <groupId>org.eclipse.m2e</groupId>
-          <artifactId>lifecycle-mapping</artifactId>
-          <version>1.0.0</version>
-          <configuration>
-            <lifecycleMappingMetadata>
-              <pluginExecutions>
-                <pluginExecution>
-                  <pluginExecutionFilter>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-dependency-plugin</artifactId>
-                    <versionRange>[2.10,)</versionRange>
-                    <phase>install</phase>
-                    <goals>
-                      <goal>copy-dependencies</goal>
-                    </goals>
-                  </pluginExecutionFilter>
-                  <action>
-                    <ignore/>
-                  </action>
-                </pluginExecution>
-              </pluginExecutions>
-            </lifecycleMappingMetadata>
-          </configuration>
-        </plugin>
       </plugins>
     </pluginManagement>
+    <!-- Copy files to docker-stage to be included in image -->
+    <resources>
+      <resource>
+        <targetPath>${basedir}/target/docker-stage</targetPath>
+        <directory>${basedir}/src/main/resources</directory>
+        <includes>
+          <include>Dockerfile</include>
+        </includes>
+      </resource>
+      <resource>
+        <targetPath>${basedir}/target/docker-stage/opt/app/dbc-client/etc</targetPath>
+        <directory>${basedir}/certs</directory>
+        <includes>
+          <include>ca.pem</include>
+          <include>client.pem</include>
+          <include>key.pem</include>
+        </includes>
+      </resource>
+      <resource>
+        <targetPath>${basedir}/target/docker-stage/opt/app/dbc-client/bin</targetPath>
+        <directory>${basedir}/misc</directory>
+        <includes>
+          <include>dbc-client</include>
+        </includes>
+      </resource>
+      <resource>
+        <targetPath>${basedir}/target/docker-stage/opt/app/dbc-client/etc</targetPath>
+        <directory>${basedir}</directory>
+        <includes>
+          <include>version.properties</include>
+        </includes>
+      </resource>
+      <resource>
+        <targetPath>${basedir}/target/docker-stage/opt/app/dbc-client/misc</targetPath>
+        <directory>${basedir}/misc</directory>
+        <includes>
+          <include>cert-client-init.sh</include>
+          <include>aaf-ca.crt</include>
+        </includes>
+      </resource>
+    </resources>
   </build>
   <profiles>
-      <profile>
-        <id>docker</id>
-        <properties>
-            <skipDockerBuild>false</skipDockerBuild>
-            <skipDockerTag>false</skipDockerTag>
-            <skipTests>true</skipTests>
-        </properties>
-        <build>
-            <!-- Copy files to docker-stage to be included in image -->        
-            <resources>
-                <resource>
-                    <targetPath>${basedir}/target/docker-stage</targetPath>
-                    <directory>${basedir}/src/main/resources</directory>
-                        <includes>
-                            <include>Dockerfile</include>
-                        </includes>
-                </resource>
-                 <resource>
-                    <targetPath>${basedir}/target/docker-stage/opt/app/dbc-client/etc</targetPath>
-                    <directory>${multiproject.basedir}/certs</directory>
-                        <includes>
-                          <include>org.onap.dmaap-bc.cred.props</include>
-                          <include>org.onap.dmaap-bc.crontab.sh</include>
-                          <include>org.onap.dmaap-bc.jks</include>
-                          <include>org.onap.dmaap-bc.keyfile</include>
-                          <include>org.onap.dmaap-bc.location.props</include>
-                          <include>org.onap.dmaap-bc.p12</include>
-                          <include>org.onap.dmaap-bc.props</include>
-                          <include>org.onap.dmaap-bc.showpass</include>
-                          <include>org.onap.dmaap-bc.trust.jks</include>
-                                           <include>ca.pem</include>
-                          <include>client.pem</include>
-                                           <include>key.pem</include>
-                        </includes>
-                </resource>
-
-                 <resource>
-                    <targetPath>${basedir}/target/docker-stage/opt/app/dbc-client/bin</targetPath>
-                    <directory>${basedir}/misc</directory>
-                        <includes>
-                          <include>dbc-client</include>
-                        </includes>
-                </resource>
-                 <resource>
-                    <targetPath>${basedir}/target/docker-stage/opt/app/dbc-client/etc</targetPath>
-                    <directory>${multiproject.basedir}</directory>
-                        <includes>
-                            <include>version.properties</include>
-                        </includes>
-                </resource>
-                 <resource>
-                    <targetPath>${basedir}/target/docker-stage/opt/app/dbc-client/misc</targetPath>
-                    <directory>${multiproject.basedir}/misc</directory>
-                        <includes>
-                            <include>cert-client-init.sh</include>
-                            <include>aaf-ca.crt</include>
-                        </includes>
-                </resource>
-              </resources>
-            <plugins>
-
-                <!-- Copy jar to docker-stage to be included in image -->
-                <plugin>
-                    <artifactId>maven-resources-plugin</artifactId>
-                    <version>2.7</version>
-                    <executions>
-                       <execution>
-                            <id>copy-jar</id>
-                            <phase>package</phase>
-                            <goals>
-                                <goal>copy-resources</goal>
-                            </goals>
-                            <configuration>
-                                <outputDirectory>${basedir}/target/docker-stage/opt/app/dmaapbc/lib</outputDirectory>
-                                <resources>
-                                    <resource>
-                                        <directory>${multiproject.basedir}/dbc-client/target</directory>
-                                        <includes>
-                                            <include>dbc-client.jar</include>
-                                        </includes>
-                                    </resource>
-                                </resources>
-                            </configuration>
-                        </execution>
-                    </executions>
-                </plugin>
-
-                <!-- Setup image tags per https://wiki.onap.org/display/DW/Independent+Versioning+and+Release+Process#IndependentVersioningandReleaseProcess-StandardizedDockerTagging -->
-                <plugin>
-                  <groupId>org.codehaus.groovy.maven</groupId>
-                  <artifactId>gmaven-plugin</artifactId>
-                  <executions>
-                      <execution>
-                          <phase>validate</phase>
-                          <goals>
-                              <goal>execute</goal>
-                          </goals>
-                          <configuration>
-                              <properties>
-                                  <ver>${project.version}</ver>
-                                  <timestamp>${maven.build.timestamp}</timestamp>
-                              </properties>
-                              <source>
-                                  println 'ver: ' + project.properties['ver'];
-                                  if ( project.properties['ver'].endsWith("-SNAPSHOT") ) {
-                                      project.properties['dockertag1']=project.properties['ver'] + "-latest";
-                                      project.properties['dockertag2']=project.properties['ver'] + "-" + project.properties['timestamp'];
-                                  } else {
-                                      project.properties['dockertag1']=project.properties['ver'] + "-STAGING-latest";
-                                      project.properties['dockertag2']=project.properties['ver'] + "-STAGING-" + project.properties['timestamp'];
-                                  }
-                                  println 'docker tag 1: ' + project.properties['dockertag1'];
-                                  println 'docker tag 2: ' + project.properties['dockertag2'];
-                              </source>
-                          </configuration>
-                      </execution>
-                  </executions>
-                </plugin>
-
-                <plugin>
-                    <groupId>io.fabric8</groupId>
-                    <artifactId>docker-maven-plugin</artifactId>
-                    <version>0.28.0</version>  
-                    <configuration>
-                        <verbose>${docker.verbose}</verbose>
-                        <apiVersion>${docker.apiVersion}</apiVersion>
-                        <pullRegistry>${docker.pull.registry}</pullRegistry>
-                        <pushRegistry>${docker.push.registry}</pushRegistry>
-                        <images>
-                            <image>                            
-                                <name>${docker.image}</name>
-                                <build>
-                                    <cleanup>try</cleanup>
-                                    <dockerFileDir>${basedir}/target/docker-stage</dockerFileDir>
-                                    <dockerFile>Dockerfile</dockerFile>
-                                    <tags>
-                                        <tag>${dockertag1}</tag>
-                                        <tag>${dockertag2}</tag>
-                                    </tags>
-                                </build>
-                            </image>
-                        </images>
-                    </configuration>
-                     <executions>
-                         <execution>
-                             <id>generate-images</id>
-                             <phase>install</phase>
-                             <goals>
-                                 <goal>build</goal>
-                             </goals>
-                         </execution>
-                         <execution>
-                             <id>push-images</id>
-                             <phase>deploy</phase>
-                             <goals>
-                                 <goal>push</goal>
-                             </goals>
-                         </execution>
-                     </executions>
-                </plugin>
-            </plugins>
-        </build>
+    <profile>
+      <id>docker</id>
+      <properties>
+        <skipDockerBuild>${skip.docker.build}</skipDockerBuild>
+        <skipDockerTag>${skip.docker.tag}</skipDockerTag>
+        <skipTests>true</skipTests>
+      </properties>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>io.fabric8</groupId>
+            <artifactId>docker-maven-plugin</artifactId>
+            <version>0.28.0</version>
+            <configuration>
+              <verbose>${docker.verbose}</verbose>
+              <apiVersion>${docker.apiVersion}</apiVersion>
+              <pullRegistry>${docker.pull.registry}</pullRegistry>
+              <pushRegistry>${docker.push.registry}</pushRegistry>
+              <images>
+                <image>
+                  <name>${dmaapbcclient.image.name}</name>
+                  <build>
+                    <cleanup>try</cleanup>
+                    <dockerFileDir>${basedir}/target/docker-stage</dockerFileDir>
+                    <dockerFile>Dockerfile</dockerFile>
+                    <tags>
+                      <tag>${dockertag1}</tag>
+                      <tag>${dockertag2}</tag>
+                    </tags>
+                  </build>
+                </image>
+              </images>
+            </configuration>
+            <executions>
+              <execution>
+                <id>generate-images</id>
+                <phase>install</phase>
+                <goals>
+                  <goal>build</goal>
+                </goals>
+              </execution>
+              <execution>
+                <id>push-images</id>
+                <phase>deploy</phase>
+                <goals>
+                  <goal>push</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
     </profile>
   </profiles>
-  <dependencyManagement>
-  </dependencyManagement>
 </project>
index 85f9426..9263e80 100644 (file)
@@ -4,6 +4,7 @@
 #  ===========================================================================
 #  Copyright Â© 2017 AT&T Intellectual Property. All rights reserved.
 #  Modifications Copyright (C) 2018 Nokia. All rights reserved.
+#  Modifications copyright (C) 2021 Nordix Foundation..
 #  ===========================================================================
 #  Licensed under the Apache License, Version 2.0 (the "License");
 #  you may not use this file except in compliance with the License.
@@ -32,24 +33,17 @@ RUN mkdir -p /usr/local/share/ca-certificates && \
     mv misc/aaf-ca.crt /usr/local/share/ca-certificates/aaf-ca.crt
 RUN update-ca-certificates
 
-# Install curl
-RUN apk add --no-cache curl
+# Install curl & openssl
+RUN apk add --no-cache curl openssl
 
-# Install client certs for curl and openssl
-RUN apk add --no-cache openssl
-RUN mkdir -p /opt/app/osaaf/local  && \
-    mv etc/org.onap.dmaap-bc.p12 /opt/app/osaaf/local && \
-    mv etc/*.pem /opt/app/osaaf/local
-
-RUN chmod +x /opt/app/dbc-client/bin/* && \
+RUN mkdir -p /opt/app/osaaf/local && \
+    chmod +x /opt/app/dbc-client/bin/* && \
     mkdir /opt/app/dbc-client/logs
 
-VOLUME /opt/app/dbc-client/log
-
 RUN addgroup -S -g 1001 onap \
-    && adduser -S -u 1000 dbc -G onap \
-    && chown -R dbc:onap /opt/
+    && adduser -S -u 1000 onap -G onap \
+    && chown -R onap:onap /opt/
 
-USER dbc
+USER onap
 
 ENTRYPOINT ["sh", "./bin/dbc-client" ]
diff --git a/dmaap-bc/README.md b/dmaap-bc/README.md
new file mode 100644 (file)
index 0000000..14ebef1
--- /dev/null
@@ -0,0 +1,172 @@
+#
+# ============LICENSE_START==========================================
+# org.onap.dmaap
+# ===================================================================
+# Copyright Â© 2018 AT&T Intellectual Property. All rights reserved.
+# ===================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END============================================
+# ECOMP is a trademark and service mark of AT&T Intellectual Property.
+#
+#
+DMaaP Bus Controller API
+=======================
+
+Data Movement as a Platform (DMaaP) Bus Controller provides an API for other ONAP infrastructure components to provision DMaaP resources.
+A typical DMaaP resource is a Data Router Feed or a Message Router Topic, and their associated publishers and subscribers.
+Other infrastucture resources such as DR Nodes and MR Clusters are also provisioned through this API.
+
+### Build Instructions for a Continuous Integration environment using Jenkins
+
+When this component is included in a Continuous Integration environment, such as structured by the Linux Foundation, the artifacts can be created and deployed via Jenkins.  The following maven targets are currently supported in the Build step:
+```
+clean install
+javadoc:javadoc
+sonar:sonar
+```
+
+### Build Instructions for external developers
+
+This project is organized as a mvn project for a jar package.
+After cloning from this git repo:
+
+```
+mvn clean install javadoc:javadoc
+```
+
+A description of the API is generated, and found in targets/generated-source/swagger.json.
+
+### Properties File
+
+This section is intended to describe the behavior customization of Bus Controller that can be obtained via properties file used by the dbcapi library.
+By default, this file is located in etc/dmaapbc.properties.
+However, a java argument -DConfigFile  can be set to a different path.  (Our kubernetes deployment relies on this and points to a configmap, for example.)
+
+The table below lists all the settings, default values (if not set), and shows any explicit setting in ONAP oom kubernetes deployment.
+
+|-|-|-|-|
+| Property | Description | Default | ONAP Kubernetes Setting | 
+|-|-|-|-|
+|UseAAF                | Flag for whether AAF authz API is to be used            | false                                  | false |
+|-|-|-|-|
+|csit                  | Flag for stubbing out many southbound calls in a CSIT environment | No                           | No |
+|-|-|-|-|
+|DR.provhost           | FQDN of Data Router Provisioning Server (deprecated - now set via API) | notSet                  | dcae-drps.domain.not.set |
+|-|-|-|-|
+|ProvisioningURI       | URI to retrieve dynamic DR configuration                | /internal/prov                         | /internal/prov |
+|-|-|-|-|
+|Feed.deleteHandling   | indicator for handling feed delete request              | DeleteOnDR                             | SimulateDelete |
+|                      | DeleteOnDR - means use the DR API to DELETE a feed.  (default for backwards compatibility) | | |
+|                      | SimulateDelete - means preserve the feed on DR (after cleaning it up), and mark as DELETED in DBCL. | | |
+|-|-|-|-|
+|UsePGSQL              | flag indicates whether to retain data in Postgresql     | false                                  | true |
+|                      | when false, objects will be kept in memory but will be  |                                        | |
+|                      | lost on restart and not shared between instances        |                                        | |
+|-|-|-|-|
+|DB.host               | FQDN or service name of Postresql host                  | dcae-pstg-write-ftl.domain.notset.com  | dbc-pg-primary |
+|-|-|-|-|
+|DB.name               | name of Postresql database                              | dmaap                                  | |
+|-|-|-|-|
+|DB.schema             | name of database schema                                 | public                                 | |
+|-|-|-|-|
+|DB.user               | username for Postgresql access                          | dmaap_admin                            | |
+|-|-|-|-|
+|DB.cred               | password for Postrgresql access                         | test234-ftl                            | onapdemodb |
+|-|-|-|-|
+|MR.multisite          | Indicates if there can be multiple sites (locations) where MR is deployed | true                 | false |
+|-|-|-|-|
+|MR.CentralCname       |  FQDN or service name of MR (deployed in central if multilocation is true) | MRcname.not.set     | message-router |
+|-|-|-|-|
+|MR ClientDeleteLevel  | MR Client Delete thoroughness                           | 0                                      |  1 |
+|                      | 0 = don't delete | | |
+|                      | 1 = delete from persistent store (PG) | | |
+|                      | 2 = delete from persistent store (PG) and authorization store (AAF) | | |
+|-|-|-|-|
+|MR.TopicFactoryNS     | AAF namespace used to create perms for MR topics        | MR.topicFactoryNS.not.set              | org.onap.dmaap.mr.topicFactory |
+|-|-|-|-|
+|MR.TopicMgrRole       | AAF Role used by Buscontroller to create topics on MR   | MR.TopicMgrRole.not.set                | org.onap.dmaap-bc-topic-mgr.client |
+|-|-|-|-|
+|MR.projectID          | Value for some constructs of fully qualified topic names | 99999                                 | ONAP |
+|-|-|-|-|
+|MR.hostnameVerify     | Indicates if we want to relax hostname verification on SSL connection  | true                                 | false |
+|-|-|-|-|
+|MR.authentication     | Authentication method used when connecting to MR | none                                 | basicAuth |
+|                      | none = no creds sent (default)                   |  |  |
+|                      | basicAuth = formulate Basic Auth HTTP Header using name and pwd credentials |     |  |
+|                      | cert = use client certificate                    |  |  |
+|-|-|-|-|
+|cadi.properties       | Path to CADI properties file                            | /opt/app/osaaf/local/org.onap.dmaap-bc.props | /opt/app/osaaf/lcoal/org.onap.dmaap-bc.props |
+|-|-|-|-|
+|aaf.URL               | URL of the AAF server                                   | https://authentication.domain.netset.com:8100/proxy/ | https://aaf-service.onap:8100/ |
+|-|-|-|-|
+|aaf.TopicMgrUser      | AAF Identity of Topic Mgr                               | noMechId@domain.netset.com             | dmaap-bc-topic-mgr@dmaap-bc-topic-mgr.onap.org | 
+|-|-|-|-|
+|aaf.TopicMgrPassword  | AAF Credential for Topic Mgr                            | notSet                                 | demo123456! |
+|-|-|-|-|
+|aaf.AdminUser         | AAF Identity of user with Admin role for API namespace  | noMechId@domain.netset.com             | aaf_admin@people.osaaf.org |
+|-|-|-|-|
+|aaf.AdminPassword     | AAF credential of AdminUser                             | notSet                                 | demo123456! |
+|-|-|-|-|
+|aaf.NsOwnerIdentity     | AAF Identity to be used as topic Namespace owner      | notSet                                 | aaf_admin@people.osaaf.org |
+|topicNsRoot           | AAF namespace value used to create FQTN                 | org.onap.dcae.dmaap                    | org.onap.dcae.dmaap | 
+|-|-|-|-|
+|CredentialCodeKeyfile | location of the codec keyfile used to decrypt passwords | LocalKey                               | etc/LocalKey |
+|                      | in this properties file before they are passed to AAF   | LocalKey                               | etc/LocalKey |
+|-|-|-|-|
+|AafDecryption.Class   | Specifies the Class to be used for decryption           | org.onap.dmaap.dbcapi.aaf.ClearDecrypt | |
+|-|-|-|-|
+|ApiNamespace          | Root namespace for AAF perms related to dbcapi access   | apiNamespace.not.set                   | org.onap.dmaap-bc.api |
+|-|-|-|-|
+|ApiPermission.Class   | the Class that determines if a call to API is authorized| allow                                  | | 
+|-|-|-|-|
+|MM.ProvRole           | AAF Role of client publishing MM prov cmds              | notSet                                 | org.onap.dmaap-bc-mm-prov.prov |
+|-|-|-|-|
+|MM.ProvUserMechId     | AAF Identity when publishing to MM command topic        | notSet                                 | dmaap-bc-mm-prov@dmaap-bc-mm-prov.onap.org|
+|-|-|-|-|
+|MM.ProvUserPwd        | AAF credenital for ProvUserMechId                       | notSet                                 | demo123456! | 
+|-|-|-|-|
+|MM.AgentRole          | AAF Role of client susbcribing to MM command topic      | notSet                                 | org.onal.dmaap-bc-mm-prov.agent |
+|-|-|-|-|
+|DR.provApi            | Version name of DR API (ONAP or AT&T)                   | ONAP                                   | ONAP |
+|-|-|-|-|
+|DR.onBehalfHeader     | String for "On Behalf Of" HTTP Header in DR API         | X-DR-ON-BEHALF-OF                      | X-DR-ON-BEHALF-OF |
+|-|-|-|-|
+|DR.feedContentType    | Value for Content-Type Header in DR Feed API            | application/vnd.dr.feed                | application/vnd.dr.feed |
+|-|-|-|-|
+|DR subContentType     | Value for Content-Type Header in DR Subscription API    | application/vnd.dr.subscription        | application/vnd.dr.subscription |
+|-|-|-|-|
+|HttpAllowed           | flag indicating whether http is supported               | false                                  | true |
+|-|-|-|-|
+|IntHttpPort           | Internal port for http service                          | 80                                     | 8080 |
+|-|-|-|-|
+|IntHttpsPort          | Internal port for https service (0 if no cert is avail) | 443                                    | 8443 |
+|-|-|-|-|
+|ExtHttpsPort          | Externally advertised port for https service (deprecated)| 443                                   | 443 |
+|-|-|-|-|
+|KeyStoreType          | Format of Java keystore                                 | jks                                    | jks |
+|-|-|-|-|
+|KeyStoreFile          | Path to java keystore                                   | etc/keystore                           | etc/keystore |
+|-|-|-|-|
+|KeyStorePassword      | Password for keystore                                   | changeit                               | <provided by Certificate Authority> |
+|-|-|-|-|
+|KeyPassword           | Password for private key in the https keystore          | changeit                               | <provided by Certificate Authority> |
+|-|-|-|-|
+|TrustStoreType        | Format of Trust Store file                              | jks                                    | jks |
+|-|-|-|-|
+|TrustStoreFile        | Path to Trust Store file                                |                                        | etc/org.onap.dmaap-bc.trust.jks |
+|-|-|-|-|
+|TrustStorePassword    | Password for Trust Store                                |                                        | <provided by Certificate Authority> |
+|-|-|-|-|
+|QuiesceFile           | Path to file which signals needs to queiesce            |                                        | etc/SHUTDOWN |
+|-|-|-|-|
+
diff --git a/dmaap-bc/misc/dbc-api.jks b/dmaap-bc/misc/dbc-api.jks
deleted file mode 100644 (file)
index 66142d3..0000000
Binary files a/dmaap-bc/misc/dbc-api.jks and /dev/null differ
diff --git a/dmaap-bc/misc/opensource.env b/dmaap-bc/misc/opensource.env
deleted file mode 100644 (file)
index a74d333..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-#
-#
-# ============LICENSE_START==========================================
-# org.onap.dmaap
-# ===================================================================
-# Copyright Â© 2018 AT&T Intellectual Property. All rights reserved.
-# ===================================================================
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ============LICENSE_END============================================
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
-#
-#      The Controller domain
-#
-CONT_DOMAIN=simpledemo.onap.org
-#
-#   The https port
-#   set to 0 if certificate is not ready
-DMAAPBC_INT_HTTPS_PORT=0
-
-#
-#      The path to the keystore for https
-#
-DMAAPBC_KSTOREFILE=/opt/app/dcae-certificates
-
-#      The password for the https keystore
-#
-DMAAPBC_KSTOREPASS=foofoofoo
-#
-#      The password for the private key in the https keystore
-#
-DMAAPBC_PVTKEYPASS=barbarbar
-#
-#      Flag for whether we are using PG connection for persistence
-#
-DMAAPBC_PG_ENABLED=false
-#
-#      The host for postgres access
-#
-DMAAPBC_PGHOST=zldciad1vipstg00.${CONT_DOMAIN}
-#
-#      For postgres access
-#
-DMAAPBC_PGCRED=test234-ftl
-#
-#      Name of this environment
-#
-DMAAPBC_INSTANCE_NAME=onap1
-#
-#      Name of DR prov server
-#
-DMAAPBC_DRPROV_FQDN=dcae-drps.${CONT_DOMAIN}
-
-#################
-# AAF Properties:
-#
-# regarding password encryption:
-# In the dependencies that Maven retrieves (e.g., under dcae_dmaapbc/target/deps/ is a jar file cadi-core-version.jar.  Generate the key file with:
-#
-# java \u2013jar wherever/cadi-core-*.jar keygen keyfilename
-# chmod 400 keyfilename
-#
-# To encrypt a key:
-#
-# java \u2013jar wherever/cadi-core-*.jar digest password-to-encrypt keyfilename
-#
-# This will generate a string.  Put \u201Cenc:\u201D on the front of the string, and put the result in this properties file.
-#
-# Location of the Codec Keyfile which is used to decrypt passwords in this properties file before they are passed to AAF
-#
-# REF: https://wiki.domain.notset.com/display/cadi/CADI+Deployment
-#
-# URL of AAF environment to use.
-#
-DMAAPBC_AAF_URL=https://aafapi.${CONT_DOMAIN}:8100/proxy/
-#
-# TopicMgr mechid@namespace
-#
-DMAAPBC_TOPICMGR_USER=m99751@dmaapBC.onap.org
-#
-# TopicMgr password
-# 
-DMAAPBC_TOPICMGR_PWD=enc:zyRL9zbI0py3rJAjMS0dFOnYfEw_mJhO
-#
-# Bus Controller Namespace Admin  mechid@namespace
-#
-DMAAPBC_ADMIN_USER=m99501@dcae.onap.org
-#
-# Bus Controller Namespace Admin password
-#
-DMAAPBC_ADMIN_PWD=enc:YEaHwOJrwhDY8a6usetlhbB9mEjUq9m
-
-#
-# endof AAF Properties
-#################
-
-#################
-# PolicyEngine Properties
-
-#
-# Flag to turn on/off Authentication
-DMAAPBC_PE_ENABLED=false
-#
-# Argument to decisionAttributes.put("AAF_ENVIRONMENT", X); 
-# where X is:  TEST= UAT, PROD = PROD, DEVL = TEST
-#
-DMAAPBC_PE_AAF_ENV=DEMO
-
-# endof PolicyEngineProperties
-#################
diff --git a/dmaap-bc/misc/policyLogger.properties b/dmaap-bc/misc/policyLogger.properties
deleted file mode 100644 (file)
index 6b9ad99..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# ============LICENSE_START==========================================
-# org.onap.dmaap
-# ===================================================================
-# Copyright Â© 2018 AT&T Intellectual Property. All rights reserved.
-# ===================================================================
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ============LICENSE_END============================================
-# ECOMP is a trademark and service mark of AT&T Intellectual Property.
-#
-#
-################################### Set concurrentHashMap and timer info  #######################
-#Timer initial delay and the delay between in milliseconds before task is to be execute.
-timer.delay.time=1000
-#Timer scheduleAtFixedRate period - time in milliseconds between successive task executions.
-check.interval= 30000
-#Longest time an event info can be stored in the concurrentHashMap for logging - in seconds. 
-event.expired.time=86400
-#Size of the concurrentHashMap which stores the event starting time, etc - when its size reaches this limit, the Timer gets executed 
-#to remove all expired records from this concurrentHashMap.
-concurrentHashMap.limit=5000
-#Size of the concurrentHashMap - when its size drops to this point, stop the Timer
-stop.check.point=2500
-################################### Set logging format #############################################
-# set EELF for EELF logging format, set LOG4J for using log4j, set SYSTEMOUT for using system.out.println
-logger.type=EELF
-#################################### Set level for EELF or SYSTEMOUT logging ##################################
-# Set level for debug file. Set DEBUG to enable .info, .warn and .debug; set INFO for enable .info and .warn; set OFF to disable all 
-debugLogger.level=OFF
-# Set level for metrics file. Set OFF to disable; set ON to enable
-metricsLogger.level=ON
-# Set level for error file. Set OFF to disable; set ON to enable
-error.level=ON
-# Set level for audit file. Set OFF to disable; set ON to enable
-audit.level=ON
index ded2cc2..e141100 100644 (file)
   ============LICENSE_END============================================
   ECOMP is a trademark and service mark of AT&T Intellectual Property.
 -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <artifactId>dmaap-bc</artifactId>
   <version>${revision}</version>
   <name>dmaap-bc</name>
+  <packaging>jar</packaging>
   <parent>
     <groupId>org.onap.dmaap.buscontroller</groupId>
     <artifactId>parent</artifactId>
     <version>${revision}</version>
     <relativePath>../pom.xml</relativePath>
   </parent>
-  <build>
-    <finalName>dmaap-bc</finalName>
-
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-enforcer-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>enforce-no-snapshots</id>
-            <goals>
-              <goal>enforce</goal>
-            </goals>
-            <configuration>
-              <rules>
-                <requireReleaseDeps>
-                  <message>No Snapshots Allowed!</message>
-                  <excludes>
-<!-- for example, these might be needed...
-                    <exclude>org.onap.dmaap.dbcapi:dbcapi</exclude>
-                    <exclude>org.onap.aaf.authz:aaf-cadi-client</exclude>
-                    <exclude>org.onap.aaf.authz:aaf-misc-env</exclude>
-                    <exclude>org.onap.aaf.authz:aaf-cadi-aaf</exclude>
-                    <exclude>org.onap.aaf.authz:aaf-auth-client</exclude>
-                    <exclude>org.onap.aaf.authz:aaf-cadi-core</exclude>
-                    <exclude>org.onap.aaf.authz:aaf-misc-rosetta</exclude>
--->
-                  </excludes>
-                </requireReleaseDeps>
-              </rules>
-              <fail>true</fail>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-
-      <!-- Package an Uber jar -->
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-shade-plugin</artifactId>
-        <version>2.4.3</version>
-        <executions>
-          <!-- Run shade goal on package phase -->
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>shade</goal>
-            </goals>
-            <configuration>
-              <createDependencyReducedPom>false</createDependencyReducedPom>
-              <!-- this filter section is needed to avoid runtime error:
-                java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
-                suggestion found at: https://stackoverflow.com/q/999489
-              -->
-              <filters>
-                <filter>
-                  <artifact>*:*</artifact>
-                  <excludes>
-                    <exclude>META-INF/*.SF</exclude>
-                    <exclude>META-INF/*.DSA</exclude>
-                    <exclude>META-INF/*.RSA</exclude>
-                  </excludes>
-                </filter>
-              </filters>
-              <transformers>
-                <!-- NOTE: Need the following transformer else gets "Could not resolve type id 'https' into a subtype" error
-                Solution found from here:
-                http://stackoverflow.com/questions/27543060/why-does-dropwizard-configuration-is-not-working
-                Some more context here:
-                https://github.com/dropwizard/dropwizard/issues/455 -->
-                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
-                <!-- add Main-Class to manifest file -->
-                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
-                  <mainClass>org.onap.dmaap.dbcapi.server.Main</mainClass>
-                </transformer>
-              </transformers>
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-
-      <!-- for Distribution management -->
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-site-plugin</artifactId>
-        <dependencies>
-          <dependency>
-            <groupId>org.apache.maven.wagon</groupId>
-            <artifactId>wagon-webdav-jackrabbit</artifactId>
-            <version>2.10</version>
-          </dependency>
-        </dependencies>
-      </plugin>
-
-    </plugins>
-    <pluginManagement>
-      <plugins>
-        <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
-        <plugin>
-          <groupId>org.eclipse.m2e</groupId>
-          <artifactId>lifecycle-mapping</artifactId>
-          <version>1.0.0</version>
-          <configuration>
-            <lifecycleMappingMetadata>
-              <pluginExecutions>
-                <pluginExecution>
-                  <pluginExecutionFilter>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-dependency-plugin</artifactId>
-                    <versionRange>[2.10,)</versionRange>
-                    <phase>install</phase>
-                    <goals>
-                      <goal>copy-dependencies</goal>
-                    </goals>
-                  </pluginExecutionFilter>
-                  <action>
-                    <ignore/>
-                  </action>
-                </pluginExecution>
-              </pluginExecutions>
-            </lifecycleMappingMetadata>
-          </configuration>
-        </plugin>
-      </plugins>
-    </pluginManagement>
-  </build>
-  <profiles>
-      <profile>
-        <id>docker</id>
-        <properties>
-            <skipDockerBuild>false</skipDockerBuild>
-            <skipDockerTag>false</skipDockerTag>
-            <skipTests>true</skipTests>
-        </properties>
-        <build>
-            <!-- Copy files to docker-stage to be included in image -->        
-            <resources>
-                <resource>
-                    <targetPath>${basedir}/target/docker-stage</targetPath>
-                    <directory>${basedir}/src/main/resources</directory>
-                        <includes>
-                            <include>Dockerfile</include>
-                        </includes>
-                </resource>
-                 <resource>
-                    <targetPath>${basedir}/target/docker-stage/opt/app/dmaapbc/etc</targetPath>
-                    <directory>${basedir}/misc</directory>
-                        <includes>
-                          <include>LocalKey</include>
-                          <include>logback.xml</include>
-                        </includes>
-                </resource>
-
-                 <resource>
-                    <targetPath>${basedir}/target/docker-stage/opt/app/dmaapbc/etc</targetPath>
-                    <directory>${multiproject.basedir}</directory>
-                        <includes>
-                            <include>version.properties</include>
-                        </includes>
-                </resource>
-                 <resource>
-                    <targetPath>${basedir}/target/docker-stage/opt/app/dmaapbc/misc</targetPath>
-                    <directory>${basedir}/misc</directory>
-                        <includes>
-                            <include>opensource.env</include>
-                            <include>*.tmpl</include>
-                        </includes>
-                </resource>
-                 <resource>
-                    <targetPath>${basedir}/target/docker-stage/opt/app/dmaapbc/bin</targetPath>
-                    <directory>${basedir}/misc</directory>
-                        <includes>
-                            <include>dmaapbc</include>
-                        </includes>
-                </resource>
-              </resources>
-            <plugins>
-              <!-- Copy jar to docker-stage to be included in image -->
-              <plugin>
-                    <artifactId>maven-resources-plugin</artifactId>
-                    <version>2.7</version>
-                    <executions>
-                       <execution>
-                            <id>copy-jar</id>
-                            <phase>package</phase>
-                            <goals>
-                                <goal>copy-resources</goal>
-                            </goals>
-                            <configuration>
-                                <outputDirectory>${basedir}/target/docker-stage/opt/app/dmaapbc/lib</outputDirectory>
-                                <resources>
-                                    <resource>
-                                        <directory>${multiproject.basedir}/dmaap-bc/target</directory>
-                                        <includes>
-                                            <include>dmaap-bc.jar</include>
-                                        </includes>
-                                    </resource>
-                                </resources>
-                            </configuration>
-                        </execution>
-                    </executions>
-                </plugin>
-
-                <!-- Setup image tags per https://wiki.onap.org/display/DW/Independent+Versioning+and+Release+Process#IndependentVersioningandReleaseProcess-StandardizedDockerTagging -->
-                <plugin>
-                  <groupId>org.codehaus.groovy.maven</groupId>
-                  <artifactId>gmaven-plugin</artifactId>
-                  <executions>
-                      <execution>
-                          <phase>validate</phase>
-                          <goals>
-                              <goal>execute</goal>
-                          </goals>
-                          <configuration>
-                              <properties>
-                                  <ver>${project.version}</ver>
-                                  <timestamp>${maven.build.timestamp}</timestamp>
-                              </properties>
-                              <source>
-                                  println 'ver: ' + project.properties['ver'];
-                                  if ( project.properties['ver'].endsWith("-SNAPSHOT") ) {
-                                      project.properties['dockertag1']=project.properties['ver'] + "-latest";
-                                      project.properties['dockertag2']=project.properties['ver'] + "-" + project.properties['timestamp'];
-                                  } else {
-                                      project.properties['dockertag1']=project.properties['ver'] + "-STAGING-latest";
-                                      project.properties['dockertag2']=project.properties['ver'] + "-STAGING-" + project.properties['timestamp'];
-                                  }
-                                  println 'docker tag 1: ' + project.properties['dockertag1'];
-                                  println 'docker tag 2: ' + project.properties['dockertag2'];
-                              </source>
-                          </configuration>
-                      </execution>
-                  </executions>
-                </plugin>
+  <description>Data Movement as a Platform (DMaaP) Bus Controller provides a REST API for other
+    DCAE infrastructure components to provision DMaaP resources. A DMaaP resource is a Data
+    Router Feed or a Message Router Topic, and their associated publishers and subscribers.
+  </description>
+  <properties>
+    <sitePath>/content/sites/site/${project.groupId}/${project.artifactId}/${project.version}
+    </sitePath>
+    <eelf.version>1.0.0</eelf.version>
+    <dmaapbc.image.name>${docker.image.root}${project.artifactId}</dmaapbc.image.name>
+    <swagger.version>1.5.19</swagger.version>
+    <jackson.version>2.9.5</jackson.version>
+    <jersey.version>2.29</jersey.version>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <jettyVersion>9.4.40.v20210413</jettyVersion>
+    <eelf.version>1.0.0</eelf.version>
+    <junit.version>4.12</junit.version>
+    <sonar.exclusions>**/gen/**,**/generated-sources/**,**/yang-gen**,**/pax/**</sonar.exclusions>
+    <docker-stage.target.path>/target/docker-stage/opt/app/dmaapbc/</docker-stage.target.path>
+  </properties>
 
-               <plugin>
-                    <groupId>io.fabric8</groupId>
-                    <artifactId>docker-maven-plugin</artifactId>
-                    <version>0.28.0</version>  
-                    <configuration>
-                        <verbose>${docker.verbose}</verbose>
-                        <apiVersion>${docker.apiVersion}</apiVersion>
-                        <pullRegistry>${docker.pull.registry}</pullRegistry>
-                        <pushRegistry>${docker.push.registry}</pushRegistry>
-                        <images>
-                            <image>                            
-                                <name>${docker.image}</name>
-                                <build>
-                                    <cleanup>try</cleanup>
-                                    <dockerFileDir>${basedir}/target/docker-stage</dockerFileDir>
-                                    <dockerFile>Dockerfile</dockerFile>
-                                    <tags>
-                                        <tag>${dockertag1}</tag>
-                                        <tag>${dockertag2}</tag>
-                                    </tags>
-                                </build>
-                            </image>
-                        </images>
-                    </configuration>
-                     <executions>
-                         <execution>
-                             <id>generate-images</id>
-                             <phase>install</phase>
-                             <goals>
-                                 <goal>build</goal>
-                             </goals>
-                         </execution>
-                         <execution>
-                             <id>push-images</id>
-                             <phase>deploy</phase>
-                             <goals>
-                                 <goal>push</goal>
-                             </goals>
-                         </execution>
-                     </executions>
-                </plugin>
-            </plugins>
-        </build>
-    </profile>
-  </profiles>
   <dependencyManagement>
     <dependencies>
       <dependency>
       </dependency>
     </dependencies>
   </dependencyManagement>
+
   <dependencies>
+    <dependency>
+      <groupId>org.glassfish.jersey.media</groupId>
+      <artifactId>jersey-media-json-jackson</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.containers</groupId>
+      <artifactId>jersey-container-servlet-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.media</groupId>
+      <artifactId>jersey-media-moxy</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+      <!-- use this if compatibility issues with jetty artifactId:
+      <artifactId>jersey-test-framework-provider-jetty</artifactId>
+          <version>${jersey.version}</version>
+      -->
+      <artifactId>jersey-test-framework-provider-jdk-http</artifactId>
+      <version>2.29.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.jersey.inject</groupId>
+      <artifactId>jersey-hk2</artifactId>
+      <version>2.29.1</version>
+    </dependency>
     <dependency>
       <groupId>org.onap.aaf.authz</groupId>
       <artifactId>aaf-cadi-aaf</artifactId>
     <dependency>
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-annotations</artifactId>
-      <version>2.9.5</version>
+      <version>${jackson.version}</version>
     </dependency>
     <dependency>
       <groupId>com.fasterxml.jackson.dataformat</groupId>
       <artifactId>jackson-dataformat-yaml</artifactId>
-      <version>2.9.5</version>
+      <version>${jackson.version}</version>
     </dependency>
     <dependency>
       <groupId>ch.qos.logback</groupId>
       <artifactId>logback-classic</artifactId>
       <version>1.2.3</version>
     </dependency>
+    <!-- DMAAP-656:
+       - override this dependency because it utilized a third party
+       - lib called com.google.guava:20.0 which had severe security threat identified.
+     -->
     <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
       <groupId>io.swagger</groupId>
       <artifactId>swagger-core</artifactId>
       <version>${swagger.version}</version>
+      <exclusions>
+        <exclusion>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
       <groupId>io.swagger</groupId>
       <artifactId>swagger-annotations</artifactId>
       <version>${swagger.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.glassfish.jersey.containers</groupId>
-      <artifactId>jersey-container-servlet-core</artifactId>
-      <!-- use the following artifactId if you don't need servlet 2.x compatibility -->
-      <!-- artifactId>jersey-container-servlet</artifactId -->
-    </dependency>
-    <dependency>
-      <groupId>org.glassfish.jersey.media</groupId>
-      <artifactId>jersey-media-moxy</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-api</artifactId>
-      <version>${log4j.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-core</artifactId>
-      <version>${log4j.version}</version>
-    </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-server</artifactId>
       <version>${jettyVersion}</version>
+      <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>postgresql</artifactId>
       <version>42.2.14</version>
     </dependency>
-    <dependency>
-      <groupId>org.onap.dmaap.dbcapi</groupId>
-      <artifactId>dbcapi</artifactId>
-      <version>2.0.4</version>
-    </dependency>
-    <dependency>
-      <groupId>com.att.eelf</groupId>
-      <artifactId>eelf-core</artifactId>
-      <version>${eelf.version}</version>
-    </dependency>
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
       <version>1.7.22</version>
     </dependency>
-    <dependency>
-      <groupId>org.glassfish.jersey.test-framework.providers</groupId>
-      <!-- use this if compatibility issues with jetty artifactId:
-      <artifactId>jersey-test-framework-provider-jetty</artifactId>
-          <version>${jersey.version}</version>
-      -->
-      <artifactId>jersey-test-framework-provider-jdk-http</artifactId>
-      <version>2.29.1</version>
-    </dependency>
-    <dependency>
-      <groupId>org.glassfish.jersey.inject</groupId>
-      <artifactId>jersey-hk2</artifactId>
-      <version>2.29.1</version>
-    </dependency>
     <dependency>
       <!-- use 2.3.1 to avoid this issue: https://github.com/eclipse-ee4j/jaxb-ri/issues/1222 -->
       <groupId>javax.xml.bind</groupId>
       <artifactId>jaxb-api</artifactId>
       <version>2.3.1</version>
     </dependency>
-    <dependency>
-      <groupId>javax.activation</groupId>
-      <artifactId>javax.activation-api</artifactId>
-      <version>1.2.0</version>
-    </dependency>
-
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
-      <version>4.12</version>
+      <version>${junit.version}</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <version>1.1.0</version>
       <scope>test</scope>
     </dependency>
+    <!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <version>3.9.0</version>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-enforcer-plugin</artifactId>
       <version>3.0.0-M3</version>
     </dependency>
+    <dependency>
+      <groupId>com.att.eelf</groupId>
+      <artifactId>eelf-core</artifactId>
+      <version>${eelf.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.mockito</groupId>
+          <artifactId>mockito-core</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.powermock</groupId>
+          <artifactId>powermock-module-junit4</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.powermock</groupId>
+          <artifactId>powermock-api-mockito</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
   </dependencies>
+  <build>
+    <finalName>dmaap-bc</finalName>
+    <!-- Copy files to docker-stage to be included in image -->
+    <resources>
+      <resource>
+        <targetPath>${basedir}/target/docker-stage</targetPath>
+        <directory>${basedir}/src/main/resources/docker</directory>
+        <includes>
+          <include>Dockerfile</include>
+        </includes>
+      </resource>
+      <resource>
+        <targetPath>${basedir}${docker-stage.target.path}etc</targetPath>
+        <directory>${basedir}/src/main/resources/misc</directory>
+        <includes>
+          <include>logback.xml</include>
+          <include>LocalKey</include>
+        </includes>
+      </resource>
+      <resource>
+        <targetPath>${basedir}${docker-stage.target.path}etc</targetPath>
+        <directory>${multiproject.basedir}</directory>
+        <includes>
+          <include>version.properties</include>
+        </includes>
+      </resource>
+      <resource>
+        <targetPath>${basedir}${docker-stage.target.path}misc</targetPath>
+        <directory>${basedir}/src/main/resources/misc</directory>
+        <includes>
+          <include>*.tmpl</include>
+        </includes>
+      </resource>
+      <resource>
+        <targetPath>${basedir}${docker-stage.target.path}bin</targetPath>
+        <directory>${basedir}/src/main/resources/misc</directory>
+        <includes>
+          <include>dmaapbc</include>
+        </includes>
+      </resource>
+      <resource>
+        <targetPath>${basedir}${docker-stage.target.path}misc</targetPath>
+        <directory>${basedir}/src/main/resources/misc</directory>
+        <includes>
+          <include>schema_all.sql</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <version>2.4</version>
+        <configuration>
+          <descriptorRefs>
+            <descriptorRef>jar-with-dependencies</descriptorRef>
+          </descriptorRefs>
+          <outputDirectory>${basedir}/target/docker-stage/opt/app/dmaapbc/lib</outputDirectory>
+          <archive>
+            <manifest>
+              <addClasspath>true</addClasspath>
+              <mainClass>org.onap.dmaap.dbcapi.server.Main</mainClass>
+            </manifest>
+          </archive>
+          <finalName>dmaap-bc.jar</finalName>
+          <appendAssemblyId>false</appendAssemblyId>
+        </configuration>
+        <executions>
+          <execution>
+            <id>make-assembly</id>
+            <!-- this is used for inheritance merges -->
+            <phase>package</phase>
+            <!-- bind to the packaging phase -->
+            <goals>
+              <goal>single</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>onap-java-style</id>
+            <configuration>
+              <consoleOutput>false</consoleOutput>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <!-- reference: https://tech.homeaway.com/development/2016/06/02/generating-swagger-spec.html -->
+      <plugin>
+        <groupId>com.github.kongchen</groupId>
+        <artifactId>swagger-maven-plugin</artifactId>
+        <version>3.1.5</version>
+        <configuration>
+          <apiSources>
+            <apiSource>
+              <springmvc>false</springmvc>
+              <locations>
+                <location>org.onap.dmaap.dbcapi.resources</location>
+              </locations>
+              <schemes>
+                <scheme>http</scheme>
+                <scheme>https</scheme>
+              </schemes>
+              <host>www.[host]:[port]</host>
+              <basePath>/webapi</basePath>
+              <info>
+                <title>DMaaP Bus Controller REST API</title>
+                <version>1.1.0</version>
+                <description>
+                  provides an API for OpenDCAE components which need to provision
+                  underlying DMaaP technologies (Data Router and Message Router).
+                  Primary clients for this API are anticipated to be the OpenDCAE
+                  Controller, OpenDCAE Orchestrator, OpenDCAE Inventory and the
+                  ECOMP Portal.
+
+                  Objects managed by DMaaP are deployed in a dcaeLocation which is
+                  a unique identifier for an OpenStack tenant for a dcaeLayer,
+                  opendcae-central (aka ecomp) or opendcae-local-ntc (aka edge).
+
+                  A dcaeEnvironment (e.g. FTL or prod) has a single DMaaP. A
+                  DMaaP is managed by a one or more stateless DMaaP Bus
+                  Controller(s), though Bus Controller relies on PGaaS for
+                  persistence. Each DMaaP has a single instance of Data Router,
+                  which has 1 or more DR_Nodes deployed at each dcaeLocation. DR
+                  Clients of type DR_Pub generally publish to a DR_Node that is
+                  local to its dcaeLocation. Routing for a Feed is determined by
+                  the dcaelocation of its DR_Sub clients.
+
+                  A DMaaP may have many Message Router instances. Each instance is
+                  deployed as an MR_Cluster. One MR_Cluster is deployed at each
+                  dcaeLocation. MR_Clients generally communicate to the
+                  MR_Cluster at the same dcaeLocation. Replication of messages
+                  between MR_Clusters is accomplished by MR Bridge, which is
+                  provioned by DMaaP Bus Controller based on Topic attributes.
+
+                  Therefore, the role of DMaaP Bus Controller is to support other
+                  DCAE infrastructure components to dynamically provision DMaaP
+                  services on behalf of DMaaP clients, and to assist in any
+                  management or discovery activity of its clients.
+
+                  A convention of this API is to return JSON responses per
+                  OpenStack style.
+                </description>
+                <termsOfService>
+                  http://www.apache.org/licenses/LICENSE-2.0
+                </termsOfService>
+                <contact>
+                  <url>http://www.onap.org</url>
+                </contact>
+                <license>
+                  <url>http://www.apache.org/licenses/LICENSE-2.0</url>
+                  <name>Licensed under the Apache License, Version 2.0</name>
+                </license>
+              </info>
+              <swaggerDirectory>target/generated-sources/</swaggerDirectory>
+            </apiSource>
+          </apiSources>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>compile</phase>
+            <goals>
+              <goal>generate</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  <profiles>
+    <profile>
+      <id>docker</id>
+      <properties>
+        <skipDockerBuild>${skip.docker.build}</skipDockerBuild>
+        <skipDockerTag>${skip.docker.tag}</skipDockerTag>
+        <skipTests>false</skipTests>
+      </properties>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>io.fabric8</groupId>
+            <artifactId>docker-maven-plugin</artifactId>
+            <version>0.28.0</version>
+            <configuration>
+              <verbose>${docker.verbose}</verbose>
+              <apiVersion>${docker.apiVersion}</apiVersion>
+              <pullRegistry>${docker.pull.registry}</pullRegistry>
+              <pushRegistry>${docker.push.registry}</pushRegistry>
+              <images>
+                <image>
+                  <name>${dmaapbc.image.name}</name>
+                  <build>
+                    <cleanup>try</cleanup>
+                    <dockerFileDir>${basedir}/target/docker-stage</dockerFileDir>
+                    <dockerFile>Dockerfile</dockerFile>
+                    <tags>
+                      <tag>${dockertag1}</tag>
+                      <tag>${dockertag2}</tag>
+                    </tags>
+                  </build>
+                </image>
+              </images>
+            </configuration>
+            <executions>
+              <execution>
+                <id>generate-images</id>
+                <phase>install</phase>
+                <goals>
+                  <goal>build</goal>
+                </goals>
+              </execution>
+              <execution>
+                <id>push-images</id>
+                <phase>deploy</phase>
+                <goals>
+                  <goal>push</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
   <reporting>
     <plugins>
       <plugin>
       </plugin>
     </plugins>
   </reporting>
-
   <distributionManagement>
     <site>
       <id>ecomp-site</id>
       <url>dav:${nexusproxy}${sitePath}</url>
     </site>
   </distributionManagement>
-  <properties>
-    <multiproject.basedir>${basedir}/..</multiproject.basedir>
-    <docker.maven.plugin.version>1.0.0</docker.maven.plugin.version>
-    <jersey.version>2.29</jersey.version>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    <log4j.version>2.13.3</log4j.version>
-    <jettyVersion>9.4.36.v20210114</jettyVersion>
-    <eelf.version>1.0.0</eelf.version>
-    <swagger.version>1.5.19</swagger.version>
-    <timestamp>${maven.build.timestamp}</timestamp>
-    <maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
-    <!--  SONAR  -->
-    <jacoco.version>0.7.7.201606060606</jacoco.version>
-    <sonar-jacoco-listeners.version>3.2</sonar-jacoco-listeners.version>
-    <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
-    <!-- Default Sonar configuration -->
-    <sonar.jacoco.reportPath>target/code-coverage/jacoco-ut.exec</sonar.jacoco.reportPath>
-    <sonar.jacoco.itReportPath>target/code-coverage/jacoco-it.exec</sonar.jacoco.itReportPath>
-    <!-- Note: This list should match jacoco-maven-plugin's exclusion list below -->
-    <sonar.exclusions>**/gen/**,**/generated-sources/**,**/yang-gen**,**/pax/**</sonar.exclusions>
-
-    <!-- docker image -->
-    <docker.image>onap/dmaap/dmaap-bc</docker.image>
-
-    <nexusproxy>https://nexus.onap.org</nexusproxy>
-    <docker.push.registry>10.12.5.45:5000</docker.push.registry>
-
-    <timestamp>${maven.build.timestamp}</timestamp>
-    <maven.build.timestamp.format>yyyyMMdd'T'HHmmss'Z'</maven.build.timestamp.format>
-
-    <!-- for Distribution Management -->
-    <sitePath>/content/sites/site/org/onap/dmaap/dmaap-bc/${revision}</sitePath>
-  </properties>
-  <description>Packaging Platform (DMaaP) Bus Controller API as a Docker container.</description>
 </project>
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafConnection.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafConnection.java
new file mode 100644 (file)
index 0000000..934e541
--- /dev/null
@@ -0,0 +1,329 @@
+/*-
+ * ============LICENSE_START=======================================================
+  * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+
+
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.net.ConnectException;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import org.apache.commons.codec.binary.Base64;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+
+public class AafConnection extends BaseLoggingClass {
+
+
+
+
+
+       private String aafCred;
+       private String unit_test;
+
+
+       private HttpsURLConnection uc;
+
+
+       public AafConnection( String cred ) {
+               aafCred = cred;
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+        unit_test = p.getProperty( "UnitTest", "No" );
+
+       }
+
+
+       private boolean makeConnection( String pURL ) {
+
+               try {
+                       URL u = new URL( pURL );
+                       uc = (HttpsURLConnection) u.openConnection();
+                       uc.setInstanceFollowRedirects(false);
+                       logger.info( "successful connect to " + pURL );
+                       return(true);
+               } catch ( UnknownHostException uhe ) {                  
+               errorLogger.error(DmaapbcLogMessageEnum.UNKNOWN_HOST_EXCEPTION,  pURL, uhe.getMessage() );
+               logger.error("Error", uhe);
+            return(false);
+               } catch (Exception e) {
+                       logger.error("Error", e);
+               errorLogger.error(DmaapbcLogMessageEnum.HTTP_CONNECTION_ERROR,  pURL, e.getMessage());
+            return(false);
+        }
+
+       }
+       
+       static String bodyToString( InputStream is ) {
+               StringBuilder sb = new StringBuilder();
+               BufferedReader br = new BufferedReader( new InputStreamReader(is));
+               String line;
+               try {
+                       while ((line = br.readLine()) != null ) {
+                               sb.append( line );
+                       }
+               } catch (IOException ex ) {
+                       errorLogger.error( DmaapbcLogMessageEnum.IO_EXCEPTION + ex.getMessage(),ex);
+               }
+
+               return sb.toString();
+       }
+       
+
+
+       public int postAaf( AafObject obj, String pURL ) {
+               logger.info( "entry: postAaf() to  " + pURL  );
+               String auth =  "Basic " + Base64.encodeBase64String(aafCred.getBytes());
+               int rc = -1;
+
+
+               if ( ! makeConnection( pURL ) ) {
+                       return rc;
+               };
+
+
+               byte[] postData = obj.getBytes();
+               //logger.info( "post fields=" + postData );  //byte isn't very readable
+               String responsemessage = null;
+               String responseBody = null;
+
+               try {
+                       if (auth != null) {
+                               uc.setRequestProperty("Authorization", auth);
+               }
+                       uc.setRequestMethod("POST");
+                       uc.setRequestProperty("Content-Type", "application/json");
+                       uc.setRequestProperty( "charset", "utf-8");
+                       uc.setRequestProperty( "Content-Length", Integer.toString( postData.length ));
+                       uc.setUseCaches(false);
+                       uc.setDoOutput(true);
+
+                       SSLContext sc = SSLContext.getInstance("SSL");
+                       sc.init(null, trustAllCerts, new java.security.SecureRandom());
+                       uc.setSSLSocketFactory(sc.getSocketFactory());
+                       OutputStream os = null;
+
+                       
+                       try {
+                 uc.connect();
+                 os = uc.getOutputStream();
+                 os.write( postData );
+
+            } catch (ProtocolException pe) {
+               logger.error("Error", pe);
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                        logger.error("Error", e);
+                 }
+            } catch ( SSLHandshakeException she ) {
+               logger.error("Error", she);
+                       errorLogger.error( DmaapbcLogMessageEnum.SSL_HANDSHAKE_ERROR, pURL);
+                       } catch ( UnknownHostException uhe ) {
+                               logger.error("Error", uhe);
+                               errorLogger.error(DmaapbcLogMessageEnum.UNKNOWN_HOST_EXCEPTION,  pURL, uhe.getMessage() );
+               rc = 500;
+               return rc;
+            } catch ( ConnectException ce ) {
+               logger.error("Error", ce);
+                               if ( "Yes".equals(unit_test) ) {
+                                       rc = 201;
+                                       return rc;
+                               }
+                               errorLogger.error(DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION,  pURL, ce.getMessage() );
+               rc = 500;
+               return rc;
+                       } 
+                       try {
+                               rc = uc.getResponseCode();
+                       } catch ( SSLHandshakeException she ) {
+                               logger.error("Error", she);
+                               errorLogger.error( DmaapbcLogMessageEnum.SSL_HANDSHAKE_ERROR, pURL);
+               rc = 500;
+               return rc;
+            }
+                       logger.info( "http response code:" + rc );
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+
+            if (responsemessage == null) {
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+            if ( rc >= 200 && rc < 300 ) {
+               responseBody = bodyToString( uc.getInputStream() );
+               logger.info( "responseBody=" + responseBody );
+            } else {
+                       logger.warn( "Unsuccessful response: " + responsemessage );
+            } 
+            
+               } catch (Exception e) {
+            logger.error("Unable to read response  ");
+            logger.error("Error", e);
+        }
+               finally {
+                       try {
+                               uc.disconnect();
+                       } catch ( Exception e ) {
+                               logger.error("Error", e);
+                       }
+               }       
+               return rc;
+               
+       }
+       
+       public int delAaf(AafObject obj, String pURL) {
+               logger.info( "entry: delAaf() to  " + pURL  );
+               String auth =  "Basic " + Base64.encodeBase64String(aafCred.getBytes());
+               int rc = -1;
+
+               
+               if ( ! makeConnection( pURL ) ) {
+                       return rc;
+               };
+               
+
+               byte[] postData = obj.getBytes();
+               //logger.info( "post fields=" + postData );  //byte isn't very readable
+               String responsemessage = null;
+               String responseBody = null;
+
+               try {
+                       if (auth != null) {
+                               uc.setRequestProperty("Authorization", auth);
+               }
+                       uc.setRequestMethod("DELETE");
+                       uc.setRequestProperty("Content-Type", "application/json");
+                       uc.setRequestProperty( "charset", "utf-8");
+                       uc.setRequestProperty( "Content-Length", Integer.toString( postData.length ));
+                       uc.setUseCaches(false);
+                       uc.setDoOutput(true);
+                       OutputStream os = null;
+
+                       
+                       try {
+                 uc.connect();
+                 os = uc.getOutputStream();
+                 os.write( postData );
+
+            } catch (ProtocolException pe) {
+               logger.error("Error", pe);
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                        logger.error("Error", e);
+                 }
+            } catch ( SSLHandshakeException she ) {
+               errorLogger.error( DmaapbcLogMessageEnum.SSL_HANDSHAKE_ERROR +"For:- "+pURL,she);
+            }
+                       try {
+                               rc = uc.getResponseCode();
+                       } catch ( SSLHandshakeException she ) {
+                               logger.error("Error", she);
+                               errorLogger.error( DmaapbcLogMessageEnum.SSL_HANDSHAKE_ERROR, pURL);
+               rc = 500;
+               return rc;
+            }
+                       logger.info( "http response code:" + rc );
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+
+            if (responsemessage == null) {
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+            if ( rc >= 200 && rc < 300 ) {
+               responseBody = bodyToString( uc.getInputStream() );
+               logger.info( "responseBody=" + responseBody );
+            } else {
+                       logger.warn( "Unsuccessful response: " + responsemessage );
+            } 
+            
+               } catch (Exception e) {
+            logger.error("Unable to read response  ");
+            logger.error("Error", e);
+        }      
+               return rc;
+               
+       }
+
+       private TrustManager[] trustAllCerts = new TrustManager[]{
+               new X509TrustManager() {
+
+                       @Override
+                       public java.security.cert.X509Certificate[] getAcceptedIssuers()
+                       {
+                               return null;
+                       }
+                       @Override
+                       public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
+                       {
+                               //No need to implement.
+                       }
+                       @Override
+                       public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
+                       {
+                               //No need to implement.
+                       }
+               }
+       };
+       
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafDecrypt.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafDecrypt.java
new file mode 100644 (file)
index 0000000..bf5ecf2
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.aaf;
+
+import java.io.IOException;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+public class AafDecrypt extends BaseLoggingClass  {
+       String dClass;
+       DecryptionInterface dec = null;
+       
+       public AafDecrypt() {
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               dClass = p.getProperty( "AafDecryption.Class", "org.onap.dmaap.dbcapi.aaf.ClearDecrypt");
+               try {
+                       dec = (DecryptionInterface) (Class.forName(dClass).newInstance());      
+                       dec.init(p.getProperty("CredentialCodecKeyfile", "LocalKey"));
+               } catch (Exception ee ) {
+                       logger.error("Error", ee);
+                       errorLogger.error(DmaapbcLogMessageEnum.UNEXPECTED_CONDITION, "attempting to instantiate " + dClass  );         
+               }       
+       }
+       
+       public String decrypt( String encPwd ) {
+       
+               String pwd = "notDecrypted";
+               try {           
+                       pwd = dec.decrypt( encPwd );
+               } catch( IOException io ) {
+                       logger.error("Error", io);
+                       errorLogger.error(DmaapbcLogMessageEnum.DECRYPT_IO_ERROR, dClass, encPwd );
+               } 
+               
+               return pwd;
+       
+       }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafEmpty.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafEmpty.java
new file mode 100644 (file)
index 0000000..87e56c4
--- /dev/null
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+class AafEmpty extends AafObject {
+    @Override
+    String toJSON() {
+        return "";
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafLurService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafLurService.java
new file mode 100644 (file)
index 0000000..fa49ae8
--- /dev/null
@@ -0,0 +1,140 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onap.aaf.cadi.Access;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.Permission;
+import org.onap.aaf.cadi.aaf.AAFPermission;
+import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn;
+import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
+import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
+import org.onap.aaf.cadi.principal.UnAuthPrincipal;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+
+/*
+ * this service uses the AAF Lur object to lookup identities and perms
+ */
+public class AafLurService extends BaseLoggingClass {
+
+       private static AAFConHttp aafcon;
+       private static AAFLurPerm aafLur;
+       private static AAFAuthn<?> aafAuthn;
+
+
+       /*
+        * singleton pattern suggested by AAF
+        */
+       private static AafLurService singleton;
+       private AafLurService() {}
+
+
+
+       private static void init( Access myAccess ) throws APIException, CadiException, LocatorException {
+               appLogger.info( "myAccess=" + myAccess );
+               try {
+                       aafcon = new AAFConHttp( myAccess );
+               } catch ( CadiException | LocatorException e) {
+                       appLogger.error( "Failure of AAFConHttp: " + e.getMessage() );
+                       errorLogger.error( "Failure of AAFConHttp: " + e.getMessage() );
+                       errorLogger.error(e.getMessage());
+
+                       throw e;
+               }
+               try {
+                       aafLur = aafcon.newLur();
+               } catch ( CadiException  e) {
+                       appLogger.error( "Failure of newLur(): " + e.getMessage() );
+                       errorLogger.error( "Failure of newLur(): " + e.getMessage() );
+                       errorLogger.error(e.getMessage());
+
+                       throw e;
+               }
+               aafAuthn = aafcon.newAuthn( aafLur );
+       }
+
+       public static synchronized AafLurService getInstance( Access myAccess ) throws APIException, CadiException, LocatorException{
+               if ( singleton == null ) {
+                       singleton = new AafLurService();
+                       try {
+                               init( myAccess );
+                       } catch (APIException | CadiException | LocatorException e) {
+                               errorLogger.error(e.getMessage());
+                               throw e;
+                       }
+
+               }
+               return singleton;
+       }
+
+
+       public boolean checkPerm(String ns, String fqi, String pwd, DmaapPerm p) throws IOException, CadiException {
+
+               boolean rc = false;
+
+               if ( aafAuthn == null ) {
+                       appLogger.error( "AafLurService: aafAuthn not set as expected.");
+                       return rc;
+               }
+
+               String ok = aafAuthn.validate( fqi,  pwd );
+               if ( ok != null ) {
+                       appLogger.info( "FAILED validation of fqi=" + fqi + "with response:" + ok );
+                       return rc;
+               }
+
+               Principal principal = new UnAuthPrincipal( fqi );
+               // if we pass ns as first arg to AAFPermission constructor it gets prpended to the instance...
+               // as in ns|instance|type|action.   we don't want that.
+               Permission aafPerm = new AAFPermission( null, p.getPermission(), p.getPtype(), p.getAction());
+               if ( aafLur == null ) {
+                       appLogger.error( "AafLurService: aafLur not set as expected.");
+                       return rc;
+               }
+               rc =  aafLur.fish( principal, aafPerm );
+               boolean flag = true;
+               if (rc == flag ) {
+                       return rc;
+               }
+
+               List<Permission> perms = new ArrayList<>();
+               aafLur.fishAll( principal,  perms);
+               String key = aafPerm.getKey();
+               for ( Permission prm: perms ) {
+                       if ( prm.getKey().equals( key )) {
+                               appLogger.info( principal + " has MATCHING perm " + prm.getKey() );
+                       } else {
+                               appLogger.info( principal + " has non-matching perm " + prm.getKey() );
+                       }
+               }
+
+               return rc;
+
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafNamespace.java
new file mode 100644 (file)
index 0000000..b6da523
--- /dev/null
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+
+public class AafNamespace extends AafObject  {
+
+       private String  name;
+       private ArrayList<String> admin;
+       private ArrayList<String> responsible;
+
+       // in some environments, an AAF Namespace must be owned by a human.
+       // So, when needed, this var can be set via a property
+       private static String NsOwnerIdentity;
+
+       public AafNamespace(String ns, String identity ) {
+               super();
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               NsOwnerIdentity = p.getProperty( "aaf.NsOwnerIdentity", "");
+               this.admin = new ArrayList<>();
+               this.responsible = new ArrayList<>();
+
+               this.name = ns;
+               this.admin.add( identity );
+               this.responsible.add( NsOwnerIdentity );
+       }
+       public void setName( String ns ) {
+               this.name = ns;
+       }
+       public String getName() {
+               return name;
+       }
+       public ArrayList<String> getAdmin() {
+               return admin;
+       }
+       public void setAdmin(ArrayList<String> admin) {
+               this.admin = admin;
+       }
+       public ArrayList<String> getResponsible() {
+               return responsible;
+       }
+       public void setResponsible(ArrayList<String> responsible) {
+               this.responsible = responsible;
+       }
+
+
+       // given an Array of Strings, return a String that is a separated list of quoted strings.
+       // e.g. input [ a, b, c ]
+       //       output  "a", "b", "c"
+       private String separatedList( ArrayList<String> list, String sep ) {
+               if (list.isEmpty()) return null;
+               String aList = "";
+               String delim = "";
+               for( String item: list) {
+                       if( ! item.isEmpty()) {
+                               aList += String.format( "%s\"%s\"", delim, item );
+                               delim = sep;
+                       }
+               }
+               return aList;
+       }
+
+       public String toJSON() {
+
+               String postJSON = String.format(" { \"name\": \"%s\", \"admin\": [",
+                               this.getName()
+                                );
+               postJSON += separatedList( this.getAdmin(), "," );
+               postJSON += "], \"responsible\":[";
+               postJSON += separatedList( this.getResponsible(), ",");
+               postJSON += "]}";
+               logger.info( "returning JSON: " + postJSON);
+
+               return postJSON;
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               if (this == o) return true;
+               if (o == null || getClass() != o.getClass()) return false;
+               AafNamespace that = (AafNamespace) o;
+               return Objects.equals(name, that.name) &&
+                               Objects.equals(admin, that.admin) &&
+                               Objects.equals(responsible, that.responsible);
+       }
+
+       @Override
+       public int hashCode() {
+               return Objects.hash(name, admin, responsible);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafObject.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafObject.java
new file mode 100644 (file)
index 0000000..9c8f99e
--- /dev/null
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import java.nio.charset.StandardCharsets;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+
+public abstract class AafObject extends BaseLoggingClass {
+
+       abstract String toJSON();
+       
+       public byte[] getBytes() {
+               return toJSON().getBytes(StandardCharsets.UTF_8);
+       }
+       
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafRole.java
new file mode 100644 (file)
index 0000000..0997dd4
--- /dev/null
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import java.util.Objects;
+
+public class AafRole extends AafObject  {
+
+       private String  namespace;
+       private String  role;
+       
+       public AafRole(String ns,  String role) {
+               super();
+               this.namespace = ns;
+               this.role = role;
+       }
+       public void setNamespace( String ns ) {
+               this.namespace = ns;
+       }
+       public String getNamespace() {
+               return namespace;
+       }
+       public void setRole(String role) {
+               this.role = role;
+       }
+       public String getRole() {
+               return role;
+       }
+       public String getFullyQualifiedRole() {
+               return namespace + "." + role;
+       }
+
+       public String toJSON() {
+
+               String postJSON = String.format(" { \"name\": \"%s.%s\"}", 
+                               this.getNamespace(), 
+                               this.getRole() );
+               logger.info( "returning JSON: " + postJSON);
+                       
+               return postJSON;
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               if (this == o) return true;
+               if (o == null || getClass() != o.getClass()) return false;
+               AafRole aafRole = (AafRole) o;
+               return Objects.equals(namespace, aafRole.namespace) &&
+                               Objects.equals(role, aafRole.role);
+       }
+
+       @Override
+       public int hashCode() {
+               return Objects.hash(namespace, role);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafService.java
new file mode 100644 (file)
index 0000000..3f009f8
--- /dev/null
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+/*
+ * this service uses the AAF REST API endpoints to provision values in AAF
+ */
+public interface AafService {
+    enum ServiceType {
+        AAF_Admin,
+        AAF_TopicMgr
+    }
+
+    String getIdentity();
+
+    int addPerm(DmaapPerm perm);
+
+    int delPerm(DmaapPerm perm, boolean force);
+
+    int addGrant(DmaapGrant grant);
+
+    int addUserRole(AafUserRole ur);
+
+    int addRole(AafRole role);
+
+    int addNamespace(AafNamespace ns);
+
+    int delNamespace(AafNamespace ns, boolean force);
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceFactory.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceFactory.java
new file mode 100644 (file)
index 0000000..cfde19b
--- /dev/null
@@ -0,0 +1,86 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+public class AafServiceFactory extends BaseLoggingClass {
+
+    private final DmaapConfig dmaapConfig;
+
+    public AafServiceFactory() {
+        this((DmaapConfig) DmaapConfig.getConfig());
+    }
+
+    AafServiceFactory(DmaapConfig dmaapConfig) {
+        this.dmaapConfig = dmaapConfig;
+    }
+
+    public AafService initAafService(ServiceType serviceType) {
+        boolean useAaf = "true".equalsIgnoreCase(dmaapConfig.getProperty("UseAAF", "false"));
+        String aafUrl = dmaapConfig.getProperty("aaf.URL", "https://authentication.domain.netset.com:8100/proxy/");
+        logger.info("AafService initAafService: useAaf={}, aafUrl={}", useAaf, aafUrl);
+
+        AafCred cred = getCred(serviceType);
+        return new AafServiceImpl(useAaf, aafUrl, cred.getIdentity(), new AafConnection(cred.toString()));
+    }
+
+    AafCred getCred(ServiceType ctype) {
+        String mechIdProperty;
+        String secretProperty;
+        AafDecrypt decryptor = new AafDecrypt();
+
+        if (ctype == ServiceType.AAF_Admin) {
+            mechIdProperty = "aaf.AdminUser";
+            secretProperty = "aaf.AdminPassword";
+        } else if (ctype == ServiceType.AAF_TopicMgr) {
+            mechIdProperty = "aaf.TopicMgrUser";
+            secretProperty = "aaf.TopicMgrPassword";
+        } else {
+            logger.error("Unexpected case for AAF credential type: " + ctype);
+            return null;
+        }
+        String identity = dmaapConfig.getProperty(mechIdProperty, "noMechId@domain.netset.com");
+        String pwd = decryptor.decrypt(dmaapConfig.getProperty(secretProperty, "notSet"));
+
+        return new AafCred(identity, pwd);
+    }
+
+    class AafCred {
+        private final String identity;
+        private final String pwd;
+
+        AafCred(String identity, String pwd) {
+            this.identity = identity;
+            this.pwd = pwd;
+        }
+
+        public String getIdentity() {
+            return identity;
+        }
+
+        public String toString() {
+            return identity + ":" + pwd;
+        }
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceImpl.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafServiceImpl.java
new file mode 100644 (file)
index 0000000..1491818
--- /dev/null
@@ -0,0 +1,163 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+
+import static java.lang.String.format;
+
+public class AafServiceImpl extends BaseLoggingClass implements AafService {
+
+    private static final int CREATED = 201;
+    private static final int OK = 200;
+    private static final String FORCE = "?force=true";
+    private final String aafUrl;
+    private final String identity;
+    private final boolean useAAF;
+    private final AafConnection aafConnection;
+
+    AafServiceImpl(boolean useAaf, String aafUrl, String identity, AafConnection aafConnection) {
+        this.useAAF = useAaf;
+        this.aafUrl = aafUrl;
+        this.identity = identity;
+        this.aafConnection = aafConnection;
+    }
+
+    @Override
+    public String getIdentity() {
+        return identity;
+    }
+
+    @Override
+    public int addPerm(DmaapPerm perm) {
+        logger.info("entry: addPerm() ");
+        return doPost(perm, "authz/perm", CREATED);
+    }
+
+    @Override
+    public int delPerm(DmaapPerm perm, boolean force) {
+        logger.info("entry: delPerm()");
+        return doDelete(new AafEmpty(), format(
+                "authz/perm/%s/%s/%s%s",
+                perm.getPermission(), perm.getPtype(), perm.getAction(), force ? FORCE : ""), OK);
+    }
+
+    @Override
+    public int addGrant(DmaapGrant grant) {
+        logger.info("entry: addGrant() ");
+        return doPost(grant, "authz/role/perm", CREATED);
+    }
+
+    @Override
+    public int addUserRole(AafUserRole ur) {
+        logger.info("entry: addUserRole() ");
+        return doPost(ur, "authz/userRole", CREATED);
+    }
+
+    @Override
+    public int addRole(AafRole role) {
+        logger.info("entry: addRole() ");
+        return doPost(role, "authz/role", CREATED);
+    }
+
+    @Override
+    public int addNamespace(AafNamespace ns) {
+        logger.info("entry: addNamespace() ");
+        return doPost(ns, "authz/ns", CREATED);
+    }
+
+    @Override
+    public int delNamespace(AafNamespace ns, boolean force) {
+        logger.info("entry: delNamespace()");
+        return doDelete(new AafEmpty(), format(
+                "authz/ns/%s%s",
+                ns.getName(), force ? FORCE : ""), OK);
+    }
+
+    private int doPost(AafObject obj, String uri, int expect) {
+        int rc;
+        logger.info("entry: doPost() ");
+        String pURL = aafUrl + uri;
+        logger.info("doPost: useAAF=" + useAAF);
+        if (useAAF) {
+            logger.info("doPost: " + obj.toJSON());
+            rc = aafConnection.postAaf(obj, pURL);
+        } else {
+            rc = expect;
+        }
+        switch (rc) {
+            case 401:
+            case 403:
+                errorLogger.error(DmaapbcLogMessageEnum.AAF_CREDENTIAL_ERROR, identity);
+                break;
+            case 409:
+                logger.warn("Object for " + uri + " already exists. Possible conflict.");
+                break;
+            default:
+                if (rc == expect) {
+                    logger.info("expected response: " + rc);
+                } else {
+                    logger.error("Unexpected response: " + rc);
+                }
+                break;
+        }
+
+        return rc;
+    }
+
+    private int doDelete(AafObject obj, String uri, int expect) {
+        int rc;
+        String pURL = aafUrl + uri;
+        if (useAAF) {
+            logger.info("doDelete: " + obj.toJSON());
+            rc = aafConnection.delAaf(obj, pURL);
+        } else {
+            rc = expect;
+        }
+        switch (rc) {
+            case 401:
+            case 403:
+                errorLogger.error(DmaapbcLogMessageEnum.AAF_CREDENTIAL_ERROR, identity);
+                break;
+            case 404:
+                logger.warn("Object not found...ignore");
+                break;
+            case OK:
+                logger.info("expected response");
+                break;
+            default:
+                logger.error("Unexpected response: " + rc);
+                break;
+        }
+
+        return rc;
+    }
+
+    String getAafUrl() {
+        return aafUrl;
+    }
+
+    boolean isUseAAF() {
+        return useAAF;
+    }
+
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafUserRole.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/AafUserRole.java
new file mode 100644 (file)
index 0000000..b948e61
--- /dev/null
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import java.util.Objects;
+
+
+public class AafUserRole extends AafObject  {
+
+       private String  identity;
+       private String  role;
+
+
+       
+       public AafUserRole(String identity,  String role ) {
+               super();
+               this.identity = identity;
+               this.role = role;
+       }
+
+       public void setRole(String role) {
+               this.role = role;
+       }
+       public String getRole() {
+               return role;
+       }
+
+       public String getIdentity() {
+               return identity;
+       }
+
+       public void setIdentity(String identity) {
+               this.identity = identity;
+       }
+
+       public String toJSON() {
+
+               String postJSON = String.format(" { \"user\": \"%s\", \"role\": \"%s\" }",  
+                               this.getIdentity(), 
+                               this.getRole()
+                               );
+               logger.info( "returning JSON: " + postJSON);
+                       
+               return postJSON;
+       }
+
+
+       @Override
+       public boolean equals(Object o) {
+               if (this == o) return true;
+               if (o == null || getClass() != o.getClass()) return false;
+               AafUserRole that = (AafUserRole) o;
+               return Objects.equals(identity, that.identity) &&
+                               Objects.equals(role, that.role);
+       }
+
+       @Override
+       public int hashCode() {
+
+               return Objects.hash(identity, role);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/ClearDecrypt.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/ClearDecrypt.java
new file mode 100644 (file)
index 0000000..fe3e3e7
--- /dev/null
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import java.io.IOException;
+
+public class ClearDecrypt implements DecryptionInterface {
+
+       @Override
+       public String decrypt(String enc) throws IOException {
+               return enc;
+       }
+
+       @Override
+       public String valueOf(String s) {
+               return s;
+       }
+
+       @Override
+       public boolean init(String codecFname) {
+               return false;
+       }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DecryptionInterface.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DecryptionInterface.java
new file mode 100644 (file)
index 0000000..eda6465
--- /dev/null
@@ -0,0 +1,30 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import java.io.IOException;
+
+public interface DecryptionInterface {
+       public boolean init( String codecFname );
+       public String decrypt(String enc) throws IOException;
+       public String valueOf( String s);
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapGrant.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapGrant.java
new file mode 100644 (file)
index 0000000..e171b88
--- /dev/null
@@ -0,0 +1,79 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import java.util.Objects;
+
+public class DmaapGrant extends AafObject {
+
+    private DmaapPerm perm;
+    private String role;
+
+    public DmaapGrant() {
+
+    }
+
+    public DmaapGrant(DmaapPerm p, String r) {
+        this.perm = p;
+        this.role = r;
+    }
+
+    public DmaapPerm getPerm() {
+        return perm;
+    }
+
+    public void setPerm(DmaapPerm perm) {
+        this.perm = perm;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public void setRole(String role) {
+        this.role = role;
+    }
+
+    public String toJSON() {
+
+        String postJSON = String.format(" { \"perm\":  %s, \"role\": \"%s\"}",
+                this.perm.toJSON(),
+                this.getRole());
+        logger.info("returning JSON: " + postJSON);
+
+        return postJSON;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DmaapGrant that = (DmaapGrant) o;
+        return Objects.equals(perm, that.perm) &&
+                Objects.equals(role, that.role);
+    }
+
+    @Override
+    public int hashCode() {
+
+        return Objects.hash(perm, role);
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapPerm.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/aaf/DmaapPerm.java
new file mode 100644 (file)
index 0000000..2f1765d
--- /dev/null
@@ -0,0 +1,89 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import java.util.Objects;
+
+
+public class DmaapPerm extends AafObject {
+
+    private String permission;
+    private String ptype;
+    private String action;
+
+    public DmaapPerm(String permission, String ptype, String action) {
+        super();
+        this.permission = permission;
+        this.ptype = ptype;
+        this.action = action;
+    }
+
+    public String getPermission() {
+        return permission;
+    }
+
+    public void setPermission(String permission) {
+        this.permission = permission;
+    }
+
+    public String getPtype() {
+        return ptype;
+    }
+
+    public void setPtype(String ptype) {
+        this.ptype = ptype;
+    }
+
+    public String getAction() {
+        return action;
+    }
+
+    public void setAction(String action) {
+        this.action = action;
+    }
+
+    public String toJSON() {
+
+        String postJSON = String.format(" { \"type\": \"%s\", \"instance\": \"%s\", \"action\": \"%s\"}",
+                this.getPermission(),
+                this.getPtype(),
+                this.getAction());
+        logger.info("returning JSON: " + postJSON);
+
+        return postJSON;
+    }
+
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DmaapPerm dmaapPerm = (DmaapPerm) o;
+        return Objects.equals(permission, dmaapPerm.permission) &&
+                Objects.equals(ptype, dmaapPerm.ptype) &&
+                Objects.equals(action, dmaapPerm.action);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(permission, ptype, action);
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFish.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFish.java
new file mode 100644 (file)
index 0000000..825b711
--- /dev/null
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+  * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.authentication;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+import org.onap.aaf.cadi.CadiException;
+import org.onap.aaf.cadi.LocatorException;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.dmaap.dbcapi.aaf.AafLurService;
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+
+public class AafLurAndFish extends BaseLoggingClass implements ApiAuthorizationCheckInterface {
+       private AafLurService svc;
+       private static String apiNamespace;
+       private static final String ERROR="Error";
+
+       AafLurAndFish()  throws AuthenticationErrorException  {
+
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               apiNamespace = p.getProperty( "ApiNamespace", "org.onap.dmaap-bc.api");
+
+               String cadiprop = p.getProperty( "cadi.properties", "/opt/app/osaaf/local/org.onap.dmaap-bc.props");
+               logger.info( "cadiprops in " + cadiprop );
+               Properties props = new Properties();
+               try {
+                       FileInputStream fis = new FileInputStream( cadiprop );
+                       try {
+                               props.load( fis );
+                       } finally {
+                               fis.close();
+                       }
+               } catch ( IOException e ) {
+                       errorLogger.error( "Unable to load " + cadiprop );
+                       errorLogger.error(ERROR, e);
+                       throw new AuthenticationErrorException( );
+               }
+               try {
+                       PropAccess myAccess = new PropAccess( props );
+
+                       svc =  AafLurService.getInstance(myAccess);
+               } catch (APIException | CadiException | LocatorException e ) {
+                       errorLogger.error(ERROR, e);
+                       errorLogger.error( e.toString() );
+                       throw new AuthenticationErrorException();
+               }
+
+       }
+
+       public void check( String mechid, String pwd, DmaapPerm p ) throws AuthenticationErrorException {
+
+               try {
+                   if (mechid.isEmpty() || pwd.isEmpty()) {
+                throw new AuthenticationErrorException("No basic authorization value provided");
+            }
+
+                       if (!svc.checkPerm( apiNamespace, mechid, pwd, p )) {
+                               throw new AuthenticationErrorException();
+                       }
+               } catch ( IOException | CadiException  e ) {
+                       errorLogger.error(ERROR, e);
+                       errorLogger.error( e.toString() );
+                       throw new AuthenticationErrorException();
+               }
+
+       }
+
+        public static void main(String[] args) throws Exception {
+               AafLurAndFish alaf = new AafLurAndFish();
+               DmaapPerm p = new DmaapPerm( "org.onap.dmaap-bc.api.dmaap", "boot", "GET");
+
+               try {
+                       alaf.check("mmanager@people.osaaf.org", "demo123456!", p);
+               } catch (AuthenticationErrorException aee ) {
+                               errorLogger.error(aee.getMessage());
+                               errorLogger.error( "Check failed for: " + p.toJSON());
+                       System.exit(-1);
+               }
+                errorLogger.info("Check succeeded for: " + p.toJSON());
+           }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AllowAll.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AllowAll.java
new file mode 100644 (file)
index 0000000..2e83e6c
--- /dev/null
@@ -0,0 +1,29 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.authentication;
+
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+
+public class AllowAll implements ApiAuthorizationCheckInterface {
+    @Override
+    public void check(String mechid, String pwd, DmaapPerm p) {
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiAuthorizationCheckInterface.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiAuthorizationCheckInterface.java
new file mode 100644 (file)
index 0000000..1fef09d
--- /dev/null
@@ -0,0 +1,29 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.authentication;
+
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+
+@FunctionalInterface
+public interface ApiAuthorizationCheckInterface {
+       public void check( String mechid, String pwd, DmaapPerm p ) throws AuthenticationErrorException;
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPerms.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPerms.java
new file mode 100644 (file)
index 0000000..b082102
--- /dev/null
@@ -0,0 +1,180 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * 
+ * Modifications Copyright (C) 2018 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.authentication;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+
+import org.onap.dmaap.dbcapi.aaf.AafService;
+import org.onap.dmaap.dbcapi.aaf.AafServiceFactory;
+import org.onap.dmaap.dbcapi.aaf.DmaapGrant;
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+public  class ApiPerms extends BaseLoggingClass {
+       static String topic = "topics";
+       static String mrClusters = "mr_clusters";
+       static String mrClients = "mr_clients";
+       static String feed = "feeds";
+       static String drSubs = "dr_subs";
+       static String drPubs = "dr_pubs";
+       static String drNodes = "dr_nodes";
+       static String dcaeLocations = "dcaeLocations";
+       static String inventory = "Inventory";
+       static String portalUser = "PortalUser";
+       static String orchestrator = "Orchestrator";
+       static String delete = "DELETE";
+       static String dmaap = "dmaap";
+       static String controller = "Controller";
+       
+       private static class PermissionMap {
+               static final EELFLogger logger = EELFManager.getInstance().getLogger( PermissionMap.class );
+               static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger();
+               String uri;
+               String action;
+               String[] roles;
+               
+               private PermissionMap( String u, String a, String[] r ) {
+                       this.setUri(u);
+                       this.setAction(a);
+                       this.setRoles(r);
+               }       
+               
+               public String getUri() {
+                       return uri;
+               }
+               public void setUri(String uri) {
+                       this.uri = uri;
+               }
+               public String getAction() {
+                       return action;
+               }
+               public void setAction(String action) {
+                       this.action = action;
+               }
+
+               public String[] getRoles() {
+                       return roles;
+               }
+               public void setRoles(String[] roles) {
+                       this.roles = roles;
+               }
+
+               public static void initMap( PermissionMap[] pmap, String instance ) {
+
+                       DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+                       String api = p.getProperty("ApiNamespace", "apiNamespace.not.set");
+
+                       AafService aaf = new AafServiceFactory().initAafService(ServiceType.AAF_Admin);
+                       
+                       for ( int i = 0; i < pmap.length ; i++ ) {
+                               String uri = new String( api + "." + pmap[i].getUri());
+                               DmaapPerm perm = new DmaapPerm( uri, instance, pmap[i].getAction() );
+                               int rc = aaf.addPerm( perm );
+                               if ( rc != 201 &&  rc != 409 ) {
+                                       errorLogger.error( DmaapbcLogMessageEnum.AAF_UNEXPECTED_RESPONSE,  Integer.toString(rc), "add perm",  perm.toString() );
+
+                               }
+                               for( String r: pmap[i].getRoles()) {
+                                       String fr = new String( api + "." + r );
+                                       logger.debug( "i:" + i + " granting perm " + perm.toString()+ " to role=" + fr );
+                                       DmaapGrant grant = new DmaapGrant( perm, fr );
+                                       rc = aaf.addGrant( grant );
+                                       if ( rc != 201 && rc != 409 ) {
+                                               errorLogger.error( DmaapbcLogMessageEnum.AAF_UNEXPECTED_RESPONSE,  Integer.toString(rc), "grant perm",  perm.toString() );
+                                       }
+                               }
+                               
+                       }
+               }
+       }
+       
+       static PermissionMap[] bootMap = {
+               new PermissionMap( dmaap, "GET", new String[] { controller }),
+               new PermissionMap( dmaap, "POST", new String[] { controller }), 
+               new PermissionMap( dmaap, "PUT", new String[] { controller }),
+               new PermissionMap( dmaap, delete, new String[] { controller })
+       
+       };
+
+       static PermissionMap[] envMap = {
+               new PermissionMap( dmaap, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }),
+               new PermissionMap( dmaap, "POST", new String[] { controller } ),                
+               new PermissionMap( dmaap, "PUT", new String[] { controller }),
+               new PermissionMap( dmaap, delete, new String[] { controller }),
+               new PermissionMap( "bridge", "GET", new String[] {  "Metrics" }),
+               //new PermissionMap( "bridge", "POST", new String[] { "Metrics" } ),            
+               //new PermissionMap( "bridge", "PUT", new String[] { "Metrics" }),
+               //new PermissionMap( "bridge", delete, new String[] { "Metrics" }),
+               new PermissionMap( dcaeLocations, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }),
+               new PermissionMap( dcaeLocations, "POST", new String[] { controller } ),                
+               new PermissionMap( dcaeLocations, "PUT", new String[] { controller }),
+               new PermissionMap( dcaeLocations, delete, new String[] { controller }),
+               new PermissionMap( drNodes, "GET", new String[] { controller, orchestrator, inventory,  portalUser }),
+               new PermissionMap( drNodes, "POST", new String[] { controller } ),              
+               new PermissionMap( drNodes, "PUT", new String[] { controller }),
+               new PermissionMap( drNodes, delete, new String[] { controller }),
+               new PermissionMap( drPubs, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }),
+               new PermissionMap( drPubs, "POST", new String[] { controller, orchestrator,portalUser } ),              
+               new PermissionMap( drPubs, "PUT", new String[] { controller, orchestrator,portalUser }),
+               new PermissionMap( drPubs, delete, new String[] { controller, orchestrator,portalUser }),
+               new PermissionMap( drSubs, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }),
+               new PermissionMap( drSubs, "POST", new String[] { controller, orchestrator,portalUser } ),              
+               new PermissionMap( drSubs, "PUT", new String[] { controller, orchestrator,portalUser }),
+               new PermissionMap( drSubs, delete, new String[] { controller, orchestrator,portalUser }),
+               new PermissionMap( feed, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }),
+               new PermissionMap( feed, "POST", new String[] { controller, orchestrator,portalUser } ),                
+               new PermissionMap( feed, "PUT", new String[] { controller, orchestrator, portalUser }),
+               new PermissionMap( feed, delete, new String[] { controller, portalUser }),
+               new PermissionMap( mrClients, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }),
+               new PermissionMap( mrClients, "POST", new String[] { controller,orchestrator, portalUser } ),           
+               new PermissionMap( mrClients, "PUT", new String[] { controller, orchestrator,portalUser }),
+               new PermissionMap( mrClients, delete, new String[] { controller,orchestrator, portalUser }),
+               new PermissionMap( mrClusters, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }),
+               new PermissionMap( mrClusters, "POST", new String[] { controller } ),           
+               new PermissionMap( mrClusters, "PUT", new String[] { controller }),
+               new PermissionMap( mrClusters, delete, new String[] { controller }),
+               new PermissionMap( topic, "GET", new String[] { controller, orchestrator, inventory, "Metrics", portalUser }),
+               new PermissionMap( topic, "POST", new String[] { controller, orchestrator } ),          
+               new PermissionMap( topic, "PUT", new String[] { controller, orchestrator }),
+               new PermissionMap( topic, delete, new String[] { controller, orchestrator })
+       };
+       
+       public void setBootMap() {
+               String instance = "boot";
+               PermissionMap.initMap( bootMap, instance );
+       }
+       
+       public void setEnvMap() {
+               Dmaap dmaapVar = new DmaapService().getDmaap();
+               String dmaapName = dmaapVar.getDmaapName();
+               PermissionMap.initMap( envMap, dmaapName );
+       }
+       
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPolicy.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/ApiPolicy.java
new file mode 100644 (file)
index 0000000..16d0367
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.authentication;
+
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+import java.util.Properties;
+
+public class ApiPolicy extends BaseLoggingClass {
+
+    private boolean permissionClassSet = true;
+    private ApiAuthorizationCheckInterface perm = null;
+
+    public ApiPolicy() {
+        this(DmaapConfig.getConfig());
+    }
+
+    ApiPolicy(Properties p) {
+        String dClass = p.getProperty("ApiPermission.Class");
+        logger.info("ApiPolicy implements " + dClass);
+        logger.info("dClass=" + dClass + " permissionClassSet=" + permissionClassSet);
+
+        try {
+            perm = (ApiAuthorizationCheckInterface) (Class.forName(dClass).newInstance());
+        } catch (Exception ee) {
+            errorLogger.error(DmaapbcLogMessageEnum.UNEXPECTED_CONDITION, "attempting to instantiate " + dClass);
+            errorLogger.error("trace is: " + ee);
+            permissionClassSet = false;
+        }
+    }
+
+    public void check(String mechid, String pwd, DmaapPerm p) throws AuthenticationErrorException {
+        perm.check(mechid, pwd, p);
+    }
+
+    public boolean isPermissionClassSet() {
+        return permissionClassSet;
+    }
+
+    ApiAuthorizationCheckInterface getPerm() {
+        return perm;
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AuthenticationErrorException.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/authentication/AuthenticationErrorException.java
new file mode 100644 (file)
index 0000000..01200f7
--- /dev/null
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.authentication;
+
+public class AuthenticationErrorException extends Exception {
+       /**
+        *
+        */
+       private static final long serialVersionUID = 1L;
+
+    public AuthenticationErrorException() {
+    }
+
+    public AuthenticationErrorException(String s) {
+        super(s);
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/DrProvConnection.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/DrProvConnection.java
new file mode 100644 (file)
index 0000000..dffe830
--- /dev/null
@@ -0,0 +1,1101 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.client;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.ConnectException;
+import java.net.ProtocolException;
+import java.net.SocketException;
+import java.net.URL;
+import java.util.Arrays;
+import javax.net.ssl.HttpsURLConnection;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Sub;
+import org.onap.dmaap.dbcapi.model.Feed;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+
+
+public class DrProvConnection extends BaseLoggingClass {
+          
+   
+       private String provURL;
+       private String provApi;
+       private String  behalfHeader;
+       private String  feedContentType;
+       private String  subContentType;
+       private String unit_test;
+       private String  provURI;
+       
+       private HttpsURLConnection uc;
+
+
+       public DrProvConnection() {
+               provURL = new DmaapService().getDmaap().getDrProvUrl();
+               if ( provURL.length() < 1 ) {
+                       errorLogger.error( DmaapbcLogMessageEnum.PREREQ_DMAAP_OBJECT, "DmaapService().getDmaap().getDrProvUrl()");
+               }
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               provApi = p.getProperty( "DR.provApi", "ONAP" );
+               behalfHeader = p.getProperty( "DR.onBehalfHeader", "X-DMAAP-DR-ON-BEHALF-OF");
+               feedContentType = p.getProperty( "DR.feedContentType", "application/vnd.dmaap-dr.feed");
+               subContentType = p.getProperty( "DR.subContentType", "application/vnd.dmaap-dr.subscription");
+               provURI = p.getProperty( "DR.ProvisioningURI", "/internal/prov");
+               logger.info( "provURL=" + provURL + " provApi=" + provApi + " behalfHeader=" + behalfHeader
+                               + " feedContentType=" + feedContentType + " subContentType=" + subContentType );
+               unit_test = p.getProperty( "UnitTest", "No" );
+                       
+       }
+       
+       public boolean makeFeedConnection() {
+               return makeConnection( provURL );
+       }
+       public boolean makeFeedConnection(String feedId) {
+               return makeConnection( provURL + "/feed/" + feedId );   
+       }
+       public boolean makeSubPostConnection( String subURL ) {
+               String[] parts = subURL.split("/");
+               String revisedURL = provURL + "/" + parts[3] + "/" + parts[4];
+               logger.info( "mapping " + subURL + " to " + revisedURL );
+               return makeConnection( revisedURL );
+       }
+       public boolean makeSubPutConnection( String subId ) {
+               String revisedURL = provURL + "/subs/" + subId;
+               logger.info( "mapping " + subId + " to " + revisedURL );
+               return makeConnection( revisedURL );
+       }
+
+       public boolean makeIngressConnection( String feed, String user, String subnet, String nodep ) {
+               String uri = String.format("/internal/route/ingress/?feed=%s&user=%s&subnet=%s&nodepatt=%s", 
+                                       feed, user, subnet, nodep );
+               return makeConnection( provURL + uri );
+       }
+       public boolean makeEgressConnection( String sub, String nodep ) {
+               String uri = String.format("/internal/route/egress/?sub=%s&node=%s", 
+                                       sub,  nodep );
+               return makeConnection( provURL + uri );
+       }
+       public boolean makeDumpConnection() {
+               String url = provURL + provURI;
+               return makeConnection( url );
+       }
+       public boolean makeNodesConnection( String varName ) {
+               
+               String uri = String.format("/internal/api/%s", varName);
+               return makeConnection( provURL + uri );
+       }
+       
+       public boolean makeNodesConnection( String varName, String val ) {
+
+               if ( val == null ) {
+                       return false;
+               } 
+               String cv = val.replaceAll("\\|", "%7C");
+               String uri = String.format( "/internal/api/%s?val=%s", varName, cv );
+
+               return makeConnection( provURL + uri );
+       }
+       
+       private boolean makeConnection( String pURL ) {
+       
+               try {
+                       URL u = new URL( pURL );
+                       uc = (HttpsURLConnection) u.openConnection();
+                       uc.setInstanceFollowRedirects(false);
+                       logger.info( "successful connect to " + pURL );
+                       uc.setSSLSocketFactory(DmaapConfig.getSSLSocketFactory());
+                       return(true);
+               } catch (Exception e) {
+                       errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_ERROR,  pURL, e.getMessage() );
+            return(false);
+        }
+       }
+       
+       public String bodyToString( InputStream is ) {
+               logger.info( "is=" + is );
+               StringBuilder sb = new StringBuilder();
+               BufferedReader br = new BufferedReader( new InputStreamReader(is));
+               String line;
+               try {
+                       while ((line = br.readLine()) != null ) {
+                               sb.append( line );
+                       }
+               } catch (IOException ex ) {
+                       errorLogger.error( DmaapbcLogMessageEnum.IO_EXCEPTION, ex.getMessage());
+               }
+                       
+               return sb.toString();
+       }
+       
+
+       public  String doPostFeed( Feed postFeed, ApiError err ) {
+
+               byte[] postData = postFeed.getBytes();
+               logger.info( "post fields=" + Arrays.toString(postData) );
+               String responsemessage = null;
+               String responseBody = null;
+               int rc = -1;
+
+               try {
+                       logger.info( "uc=" + uc );
+                       uc.setRequestMethod("POST");
+                       uc.setRequestProperty("Content-Type", feedContentType);
+                       uc.setRequestProperty( "charset", "utf-8");
+                       uc.setRequestProperty( behalfHeader, postFeed.getOwner() );
+                       uc.setRequestProperty( "Content-Length", Integer.toString( postData.length ));
+                       uc.setUseCaches(false);
+                       uc.setDoOutput(true);
+                       OutputStream os = null;
+
+                       try {
+                 uc.connect();
+                 os = uc.getOutputStream();
+                 os.write( postData );
+
+            } catch (ProtocolException pe) {
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                 }
+            } catch (Exception e) {
+                               logger.info( "Exception: " + e.getMessage() );
+                               e.printStackTrace();
+                       }
+                       rc = uc.getResponseCode();
+                       logger.info( "http response code:" + rc );
+                       responsemessage = uc.getResponseMessage();
+                       logger.info( "responsemessage=" + responsemessage );
+            if (responsemessage == null) {
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+            if (rc == 201 ) {
+                       responseBody = bodyToString( uc.getInputStream() );
+                       logger.info( "responseBody=" + responseBody );
+
+            } else {
+               err.setCode( rc );
+               err.setMessage(responsemessage);
+            }
+            
+               } catch (ConnectException ce) {
+                       errorLogger.error(DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage() );
+            err.setCode( 500 );
+               err.setMessage("Backend connection refused");
+               } catch (SocketException se) {
+                       errorLogger.error( DmaapbcLogMessageEnum.SOCKET_EXCEPTION, se.getMessage(), "response from prov server" );
+                       err.setCode( 500 );
+                       err.setMessage( "Unable to read response from DR");
+        } catch (Exception e) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doPostFeed because unit_test =" + unit_test );
+               } else {
+                   logger.warn("Unable to read response  " );
+                   errorLogger.error("Unable to read response  ", e.getMessage());
+                   try {
+                           err.setCode( uc.getResponseCode());
+                           err.setMessage(uc.getResponseMessage());
+                   } catch (Exception e2) {
+                       err.setCode( 500 );
+                       err.setMessage("Unable to determine response message");
+                   }
+               }
+        } 
+               finally {
+                       try {
+                               uc.disconnect();
+                       } catch ( Exception e ) {
+                               logger.error(e.getMessage(), e);
+                       }
+               }
+               return responseBody;
+
+       }
+
+       
+       // the POST for /internal/route/ingress doesn't return any data, so needs a different function
+       // the POST for /internal/route/egress doesn't return any data, so needs a different function   
+       public int doXgressPost( ApiError err ) {
+               
+               String responsemessage = null;
+               int rc = -1;
+
+               try {
+                       uc.setRequestMethod("POST");
+
+
+                       try {
+                 uc.connect();
+
+            } catch (ProtocolException pe) {
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                       logger.error(e.getMessage(), e);
+                 }
+                       } catch (Exception e) {
+                               logger.info( "Exception: " + e.getMessage() );
+                               e.printStackTrace();
+                       }
+                       rc = uc.getResponseCode();
+                       logger.info( "http response code:" + rc );
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+
+
+
+            if (rc < 200 || rc >= 300 ) {
+               err.setCode( rc );
+               err.setMessage(responsemessage);
+            }
+               } catch (Exception e) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doXgressPost because unit_test =" + unit_test );
+               } else {
+                   logger.error("Unable to read response  " );
+                   logger.error(e.getMessage(), e);
+               }
+        }              
+        finally {
+                       try {
+                               uc.disconnect();
+                       } catch ( Exception e ) {
+                               logger.error(e.getMessage(), e);
+                       }
+               }
+       
+               return rc;
+
+       }
+       
+       public String doPostDr_Sub( DR_Sub postSub, ApiError err ) {
+               logger.info( "entry: doPostDr_Sub() "  );
+               byte[] postData = postSub.getBytes(provApi );
+               logger.info( "post fields=" + postData );
+               String responsemessage = null;
+               String responseBody = null;
+
+               try {
+       
+                       uc.setRequestMethod("POST");
+               
+                       uc.setRequestProperty("Content-Type", subContentType );
+                       uc.setRequestProperty( "charset", "utf-8");
+                       uc.setRequestProperty( behalfHeader, "DGL" );
+                       uc.setRequestProperty( "Content-Length", Integer.toString( postData.length ));
+                       uc.setUseCaches(false);
+                       uc.setDoOutput(true);
+                       OutputStream os = null;
+                       int rc = -1;
+                       
+                       try {
+                 uc.connect();
+                 os = uc.getOutputStream();
+                 os.write( postData );
+
+            } catch (ProtocolException pe) {
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                        logger.error(e.getMessage(), e);
+                 }
+                       } catch (Exception e) {
+                               logger.info( "Exception: " + e.getMessage() );
+                               e.printStackTrace();
+                       }
+                       rc = uc.getResponseCode();
+                       logger.info( "http response code:" + rc );
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+
+
+            if (responsemessage == null) {
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+            if (rc == 201 ) {
+                       responseBody = bodyToString( uc.getInputStream() );
+                       logger.info( "responseBody=" + responseBody );
+
+            } else {
+               err.setCode(rc);
+               err.setMessage(responsemessage);
+            }
+            
+               } catch (Exception e) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doPostDr_Sub because unit_test =" + unit_test );
+               } else {
+                   logger.error("Unable to read response  ", e.getMessage());
+               }
+        }              
+               finally {
+                       try {
+                               uc.disconnect();
+                       } catch ( Exception e ) {
+                               logger.error(e.getMessage(), e);
+                       }
+               }
+               return responseBody;
+
+       }
+       
+
+       public String doPutFeed(Feed putFeed, ApiError err) {
+               byte[] postData = putFeed.getBytes();
+               logger.info( "post fields=" + Arrays.toString(postData) );
+               String responsemessage = null;
+               String responseBody = null;
+
+               try {
+                       logger.info( "uc=" + uc );
+                       uc.setRequestMethod("PUT");
+                       uc.setRequestProperty("Content-Type", feedContentType );
+                       uc.setRequestProperty( "charset", "utf-8");
+                       uc.setRequestProperty( behalfHeader, putFeed.getOwner() );
+                       uc.setRequestProperty( "Content-Length", Integer.toString( postData.length ));
+                       uc.setUseCaches(false);
+                       uc.setDoOutput(true);
+                       OutputStream os = null;
+                       int rc = -1;
+                       
+                       try {
+                 uc.connect();
+                 os = uc.getOutputStream();
+                 os.write( postData );
+
+            } catch (ProtocolException pe) {
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                        logger.error(e.getMessage(), e);
+                 }
+                       } catch (Exception e) {
+                               logger.info( "Exception: " + e.getMessage() );
+                               e.printStackTrace();
+                       }
+                       rc = uc.getResponseCode();
+                       logger.info( "http response code:" + rc );
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+
+
+            if (responsemessage == null) {
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+            if (rc >= 200 && rc < 300 ) {
+                       responseBody = bodyToString( uc.getInputStream() );
+                       logger.info( "responseBody=" + responseBody );
+                       err.setCode( rc );
+            } else if ( rc == 404 ) {
+               err.setCode( rc );
+               err.setFields( "feedid");
+               String message =  "FeedId " + putFeed.getFeedId() + " not found on DR to update.  Out-of-sync condition?";
+               err.setMessage( message );
+               errorLogger.error( DmaapbcLogMessageEnum.PROV_OUT_OF_SYNC, "Feed", putFeed.getFeedId() );
+               
+            } else {
+               err.setCode( rc );
+               err.setMessage(responsemessage);
+            }
+            
+               } catch (ConnectException ce) {
+                       if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doPutFeed because unit_test =" + unit_test );
+                       } else {
+                               errorLogger.error(DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage());
+                               err.setCode(500);
+                               err.setMessage("Backend connection refused");
+                       }
+               } catch (SocketException se) {
+                       errorLogger.error( DmaapbcLogMessageEnum.SOCKET_EXCEPTION, se.getMessage(), "response from Prov server" );
+                       err.setCode( 500 );
+                       err.setMessage( "Unable to read response from DR");
+        } catch (Exception e) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doPutFeed because unit_test =" + unit_test );
+               } else {
+                   logger.warn("Unable to read response  " );
+                   logger.error(e.getMessage(), e);
+               }
+            try {
+                   err.setCode( uc.getResponseCode());
+                   err.setMessage(uc.getResponseMessage());
+            } catch (Exception e2) {
+               err.setCode( 500 );
+               err.setMessage("Unable to determine response message");
+               logger.error(e2.getMessage(), e2);
+            }
+        }              finally {
+                       try {
+                               uc.disconnect();
+                       } catch ( Exception e ) {
+                               logger.error(e.getMessage(), e);
+                       }
+               }
+               return responseBody;
+       }
+       public String doPutDr_Sub(DR_Sub postSub, ApiError err) {
+               logger.info( "entry: doPutDr_Sub() "  );
+               byte[] postData = postSub.getBytes(provApi);
+               logger.info( "post fields=" + postData );
+               String responsemessage = null;
+               String responseBody = null;
+
+               try {
+       
+                       uc.setRequestMethod("PUT");
+               
+                       uc.setRequestProperty("Content-Type", subContentType );
+                       uc.setRequestProperty( "charset", "utf-8");
+                       uc.setRequestProperty( behalfHeader, "DGL" );
+                       uc.setRequestProperty( "Content-Length", Integer.toString( postData.length ));
+                       uc.setUseCaches(false);
+                       uc.setDoOutput(true);
+                       OutputStream os = null;
+                       int rc = -1;
+                       
+                       try {
+                 uc.connect();
+                 os = uc.getOutputStream();
+                 os.write( postData );
+
+            } catch (ProtocolException pe) {
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                        logger.error(e.getMessage(), e);
+                 }
+                       } catch (Exception e) {
+                               logger.info( "Exception: " + e.getMessage() );
+                               e.printStackTrace();
+                       }
+                       rc = uc.getResponseCode();
+                       logger.info( "http response code:" + rc );
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+
+
+            if (responsemessage == null) {
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+            if (rc == 200 ) {
+                       responseBody = bodyToString( uc.getInputStream() );
+                       logger.info( "responseBody=" + responseBody );
+
+            } else {
+               err.setCode(rc);
+               err.setMessage(responsemessage);
+            }
+            
+               } catch (ConnectException ce) {
+            errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage() );
+            err.setCode( 500 );
+               err.setMessage("Backend connection refused");
+               logger.error(ce.getMessage(), ce);
+               } catch (Exception e) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doPutDr_Sub because unit_test =" + unit_test );
+               } else {
+                   logger.error("Unable to read response  " );
+                   logger.error(e.getMessage(), e);
+               }
+        } finally {
+               if(null != uc){
+                   uc.disconnect();
+               }
+        }
+               return responseBody;
+
+       }
+       
+       public String doGetNodes( ApiError err ) {
+               logger.info( "entry: doGetNodes() "  );
+               //byte[] postData = postSub.getBytes();
+               //logger.info( "get fields=" + postData );
+               String responsemessage = null;
+               String responseBody = null;
+
+               try {
+       
+                       uc.setRequestMethod("GET");
+                       int rc = -1;
+                       
+
+                       try {
+                uc.connect();
+       
+
+            } catch (ProtocolException pe) {
+
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                        logger.error(e.getMessage(), e);
+                 }
+                       } catch (Exception e) {
+                               logger.info( "Exception: " + e.getMessage() );
+                               e.printStackTrace();
+                       }
+       
+                       rc = uc.getResponseCode();
+                       logger.info( "http response code:" + rc );
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+
+            if (responsemessage == null) {
+
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+       
+               err.setCode(rc);  // may not really be an error, but we save rc
+            if (rc == 200 ) {
+                       responseBody = bodyToString( uc.getInputStream() );
+                       logger.info( "responseBody=" + responseBody );
+            } else {
+               err.setMessage(responsemessage);
+            }
+            
+
+               } catch (ConnectException ce) {
+                       if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doGetNodes because unit_test =" + unit_test );
+                       } else {
+                               errorLogger.error(DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage());
+                               err.setCode(500);
+                               err.setMessage("Backend connection refused");
+                               logger.error(ce.getMessage(), ce);
+                       }
+               } catch (Exception e) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doGetNodes because unit_test =" + unit_test );
+               } else {
+                   logger.error("Unable to read response  ", e.getMessage());
+               }
+        } finally {
+
+                       if ( uc != null ) uc.disconnect();
+        }
+
+               return responseBody;
+
+       }
+       public String doPutNodes( ApiError err ) {
+               logger.info( "entry: doPutNodes() "  );
+               String responsemessage = null;
+               String responseBody = null;
+
+               try {
+                       uc.setRequestMethod("PUT");
+                       uc.setUseCaches(false);
+                       int rc = -1;
+                       
+                       try {
+                 uc.connect();
+            } catch (ProtocolException pe) {
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                 }
+                       } catch (Exception e) {
+                               logger.info( "Exception: " + e.getMessage() );
+                               e.printStackTrace();
+                       }
+                       rc = uc.getResponseCode();
+                       logger.info( "http response code:" + rc );
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+
+
+            if (responsemessage == null) {
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+               err.setCode(rc);
+            if (rc == 200 ) {
+                       responseBody = bodyToString( uc.getInputStream() );
+                       logger.info( "responseBody=" + responseBody );
+
+            } else {
+  
+               err.setMessage(responsemessage);
+            }
+            
+               } catch (Exception e) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doPutNodes because unit_test =" + unit_test );
+               } else {
+                   logger.error("Unable to read response  ", e.getMessage());
+               }
+        } finally {
+                       if ( uc != null ) {
+                       uc.disconnect();
+                       }
+        }
+               return responseBody;
+
+       }
+       
+       public String doDeleteFeed(Feed putFeed, ApiError err) {
+               String responsemessage = null;
+               String responseBody = null;
+
+               try {
+                       logger.info( "uc=" + uc );
+                       uc.setRequestMethod("DELETE");
+                       uc.setRequestProperty("Content-Type", feedContentType );
+                       uc.setRequestProperty( "charset", "utf-8");
+                       uc.setRequestProperty( behalfHeader, putFeed.getOwner() );
+                       uc.setUseCaches(false);
+                       uc.setDoOutput(true);
+                       OutputStream os = null;
+                       int rc = -1;
+                       
+                       try {
+                 uc.connect();
+                 os = uc.getOutputStream();
+                 //os.write( postData );
+
+            } catch (ProtocolException pe) {
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                        logger.error(e.getMessage(), e);
+                 }
+                       } catch (Exception e) {
+                               logger.info( "Exception: " + e.getMessage() );
+                               e.printStackTrace();
+                       }
+                       rc = uc.getResponseCode();
+                       logger.info( "http response code:" + rc );
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+
+
+            if (responsemessage == null) {
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+            if (rc >= 200 && rc < 300 ) {
+                       responseBody = bodyToString( uc.getInputStream() );
+                       logger.info( "responseBody=" + responseBody );
+
+            } else if ( rc == 404 ) {
+               err.setCode( rc );
+               err.setFields( "feedid");
+               String message =  "FeedId " + putFeed.getFeedId() + " not found on DR to update.  Out-of-sync condition?";
+               err.setMessage( message );
+               errorLogger.error( DmaapbcLogMessageEnum.PROV_OUT_OF_SYNC, "Feed", putFeed.getFeedId() );
+               
+            } else {
+               err.setCode( rc );
+               err.setMessage(responsemessage);
+            }
+            
+               } catch (ConnectException ce) {
+                       errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage() );
+            err.setCode( 500 );
+               err.setMessage("Backend connection refused");
+               logger.error(ce.getMessage(), ce);
+               } catch (SocketException se) {
+                       errorLogger.error( DmaapbcLogMessageEnum.SOCKET_EXCEPTION, se.getMessage(), "response from Prov server" );
+                       err.setCode( 500 );
+                       err.setMessage( "Unable to read response from DR");
+                       logger.error(se.getMessage(), se);
+        } catch (Exception e) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doDeleteFeed because unit_test =" + unit_test );
+               } else {
+                   logger.warn("Unable to read response  " );
+                   logger.error(e.getMessage(), e);
+                   try {
+                           err.setCode( uc.getResponseCode());
+                           err.setMessage(uc.getResponseMessage());
+                   } catch (Exception e2) {
+                       err.setCode( 500 );
+                       err.setMessage("Unable to determine response message");
+                       logger.error(e2.getMessage(), e2);
+                   }
+               }
+        }              finally {
+                       try {
+                               if(uc != null) {
+                                   uc.disconnect();
+                               }
+                       } catch ( Exception e ) {
+                               logger.error(e.getMessage(), e);
+                       }
+               }
+               return responseBody;
+       }
+       
+       public String doDeleteDr_Sub(DR_Sub delSub, ApiError err) {
+               logger.info( "entry: doDeleteDr_Sub() "  );
+               byte[] postData = delSub.getBytes(provApi);
+               logger.info( "post fields=" + Arrays.toString(postData));
+               String responsemessage = null;
+               String responseBody = null;
+
+               try {
+       
+                       uc.setRequestMethod("DELETE");
+               
+                       uc.setRequestProperty("Content-Type", subContentType);
+                       uc.setRequestProperty( "charset", "utf-8");
+                       uc.setRequestProperty( behalfHeader, "DGL" );
+                       uc.setUseCaches(false);
+                       uc.setDoOutput(true);
+                       OutputStream os = null;
+                       int rc = -1;
+                       
+                       try {
+                 uc.connect();
+                 os = uc.getOutputStream();
+                 //os.write( postData );
+
+            } catch (ProtocolException pe) {
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                        logger.error(e.getMessage(), e);
+                 }
+                       } catch (Exception e) {
+                               logger.info( "Exception: " + e.getMessage() );
+                               e.printStackTrace();
+                       }
+                       rc = uc.getResponseCode();
+                       logger.info( "http response code:" + rc );
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+
+
+            if (responsemessage == null) {
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+               err.setCode(rc);
+            if (rc == 204 ) {
+                       responseBody = bodyToString( uc.getInputStream() );
+                       logger.info( "responseBody=" + responseBody );
+            } else {
+               err.setMessage(responsemessage);
+            }
+            
+               } catch (ConnectException ce) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doDeleteDr_Sub because unit_test =" + unit_test );
+               } else {
+                   errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage() );
+                   err.setCode( 500 );
+                       err.setMessage("Backend connection refused");
+               }
+               } catch (Exception e) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doDeleteDr_Sub because unit_test =" + unit_test );
+               } else {
+                   logger.error("Unable to read response  ", e.getMessage());
+               }
+        } finally {
+               if(uc != null){
+                   uc.disconnect();
+               }
+        }
+               return responseBody;
+
+       }
+       
+       // add double-quotes around a value
+       // hope his is easier to read than in-line escaping...
+       private String dq( String v ) {
+               return ( "\"" + v + "\"");
+       }
+       private String dq( String k, String v) {
+               return( dq(k) + ":" + dq(v));
+       }
+       private String dqc( String k, String v) {
+               return( dq(k) + ":" + dq(v) + ",");
+       }
+       
+       private String dumpSimulation() {
+               logger.info( "enter dumpSimulation()");
+               String                                  responseBody = 
+                               "{"
+                               + dq("feeds") + ":["
+                               + "{" + dq( "suspend") + ":false,"
+                                         + dq( "groupid") + ":0,"
+                                         + dqc( "description", "Some description" )
+                                         + dqc( "version", "m1.1") 
+                                         + dq( "authorization") + ":"
+                                         + "{" + dq( "endpoint_addrs" ) + ":[],"
+                                                       + dq( "classification", "unclassified")
+                                                       + dq( "endpoint_ids") + ":[{"
+                                                               + dqc( "password", "dradmin" )
+                                                               + dq( "id", "dradmin")
+                                                               + "}]}"
+                                               + dq( "last_mod") + ":1553738110000,"
+                                               + dq( "deleted") + ":false,"
+                                               + dq( "feedid") + ":1,"
+                                               + dqc( "name", "Default PM Feed")
+                                               + dq( "business_description") + ":\"\","
+                                               + dqc( "publisher", "onap")
+                                               + dq( "links") + ":{"
+                                                       + dqc( "subscribe", "https://dmaap-dr-prov/subscribe/1")
+                                                       + dqc( "log", "https://dmaap-dr-prov/feedlog/1")
+                                                       + dqc( "publish", "https://dmaap-dr-prov/publish/1")
+                                                       + dq( "self", "https:/dmaap-dr-prov/feed/1")
+                                                       + "}"
+                                               + dq( "created_date") + ":1553738110000 }"
+                               + "],"
+                               + dq( "groups") + ":["
+                               + "],"
+                               + dq( "subscriptions") + ":["
+                               + "],"
+                               + dq( "ingress") + ":["
+                               + "],"
+                               + dq( "egress") + ":{"
+                               + "},"
+                               + dq( "routing") + ":["
+                               + "],"
+                         + "}";
+               return responseBody;
+       }
+       
+       public String doGetDump( ApiError err ) {
+               logger.info( "entry: doGetDump() "  );
+
+               String responsemessage = null;
+               String responseBody = null;
+
+               try {
+       
+                       uc.setRequestMethod("GET");
+                       int rc = -1;
+                       
+
+                       try {
+                uc.connect();
+       
+
+            } catch (ProtocolException pe) {
+
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                        logger.error(e.getMessage(), e);
+                 }
+                       } catch (Exception e) {
+                               logger.info( "Exception: " + e.getMessage() );
+                               e.printStackTrace();
+                       }
+       
+                       rc = uc.getResponseCode();
+                       logger.info( "http response code:" + rc );
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+       
+
+
+            if (responsemessage == null) {
+
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+       
+               err.setCode(rc);  // may not really be an error, but we save rc
+            if (rc == 200 ) {
+                       responseBody = bodyToString( uc.getInputStream() );
+                       logger.info( "responseBody=" + responseBody );
+            } else {
+               err.setMessage(responsemessage);
+            }
+            
+
+               } catch (ConnectException ce) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doGetNodes because unit_test =" + unit_test );
+                               responseBody = dumpSimulation();
+                                                         
+               } else {
+                   errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, ce.getMessage() );
+                   err.setCode( 500 );
+                       err.setMessage("Backend connection refused");
+                       logger.error(ce.getMessage(), ce);
+               }
+               } catch (Exception e) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doGetNodes because unit_test =" + unit_test );
+                               responseBody = dumpSimulation();
+                                                         
+               } else {
+                   logger.error("Unable to read response  ", e.getMessage());
+               }
+        } finally {
+
+                       if ( uc != null ) uc.disconnect();
+        }
+
+               return responseBody;
+
+       }
+               
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrProvConnection.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrProvConnection.java
new file mode 100644 (file)
index 0000000..9c3fa4e
--- /dev/null
@@ -0,0 +1,271 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.client;
+
+import org.apache.commons.codec.binary.Base64;
+import org.onap.dmaap.dbcapi.aaf.AafDecrypt;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
+
+import java.io.*;
+import java.net.*;
+import java.util.Arrays;
+
+public class MrProvConnection extends BaseLoggingClass{
+        
+    private String provURL;
+    
+    private HttpURLConnection uc;
+
+    
+    private String topicMgrCred;
+    private String authMethod;
+    private    String    user;
+    private    String    encPwd;
+    private    String  unit_test;
+    private boolean hostnameVerify;
+    
+    public MrProvConnection() {
+        String mechIdProperty = "aaf.TopicMgrUser";
+        String pwdProperty = "aaf.TopicMgrPassword";
+        DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+        user = p.getProperty( mechIdProperty, "noMechId@domain.netset.com" );
+        encPwd = p.getProperty( pwdProperty, "notSet" );
+        authMethod = p.getProperty("MR.authentication", "none");
+        topicMgrCred =  getCred();
+        hostnameVerify= "true".equalsIgnoreCase(p.getProperty("MR.hostnameVerify", "true"));
+        unit_test = p.getProperty( "UnitTest", "No" );
+        
+    }
+    
+    private String getCred( ) {
+
+
+        String pwd = "";
+        AafDecrypt decryptor = new AafDecrypt();    
+        pwd = decryptor.decrypt(encPwd);
+        return user + ":" + pwd;    
+    }
+    
+    
+    public boolean makeTopicConnection( MR_Cluster cluster ) {
+        boolean rc = false;
+       logger.info( "connect to cluster: " + cluster.getDcaeLocationName());
+        
+
+        provURL = cluster.getTopicProtocol() + "://" + cluster.getFqdn() + ":" + cluster.getTopicPort() + "/topics/create";
+
+        if ( cluster.getTopicProtocol().equals( "https" ) ) {
+            rc = makeSecureConnection( provURL );
+        } else {
+               rc = makeConnection( provURL );
+        }
+       if ( rc  && unit_test.equals( "Yes" ) ) {
+               // set timeouts low so we don't hold up unit tests in build process
+            uc.setReadTimeout(5);
+            uc.setConnectTimeout(5);                   
+       }
+       return rc;
+        
+    }
+
+    private boolean makeSecureConnection( String pURL ) {
+        logger.info( "makeConnection to " + pURL );
+    
+        try {
+       
+                       HostnameVerifier hostnameVerifier = new HostnameVerifier() {
+                               @Override
+                               public boolean verify( String hostname, SSLSession session ) {
+                                       return true;
+                               }
+                       
+                       };
+            URL u = new URL( pURL );
+            uc = (HttpsURLConnection) u.openConnection();
+            uc.setInstanceFollowRedirects(false);
+            if ( ! hostnameVerify ) {
+                               HttpsURLConnection ucs = (HttpsURLConnection) uc;
+                               ucs.setHostnameVerifier(hostnameVerifier);
+                       }
+            logger.info( "open secure connect to " + pURL );
+            return(true);
+        } catch( UnknownHostException uhe ){
+            logger.error( "Caught UnknownHostException for " + pURL);
+            return(false);
+        } catch (Exception e) {
+            logger.error("Unexpected error during openConnection of " + pURL );
+            logger.error("Unexpected error during openConnection of ",e );
+            return(false);
+        } 
+
+    }
+    private boolean makeConnection( String pURL ) {
+        logger.info( "makeConnection to " + pURL );
+    
+        try {
+            URL u = new URL( pURL );
+            uc = (HttpURLConnection) u.openConnection();
+            uc.setInstanceFollowRedirects(false);                      
+
+            logger.info( "open connect to " + pURL );
+            return(true);
+        } catch( UnknownHostException uhe ){
+            logger.error( "Caught UnknownHostException for " + pURL);
+            return(false);
+        } catch (Exception e) {
+            logger.error("Unexpected error during openConnection of " + pURL );
+            logger.error("Unexpected error during openConnection of ",e );
+            return(false);
+        } 
+
+    }
+    
+    static String bodyToString( InputStream is ) {
+        StringBuilder sb = new StringBuilder();
+        BufferedReader br = new BufferedReader( new InputStreamReader(is));
+        String line;
+        try {
+            while ((line = br.readLine()) != null ) {
+                sb.append( line );
+            }
+        } catch (IOException ex ) {
+            errorLogger.error( "IOexception:" + ex);
+        }
+            
+        return sb.toString();
+    }
+    
+    public String doPostTopic( Topic postTopic, ApiError err ) {
+        String auth =  "Basic " + Base64.encodeBase64String(topicMgrCred.getBytes());
+
+
+        String responsemessage = null;
+        int rc = -1;
+
+
+        try {
+            byte[] postData = postTopic.getBytes();
+            logger.info( "post fields=" + Arrays.toString(postData));
+            
+                       if ( authMethod.equalsIgnoreCase("basicAuth") ) {
+                               uc.setRequestProperty("Authorization", auth);
+                               logger.info( "Authenticating with " + auth );
+                       } else if ( authMethod.equalsIgnoreCase("cert")) {
+                               logger.error( "MR.authentication set for client certificate.  Not supported yet.");
+                       }
+            uc.setRequestMethod("POST");
+            uc.setRequestProperty("Content-Type", "application/json");
+            uc.setRequestProperty( "charset", "utf-8");
+            uc.setRequestProperty( "Content-Length", Integer.toString( postData.length ));
+            uc.setUseCaches(false);
+            uc.setDoOutput(true);
+            OutputStream os = null;
+
+            
+            try {
+                 uc.connect();
+                 os = uc.getOutputStream();
+                 os.write( postData );
+
+            } catch (ProtocolException pe) {
+                 // Rcvd error instead of 100-Continue
+                 try {
+                     // work around glitch in Java 1.7.0.21 and likely others
+                     // without this, Java will connect multiple times to the server to run the same request
+                     uc.setDoOutput(false);
+                 } catch (Exception e) {
+                 }
+            } catch ( UnknownHostException uhe ) {
+                errorLogger.error( DmaapbcLogMessageEnum.UNKNOWN_HOST_EXCEPTION , "Unknown Host Exception" , provURL );
+                err.setCode(500);
+                err.setMessage("Unknown Host Exception");
+                err.setFields( uc.getURL().getHost());
+                return new String( "500: " + uhe.getMessage());
+            }catch ( ConnectException ce ) {
+                       if ( unit_test.equals( "Yes" ) ) {
+                               err.setCode(200);
+                               err.setMessage( "simulated response");
+                               logger.info( "artificial 200 response from doPostMessage because unit_test =" + unit_test );
+               } else { 
+                       errorLogger.error( DmaapbcLogMessageEnum.HTTP_CONNECTION_EXCEPTION, provURL, "HTTP Connection Exception"  );
+                       err.setCode(500);
+                       err.setMessage("HTTP Connection Exception");
+                       err.setFields( uc.getURL().getHost());
+                return new String( "500: " + ce.getMessage());
+               }
+            }
+            rc = uc.getResponseCode();
+            logger.info( "http response code:" + rc );
+            err.setCode(rc);
+            responsemessage = uc.getResponseMessage();
+            logger.info( "responsemessage=" + responsemessage );
+            err.setMessage(responsemessage);
+
+
+            if (responsemessage == null) {
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         responsemessage = h0.substring(j + 1);
+                     }
+                 }
+            }
+            if (rc >= 200 && rc < 300 ) {
+                String responseBody = null;
+                 responseBody = bodyToString( uc.getInputStream() );
+                logger.info( "responseBody=" + responseBody );
+                return responseBody;
+
+            } 
+            
+        } catch (Exception e) {
+            errorLogger.error("Unable to read response:  " + e.getMessage() );
+           
+        }
+        finally {
+            try {
+                uc.disconnect();
+            } catch ( Exception e ) {
+                errorLogger.error("Unable to disconnect");
+            }
+        }
+        return new String( rc +": " + responsemessage );
+
+    }
+    
+
+
+        
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrTopicConnection.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/client/MrTopicConnection.java
new file mode 100644 (file)
index 0000000..b3f713f
--- /dev/null
@@ -0,0 +1,239 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.client;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.net.HttpURLConnection;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+import org.apache.commons.codec.binary.Base64;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+public class MrTopicConnection extends BaseLoggingClass  {
+       private String topicURL;
+       
+       private HttpURLConnection uc;
+
+       
+       private  String mmProvCred; 
+       private String unit_test;
+       private String authMethod;
+       private boolean hostnameVerify;
+
+       public MrTopicConnection(String user, String pwd ) {
+               mmProvCred = new String( user + ":" + pwd );
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+        unit_test = p.getProperty( "UnitTest", "No" );
+       authMethod = p.getProperty("MR.authentication", "none");
+       hostnameVerify= "true".equalsIgnoreCase(p.getProperty("MR.hostnameVerify", "true"));
+       }
+       
+       public boolean makeTopicConnection( MR_Cluster cluster, String topic, String overrideFqdn ) {
+               String fqdn = overrideFqdn != null ? overrideFqdn : cluster.getFqdn();
+               logger.info( "connect to cluster: " + fqdn + " for topic: " + topic );
+       
+
+               topicURL = cluster.getTopicProtocol() + "://" + fqdn + ":" + cluster.getTopicPort() + "/events/" + topic ;
+
+               if ( "https".equals(cluster.getTopicProtocol())) {
+                       return makeSecureConnection( topicURL );
+               }
+               return makeConnection( topicURL );
+       }
+
+       
+       private boolean makeSecureConnection( String pURL ) {
+               logger.info( "makeConnection to " + pURL );
+               
+               try {
+                       HostnameVerifier hostnameVerifier = new HostnameVerifier() {
+                               @Override
+                               public boolean verify( String hostname, SSLSession session ) {
+                                       return true;
+                               }
+                       
+                       };
+       
+               
+                       URL u = new URL( pURL );
+                       uc = (HttpsURLConnection) u.openConnection();                   
+                       uc.setInstanceFollowRedirects(false);
+                       if ( ! hostnameVerify ) {
+                               HttpsURLConnection ucs = (HttpsURLConnection) uc;
+                               ucs.setHostnameVerifier(hostnameVerifier);
+                       }
+       
+                       logger.info( "open connection to " + pURL );
+                       return(true);
+               } catch (Exception e) {
+            logger.error("Unexpected error during openConnection of " + pURL );
+            logger.error("Error", e);;
+            return(false);
+        }
+
+       }
+       private boolean makeConnection( String pURL ) {
+               logger.info( "makeConnection to " + pURL );
+       
+               try {
+                       URL u = new URL( pURL );
+                       uc = (HttpURLConnection) u.openConnection();
+                       uc.setInstanceFollowRedirects(false);
+                       logger.info( "open connection to " + pURL );
+                       return(true);
+               } catch (Exception e) {
+            logger.error("Unexpected error during openConnection of " + pURL );
+            logger.error("error", e);
+            return(false);
+        }
+
+       }
+       
+       static String bodyToString( InputStream is ) {
+               StringBuilder sb = new StringBuilder();
+               BufferedReader br = new BufferedReader( new InputStreamReader(is));
+               String line;
+               try {
+                       while ((line = br.readLine()) != null ) {
+                               sb.append( line );
+                       }
+               } catch (IOException ex ) {
+                       errorLogger.error( "IOexception:" + ex);
+               }
+                       
+               return sb.toString();
+       }
+       
+       public ApiError doPostMessage( String postMessage ) {
+               ApiError response = new ApiError();
+               String auth =  "Basic " + Base64.encodeBase64String(mmProvCred.getBytes());
+
+
+
+               try {
+                       byte[] postData = postMessage.getBytes();
+                       logger.info( "post fields=" + postMessage );
+                       if ( authMethod.equalsIgnoreCase("basicAuth") ) {
+                               uc.setRequestProperty("Authorization", auth);
+                               logger.info( "Authenticating with " + auth );
+                       } else if ( authMethod.equalsIgnoreCase("cert")) {
+                               logger.error( "MR.authentication set for client certificate.  Not supported yet.");
+                       }
+                       uc.setRequestMethod("POST");
+                       uc.setRequestProperty("Content-Type", "application/json");
+                       uc.setRequestProperty( "charset", "utf-8");
+                       uc.setRequestProperty( "Content-Length", Integer.toString( postData.length ));
+                       uc.setUseCaches(false);
+                       uc.setDoOutput(true);
+                       OutputStream os = null;
+
+                       
+                       try {
+                 uc.connect();
+                 os = uc.getOutputStream();
+                 os.write( postData );
+
+            } catch (ProtocolException pe) {
+                 // Rcvd error instead of 100-Continue
+               callSetDoOutputOnError();
+                 
+            }  catch ( SSLException se ) {
+               logger.error("Error", se);
+                       response.setCode(500);
+                       response.setMessage( se.getMessage());
+                       return response;
+               
+            }
+                       response.setCode( uc.getResponseCode());
+                       logger.info( "http response code:" + response.getCode());
+            response.setMessage( uc.getResponseMessage() ); 
+            logger.info( "response message=" + response.getMessage() );
+
+
+            if ( response.getMessage() == null) {
+                 // work around for glitch in Java 1.7.0.21 and likely others
+                 // When Expect: 100 is set and a non-100 response is received, the response message is not set but the response code is
+                 String h0 = uc.getHeaderField(0);
+                 if (h0 != null) {
+                     int i = h0.indexOf(' ');
+                     int j = h0.indexOf(' ', i + 1);
+                     if (i != -1 && j != -1) {
+                         response.setMessage( h0.substring(j + 1) );
+                     }
+                 }
+            }
+            if ( response.is2xx() ) {
+                       response.setFields( bodyToString( uc.getInputStream() ) );
+                       logger.info( "responseBody=" + response.getFields() );
+                       return response;
+
+            } 
+            
+               } catch (Exception e) {
+               if ( unit_test.equals( "Yes" ) ) {
+                               response.setCode(201);
+                               response.setMessage( "simulated response");
+                               logger.info( "artificial 201 response from doPostMessage because unit_test =" + unit_test );
+               } else {
+
+                               response.setCode(500);
+                               response.setMessage( "Unable to read response");
+                               logger.warn( response.getMessage() );
+               logger.error("Error", e);
+                       }
+        }
+               finally {
+                       try {
+                               uc.disconnect();
+                       } catch ( Exception e ) {
+                               logger.error("Error", e);
+                       }
+               }
+               return response;
+
+       }
+       
+       public void callSetDoOutputOnError() {
+               try {
+            // work around glitch in Java 1.7.0.21 and likely others
+            // without this, Java will connect multiple times to the server to run the same request
+            uc.setDoOutput(false);
+        } catch (Exception e) {
+                       logger.error("Error", e);
+        }
+       }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnWrapper.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnWrapper.java
new file mode 100644 (file)
index 0000000..2317fe4
--- /dev/null
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.database;
+
+import java.sql.*;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+
+
+public abstract class ConnWrapper<T, U>        {
+       EELFLogger logger = EELFManager.getInstance().getLogger( ConnWrapper.class );
+       protected Connection c;
+       protected PreparedStatement ps;
+       protected ResultSet     rs;
+       protected abstract T run(U u) throws Exception;
+       public T protect(ConnectionFactory cf, U u) {
+               try {
+                       try {
+                               return(attempt(cf, u, false));
+                       } catch (SQLException sqle) {
+                               logger.error("Error", sqle);
+                               return(attempt(cf, u, true));
+                       }
+               } catch (RuntimeException rte) {
+                       throw rte;
+               } catch (Exception e) {
+                       throw new DBException(e);
+               }
+       }
+       private T attempt(ConnectionFactory cf, U u, boolean fresh) throws Exception {
+               c = null;
+               ps = null;
+               rs = null;
+               try {
+                       c = cf.get(fresh);
+                       T ret = run(u);
+                       cf.release(c);
+                       c = null;
+                       return(ret);
+               } finally {
+                       if (rs != null) { 
+                               try { 
+                                       rs.close(); 
+                               } catch (Exception e) {
+                                       logger.error("Error", e);
+                               }}
+                       rs = null;
+                       if (ps != null) { 
+                               try { 
+                                       ps.close(); 
+                               } catch (Exception e) {
+                                       logger.error("Error", e);
+                               }}
+                       ps = null;
+                       if (c != null) { 
+                               try { 
+                                       c.close(); 
+                               } catch (Exception e) {
+                                       logger.error("Error", e);
+                               }}
+                       c = null;
+               }
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnectionFactory.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/ConnectionFactory.java
new file mode 100644 (file)
index 0000000..6004d9b
--- /dev/null
@@ -0,0 +1,117 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.database;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+public class ConnectionFactory extends BaseLoggingClass {
+
+        static final int PREPARE_PSQL_CONNECTION_ATTEMPTS = 5;
+
+       static {
+               try {
+                       Class.forName("org.postgresql.Driver");
+               } catch (Exception e) {
+                       errorLogger.error("Unable to load postgres driver " + e, e);
+               }
+       }
+       private static ConnectionFactory instance = new ConnectionFactory();
+       private String  host;
+       private String  dbname;
+       private String  dbuser;
+       private String  dbcr;
+       private String  schema;
+       
+       public ConnectionFactory() {
+               Properties p = DmaapConfig.getConfig();
+               host = p.getProperty("DB.host", "dcae-pstg-write-ftl.domain.notset.com");
+               dbname = p.getProperty("DB.name", "dmaap");
+               dbuser = getValue(p, "DB.user", "dmaap_admin");
+               dbcr = getValue(p, "DB.cred", "test234-ftl");
+               schema = p.getProperty("DB.schema", "dmaap_admin");
+       }
+
+       private static String getValue(final Properties props, final String value, final String defaultValue) {
+               String prop = props.getProperty(value, defaultValue);
+               if (prop != null && prop.matches("[$][{].*[}]$")) {
+                       return System.getenv(prop.substring(2, prop.length() - 1));
+               }
+               return prop;
+       }
+
+       public static ConnectionFactory getDefaultInstance() {
+               return(instance);
+       }
+       private Connection[] pool = new Connection[5];
+       private int     cur;
+       public Connection get(boolean fresh) throws SQLException {
+               if (!fresh) {
+                       synchronized(this) {
+                               if (cur > 0) {
+                                       return(pool[--cur]);
+                               }
+                       }
+               }
+               Properties p = new Properties();
+               p.put("user", dbuser);
+               p.put("password", dbcr);
+               for (int i=1; i<PREPARE_PSQL_CONNECTION_ATTEMPTS; i++){
+                       try{
+                               return(DriverManager.getConnection("jdbc:postgresql://" + host + "/" + dbname, p));
+                       }catch(SQLException e){
+                               errorLogger.error("Unable to connect to the postgres server. " + i + "attempt failed. ", e);
+                               waitFor(1);
+                       }
+               }
+               return(DriverManager.getConnection("jdbc:postgresql://" + host + "/" + dbname, p));
+       }
+       public String getSchema() {
+               return(schema);
+       }
+       public void release(Connection c) {
+               synchronized(this) {
+                       if (cur < pool.length) {
+                               pool[cur++] = c;
+                               return;
+                       }
+               }
+               try { 
+                       c.close(); 
+               } catch (Exception e) {
+                       errorLogger.error("Error", e);
+               }
+       }
+       private void waitFor(long seconds){
+               try {
+                       TimeUnit.SECONDS.sleep(seconds);
+               } catch (InterruptedException e) {
+                       debugLogger.debug("Waiting interrupted. ", e);
+                       Thread.currentThread().interrupt();
+               }
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBException.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBException.java
new file mode 100644 (file)
index 0000000..6538167
--- /dev/null
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.database;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+
+public class DBException extends RuntimeException {
+       static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger();
+       public DBException(Exception e) {
+               super(e);
+               errorLogger.error(DmaapbcLogMessageEnum.DB_ACCESS_ERROR,  e.getMessage());
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBFieldHandler.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBFieldHandler.java
new file mode 100644 (file)
index 0000000..618932e
--- /dev/null
@@ -0,0 +1,205 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.database;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import java.lang.reflect.Method;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.HashMap;
+import java.util.Map;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+
+
+public class DBFieldHandler    {
+       static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger();       
+
+       public DBFieldHandler(Class<?> c, String fieldname, int fieldnum) throws Exception {
+               this(c, fieldname, fieldnum, null);
+       }
+       public DBFieldHandler(Class<?> c, String fieldname, int fieldnum, SqlOp op) throws Exception {
+               this.fieldnum = fieldnum;
+               StringBuilder sb = new StringBuilder();
+               for (String s: fieldname.split("_")) {  
+                       sb.append(s.substring(0, 1).toUpperCase()).append(s.substring(1));
+               }
+               String camelcase = sb.toString();
+               try {
+                       objget = c.getMethod("is" + camelcase);
+               } catch (Exception e) {
+                       errorLogger.warn("No 'is' method for " + c.getName() + " so trying 'get' method");
+                       objget = c.getMethod("get" + camelcase);
+               }
+               objset = c.getMethod("set" + camelcase, objget.getReturnType());
+               sqlop = op;
+               if (sqlop != null) {
+                       return;
+               }
+               Class<?> x = objget.getReturnType();
+               if (x.isEnum()) {
+                       sqlop = new EnumSql(x);
+                       return;
+               }
+               sqlop = sqltypes.get(x.getName());
+               if (sqlop != null) {
+                       return;
+               }
+               errorLogger.error(DmaapbcLogMessageEnum.DB_NO_FIELD_HANDLER,  c.getName(),  fieldname,  Integer.toString(fieldnum),  x.getName());
+       }
+       
+       public static interface SqlOp   {
+               public Object get(ResultSet rs, int index) throws Exception;
+               public void set(PreparedStatement ps, int index, Object value) throws Exception;
+       }
+       private static class    AofString implements SqlOp {
+               public Object get(ResultSet rs, int index) throws Exception {
+                       String val = rs.getString(index);
+                       if (val == null) {
+                               return(null);
+                       }
+                       String[] ret = val.split(",");
+                       for (int i = 0; i < ret.length; i++) {
+                               ret[i] = funesc(ret[i]);
+                       }
+                       return(ret);
+               }
+               public void set(PreparedStatement ps, int index, Object x) throws Exception {
+                       String[] val = (String[])x;
+                       if (val == null) {
+                               ps.setString(index, null);
+                               return;
+                       }
+                       StringBuilder sb = new StringBuilder();
+                       String sep = "";
+                       for (String s: val) {
+                               sb.append(sep).append(fesc(s));
+                               sep = ",";
+                       }
+                       ps.setString(index, sb.toString());
+               }
+       }
+       private static class    EnumSql implements SqlOp {
+               private Class   enclass;
+               public EnumSql(Class enclass) {
+                       this.enclass = enclass;
+               }
+               @SuppressWarnings("unchecked")
+               public Object get(ResultSet rs, int index) throws Exception {
+                       String val = rs.getString(index);
+                       if (val == null) {
+                               return(null);
+                       } else {
+                               return(Enum.valueOf(enclass, val));
+                       }
+               }
+               public void set(PreparedStatement ps, int index, Object value) throws Exception {
+                       if (value == null) {
+                               ps.setString(index, null);
+                       } else {
+                               ps.setString(index, value.toString());
+                       }
+               }
+       }
+       private static class    SqlDate implements SqlOp {
+               public Object get(ResultSet rs, int index) throws Exception {
+                       return(rs.getTimestamp(index));
+               }
+               public void set(PreparedStatement ps, int index, Object val) throws Exception {
+                       if (val instanceof java.util.Date && !(val instanceof java.sql.Timestamp)) {
+                               val = new java.sql.Timestamp(((java.util.Date)val).getTime());
+                       }
+                       ps.setTimestamp(index, (java.sql.Timestamp)val);
+               }
+       }
+        private static class    SqlType implements SqlOp {
+                private Method   sqlget;
+                private Method   sqlset;
+                private SqlType(String tag) throws Exception {
+                       sqlget = ResultSet.class.getMethod("get" + tag, Integer.TYPE);
+                        sqlset = PreparedStatement.class.getMethod("set" + tag, Integer.TYPE, sqlget.getReturnType());
+                        sqltypes.put(sqlget.getReturnType().getName(), this);
+                }
+               public Object get(ResultSet rs, int index) throws Exception {
+                       return(sqlget.invoke(rs, index));
+               }
+               public void set(PreparedStatement ps, int index, Object val) throws Exception {
+                       try {
+                               sqlset.invoke(ps, index, val);
+                       } catch (Exception e) {
+                               errorLogger.error(DmaapbcLogMessageEnum.DB_FIELD_INIT_ERROR,  Integer.toString(index), val.toString(), ps.toString());
+                               throw e;
+                       }
+               }
+        }
+        private static Map<String, SqlOp> sqltypes;
+        static {
+                sqltypes = new HashMap<>();
+               sqltypes.put("[Ljava.lang.String;", new AofString());
+               sqltypes.put("java.util.Date", new SqlDate());
+                try {
+                        new SqlType("Boolean");
+                        new SqlType("Timestamp");
+                        new SqlType("Double");
+                        new SqlType("Float");
+                        new SqlType("Int");
+                        new SqlType("Long");
+                        new SqlType("Short");
+                        new SqlType("String");
+                } catch (Exception e) {
+                       errorLogger.error("Error", e);
+                       errorLogger.error(DmaapbcLogMessageEnum.DB_ACCESS_INIT_ERROR,  e.getMessage() );
+                }
+        }
+       private Method  objget;
+       private Method  objset;
+       private SqlOp   sqlop;
+       private int     fieldnum;
+       public void copy(Object from, Object to) throws Exception {
+               objset.invoke(to, objget.invoke(from));
+       }
+       public void setKey(Object o, String key) throws Exception {
+               objset.invoke(o, key);
+       }
+       public String getKey(Object o) throws Exception {
+               return((String)objget.invoke(o));
+       }
+       public void toSQL(Object o, PreparedStatement ps) throws Exception {
+               sqlop.set(ps, fieldnum, objget.invoke(o));
+       }
+       public void fromSQL(ResultSet r, Object o) throws Exception {
+               objset.invoke(o, sqlop.get(r, fieldnum));
+       }
+       public static String fesc(String s) {
+               if (s == null) {
+                       return(s);
+               }
+               return(s.replaceAll("@", "@a").replaceAll(";", "@s").replaceAll(",", "@c"));
+       }
+       public static String funesc(String s) {
+               if (s == null) {
+                       return(s);
+               }
+               return(s.replaceAll("@c", ",").replaceAll("@s", ";").replaceAll("@a", "@"));
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBMap.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBMap.java
new file mode 100644 (file)
index 0000000..b3282ed
--- /dev/null
@@ -0,0 +1,137 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.database;
+
+import java.sql.*;
+import java.util.*;
+
+public class DBMap<C> extends TableHandler<C> implements Map<String, C>        {
+       public DBMap(Class<C> cls, String tabname, String keyfield) throws Exception {
+               this(ConnectionFactory.getDefaultInstance(), cls, tabname, keyfield);
+       }
+       public DBMap(ConnectionFactory cf, Class<C> cls, String tabname, String keyfield) throws Exception {
+               super(cf, cls, tabname, keyfield);
+       }
+       public void clear() {
+               throw new UnsupportedOperationException();
+       }
+       public boolean containsKey(Object key) {
+               return(get(key) != null);
+       }
+       public boolean containsValue(Object value) {
+               throw new UnsupportedOperationException();
+       }
+       public boolean isEmpty() {
+               return(false);
+       }
+       public Set<Map.Entry<String, C>> entrySet() {
+               return(list());
+       }
+       public Set<String> keySet() {
+               Set<String> ret = new HashSet<>();
+               for (Map.Entry<String, C> x: list()) {
+                       ret.add(x.getKey());
+               }
+               return(ret);
+       }
+       public void putAll(Map<? extends String, ? extends C> m) {
+               throw new UnsupportedOperationException();
+       }
+       public int size() {
+               return(2);
+       }
+       public Collection<C> values() {
+               Collection<C> ret = new ArrayList<>();
+               for (Map.Entry<String, C> x: list()) {
+                       ret.add(x.getValue());
+               }
+               return(ret);
+       }
+       public C get(Object key) {
+               if (!(key instanceof String)) {
+                       return(null);
+               }
+               return((new ConnWrapper<C, String>() {
+                       protected C run(String key) throws Exception {
+                               ps = c.prepareStatement(getstmt);
+                               ps.setString(1, (String)key);
+                               rs = ps.executeQuery();
+                               if (!rs.next()) {
+                                       return(null);
+                               }
+                               C ret = cls.newInstance();
+                               for (DBFieldHandler f: fields) {
+                                       f.fromSQL(rs, ret);
+                               }
+                               return(ret);
+                       }
+               }).protect(cf, (String)key));
+       }
+       public Set<Map.Entry<String, C>> list() {
+               return((new ConnWrapper<Set<Map.Entry<String, C>>, Object>() {
+                       protected Set<Map.Entry<String, C>> run(Object junk) throws Exception {
+                               DBFieldHandler keyfield = fields[fields.length - 1];
+                               ps = c.prepareStatement(liststmt);
+                               rs = ps.executeQuery();
+                               Set<Map.Entry<String, C>> ret = new HashSet<>();
+                               while (rs.next()) {
+                                       C val = cls.newInstance();
+                                       for (DBFieldHandler f: fields) {
+                                               f.fromSQL(rs, val);
+                                       }
+                                       String key = keyfield.getKey(val);
+                                       ret.add(new AbstractMap.SimpleEntry<String, C>(key, val));
+                               }
+                               return(ret);
+                       }
+               }).protect(cf, null));
+       }
+       public C put(String key, C val) {
+               try {
+                       fields[fields.length - 1].setKey(val, key);
+               } catch (Exception e) {
+                       throw new DBException(e);
+               }
+               return((new ConnWrapper<C, C>() {
+                       protected C run(C val) throws Exception {
+                               ps = c.prepareStatement(insorreplstmt);
+                               for (DBFieldHandler f: fields) {
+                                       f.toSQL(val, ps);
+                               }
+                               ps.executeUpdate();
+                               return(null);
+                       }
+               }).protect(cf, val));
+       }
+       public C remove(Object key) {
+               if (!(key instanceof String)) {
+                       return(null);
+               }
+               return((new ConnWrapper<C, String>() {
+                       protected C run(String key) throws Exception {
+                               ps = c.prepareStatement(delstmt);
+                               ps.setString(1, key);
+                               ps.executeUpdate();
+                               return(null);
+                       }
+               }).protect(cf, (String)key));
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBSingleton.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DBSingleton.java
new file mode 100644 (file)
index 0000000..2633d70
--- /dev/null
@@ -0,0 +1,98 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.database;
+
+import java.sql.*;
+import java.util.*;
+
+import org.onap.dmaap.dbcapi.util.Singleton;
+
+public class DBSingleton<C> extends TableHandler<C> implements Singleton<C>    {
+       private C singleton;
+       public DBSingleton(Class<C> cls, String tabname) throws Exception {
+               this(ConnectionFactory.getDefaultInstance(), cls, tabname);
+       }
+       public DBSingleton(ConnectionFactory cf, Class<C> cls, String tabname) throws Exception {
+               super(cf, cls, tabname, null);
+               singleton = cls.newInstance();
+       }
+       public C get() {
+               return((new ConnWrapper<C, Object>() {
+                       protected C run(Object junk) throws Exception {
+                               ps = c.prepareStatement(getstmt);
+                               rs = ps.executeQuery();
+                               if (!rs.next()) {
+                                       return(null);
+                               }
+                               for (DBFieldHandler f: fields) {
+                                       f.fromSQL(rs, singleton);
+                               }
+                               return(singleton);
+                       }
+               }).protect(cf, null));
+       }
+       public void init(C val) {
+               if (get() != null) {
+                       return;
+               }
+               (new ConnWrapper<Void, C>() {
+                       protected Void run(C val) throws Exception {
+                               ps = c.prepareStatement(initstmt);
+                               for (DBFieldHandler f: fields) {
+                                       f.toSQL(val, ps);
+                               }
+                               ps.executeUpdate();
+                               if (val != singleton) {
+                                       for (DBFieldHandler f: fields) {
+                                               f.copy(val, singleton);
+                                       }
+                               }
+                               return(null);
+                       }
+               }).protect(cf, val);
+       }
+       public void update(C val) {
+               (new ConnWrapper<Void, C>() {
+                       protected Void run(C val) throws Exception {
+                               ps = c.prepareStatement(insorreplstmt);
+                               for (DBFieldHandler f: fields) {
+                                       f.toSQL(val, ps);
+                               }
+                               ps.executeUpdate();
+                               if (val != singleton) {
+                                       for (DBFieldHandler f: fields) {
+                                               f.copy(val, singleton);
+                                       }
+                               }
+                               return(null);
+                       }
+               }).protect(cf, val);
+       }
+       public void remove() throws DBException {
+               (new ConnWrapper<Void, Object>() {
+                       protected Void run(Object junk) throws Exception {
+                               ps = c.prepareStatement(delstmt);
+                               ps.executeUpdate();
+                               return(null);
+                       }
+               }).protect(cf, null);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DatabaseClass.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/DatabaseClass.java
new file mode 100644 (file)
index 0000000..c82e964
--- /dev/null
@@ -0,0 +1,286 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.database;
+
+import java.util.*;
+import java.sql.*;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.Singleton;
+
+
+public class DatabaseClass extends BaseLoggingClass {
+
+    private static Singleton<Dmaap> dmaap;
+    private static Map<String, DcaeLocation> dcaeLocations;
+    private static Map<String, DR_Node> dr_nodes;
+    private static Map<String, DR_Pub> dr_pubs;
+    private static Map<String, DR_Sub> dr_subs;
+    private static Map<String, MR_Client> mr_clients;
+    private static Map<String, MR_Cluster> mr_clusters;
+    private static Map<String, Feed> feeds;
+    private static Map<String, Topic> topics;
+    private static Map<String, MirrorMaker> mirrors;
+
+    private static long lastTime = 0L;
+    private static DBType databaseType;
+
+    private enum DBType {
+        PGSQL, MEMORY
+    }
+
+    public static Singleton<Dmaap> getDmaap() {
+        return dmaap;
+    }
+
+
+    public static Map<String, DcaeLocation> getDcaeLocations() {
+        return dcaeLocations;
+    }
+
+    public static Map<String, DR_Node> getDr_nodes() {
+        return dr_nodes;
+    }
+
+    public static Map<String, DR_Sub> getDr_subs() {
+        return dr_subs;
+    }
+
+    public static Map<String, DR_Pub> getDr_pubs() {
+        return dr_pubs;
+    }
+
+    public static Map<String, MR_Client> getMr_clients() {
+        return mr_clients;
+    }
+
+
+    public static Map<String, MR_Cluster> getMr_clusters() {
+        return mr_clusters;
+    }
+
+    public static Map<String, Feed> getFeeds() {
+        return feeds;
+    }
+
+    public static Map<String, Topic> getTopics() {
+        return topics;
+    }
+
+    public static Map<String, MirrorMaker> getMirrorMakers() {
+        return mirrors;
+    }
+
+    static {
+        try {
+            appLogger.info("begin static initialization");
+            appLogger.info("initializing dmaap");
+            determineDatabaseType();
+
+            switch (databaseType) {
+                case PGSQL:
+                    databaseResourceInit();
+                    break;
+                case MEMORY:
+                    inMemoryResourceInit();
+                    break;
+            }
+
+            dmaap.init(new Dmaap.DmaapBuilder().setVer("0").setTnr("").setDn("").setDpu("").setLu("").setBat("").setNk("").setAko("").createDmaap());
+            // force initial read from DB, if it exists
+            @SuppressWarnings("unused")
+            Dmaap dmx = dmaap.get();
+
+            // old code in this spot would read from properties file as part of init.
+            // but all those properties are now set via /dmaap API
+
+        } catch (Exception e) {
+            errorLogger.error("Error", e);
+            errorLogger.error(DmaapbcLogMessageEnum.DB_UPDATE_ERROR, e.getMessage());
+        }
+
+    }
+
+    public static synchronized String getNextClientId() {
+
+        long id = System.currentTimeMillis();
+        if (id <= lastTime) {
+            id = lastTime + 1;
+        }
+        lastTime = id;
+        return Long.toString(id);
+    }
+
+    public static synchronized void clearDatabase() {
+        switch (databaseType) {
+            case PGSQL:
+                try {
+                    initDatabase();
+                } catch (Exception e) {
+                    errorLogger.error("Error initializing database access " + e, e);
+                }
+                break;
+            case MEMORY:
+                initMemoryDatabase();
+                break;
+        }
+    }
+
+    private static void inMemoryResourceInit() {
+        appLogger.info("Data from memory");
+        dmaap = new Singleton<Dmaap>() {
+            private Dmaap dmaap;
+
+            public void remove() {
+                dmaap = null;
+            }
+
+            public void init(Dmaap val) {
+                if (dmaap == null) {
+                    dmaap = val;
+                } else {
+                    update(val);
+                }
+            }
+
+            public Dmaap get() {
+                return (dmaap);
+            }
+
+            public void update(Dmaap nd) {
+                dmaap.setVersion(nd.getVersion());
+                dmaap.setTopicNsRoot(nd.getTopicNsRoot());
+                dmaap.setDmaapName(nd.getDmaapName());
+                dmaap.setDrProvUrl(nd.getDrProvUrl());
+                dmaap.setBridgeAdminTopic(nd.getBridgeAdminTopic());
+                dmaap.setLoggingUrl(nd.getLoggingUrl());
+                dmaap.setNodeKey(nd.getNodeKey());
+                dmaap.setAccessKeyOwner(nd.getAccessKeyOwner());
+            }
+        };
+        initMemoryDatabase();
+    }
+
+    private static void databaseResourceInit() {
+        appLogger.info("Data from database");
+        try {
+            LoadSchema.loadSchema();
+        } catch (Exception e) {
+            appLogger.warn("Problem updating DB schema", e);
+        }
+        try {
+            Thread.sleep(5000);
+            dmaap = new DBSingleton<>(Dmaap.class, "dmaap");
+            TableHandler.setSpecialCase("topic", "replication_case", new TopicReplicationTypeHandler());
+            TableHandler.setSpecialCase("mirror_maker", "topics", new MirrorTopicsHandler());
+            initDatabase();
+        } catch (Exception e) {
+            errorLogger.error("Error initializing database access " + e, e);
+            System.exit(1);
+        }
+    }
+
+    private static class MirrorTopicsHandler implements DBFieldHandler.SqlOp {
+
+        public Object get(ResultSet rs, int index) throws Exception {
+            String val = rs.getString(index);
+            if (val == null) {
+                return (null);
+            }
+            List<String> rv = new ArrayList<>();
+            for (String s : val.split(",")) {
+                rv.add(new String(s));
+            }
+            return (rv);
+        }
+
+        public void set(PreparedStatement ps, int index, Object val) throws Exception {
+            if (val == null) {
+                ps.setString(index, null);
+                return;
+            }
+            @SuppressWarnings("unchecked")
+            List<String> xv = (List<String>) val;
+            StringBuilder sb = new StringBuilder();
+            String sep = "";
+            for (Object o : xv) {
+                String rv = (String) o;
+                sb.append(sep).append(DBFieldHandler.fesc(rv));
+                sep = ",";
+            }
+            ps.setString(index, sb.toString());
+        }
+    }
+
+    private static class TopicReplicationTypeHandler implements DBFieldHandler.SqlOp {
+
+        public Object get(ResultSet rs, int index) throws Exception {
+            int val = rs.getInt(index);
+
+            return (ReplicationType.valueOf(val));
+        }
+
+        public void set(PreparedStatement ps, int index, Object val) throws Exception {
+            if (val == null) {
+                ps.setInt(index, 0);
+                return;
+            }
+            @SuppressWarnings("unchecked")
+            ReplicationType rep = (ReplicationType) val;
+            ps.setInt(index, rep.getValue());
+        }
+    }
+
+    private static void initMemoryDatabase() {
+        dcaeLocations = new HashMap<>();
+        dr_nodes = new HashMap<>();
+        dr_pubs = new HashMap<>();
+        dr_subs = new HashMap<>();
+        mr_clients = new HashMap<>();
+        mr_clusters = new HashMap<>();
+        feeds = new HashMap<>();
+        topics = new HashMap<>();
+        mirrors = new HashMap<>();
+    }
+
+    private static void initDatabase() throws Exception {
+        dcaeLocations = new DBMap<>(DcaeLocation.class, "dcae_location", "dcae_location_name");
+        dr_nodes = new DBMap<>(DR_Node.class, "dr_node", "fqdn");
+        dr_pubs = new DBMap<>(DR_Pub.class, "dr_pub", "pub_id");
+        dr_subs = new DBMap<>(DR_Sub.class, "dr_sub", "sub_id");
+        mr_clients = new DBMap<>(MR_Client.class, "mr_client", "mr_client_id");
+        mr_clusters = new DBMap<>(MR_Cluster.class, "mr_cluster", "dcae_location_name");
+        feeds = new DBMap<>(Feed.class, "feed", "feed_id");
+        topics = new DBMap<>(Topic.class, "topic", "fqtn");
+        mirrors = new DBMap<>(MirrorMaker.class, "mirror_maker", "mm_name");
+    }
+
+    private static void determineDatabaseType() {
+        DmaapConfig dmaapConfig = (DmaapConfig) DmaapConfig.getConfig();
+        String isPgSQLset = dmaapConfig.getProperty("UsePGSQL", "false");
+        databaseType = isPgSQLset.equalsIgnoreCase("true") ? DBType.PGSQL : DBType.MEMORY;
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/LoadSchema.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/LoadSchema.java
new file mode 100644 (file)
index 0000000..97bea4d
--- /dev/null
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.database;
+
+import java.io.FileReader;
+import java.io.LineNumberReader;
+import java.sql.Connection;
+import java.sql.Statement;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+
+public class LoadSchema        extends BaseLoggingClass {
+
+       private LoadSchema(){}
+
+       static void loadSchema() {
+               ConnectionFactory cf = ConnectionFactory.getDefaultInstance();
+               try (LineNumberReader lineReader = new LineNumberReader(new FileReader("/opt/app/dmaapbc/misc/schema_all.sql"));
+                       Connection c = cf.get(true);
+                       Statement stmt = c.createStatement()) {
+                       StringBuilder strBuilder = new StringBuilder();
+                       String line;
+                       while ((line = lineReader.readLine()) != null) {
+                               if (!line.startsWith("--")) {
+                                       line = line.trim();
+                                       strBuilder.append(line);
+                                       if (line.endsWith(";")) {
+                                               String sql = strBuilder.toString();
+                                               strBuilder.setLength(0);
+                                               stmt.execute(sql);
+                                               appLogger.debug("SQL EXECUTE SUCCESS: " + sql);
+                                       }
+                               }
+                       }
+                       strBuilder.setLength(0);
+               } catch (Exception e) {
+                       errorLogger.error("Error when initializing table: " + e.getMessage(), e);
+               }
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/TableHandler.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/database/TableHandler.java
new file mode 100644 (file)
index 0000000..a85bdae
--- /dev/null
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.database;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+
+class TableHandler<C> extends BaseLoggingClass {
+       protected ConnectionFactory cf;
+       protected boolean       haskey;
+       protected String        delstmt;
+       protected String        insorreplstmt;
+       protected String        getstmt;
+       protected String        liststmt;
+       protected String        initstmt;
+       protected Class<C>      cls;
+       protected DBFieldHandler[] fields;
+       private static Map<String, Map<String, DBFieldHandler.SqlOp>> exceptions = new HashMap<>();
+       private String select = "SELECT ";
+       private String from = " FROM ";
+       
+       protected TableHandler(Class<C> cls, String tabname, String keyname) throws Exception {
+               this(ConnectionFactory.getDefaultInstance(), cls, tabname, keyname);
+       }
+       protected TableHandler(ConnectionFactory cf, Class<C> cls, String tabname, String keyname) throws Exception {
+               this.cf = cf;
+               Connection c = null;
+               try {
+                       c = cf.get(false);
+                       setup(c.getMetaData(), cls, tabname, keyname);
+               } finally {
+                       if (c != null) {
+                               cf.release(c);
+                       }
+               }
+       }
+       
+       public static void setSpecialCase(String dbtabname, String dbfldname, DBFieldHandler.SqlOp handler) {
+               Map<String, DBFieldHandler.SqlOp> m = exceptions.get(dbtabname);
+               if (m == null) {
+                       m = new HashMap<>();
+                       exceptions.put(dbtabname, m);
+               }
+               m.put(dbfldname, handler);
+       }
+       public static DBFieldHandler.SqlOp getSpecialCase(String dbtabname, String dbfldname) {
+               Map<String, DBFieldHandler.SqlOp> m = exceptions.get(dbtabname);
+               if (m != null) {
+                       return(m.get(dbfldname));
+               }
+               return(null);
+       }
+       
+       private void setup(DatabaseMetaData dmd, Class<C> cls, String tabname, String keyname) throws Exception {
+               this.cls = cls;
+               Vector<DBFieldHandler> h = new Vector<>();
+               String qualifiedTableName = String.format( "%s.%s", cf.getSchema(), tabname );
+               ResultSet rs = dmd.getColumns("", cf.getSchema(), tabname, null);
+               StringBuilder sb1 = new StringBuilder();
+               StringBuilder sb2 = new StringBuilder();
+               StringBuilder sb3 = new StringBuilder();
+               int     count = 0;
+               while (rs.next()) {
+                       if (!rs.getString(3).equals(tabname)) {
+                               continue;
+                       }
+                       String cname = rs.getString(4);
+                       if (cname.equals(keyname)) {
+                               haskey = true;
+                               continue;
+                       }
+                       sb1.append(", ").append(cname);
+                       sb2.append(", ?");
+                       sb3.append(", EXCLUDED.").append(cname);
+                       count++;
+                       h.add(new DBFieldHandler(cls, cname, count, getSpecialCase(tabname, cname)));
+               }
+               if (count == 0) {
+                       throw new SQLException("Table " + tabname + " not found");
+               }
+               String clist = sb1.substring(2);
+               String qlist = sb2.substring(2);
+               String elist = sb3.substring(2);
+               if (keyname != null && !haskey) {
+                       throw new SQLException("Table " + tabname + " does not have key column " + keyname + " not found");
+               }
+               if (haskey) {
+                       count++;
+                       h.add(new DBFieldHandler(cls, keyname, count, getSpecialCase(tabname, keyname)));
+                       delstmt = "DELETE FROM " + qualifiedTableName + " WHERE " + keyname + " = ?";
+                       insorreplstmt = "INSERT INTO " + qualifiedTableName + " (" + clist + ", " + keyname + ") VALUES (" + qlist + ", ?) ON CONFLICT(" + keyname + ") DO UPDATE SET (" + clist + ") = (" + elist + ")";
+                       getstmt = select + clist + ", " + keyname + from + qualifiedTableName + " WHERE " + keyname + " = ?";
+                       liststmt = select + clist + ", " + keyname + from + qualifiedTableName;
+               } else {
+                       delstmt = "DELETE FROM " + qualifiedTableName;
+                       initstmt = "INSERT INTO " + qualifiedTableName + " (" + clist + ") VALUES (" + qlist + ")";
+                       insorreplstmt = "UPDATE " + qualifiedTableName + " SET (" + clist + ") = (" + qlist + ")";
+                       getstmt = select + clist + ", " + keyname + from + qualifiedTableName;
+               }
+               fields = h.toArray(new DBFieldHandler[h.size()]);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/BaseLoggingClass.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/BaseLoggingClass.java
new file mode 100644 (file)
index 0000000..4bde17a
--- /dev/null
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.logging;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+
+public abstract class BaseLoggingClass {
+       protected EELFLogger logger = EELFManager.getInstance().getLogger( super.getClass());
+       protected static final EELFLogger appLogger = EELFManager.getInstance().getApplicationLogger();
+       protected static final EELFLogger auditLogger = EELFManager.getInstance().getAuditLogger();
+       protected static final EELFLogger debugLogger = EELFManager.getInstance().getDebugLogger();
+       protected static final EELFLogger errorLogger = EELFManager.getInstance().getErrorLogger();
+       protected static final EELFLogger metricsLogger = EELFManager.getInstance().getMetricsLogger();
+       protected static final EELFLogger serverLogger = EELFManager.getInstance().getServerLogger();
+
+       
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/DmaapbcLogMessageEnum.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/DmaapbcLogMessageEnum.java
new file mode 100644 (file)
index 0000000..86c5fe0
--- /dev/null
@@ -0,0 +1,81 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.logging;
+
+import com.att.eelf.i18n.EELFResourceManager;
+import com.att.eelf.i18n.EELFResolvableErrorEnum;
+
+public enum DmaapbcLogMessageEnum implements EELFResolvableErrorEnum {
+//0xx sample stock messages
+  MESSAGE_SAMPLE_NOARGS,
+  MESSAGE_SAMPLE_ONE_ARG,
+  MESSAGE_SAMPLE_TWO_ARGS,
+
+// 1xx Permission Errors
+  AAF_CREDENTIAL_ERROR,
+  CODEC_CREDENTIAL_ERROR,
+  PE_AUTHENTICATION_ERROR,
+  DR_PROV_AUTHORIZATION,
+
+// 2xx Availability Errors/Timeouts
+  DRIVER_UNAVAILABLE,
+  HTTP_CONNECTION_ERROR,
+  HTTP_CONNECTION_EXCEPTION,
+  UNKNOWN_HOST_EXCEPTION,
+
+  
+// 3xx Data Errors
+  IO_EXCEPTION,
+  SSL_HANDSHAKE_ERROR,
+  AAF_UNEXPECTED_RESPONSE,
+  PE_EXCEPTION,
+  SOCKET_EXCEPTION,
+  JSON_PARSING_ERROR,
+  DECRYPT_IO_ERROR,
+  
+//4xx Schema Errors
+  DB_UPGRADE_ERROR,
+  DB_INIT_ERROR,
+  DB_UPDATE_ERROR,
+  DB_ACCESS_ERROR,
+  DB_FIELD_INIT_ERROR,
+  DB_ACCESS_INIT_ERROR,
+  DB_NO_FIELD_HANDLER,
+
+
+// 5xx Business Process Errors
+  PREREQ_DMAAP_OBJECT,
+  PROV_OUT_OF_SYNC,
+  MM_CIRCULAR_REF,
+  TOPIC_CREATE_ERROR,
+  INGRESS_CREATE_ERROR,
+  FEED_PUB_PROV_ERROR,
+  FEED_SUB_PROV_ERROR,
+  MM_PUBLISH_ERROR,
+  EGRESS_CREATE_ERROR,
+
+// 900 Unknown Errors
+  UNEXPECTED_CONDITION;
+
+       static {
+               EELFResourceManager.loadMessageBundle("logmsg");
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/logmsg.properties b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/logging/logmsg.properties
new file mode 100644 (file)
index 0000000..6548433
--- /dev/null
@@ -0,0 +1,240 @@
+###
+# ============LICENSE_START=======================================================
+#  org.onap.dmaap
+# ================================================================================
+# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=========================================================
+###
+
+# 0xx sample stock messages
+MESSAGE_SAMPLE_NOARGS=\
+  01|\
+  Ignore: demo msg with no arg|\
+  No resolution|\
+  An example of a message with no args
+
+MESSAGE_SAMPLE_ONE_ARG=\
+  02|\
+  Ignore: demo  msg with 1 arg {0}|\
+  No resolution|\
+  An example of a message with 1 arg
+
+MESSAGE_SAMPLE_TWO_ARGS=\
+  03|\
+  Ignore: demo msg with arg1 {0} and arg2 {1}|\
+  No resolution|\
+  An example of a message with 2 args
+  
+
+  
+# 1xx Permission Errors
+AAF_CREDENTIAL_ERROR=\
+  101|\
+  Service credentials ({0}) are not valid for AAF connection|\
+  Check credentials are valid in appropriate AAF environment.|\
+  Connection to AAF was not allowed for the specified credentials.
+  
+CODEC_CREDENTIAL_ERROR=\
+  102|\
+  Failed to read CredentialCodecKeyfile {0} with error {1}|\
+  Check if CredentialCodecKeyfile has been corrupted.|\
+  CredentialCodecKeyfile is not in sync with application
+  
+PE_AUTHENTICATION_ERROR=\
+  103|\
+  User {0} perms {1} caught PolicyEngineException {1}|\
+  User needs to be granted perm before access.|\
+  Identified user was not authorized for the specific perm.
+  
+DR_PROV_AUTHORIZATION=\
+  104|\
+  Not authorized for API {0}|\
+  Bus Controller host needs to be provisioned as a Node and an AUTHORIZED_HOST.|\
+  DR Prov indicates that Bus Controller host is not authorized for the specified API.
+
+# 2xx Availability Errors/Timeouts
+DRIVER_UNAVAILABLE=\
+  201|\
+  Unable to load driver {0}. Error {1}|\
+  Check that specified driver is installed and accessible to application.|\
+  The software attempted to load a driver and was not successful.
+  
+HTTP_CONNECTION_ERROR=\
+  202|\
+  Exception during openConnection to {0} failed with {1}|\
+  Confirm syntax of URL is correct and network access from this host is allowed.|\
+  An attempt to URL.openConnection failed
+
+HTTP_CONNECTION_EXCEPTION=\
+  203|\
+  Connection to {0} refused because {1}|\  
+  Check if this is the proper server.|\
+  Application caught a ConnectionException
+
+UNKNOWN_HOST_EXCEPTION=\
+  204|\
+  Caught exception {0} attempting to access {1}|\
+  Confirm that host is in DNS|\
+  Caught UnknownHostException when connecting to the designated host name.
+
+# 3xx Data Errors
+IO_EXCEPTION=\
+  301|\
+  IOexception {0}|\
+  No resolution.|\
+  Generic IO Exception condition
+
+SSL_HANDSHAKE_ERROR=\
+  302|\
+  SSLHandshakeException from URL {0}|\
+  Confirm that target host has proper SSL certificate for DNS value used to access it.|\
+  SSLHandshake exception thrown on HttpsURLConnection method
+  
+AAF_UNEXPECTED_RESPONSE=\
+  303|\
+  rc= {0} :unable to {1} for {2}|\
+  Check configuration for this AAF instance.|\
+  Unexpected response from AAF for the intended action
+  
+PE_EXCEPTION=\
+  304|\
+  Trying to read {0} and caught PolicyEngineException {1}|\
+  Check config file exists and has proper settings.|\
+  An unexpected exception from PE was caught.
+  
+SOCKET_EXCEPTION=\
+  305|\
+  Caught exception {0} while {1}|\
+  No comment.|\
+  An unexpected socket exception was caught while performing the specified action.
+  
+JSON_PARSING_ERROR=\
+  306|\
+  ParsingException for object {0} using data:{1}|\
+  No comment.|\
+  The JSON data provided to the object was not in the expected format
+ DECRYPT_IO_ERROR=\
+   307|\
+   IO Error attempting using {0} to decrypt value {1}|\
+   Check permissions of file set for property CredentialCodecKeyfile.|\
+   Error using codec file for decryption. 
+  
+# 4xx Schema Errors
+
+DB_UPGRADE_ERROR=\
+  401|\
+  Problem updating DB schema. {0}|\
+  Examine stack trace for clues.|\
+  The software was not able to process the sql file resources in the jar file.
+DB_INIT_ERROR=\
+  402|\
+  Error initializing database access: {0}|\
+  Correct configuration based on detail.|\
+  The software was not able initialize objects from the DB.
+  
+DB_UPDATE_ERROR=\
+  403|\
+  Error while updating DB: {0}|\
+  Correct configuration based on detail.|\
+  The software was not able to update record(s) in the DB.
+DB_ACCESS_ERROR=\
+  404|\
+  Database access problem: {0}|\
+  Correct configuration based on detail.|\
+  An exception related to DB access was caught and logged.
+  
+DB_FIELD_INIT_ERROR=\
+  405|\
+  Problem setting field {0} to {1} statement is {2}|\
+  DB schema may be out of sync with code.|\
+  SQLDate.set() failed to set field value.
+
+DB_ACCESS_INIT_ERROR=\
+  406|\
+  Problem initializing sql access methods {0} |\
+  No comment.|\
+  Error encountered while initializing basic field types.
+  
+DB_NO_FIELD_HANDLER=\
+  407|\
+  No field handler for class {0} field {1} index {2} type {3}|\
+  No comment.|\
+  Missing field handler for specified code.
+
+
+# 5xx Business Process Errors
+PREREQ_DMAAP_OBJECT=\
+  501|\
+  Attempt to access {0} before dmaap object resource is available.|\
+  No remediation.|\
+  The dmaap object needs to be defined before attempting the desired access
+  
+PROV_OUT_OF_SYNC=\
+  502|\
+  Resource {0} with id {1} was not in sync with DR Prov.\
+  May need manual sync steps.\
+  The Bus Controller view of a resource does not match what was found on DR Prov
+  
+MM_CIRCULAR_REF=\
+  503|\
+  Trying to add edge from source {0} into Map belonging to {1}|\
+  May indicate a provisioning error.|\
+  Some error in logic is attempting to add an edge to a Map that is an edge.
+  
+TOPIC_CREATE_ERROR=\
+  504|\
+  Unable to create topic for {0} err={1} fields={2} msg={3}|\
+  No comment.|\
+  Reporting an error caught while creating a topic
+  
+INGRESS_CREATE_ERROR=\
+  505|\
+  rc={0} unable to create ingress rule for {1} on feed {2} to {3}|\
+  No comment.|\
+  Unexpected response while creating ingress rule
+  
+FEED_PUB_PROV_ERROR=\
+  506|\
+  For feed {0} resulting set of publishers do not match requested set of publishers {1} vs {2}|\
+  No comment.|\
+  The number of publishers on a feed do not match after provisioning request.
+  
+FEED_SUB_PROV_ERROR=\
+  507|\
+  For feed {0} i={1} url={2}  err={3}|\
+  No comment.|\
+  An error occurred when provisioning subs on a feed.
+  
+MM_PUBLISH_ERROR=\
+  508|\
+  Unable to publish {0} provisioning message. rc={1} msg={2}|\
+  No comment.|\
+  An error occurred when publishing a message to MM
+  
+EGRESS_CREATE_ERROR=\
+  509|\
+  rc={0} unable to create egress rule for {1} on feed {2} to {3}|\
+  No comment.|\
+  Unexpected response while creating egress rule
+  
+# 900 Unknown Errors
+UNEXPECTED_CONDITION=\
+  901|\
+  Unexpected exception encountered {0}|\
+  No resolution|\
+  An error to catch unexpected conditions.  Hopefully a clue in the stack trace.
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ApiError.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ApiError.java
new file mode 100644 (file)
index 0000000..c67e55b
--- /dev/null
@@ -0,0 +1,92 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+import java.util.Objects;
+
+@XmlRootElement
+public class ApiError implements Serializable {
+       private int code;
+       private String message;
+       private String fields;
+
+       public ApiError() {
+               this(0, null, null);
+       }
+
+       public ApiError(int code, String message) {
+               this(code, message, null);
+       }
+
+       public ApiError(int code, String message, String fields) {
+               this.code = code;
+               this.message = message;
+               this.fields = fields;
+       }
+
+       public int getCode() {
+               return code;
+       }
+       public void setCode(int rc) {
+               this.code = rc;
+       }
+       public String getMessage() {
+               return message;
+       }
+       public void setMessage(String message) {
+               this.message = message;
+       }
+       public String getFields() {
+               return fields;
+       }
+       public void setFields(String fields) {
+               this.fields = fields;
+       }
+       public String toString() {
+               return String.format( "code=%d msg=%s fields=%s", this.code, this.message, this.fields );
+       }
+       public boolean is2xx() {
+               
+               return code >= 200 && code < 300;
+       }
+       public void reset() {
+               code = 0;
+               message = null;
+               fields = null;
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               if (this == o) return true;
+               if (o == null || getClass() != o.getClass()) return false;
+               ApiError apiError = (ApiError) o;
+               return code == apiError.code &&
+                               Objects.equals(message, apiError.message) &&
+                               Objects.equals(fields, apiError.fields);
+       }
+
+       @Override
+       public int hashCode() {
+               return Objects.hash(code, message, fields);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/BrTopic.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/BrTopic.java
new file mode 100644 (file)
index 0000000..ba050c3
--- /dev/null
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class BrTopic {
+
+       private String brSource;
+       private String brTarget;
+       private String mmAgentName;
+       private int topicCount;
+       
+       // no-op constructor used by framework
+       public BrTopic() {
+       }
+
+       public String getBrSource() {
+               return brSource;
+       }
+
+       public void setBrSource(String brSource) {
+               this.brSource = brSource;
+       }
+
+       public String getBrTarget() {
+               return brTarget;
+       }
+
+       public void setBrTarget(String brTarget) {
+               this.brTarget = brTarget;
+       }
+
+       public int getTopicCount() {
+               return topicCount;
+       }
+
+       public void setTopicCount(int topicCount) {
+               this.topicCount = topicCount;
+       }
+
+       public String getMmAgentName() {
+               return mmAgentName;
+       }
+
+       public void setMmAgentName(String mmAgentName) {
+               this.mmAgentName = mmAgentName;
+       }
+
+
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Node.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Node.java
new file mode 100644 (file)
index 0000000..4b2ef90
--- /dev/null
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.Objects;
+
+@XmlRootElement
+public class DR_Node extends DmaapObject {
+       private String fqdn;
+       private String dcaeLocationName;
+       private String hostName;
+       private String version;
+       
+       public DR_Node() {
+               
+       }
+       
+       public DR_Node( String f,
+                                       String dLN,
+                                       String hN,
+                                       String v ) {
+               this.fqdn = f;
+               this.dcaeLocationName = dLN;
+               this.hostName = hN;
+               this.version = v;
+       }
+
+       public String getFqdn() {
+               return fqdn;
+       }
+
+       public void setFqdn(String fqdn) {
+               this.fqdn = fqdn;
+       }
+
+       public String getDcaeLocationName() {
+               return dcaeLocationName;
+       }
+
+       public void setDcaeLocationName(String dcaeLocationName) {
+               this.dcaeLocationName = dcaeLocationName;
+       }
+
+       public String getHostName() {
+               return hostName;
+       }
+
+       public void setHostName(String hostName) {
+               this.hostName = hostName;
+       }
+
+       public String getVersion() {
+               return version;
+       }
+
+       public void setVersion(String version) {
+               this.version = version;
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               if (this == o) return true;
+               if (o == null || getClass() != o.getClass()) return false;
+               DR_Node dr_node = (DR_Node) o;
+               return Objects.equals(fqdn, dr_node.fqdn) &&
+                               Objects.equals(dcaeLocationName, dr_node.dcaeLocationName) &&
+                               Objects.equals(hostName, dr_node.hostName) &&
+                               Objects.equals(version, dr_node.version);
+       }
+
+       @Override
+       public int hashCode() {
+               return Objects.hash(fqdn, dcaeLocationName, hostName, version);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Pub.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Pub.java
new file mode 100644 (file)
index 0000000..4e64089
--- /dev/null
@@ -0,0 +1,187 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.onap.dmaap.dbcapi.util.RandomString;
+
+import java.util.Objects;
+
+@XmlRootElement
+public class DR_Pub extends DmaapObject {
+
+       private String dcaeLocationName;
+       private String username;
+       private String userpwd;
+       private String feedId;
+       private String pubId;
+       
+       // NOTE: the following fields are optional in the API but not stored in the DB
+       private String  feedName;
+       private String  feedVersion;
+
+       
+       public DR_Pub() {
+               status = DmaapObject_Status.EMPTY;
+               
+       }
+       
+       public DR_Pub( String dLN ) {
+               this.dcaeLocationName = dLN;
+               this.status = DmaapObject_Status.STAGED;
+       }
+       
+       public DR_Pub( String dLN, 
+                                       String uN,
+                                       String uP,
+                                       String fI,
+                                       String pI ) {
+               this.dcaeLocationName = dLN;
+               this.username = uN;
+               this.userpwd = uP;
+               this.feedId = fI;
+               this.pubId = pI;
+               this.status = DmaapObject_Status.VALID;
+       }
+
+
+       public DR_Pub( String dLN, 
+                                                       String uN,
+                                                       String uP,
+                                                       String fI ) {
+               this.dcaeLocationName = dLN;
+               this.username = uN;
+               this.userpwd = uP;
+               this.feedId = fI;
+               this.pubId = fI + "." +  DR_Pub.nextKey();
+               this.status = DmaapObject_Status.VALID; 
+       }
+                       
+
+       public String getDcaeLocationName() {
+               return dcaeLocationName;
+       }
+
+       public void setDcaeLocationName(String dcaeLocationName) {
+               this.dcaeLocationName = dcaeLocationName;
+       }
+
+       public String getUsername() {
+               return username;
+       }
+
+       public void setUsername(String username) {
+               this.username = username;
+       }
+
+       public String getUserpwd() {
+               return userpwd;
+       }
+
+       public void setUserpwd(String userpwd) {
+               this.userpwd = userpwd;
+       }
+
+       public String getFeedId() {
+               return feedId;
+       }
+
+       public void setFeedId(String feedId) {
+               this.feedId = feedId;
+       }
+
+       public String getPubId() {
+               return pubId;
+       }
+
+       public void setPubId(String pubId) {
+               this.pubId = pubId;
+       }
+       
+       public void setNextPubId() {
+               this.pubId = this.feedId + "." +  DR_Pub.nextKey();
+       }
+       
+       public String getFeedName() {
+               return feedName;
+       }
+
+       public void setFeedName(String feedName) {
+               this.feedName = feedName;
+       }
+
+       public String getFeedVersion() {
+               return feedVersion;
+       }
+
+       public void setFeedVersion(String feedVersion) {
+               this.feedVersion = feedVersion;
+       }
+
+       public DR_Pub setRandomUserName() {
+               RandomString r = new RandomString(15);
+               this.username = "tmp_" + r.nextString();        
+               return this;
+       }
+       public DR_Pub setRandomPassword() {
+               RandomString r = new RandomString(15);
+               this.userpwd = r.nextString();
+               return this;
+       }
+
+       public static String nextKey() {
+               RandomString ri = new RandomString(5);
+               return ri.nextString();
+               
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               if (this == o) return true;
+               if (o == null || getClass() != o.getClass()) return false;
+               DR_Pub dr_pub = (DR_Pub) o;
+               return Objects.equals(dcaeLocationName, dr_pub.dcaeLocationName) &&
+                               Objects.equals(username, dr_pub.username) &&
+                               Objects.equals(userpwd, dr_pub.userpwd) &&
+                               Objects.equals(feedId, dr_pub.feedId) &&
+                               Objects.equals(pubId, dr_pub.pubId);
+       }
+
+       @Override
+       public int hashCode() {
+
+               return Objects.hash(dcaeLocationName, username, userpwd, feedId, pubId);
+       }
+
+       @Override
+       public String toString() {
+               return "DR_Pub{" +
+                               "dcaeLocationName='" + dcaeLocationName + '\'' +
+                               ", username='" + username + '\'' +
+                               ", userpwd='" + userpwd + '\'' +
+                               ", feedId='" + feedId + '\'' +
+                               ", pubId='" + pubId + '\'' +
+                               ", feedName='" + feedName + '\'' +
+                               ", feedVersion='" + feedVersion + '\'' +
+                               '}';
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Sub.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DR_Sub.java
new file mode 100644 (file)
index 0000000..90da956
--- /dev/null
@@ -0,0 +1,391 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import java.nio.charset.StandardCharsets;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+
+@XmlRootElement
+public class DR_Sub extends DmaapObject {
+
+       private String dcaeLocationName;
+       private String username;
+       private String userpwd;
+       private String feedId;
+       private String deliveryURL;
+       private String logURL;
+       private String subId;
+       private boolean use100;
+       private boolean suspended;
+       private String owner;
+       private boolean guaranteedDelivery;
+       private boolean guaranteedSequence;
+       private boolean privilegedSubscriber;
+       private boolean decompress;
+
+       // NOTE: the following fields are optional in the API but not stored in the DB
+
+       private String  feedName;
+       private String  feedVersion;
+       public DR_Sub() {
+
+       }
+
+       public DR_Sub( String dLN,
+                                       String uN,
+                                       String uP,
+                                       String fI,
+                                       String dU,
+                                       String lU,
+                                       boolean u100 ) {
+               this.dcaeLocationName = dLN;
+               this.username = uN;
+               this.userpwd = uP;
+               this.feedId = fI;
+               this.deliveryURL = dU;
+               this.logURL = lU;
+               this.use100 = u100;
+               this.setStatus( DmaapObject_Status.NEW );
+               this.subId = "0";
+       }
+
+       public DR_Sub ( String json ) {
+               logger.info( "DR_Sub:" + json );
+               JSONParser parser = new JSONParser();
+               JSONObject jsonObj;
+
+               try {
+                       jsonObj = (JSONObject) parser.parse( json );
+               } catch ( ParseException pe ) {
+                       errorLogger.error( DmaapbcLogMessageEnum.JSON_PARSING_ERROR, "DR_Sub", json );
+            this.setStatus( DmaapObject_Status.INVALID );
+            return;
+        }
+
+               this.setOwner( (String) jsonObj.get("subscriber"));
+               this.setSuspended( (boolean) jsonObj.get("suspend"));
+
+               try {
+                       JSONObject links = (JSONObject) jsonObj.get("links");
+                       String url = (String) links.get("feed");
+                       this.setFeedId( url.substring( url.lastIndexOf('/')+1, url.length() ));
+                       url = (String) links.get("self");
+                       this.setSubId( url.substring( url.lastIndexOf('/')+1, url.length() ));
+                       logger.info( "feedid="+ this.getFeedId() );
+                       this.setLogURL( (String) links.get("log") );
+               } catch (NullPointerException npe ) {
+
+               }
+               try {
+                       this.setGuaranteedDelivery( (boolean) jsonObj.get("guaranteed_delivery"));
+               } catch( NullPointerException npe ) {
+                       this.setGuaranteedDelivery(false);
+               }
+               try {
+                       this.setGuaranteedSequence( (boolean) jsonObj.get("guaranteed_sequence"));
+               } catch( NullPointerException npe ) {
+                       this.setGuaranteedSequence(false);
+               }
+               try {
+                       this.setPrivilegedSubscriber((boolean) jsonObj.get("privilegedSubscriber"));
+               } catch( NullPointerException npe ) {
+                       this.setPrivilegedSubscriber(false);
+               }
+               try {
+                       this.setDecompress((boolean) jsonObj.get("decompress"));
+               } catch( NullPointerException npe ) {
+                       this.setDecompress(false);
+               }
+
+               JSONObject del = (JSONObject) jsonObj.get("delivery");
+               this.setDeliveryURL( (String) del.get("url") );
+               this.setUsername( (String) del.get("user"));
+               this.setUserpwd( (String) del.get( "password"));
+               this.setUse100((boolean) del.get( "use100"));
+
+               this.setStatus( DmaapObject_Status.VALID );
+
+               logger.info( "new DR_Sub returning");
+       }
+
+       public String getOwner() {
+               return owner;
+       }
+
+       public void setOwner(String owner) {
+               this.owner = owner;
+       }
+
+       public boolean isSuspended() {
+               return suspended;
+       }
+
+       public void setSuspended(boolean suspended) {
+               this.suspended = suspended;
+       }
+
+
+
+       public boolean isUse100() {
+               return use100;
+       }
+
+       public void setUse100(boolean use100) {
+               this.use100 = use100;
+       }
+
+       public String getDcaeLocationName() {
+               return dcaeLocationName;
+       }
+
+       public void setDcaeLocationName(String dcaeLocationName) {
+               this.dcaeLocationName = dcaeLocationName;
+       }
+
+       public String getUsername() {
+               return username;
+       }
+
+       public void setUsername(String username) {
+               this.username = username;
+       }
+
+       public String getUserpwd() {
+               return userpwd;
+       }
+
+       public void setUserpwd(String userpwd) {
+               this.userpwd = userpwd;
+       }
+
+       public String getFeedId() {
+               return feedId;
+       }
+
+       public void setFeedId(String feedId) {
+               this.feedId = feedId;
+       }
+
+       public String getDeliveryURL() {
+               return deliveryURL;
+       }
+
+       public void setDeliveryURL(String deliveryURL) {
+               this.deliveryURL = deliveryURL;
+       }
+
+       public String getLogURL() {
+               return logURL;
+       }
+
+       public void setLogURL(String logURL) {
+               this.logURL = logURL;
+       }
+
+       public String getSubId() {
+               return subId;
+       }
+
+       public void setSubId(String subId) {
+               this.subId = subId;
+       }
+
+
+       public boolean isGuaranteedDelivery() {
+               return guaranteedDelivery;
+       }
+
+       public void setGuaranteedDelivery(boolean guaranteedDelivery) {
+               this.guaranteedDelivery = guaranteedDelivery;
+       }
+
+       public boolean isGuaranteedSequence() {
+               return guaranteedSequence;
+       }
+
+       public void setGuaranteedSequence(boolean guaranteedSequence) {
+               this.guaranteedSequence = guaranteedSequence;
+       }
+
+       public boolean isPrivilegedSubscriber() {
+               return privilegedSubscriber;
+       }
+
+       public void setPrivilegedSubscriber(boolean privilegedSubscriber) {
+               this.privilegedSubscriber = privilegedSubscriber;
+       }
+
+       public boolean isDecompress() {
+               return decompress;
+       }
+
+       public void setDecompress(boolean decompressData) {
+               this.decompress = decompressData;
+       }
+
+       public String getFeedName() {
+               return feedName;
+       }
+
+       public void setFeedName(String feedName) {
+               this.feedName = feedName;
+       }
+
+       public String getFeedVersion() {
+               return feedVersion;
+       }
+
+       public void setFeedVersion(String feedVersion) {
+               this.feedVersion = feedVersion;
+       }
+
+       public byte[] getBytes(String provApi) {
+               if ( "AT&T".equals(provApi)) {
+                       return toProvJSONforATT().getBytes(StandardCharsets.UTF_8);
+               }
+               return toProvJSON().getBytes(StandardCharsets.UTF_8);
+       }
+       // returns the DR_Sub object in JSON that conforms to ONAP DR Prov Server expectations
+       public String toProvJSON() {
+               // this is the original DR API that was contributed to ONAP
+               String postJSON = String.format("{\"suspend\": %s, \"delivery\":"
+                               + "{\"url\": \"%s\", \"user\": \"%s\", \"password\": \"%s\", \"use100\":  %s }"
+                               + ", \"metadataOnly\": %s, \"groupid\": \"%s\", \"follow_redirect\": %s "
+                               + ", \"privilegedSubscriber\": %s, \"decompress\": %s "
+                               + "}"
+                               ,this.suspended
+                               ,this.getDeliveryURL()
+                               ,this.getUsername()
+                               ,this.getUserpwd()
+                               ,this.isUse100()
+                               ,"false"
+                               ,"0"
+                               ,"true"
+                               ,this.isPrivilegedSubscriber()
+                               ,this.isDecompress()
+                       );
+
+               logger.info( postJSON );
+               return postJSON;
+       }
+       // returns the DR_Sub object in JSON that conforms to AT&T DR Prov Server expectations
+       // In Jan, 2019, the DR API used internally at AT&T diverged, so this function can be used in
+       // that runtime environment
+       public String toProvJSONforATT() {
+               // in DR 3.0, API v2.1 a new groupid field is added.  We are not using this required field so just set it to 0.
+               // we send this regardless of DR Release because older versions of DR seem to safely ignore it
+               // and soon those versions won't be around anyway...
+               // Similarly, in the 1704 Release, a new subscriber attribute "follow_redirect" was introduced.
+               // We are setting it to "true" because that is the general behavior desired in OpenDCAE.
+               // But it is really a no-op for OpenDCAE because we've deployed DR with the SYSTEM-level parameter for FOLLOW_REDIRECTS set to true.
+               // In the event we abandon that, then setting the sub attribute to true will be a good thing.
+               // Update Jan, 2019: added guaranteed_delivery and guaranteed_sequence with value false for
+               // backwards compatibility
+               // TODO:
+               //   - introduce Bus Controller API support for these attributes
+               //   - store the default values in the DB
+               String postJSON = String.format("{\"suspend\": %s, \"delivery\":"
+                               + "{\"url\": \"%s\", \"user\": \"%s\", \"password\": \"%s\", \"use100\":  %s}"
+                               + ", \"metadataOnly\": %s, \"groupid\": \"%s\", \"follow_redirect\": %s "
+                               + ", \"guaranteed_delivery\": %s, \"guaranteed_sequence\": %s"
+                               + "}"
+                               ,this.suspended
+                               ,this.getDeliveryURL()
+                               ,this.getUsername()
+                               ,this.getUserpwd()
+                               ,this.isUse100()
+                               ,"false"
+                               ,"0"
+                               ,"true"
+                               ,this.isGuaranteedDelivery()
+                               ,this.isGuaranteedSequence()
+                               );
+
+               logger.info( postJSON );
+               return postJSON;
+       }
+
+       @Override
+       public String toString() {
+               return String.format ( "DR_Sub: {dcaeLocationName=%s username=%s userpwd=%s feedId=%s deliveryURL=%s logURL=%s subid=%s use100=%s suspended=%s owner=%s}",
+                               dcaeLocationName,
+                               username,
+                               userpwd,
+                               feedId,
+                               deliveryURL,
+                               logURL,
+                               subId,
+                               use100,
+                               suspended,
+                               owner
+                               );
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               if (this == o) {
+                       return true;
+               }
+               if (o == null || getClass() != o.getClass()) {
+                       return false;
+               }
+
+               DR_Sub drSub = (DR_Sub) o;
+
+               if (use100 != drSub.use100) {
+                       return false;
+               }
+               if (suspended != drSub.suspended) {
+                       return false;
+               }
+               if (!dcaeLocationName.equals(drSub.dcaeLocationName)) {
+                       return false;
+               }
+               if (!username.equals(drSub.username)) {
+                       return false;
+               }
+               if (!userpwd.equals(drSub.userpwd)) {
+                       return false;
+               }
+               if (!feedId.equals(drSub.feedId)) {
+                       return false;
+               }
+               return subId.equals(drSub.subId);
+       }
+
+       @Override
+       public int hashCode() {
+               int result = dcaeLocationName.hashCode();
+               result = 31 * result + username.hashCode();
+               result = 31 * result + userpwd.hashCode();
+               result = 31 * result + feedId.hashCode();
+               result = 31 * result + subId.hashCode();
+               result = 31 * result + (use100 ? 1 : 0);
+               result = 31 * result + (suspended ? 1 : 0);
+               return result;
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DcaeLocation.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DcaeLocation.java
new file mode 100644 (file)
index 0000000..f459c6c
--- /dev/null
@@ -0,0 +1,119 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import java.util.Objects;
+
+@XmlRootElement
+public class DcaeLocation extends DmaapObject {
+       private String clli;
+       private String dcaeLayer;
+       private String dcaeLocationName;
+       private String openStackAvailabilityZone;
+       private String subnet;
+
+       
+
+       public DcaeLocation() {
+
+       }
+
+       public DcaeLocation( String c,
+                                               String dL,
+                                               String dLN,
+                                               String oSAZ,
+                                               String s ) {
+               
+               this.clli = c;
+               this.dcaeLayer = dL;
+               this.dcaeLocationName = dLN;
+               this.openStackAvailabilityZone = oSAZ;
+               this.subnet = s;
+       }
+
+       public String getClli() {
+               return clli;
+       }
+
+       public void setClli(String clli) {
+               this.clli = clli;
+       }
+
+       public String getDcaeLayer() {
+               return dcaeLayer;
+       }
+
+       public void setDcaeLayer(String dcaeLayer) {
+               this.dcaeLayer = dcaeLayer;
+       }
+       public boolean isCentral() {
+               return dcaeLayer != null && dcaeLayer.contains("central");
+       }
+       public boolean isLocal() {
+               return dcaeLayer != null && dcaeLayer.contains("local");
+       }
+
+       public String getDcaeLocationName() {
+               return dcaeLocationName;
+       }
+
+       public void setDcaeLocationName(String dcaeLocationName) {
+               this.dcaeLocationName = dcaeLocationName;
+       }
+       
+
+
+       public String getOpenStackAvailabilityZone() {
+               return openStackAvailabilityZone;
+       }
+
+       public void setOpenStackAvailabilityZone(String openStackAvailabilityZone) {
+               this.openStackAvailabilityZone = openStackAvailabilityZone;
+       }
+       
+       public String getSubnet() {
+               return subnet;
+       }
+
+       public void setSubnet(String subnet) {
+               this.subnet = subnet;
+       }
+
+       @Override
+       public boolean equals(Object o) {
+               if (this == o) return true;
+               if (o == null || getClass() != o.getClass()) return false;
+               DcaeLocation that = (DcaeLocation) o;
+               return Objects.equals(clli, that.clli) &&
+                               Objects.equals(dcaeLayer, that.dcaeLayer) &&
+                               Objects.equals(dcaeLocationName, that.dcaeLocationName) &&
+                               Objects.equals(openStackAvailabilityZone, that.openStackAvailabilityZone) &&
+                               Objects.equals(subnet, that.subnet);
+       }
+
+       @Override
+       public int hashCode() {
+
+               return Objects.hash(clli, dcaeLayer, dcaeLocationName, openStackAvailabilityZone, subnet);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Dmaap.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Dmaap.java
new file mode 100644 (file)
index 0000000..96248f3
--- /dev/null
@@ -0,0 +1,177 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class Dmaap extends DmaapObject {
+       
+       private String version;
+       private String topicNsRoot;
+       private String dmaapName;
+       private String drProvUrl;
+       private String  bridgeAdminTopic;
+       private String loggingUrl;
+       private String nodeKey;
+       private String  accessKeyOwner;
+
+
+       // no-op constructor used by framework
+       public Dmaap() {
+               
+       }
+       
+       public Dmaap( DmaapBuilder builder ) {
+               this.version = builder.ver;
+               this.topicNsRoot = builder.tnr;
+               this.dmaapName = builder.dn;
+               this.drProvUrl = builder.dpu;
+               this.bridgeAdminTopic = builder.bat;
+               this.loggingUrl = builder.lu;
+               this.nodeKey = builder.nk;
+               this.accessKeyOwner = builder.ako;
+               this.setStatus( DmaapObject_Status.NEW );
+
+       }
+
+       public static class DmaapBuilder {
+               private String ver;
+               private String tnr;
+               private String dn;
+               private String dpu;
+               private String lu;
+               private String bat;
+               private String nk;
+               private String ako;
+
+               public DmaapBuilder setVer(String ver) {
+                       this.ver = ver;
+                       return this;
+               }
+
+               public DmaapBuilder setTnr(String tnr) {
+                       this.tnr = tnr;
+                       return this;
+               }
+
+               public DmaapBuilder setDn(String dn) {
+                       this.dn = dn;
+                       return this;
+               }
+
+               public DmaapBuilder setDpu(String dpu) {
+                       this.dpu = dpu;
+                       return this;
+               }
+
+               public DmaapBuilder setLu(String lu) {
+                       this.lu = lu;
+                       return this;
+               }
+
+               public DmaapBuilder setBat(String bat) {
+                       this.bat = bat;
+                       return this;
+               }
+
+               public DmaapBuilder setNk(String nk) {
+                       this.nk = nk;
+                       return this;
+               }
+
+               public DmaapBuilder setAko(String ako) {
+                       this.ako = ako;
+                       return this;
+               }
+
+               public Dmaap createDmaap() {
+                       return new Dmaap(this);
+               }
+       }
+
+       public String getVersion() {
+               return version;
+       }
+
+       public void setVersion(String version) {
+               this.version = version;
+       }
+
+       public String getTopicNsRoot() {
+               return topicNsRoot;
+       }
+
+       public void setTopicNsRoot(String topicNsRoot) {
+               this.topicNsRoot = topicNsRoot;
+       }
+
+       public String getDmaapName() {
+               return dmaapName;
+       }
+
+       public void setDmaapName(String dmaapName) {
+               this.dmaapName = dmaapName;
+       }
+
+       public String getDrProvUrl() {
+               return drProvUrl;
+       }
+
+       public void setDrProvUrl(String drProvUrl) {
+               this.drProvUrl = drProvUrl;
+       }
+
+
+       public String getNodeKey() {
+               return nodeKey;
+       }
+
+       public void setNodeKey(String nodeKey) {
+               this.nodeKey = nodeKey;
+       }
+
+       public String getAccessKeyOwner() {
+               return accessKeyOwner;
+       }
+
+       public void setAccessKeyOwner(String accessKeyOwner) {
+               this.accessKeyOwner = accessKeyOwner;
+       }
+
+       
+       public String getBridgeAdminTopic() {
+               return bridgeAdminTopic;
+       }
+
+       public void setBridgeAdminTopic(String bridgeAdminTopic) {
+               this.bridgeAdminTopic = bridgeAdminTopic;
+       }
+
+       public String getLoggingUrl() {
+               return loggingUrl;
+       }
+
+       public void setLoggingUrl(String loggingUrl) {
+               this.loggingUrl = loggingUrl;
+       }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DmaapObject.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/DmaapObject.java
new file mode 100644 (file)
index 0000000..4ff2eec
--- /dev/null
@@ -0,0 +1,144 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import io.swagger.annotations.ApiModelProperty;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+
+@XmlRootElement
+public abstract class DmaapObject extends BaseLoggingClass {
+       @ApiModelProperty( value = "datestamp for last update to this object")
+       protected Date lastMod;
+       protected       DmaapObject_Status      status;
+       
+       public Date getLastMod() {
+               return lastMod;
+       }
+
+       public void setLastMod(Date lastMod) {
+               this.lastMod = lastMod;
+       }
+
+       public void setLastMod() {
+               this.lastMod = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTime();
+       }
+       
+       public enum DmaapObject_Status {
+               EMPTY,
+               NEW,
+               STAGED,
+               VALID,
+               INVALID,
+               DELETED
+       }
+       public DmaapObject_Status getStatus() {
+               return status;
+       }
+
+       public void setStatus(DmaapObject_Status status) {
+               this.status = status;
+       }
+       
+       public void setStatus( String val ) {
+               if ( val == null || val.isEmpty() ) {
+                       this.status = DmaapObject_Status.EMPTY;
+               } else if (val.compareToIgnoreCase("new") == 0 ) {
+                       this.status = DmaapObject_Status.NEW;
+               } else if ( val.compareToIgnoreCase("staged" ) == 0) {
+                       this.status = DmaapObject_Status.STAGED;
+               } else if ( val.compareToIgnoreCase("valid") == 0) {
+                       this.status = DmaapObject_Status.VALID;
+               } else if ( val.compareToIgnoreCase("invalid") == 0) {
+                       this.status = DmaapObject_Status.INVALID;
+               } else if ( val.compareToIgnoreCase("deleted") == 0) {
+                       this.status = DmaapObject_Status.DELETED;
+               } else {
+                       this.status = DmaapObject_Status.INVALID;
+               }
+       }
+       
+       @ApiModelProperty( hidden=true )
+       public boolean isStatusValid() {
+               if ( this.status == DmaapObject_Status.VALID ) {
+                       return true;
+               }
+               return false;
+       }
+       
+       /*
+        * TODO: get this working so arrays and sub-class within an Object can be logged
+        * 
+       public String toString() {
+                       return classToString( this );
+       }
+       
+       private String classToString( Object obj ) {
+               Field[] fields = obj.getClass().getDeclaredFields();
+               StringBuilder res = new StringBuilder( "{");
+               boolean first = true;
+               for ( Field field: fields ) {
+                       logger.info( field.getName() + " toString=" + field.toString() + " toGenericString=" + field.toGenericString());
+                       if ( first ) {
+                               first = false;
+                       } else {
+                               res.append( ", ");
+                       }
+
+
+                       field.setAccessible(true);  // avoid IllegalAccessException
+
+                       
+                       Class<?> t = field.getType();
+                       
+                       if ( t == String.class ) {
+                               res.append( "\"" ).append( field.getName() ).append( "\": \"");
+       
+                               try {
+                                       res.append(field.get(this));
+                               } catch ( IllegalAccessException iae) {
+                                       res.append( "UNK(iae)");
+                               } catch (IllegalArgumentException iae2 ) {
+                                       res.append( "UNK(iae2)");
+                               } catch ( NullPointerException npe ) {
+                                       res.append( "UNK(npe)");
+                               } catch ( ExceptionInInitializerError eie ) {
+                                       res.append( "UNK(eie)");
+                               }
+                               res.append( "\"");
+                       } else if ( t == ArrayList.class ){
+                               res.append( "[");
+                               res.append( classToString( field ));
+                               res.append( "]");
+                               
+                       }
+               }
+               res.append( "}");
+               return( res.toString());
+       
+               
+       }
+       */
+       
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Feed.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Feed.java
new file mode 100644 (file)
index 0000000..b3c7332
--- /dev/null
@@ -0,0 +1,294 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.json.simple.*;
+import org.json.simple.parser.*;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+
+@XmlRootElement
+public class Feed extends DmaapObject {
+               
+               private String feedId;
+
+               private String feedName;
+               private String feedVersion;
+               private String feedDescription;
+               private String owner;
+               private String asprClassification;
+               private String publishURL;
+               private String subscribeURL;
+               private boolean suspended;
+               private String logURL;
+               private String formatUuid;
+
+               private ArrayList<DR_Pub> pubs;
+               private ArrayList<DR_Sub> subs;
+
+
+               public Feed() {
+                       this.pubs = new ArrayList<>();
+                       this.subs = new ArrayList<>();
+                       this.setStatus( DmaapObject_Status.EMPTY );
+
+               }
+
+               public  Feed( String name,
+                                               String version,
+                                               String description,
+                                               String owner,
+                                               String aspr) {
+                       this.feedName = name;
+                       this.feedVersion = version;
+                       this.feedDescription = description;
+                       this.owner = owner;
+                       this.asprClassification = aspr;
+                       this.pubs = new ArrayList<>();
+                       this.subs = new ArrayList<>();
+                       this.setStatus( DmaapObject_Status.NEW );
+
+               }
+
+               // expects a String in JSON format, with known fields to populate Feed object
+               public Feed ( String json ) {
+                       JSONParser parser = new JSONParser();
+                       JSONObject jsonObj;
+                       try {
+                               jsonObj = (JSONObject) parser.parse( json );
+                       } catch ( ParseException pe ) {
+                               logger.error( "Error parsing provisioning data: " + json );
+                               this.setStatus( DmaapObject_Status.INVALID );
+                               return;
+                       }
+                       this.setFeedName( (String) jsonObj.get("name"));
+
+                       this.setFeedVersion( (String) jsonObj.get("version"));
+                       this.setFeedDescription( (String) jsonObj.get("description"));
+                       this.setOwner( (String) jsonObj.get("publisher"));
+
+                       this.setSuspended( (boolean) jsonObj.get("suspend"));
+                       JSONObject links = (JSONObject) jsonObj.get("links");
+                       String url = (String) links.get("publish");
+                       this.setPublishURL( url );
+                       this.setFeedId( url.substring( url.lastIndexOf('/')+1, url.length() ));
+                       logger.info( "feedid="+ this.getFeedId() );
+                       this.setSubscribeURL( (String) links.get("subscribe") );
+                       this.setLogURL( (String) links.get("log") );
+                       JSONObject auth = (JSONObject) jsonObj.get("authorization");
+                       this.setAsprClassification( (String) auth.get("classification"));
+                       JSONArray pubs = (JSONArray) auth.get( "endpoint_ids");
+                       int i;
+                       ArrayList<DR_Pub> dr_pub = new ArrayList<>();
+                       this.subs = new ArrayList<>();
+
+                       for( i = 0; i < pubs.size(); i++ ) {
+                               JSONObject entry = (JSONObject) pubs.get(i);
+                               dr_pub.add(  new DR_Pub( "someLocation",
+                                               (String) entry.get("id"),
+                                               (String) entry.get("password"),
+                                               this.getFeedId(),
+                                               this.getFeedId() + "." +  DR_Pub.nextKey() ));
+
+                       }
+                       this.setPubs( dr_pub );
+
+                       this.setStatus( DmaapObject_Status.VALID );
+
+               }
+
+               
+
+               public boolean isSuspended() {
+                       return suspended;
+               }
+
+               public void setSuspended(boolean suspended) {
+                       this.suspended = suspended;
+               }
+
+               public String getSubscribeURL() {
+                       return subscribeURL;
+               }
+
+               public void setSubscribeURL(String subscribeURL) {
+                       this.subscribeURL = subscribeURL;
+               }
+
+               public String getFeedId() {
+                       return feedId;
+               }
+
+               public void setFeedId(String feedId) {
+                       this.feedId = feedId;
+               }
+
+               public String getFeedName() {
+                       return feedName;
+               }
+
+               public void setFeedName(String feedName) {
+                       this.feedName = feedName;
+               }
+
+               public String getFeedVersion() {
+                       return feedVersion;
+               }
+
+               public void setFeedVersion(String feedVersion) {
+                       this.feedVersion = feedVersion;
+               }
+
+               public String getFeedDescription() {
+                       return feedDescription;
+               }
+
+               public void setFeedDescription(String feedDescription) {
+                       this.feedDescription = feedDescription;
+               }
+
+               public String getOwner() {
+                       return owner;
+               }
+
+               public void setOwner(String owner) {
+                       this.owner = owner;
+               }
+
+               public String getAsprClassification() {
+                       return asprClassification;
+               }
+
+               public void setAsprClassification(String asprClassification) {
+                       this.asprClassification = asprClassification;
+               }
+
+               public String getPublishURL() {
+                       return publishURL;
+               }
+
+               public void setPublishURL(String publishURL) {
+                       this.publishURL = publishURL;
+               }
+
+               public String getLogURL() {
+                       return logURL;
+               }
+
+               public void setLogURL(String logURL) {
+                       this.logURL = logURL;
+               }
+
+
+               
+               public String getFormatUuid() {
+                       return formatUuid;
+               }
+
+               public void setFormatUuid(String formatUuid) {
+                       this.formatUuid = formatUuid;
+               }
+
+               // returns the Feed object in JSON that conforms to DR Prov Server expectations
+               public String toProvJSON() {
+
+                       String postJSON = String.format("{\"name\": \"%s\", \"version\": \"%s\", \"description\": \"%s\", \"suspend\": %s, \"authorization\": { \"classification\": \"%s\", ",
+                                       this.getFeedName(), 
+                                       this.getFeedVersion(),
+                                       this.getFeedDescription(),
+                                       this.isSuspended() ,
+                                       this.getAsprClassification()
+                                       );
+                       int i;
+                       postJSON += "\"endpoint_addrs\": [],\"endpoint_ids\": [";
+                       String comma = "";
+                       for( i = 0 ; i < pubs.size(); i++) {
+                               postJSON +=     String.format(" %s{\"id\": \"%s\",\"password\": \"%s\"}", 
+                                               comma,
+                                               pubs.get(i).getUsername(),
+                                               pubs.get(i).getUserpwd()
+                                               ) ;
+                               comma = ",";
+                       }
+                       postJSON += "]}}";
+                       
+                       logger.info( "postJSON=" + postJSON);           
+                       return postJSON;
+               }
+               
+               public ArrayList<DR_Pub> getPubs() {
+                       return pubs;
+               }
+
+               public void setPubs( ArrayList<DR_Pub> pubs) {
+                       this.pubs = pubs;
+               }
+
+               public ArrayList<DR_Sub> getSubs() {
+                       return subs;
+               }
+
+               public void setSubs( ArrayList<DR_Sub> subs) {
+                       this.subs = subs;
+               }
+
+               public byte[] getBytes() {
+                       return toProvJSON().getBytes(StandardCharsets.UTF_8);
+               }
+               
+               public static String getSubProvURL( String feedId ) {
+                       return new DmaapService().getDmaap().getDrProvUrl() + "/subscribe/" + feedId;
+               }
+
+               @Override
+               public String toString() {
+                       String rc = String.format ( "Feed: {feedId=%s feedName=%s feedVersion=%s feedDescription=%s owner=%s asprClassification=%s publishURL=%s subscriberURL=%s suspended=%s logURL=%s formatUuid=%s}",
+                                       feedId,
+                                       feedName,
+                                       feedVersion,
+                                       feedDescription,
+                                       owner,
+                                       asprClassification,
+                                       publishURL,
+                                       subscribeURL,
+                                       suspended,
+                                       logURL,
+                                       formatUuid
+
+               
+                                       );
+
+                       for( DR_Pub pub: pubs) {
+                               rc += "\n" + pub.toString();
+                       }
+
+                       for( DR_Sub sub: subs ) {
+                               rc += "\n" + sub.toString();
+                       }
+                       return rc;
+               }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/FqtnType.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/FqtnType.java
new file mode 100644 (file)
index 0000000..697e9b8
--- /dev/null
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+@XmlRootElement
+public enum FqtnType {
+       FQTN_NOT_SPECIFIED(0),
+       FQTN_LEGACY_FORMAT(1),
+       FQTN_PROJECTID_FORMAT(2),
+       FQTN_PROJECTID_VERSION_FORMAT(3);
+
+
+    private int value;
+    private static Map map = new HashMap<>();
+
+    private FqtnType(int value) {
+        this.value = value;
+    }
+
+    static {
+        for (FqtnType repType : FqtnType.values()) {
+            map.put(repType.value, repType);
+        }
+    }
+
+    public static FqtnType valueOf(int repType) {
+        return (FqtnType) map.get(repType);
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    static public FqtnType Validator( String input ){
+   
+               FqtnType t;
+               try {
+                       t = FqtnType.valueOf( input );
+               } catch ( IllegalArgumentException e ) {
+                       t = FQTN_NOT_SPECIFIED;
+               }
+               return t;
+       }
+
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Client.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Client.java
new file mode 100644 (file)
index 0000000..0631f07
--- /dev/null
@@ -0,0 +1,162 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import java.util.Date;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+
+import io.swagger.annotations.ApiModelProperty;
+
+@XmlRootElement
+public class MR_Client extends DmaapObject {
+
+       @ApiModelProperty( value="a tag indicating a logical deployment site")
+       private String dcaeLocationName;
+       @ApiModelProperty( value="the URL for a MR instance - typically in the same dcaeLocation - that this client should use to access the topic")
+       private String  topicURL;
+       @ApiModelProperty( value="Fully Qualified Topic Name constructed by dbcapi")
+       private String fqtn;
+       @ApiModelProperty( value="an AAF Role to be granted an appropriate Permission.  If specified, takes precedence over clientIdentity, for backwards compatibility.")
+       private String clientRole;
+       @ApiModelProperty( value="one or more actions from the set (\"pub\", \"sub\", \"view\") for which this client needs Permission")
+       private String[] action;
+       @ApiModelProperty( value="a unique identifier generated by dbcapi for this client")
+       private String mrClientId;
+       @ApiModelProperty( value="an AAF identity to be associated to an appropriate topic Role")
+       private String clientIdentity;
+       
+
+       public MR_Client() {
+               this.mrClientId = DatabaseClass.getNextClientId();
+               this.lastMod = new Date();
+               this.setLastMod();
+               debugLogger.debug( "MR_Client constructor " + this.lastMod );
+                       
+       }
+       
+       public MR_Client( String dLN,
+                                       String f,
+                                       String cR,
+                                       String[] a ) {
+               this.dcaeLocationName = dLN;
+               this.fqtn = f;
+               this.clientRole = cR;
+               int i = 0;
+               
+               if (a != null) {
+                       this.action = new String[a.length];
+                       for (String aa : a) {
+                               this.action[i++] = new String(aa);
+                       }
+               }
+               this.setStatus( DmaapObject_Status.NEW );
+               this.mrClientId = DatabaseClass.getNextClientId();
+               this.setLastMod();
+               debugLogger.debug( "MR_Client constructor w initialization " + this.lastMod );
+       }
+
+       public String getDcaeLocationName() {
+               return dcaeLocationName;
+       }
+
+       public void setDcaeLocationName(String dcaeLocationName) {
+               this.dcaeLocationName = dcaeLocationName;
+       }
+
+       public String getFqtn() {
+               return fqtn;
+       }
+
+       public void setFqtn(String fqtn) {
+               this.fqtn = fqtn;
+       }
+
+       public String getClientRole() {
+               return clientRole;
+       }
+
+       public void setClientRole(String clientRole) {
+               this.clientRole = clientRole;
+       }
+
+       public String[] getAction() {
+               return action;
+       }
+
+       public void setAction(String[] action) {
+               this.action = action;
+       }
+       
+       @ApiModelProperty( hidden=true )
+       public boolean isPublisher() {
+               return hasAction( "pub");
+       }
+       @ApiModelProperty( hidden=true )
+       public boolean isSubscriber() {
+               return hasAction( "sub");
+       }
+       
+       public boolean hasAction( String val ) {
+               for (String s: this.action) {
+                       if ( s!= null && s.equals(val)) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+       public String getMrClientId() {
+               return mrClientId;
+       }
+
+       public void setMrClientId(String mrClientId) {
+               this.mrClientId = mrClientId;
+       }
+
+
+
+       public String getTopicURL() {
+               return topicURL;
+       }
+
+       public void setTopicURL(String topicURL) {
+               this.topicURL = topicURL;
+       }
+
+       public String getClientIdentity() {
+               return clientIdentity;
+       }
+
+       public void setClientIdentity(String clientIdentity) {
+               this.clientIdentity = clientIdentity;
+       }
+       public boolean hasClientIdentity() {
+               if ( this.clientIdentity == null || this.clientIdentity.isEmpty() ) {
+                       return false;
+               } else {
+                       return true;
+               }
+       }
+
+       
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Cluster.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MR_Cluster.java
new file mode 100644 (file)
index 0000000..06b6194
--- /dev/null
@@ -0,0 +1,235 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.DmaapTimestamp;
+
+
+
+@XmlRootElement
+public class MR_Cluster extends DmaapObject {
+
+       private String dcaeLocationName;
+       private String fqdn;
+       private DmaapTimestamp lastMod;
+       private String  topicProtocol;
+       private String  topicPort;
+       private String  replicationGroup;
+       private String  sourceReplicationPort;
+       private String  targetReplicationPort;
+
+       
+       // TODO: make this a system property
+       private static  String defaultTopicProtocol;
+       private static   String defaultTopicPort;
+       private static  String defaultReplicationGroup;
+       private static  String defaultSourceReplicationPort;
+       private static  String defaultTargetReplicationPort;
+       
+       private static void setDefaults() {
+               /* boolean been_here = false;
+               if ( been_here ) {
+                       return;
+               } */
+               DmaapConfig dc = (DmaapConfig)DmaapConfig.getConfig();
+               defaultTopicProtocol = dc.getProperty("MR.TopicProtocol", "https");
+               defaultTopicPort = dc.getProperty( "MR.TopicPort", "3905");
+               defaultReplicationGroup = dc.getProperty( "MR.ReplicationGroup", "" );
+               defaultSourceReplicationPort = dc.getProperty( "MR.SourceReplicationPort", "2181");
+               defaultTargetReplicationPort = dc.getProperty( "MR.TargetReplicationPort", "9092");
+               // been_here = true;
+       }
+
+
+       public MR_Cluster() {
+               setDefaults();
+               this.topicProtocol = defaultTopicProtocol;
+               this.topicPort = defaultTopicPort;
+               this.replicationGroup = null;
+               this.sourceReplicationPort = defaultSourceReplicationPort;
+               this.targetReplicationPort = defaultTargetReplicationPort;
+               this.lastMod = new DmaapTimestamp();
+               this.lastMod.mark();
+
+               debugLogger.debug( "MR_Cluster constructor " + this.lastMod );
+               
+       }
+
+       // new style constructor
+       public MR_Cluster( String dLN,
+                       String f,
+                       String prot,
+                       String port) {
+               setDefaults();
+               this.dcaeLocationName = dLN;
+               this.fqdn = f;
+
+               if ( prot == null || prot.isEmpty() ) {
+                       this.topicProtocol = defaultTopicProtocol;
+               } else {
+                       this.topicProtocol = prot;
+               }
+               if ( port == null || port.isEmpty() ) {
+                       this.topicPort = defaultTopicPort;
+               } else {
+                       this.topicPort = port;
+               }
+
+               this.replicationGroup = defaultReplicationGroup;
+               this.sourceReplicationPort = defaultSourceReplicationPort;
+               this.targetReplicationPort = defaultTargetReplicationPort;
+
+               this.lastMod = new DmaapTimestamp();
+               this.lastMod.mark();
+               
+               debugLogger.debug( "MR_Cluster constructor w initialization complete" + this.lastMod.getVal() );
+       }
+
+       public MR_Cluster( String dLN,
+                       String f,
+                       String prot,
+                       String port,
+                       String repGroup,
+                       String sourceRepPort,
+                       String targetRepPort ) {
+               setDefaults();
+               this.dcaeLocationName = dLN;
+               this.fqdn = f;
+
+               if ( prot == null || prot.isEmpty() ) {
+                       this.topicProtocol = defaultTopicProtocol;
+               } else {
+                       this.topicProtocol = prot;
+               }
+               if ( port == null || port.isEmpty() ) {
+                       this.topicPort = defaultTopicPort;
+               } else {
+                       this.topicPort = port;
+               }
+               if ( repGroup == null || repGroup.isEmpty() ) {
+                       this.replicationGroup = defaultReplicationGroup;
+               } else {
+                       this.replicationGroup = repGroup;
+               }
+               if ( sourceRepPort == null || sourceRepPort.isEmpty()) {
+                       this.sourceReplicationPort = defaultSourceReplicationPort;
+               } else {
+                       this.sourceReplicationPort = sourceRepPort;
+               }
+               if ( targetRepPort == null || targetRepPort.isEmpty()) {
+                       this.targetReplicationPort = defaultTargetReplicationPort;
+               } else {
+                       this.targetReplicationPort = targetRepPort;
+               }
+                               
+               this.lastMod = new DmaapTimestamp();
+               this.lastMod.mark();
+               
+               debugLogger.debug( "MR_Cluster constructor w initialization complete" + this.lastMod.getVal() );
+       }
+       public String getDcaeLocationName() {
+               return dcaeLocationName;
+       }
+
+       public void setDcaeLocationName(String dcaeLocationName) {
+               this.dcaeLocationName = dcaeLocationName;
+       }
+
+       public String getFqdn() {
+               return fqdn;
+       }
+
+       public void setFqdn(String fqdn) {
+               this.fqdn = fqdn;
+       }
+
+
+       public String getTopicProtocol() {
+               return topicProtocol;
+       }
+
+       public void setTopicProtocol(String topicProtocol) {
+               this.topicProtocol = topicProtocol;
+       }
+
+       public String getTopicPort() {
+               return topicPort;
+       }
+
+       public void setTopicPort(String topicPort) {
+               this.topicPort = topicPort;
+       }
+
+       public String getReplicationGroup() {
+               return replicationGroup;
+       }
+
+       public void setReplicationGroup(String replicationGroup) {
+               this.replicationGroup = replicationGroup;
+       }
+
+
+
+
+       public String getSourceReplicationPort() {
+               return sourceReplicationPort;
+       }
+
+
+
+       public void setSourceReplicationPort(String sourceReplicationPort) {
+               this.sourceReplicationPort = sourceReplicationPort;
+       }
+
+
+
+       public String getTargetReplicationPort() {
+               return targetReplicationPort;
+       }
+
+
+
+       public void setTargetReplicationPort(String targetReplicationPort) {
+               this.targetReplicationPort = targetReplicationPort;
+       }
+
+
+
+       public String genTopicURL(String overideFqdn, String topic) {
+
+               StringBuilder str = new StringBuilder( topicProtocol );
+               str.append("://")
+                       .append( overideFqdn != null ? overideFqdn : fqdn)
+                       .append(":")
+                       .append(topicPort)
+                       .append("/events/")
+                       .append(topic);
+               
+               return str.toString();
+
+
+       }
+
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MirrorMaker.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/MirrorMaker.java
new file mode 100644 (file)
index 0000000..098524c
--- /dev/null
@@ -0,0 +1,165 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import org.onap.dmaap.dbcapi.service.MirrorMakerService;
+
+import java.util.ArrayList;
+
+public class MirrorMaker extends DmaapObject {
+
+       private String  sourceCluster;
+       private String  targetCluster;
+       private String  mmName;
+       private ArrayList<String> topics;  //re-using this var name for backwards DB compatibility
+
+       public MirrorMaker(){
+               
+       }
+
+       public MirrorMaker(String source, String target, int i) {
+               initMM( source, target );
+               // original mm names did not have any index, so leave off index 0 for
+               // backwards compatibility
+               if ( i != 0 ) {
+                       String n = this.getMmName() + "_" + i;
+                       this.setMmName(n);
+               }
+       }
+
+       public MirrorMaker(String source, String target) {
+               initMM( source, target );
+       }
+       
+       private void initMM(String source, String target) {
+               sourceCluster = source;
+               targetCluster = target;
+               mmName = genKey(source, target);
+               topics = new ArrayList<>();
+
+       }
+       
+       public String getMmName() {
+               return mmName;
+       }
+
+       public void setMmName(String mmName) {
+               this.mmName = mmName;
+       }
+
+       // returns the JSON for MM message containing which Topics to replicate
+       /* 
+        * example:
+        * 
+                       {
+                           "messageID":"12349",
+                           "updateWhiteList":
+                               {
+                                   "name":"Global1ToGlobal3",
+                                   "whitelist":"org.openecomp.dcae.topic1,org.openecomp.dcae.topic2"
+                               }
+                       }   
+        */
+       public String getWhitelistUpdateJSON() {
+               StringBuilder str = new StringBuilder( "{ \"messageID\": \"" + MirrorMakerService.genTransactionId() + "\", \"updateWhiteList\": {"  );
+               str.append( " \"name\": \"" + this.getMmName() + "\", \"whitelist\": \"" );
+               int numTargets = 0;
+
+               for (String rv: topics) {
+                       if ( numTargets > 0 ) {
+                               str.append( ",");
+                       }
+                       str.append( rv );
+                       numTargets++;
+               }
+               str.append( "\" } }" );
+               
+               return str.toString();
+       }
+       
+       // returns the JSON for MM message indicating that a MM agent is needed between two clusters
+       // example:
+       /*
+        * 
+                       {
+                           "messageID":"12345"
+                           "createMirrorMaker":
+                               {
+                                   "name":"Global1ToGlobal2",
+                                   "consumer":"192.168.0.1:2181",
+                                   "producer":"192.168.0.2:9092"
+                               }
+                       }
+        */
+       public String createMirrorMaker( String consumerPort, String producerPort ) {
+               StringBuilder str = new StringBuilder( "{ \"messageID\": \"" + MirrorMakerService.genTransactionId() + "\", \"createMirrorMaker\": {"  );
+               str.append( " \"name\": \"" + this.getMmName() + "\", " );
+               str.append( " \"consumer\": \"" + this.sourceCluster + ":" + consumerPort + "\", " );
+               str.append( " \"producer\": \"" + this.targetCluster + ":" + producerPort + "\", ");
+               
+               str.append( " \"numStreams\": \"10\" } }" );
+               
+               return str.toString();
+       }
+
+       public String getSourceCluster() {
+               return sourceCluster;
+       }
+
+       public void setSourceCluster(String sourceCluster) {
+               this.sourceCluster = sourceCluster;
+       }
+
+       public String getTargetCluster() {
+               return targetCluster;
+       }
+
+       public void setTargetCluster(String targetCluster) {
+               this.targetCluster = targetCluster;
+       }
+
+       public ArrayList<String> getTopics() {
+               return topics;
+       }
+
+       public void setTopics(ArrayList<String> topics) {
+               this.topics = topics;
+       }
+
+       public static String genKey( String s, String t) {
+               StringBuilder str = new StringBuilder();
+               str.append(s);
+               str.append("-To-");
+               str.append(t);
+               return str.toString();
+       }
+
+       public void addTopic( String topic ) {
+               if ( ! topics.contains(topic)) {        
+                       topics.add(topic);
+               }
+               logger.info( "Mirrormaker.addTopic: topic=" + topic + " . Now have " + topics.size() + " topics" );
+       }
+
+       public int getTopicCount() {
+               return topics.size();
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ReplicationType.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/ReplicationType.java
new file mode 100644 (file)
index 0000000..5d5b6c6
--- /dev/null
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+
+@XmlRootElement
+public enum ReplicationType {
+       REPLICATION_NOT_SPECIFIED(0),
+       REPLICATION_NONE(1),
+       REPLICATION_EDGE_TO_CENTRAL(10),
+       REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL(110),
+       REPLICATION_CENTRAL_TO_EDGE(20),
+       REPLICATION_CENTRAL_TO_GLOBAL(21),
+       REPLICATION_GLOBAL_TO_CENTRAL(30),
+       REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE(120),
+       REPLICATION_EDGE_TO_FQDN(40),
+       REPLICATION_FQDN_TO_EDGE(41),
+       REPLICATION_FQDN_TO_GLOBAL(50),
+       REPLICATION_GLOBAL_TO_FQDN(51),
+       REPLICATION_EDGE_TO_FQDN_TO_GLOBAL(130),
+       REPLICATION_GLOBAL_TO_FQDN_TO_EDGE (140);
+
+    private int value;
+    private static Map map = new HashMap<>();
+
+    private ReplicationType(int value) {
+        this.value = value;
+    }
+
+    static {
+        for (ReplicationType repType : ReplicationType.values()) {
+            map.put(repType.value, repType);
+        }
+    }
+
+    public static ReplicationType valueOf(int repType) {
+        return (ReplicationType) map.get(repType);
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    static public ReplicationType Validator( String input ){
+   
+               ReplicationType t;
+               try {
+                       t = ReplicationType.valueOf( input );
+               } catch ( IllegalArgumentException e ) {
+                       t = REPLICATION_NOT_SPECIFIED;
+               }
+               return t;
+       }
+
+       public boolean involvesGlobal() {
+       
+               
+               if ( ( this.compareTo(REPLICATION_CENTRAL_TO_GLOBAL) == 0 ) ||
+                        ( this.compareTo(REPLICATION_GLOBAL_TO_CENTRAL) == 0 ) ||
+                        ( this.compareTo(REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL) == 0 ) ||
+                        ( this.compareTo(REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE) == 0 ) ||
+                        ( this.compareTo(REPLICATION_EDGE_TO_FQDN_TO_GLOBAL) == 0 ) ||
+                        ( this.compareTo(REPLICATION_GLOBAL_TO_FQDN_TO_EDGE) == 0 ) ||
+                        ( this.compareTo(REPLICATION_FQDN_TO_GLOBAL) == 0 ) ||
+                        ( this.compareTo(REPLICATION_GLOBAL_TO_FQDN) == 0 ) ) {
+                       return true;
+               }
+               return false;
+       }
+       
+       public boolean involvesFQDN() {
+               if ( 
+                               ( this.compareTo(REPLICATION_EDGE_TO_FQDN) == 0 ) ||
+                               ( this.compareTo(REPLICATION_EDGE_TO_FQDN_TO_GLOBAL) == 0 ) ||
+                               ( this.compareTo(REPLICATION_GLOBAL_TO_FQDN_TO_EDGE) == 0 ) ||
+                               ( this.compareTo(REPLICATION_FQDN_TO_GLOBAL) == 0 ) ||
+                               ( this.compareTo(REPLICATION_GLOBAL_TO_FQDN) == 0 ) ||
+                               ( this.compareTo(REPLICATION_FQDN_TO_EDGE) == 0 ) 
+                               ) {
+                       return true;
+               }
+               return false;
+       }
+
+
+
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/model/Topic.java
new file mode 100644 (file)
index 0000000..d2a9077
--- /dev/null
@@ -0,0 +1,352 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import com.google.common.base.Objects;
+import io.swagger.annotations.ApiModelProperty;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import javax.xml.bind.annotation.XmlRootElement;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+
+@XmlRootElement
+public class Topic extends DmaapObject  {
+
+       @ApiModelProperty( value="Fully Qualified Topic Name constructed by dbcapi, following the rules for `fqtnStyle`")
+       private String fqtn;
+       @ApiModelProperty( value="the short name used by humans, and utilized to construct the `FQTN`")
+       private String topicName;
+       @ApiModelProperty( value="a description of what this Topic is used for")
+       private String  topicDescription;
+       private String  tnxEnabled;
+       @ApiModelProperty( value="a label used to identify who requested this `Topic` to be provisioned.  In the future this "
+                       + "may be an AAF Identity.")
+       private String  owner;
+       @ApiModelProperty( value="a reference to an identifier that describes a data format used for this `Topic`")
+       private String  formatUuid;
+       @ApiModelProperty( value="An indicator for how this `Topic` should be replicated when there are more than one `MR_Cluster` instances")
+       private ReplicationType replicationCase;  
+       @ApiModelProperty( value="the URL of an outside MR instance")
+       private String  globalMrURL;            // optional: URL of global MR to replicate to/from
+       @ApiModelProperty( value="the construction rule for the `fqtn` field")
+       private FqtnType  fqtnStyle;
+       @ApiModelProperty( value="a hook for any versioning needed for managing a `Topic` over time")
+       private String  version;
+       @ApiModelProperty( value="the kafka attribute for specifying the number of partitions")
+       private String  partitionCount;
+       @ApiModelProperty( value="the kafka attribute for specifying replication within an `MR_Cluster` instance")
+       private String  replicationCount;
+       @ApiModelProperty( value="a value generated by dbcapi, this AAF Role has permission to publish to this `Topic`")
+       private String  publisherRole;
+       @ApiModelProperty( value="a value generated by dbcapi, this AAF Role has permission to subscribe to this `Topic`")
+       private String  subscriberRole;
+
+       @ApiModelProperty( value="an array of `MR_Client` objects associated to this `Topic`")
+       private List<MR_Client> clients;
+
+
+       
+       private static Dmaap dmaap = new DmaapService().getDmaap();
+       
+       private static String defaultPartitionCount;
+       private static String defaultReplicationCount;
+       
+       // during unit testing, discovered that presence of dots in some values
+       // creates an unplanned topic namespace as we compose the FQTN.
+       // this may create sensitivity (i.e. 403) for subsequent creation of AAF perms, so best to not allow it 
+       private static String removeDots( String source, String def ) {
+               if ( source == null || source.isEmpty()) {
+                       return def;
+               }
+               return source.replaceAll("\\.", "_");
+       }
+       //
+       // utility function to generate the FQTN of a topic
+       public  String genFqtn(  ) {
+               DmaapConfig dc = (DmaapConfig)DmaapConfig.getConfig();
+               String projectId = dc.getProperty("MR.projectID", "99999");
+               CharSequence signal = ".";
+               String ret;
+               if ( this.getTopicName().contains( signal )) {
+                       // presence of a dot indicates the name is already fully qualified
+                       ret = this.getTopicName();
+               } else {
+                       // these vars may not contain dots
+                       String p = removeDots( projectId, "90909");
+                       String v = removeDots( this.getVersion(), "v1");
+                       switch( this.getFqtnStyle() ) {
+                       case FQTN_PROJECTID_VERSION_FORMAT:
+
+                               ret = dmaap.getTopicNsRoot() + "."  + dmaap.getDmaapName() + "." + p + "-" + this.getTopicName()  + "-" + v;
+                               break;
+                               
+                       case FQTN_PROJECTID_FORMAT:
+
+                               ret = dmaap.getTopicNsRoot() + "."  + dmaap.getDmaapName() + "." + p + "-" + this.getTopicName();
+                               break;
+                       
+                       case FQTN_LEGACY_FORMAT:
+                       default:  // for backwards compatibility
+                               ret = dmaap.getTopicNsRoot() + "." + dmaap.getDmaapName() + "." + this.getTopicName();
+                               break;
+                       
+
+                       }
+                       
+               }
+               return ret;
+       }
+
+
+
+       public Topic() {
+               super();
+               this.clients = new ArrayList<>();
+               this.lastMod = new Date();
+               this.replicationCase = ReplicationType.Validator("none");
+               this.setLastMod();
+               logger.debug( "Topic constructor " + this.lastMod );
+       }
+       public Topic(String fqtn, String topicName, String topicDescription,
+                        String tnxEnabled, String owner) {
+               super();
+               this.fqtn = fqtn;
+               this.topicName = topicName;
+               this.topicDescription = topicDescription;
+               this.tnxEnabled = tnxEnabled;
+               this.owner = owner;
+               this.init();
+               this.setLastMod();
+               logger.debug( "Topic constructor w args " + this.getLastMod() );
+       }
+       
+       public Topic init() {
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               
+               defaultPartitionCount = p.getProperty( "MR.partitionCount", "2");
+               defaultReplicationCount = p.getProperty( "MR.replicationCount", "1");
+               
+               this.setStatus( DmaapObject_Status.NEW );
+               this.replicationCase = ReplicationType.Validator("none");
+               this.fqtnStyle = FqtnType.Validator("none");
+               this.setPartitionCount( defaultPartitionCount );
+               this.setReplicationCount( defaultReplicationCount );
+               
+               return this;
+       }
+
+       // expects a String in JSON format, with known fields to populate Topic object
+       public Topic ( String json ) {
+               JSONParser parser = new JSONParser();
+               JSONObject jsonObj;
+               try {
+                       jsonObj = (JSONObject) parser.parse( json );
+               } catch ( ParseException pe ) {
+                  logger.error( "Error parsing provisioning data: " + json );
+                  this.setStatus( DmaapObject_Status.INVALID );
+                  return;
+           }
+               this.setFqtn( (String) jsonObj.get( "fqtn" ) );
+               this.setTopicName( (String) jsonObj.get( "topicName" ) );
+               this.setTopicDescription( (String) jsonObj.get( "topicDescription" ));
+               this.setOwner( (String) jsonObj.get( "owner" ) );
+               this.setStatus( (String) jsonObj.get( "status" ) );
+               this.setReplicationCase( ReplicationType.Validator( (String) jsonObj.get( "replicationCase" ) ));
+               this.setFqtnStyle( FqtnType.Validator( (String) jsonObj.get( "fqtnStyle" ) ) );
+               this.setPartitionCount( (String) jsonObj.get("partitionCount"));
+
+       }
+       public String getFqtn() {
+               return fqtn;
+       }
+       public void setFqtn(String fqtn) {
+               this.fqtn = fqtn;
+       }
+       public String getTopicName() {
+               return topicName;
+       }
+       public void setTopicName(String topicName) {
+               this.topicName = topicName;
+       }
+       public String getTopicDescription() {
+               return topicDescription;
+       }
+       public void setTopicDescription(String topicDescription) {
+               this.topicDescription = topicDescription;
+       }
+
+       public String getTnxEnabled() {
+               return tnxEnabled;
+       }
+       public void setTnxEnabled(String tnxEnabled) {
+               this.tnxEnabled = tnxEnabled;
+       }
+       public String getOwner() {
+               return owner;
+       }
+       public void setOwner(String owner) {
+               this.owner = owner;
+       }
+       public String getPartitionCount() {
+               return partitionCount;
+       }
+       public void setPartitionCount(String partitions) {
+               this.partitionCount = partitions;
+       }
+       public String getReplicationCount() {
+               return replicationCount;
+       }
+       public void setReplicationCount(String replicationCount) {
+               this.replicationCount = replicationCount;
+       }
+
+
+       public void setClients(List<MR_Client> clients) {
+               this.clients = clients;
+       }
+
+       public List<MR_Client> getClients() {
+               return clients;
+       }
+
+       @ApiModelProperty( hidden=true )
+       public int getNumClients() {
+               if ( this.clients == null ) {
+                       return 0;
+               }
+               return this.clients.size();
+       }
+
+
+
+
+       public String getFormatUuid() {
+               return formatUuid;
+       }
+
+
+
+       public void setFormatUuid(String formatUuid) {
+               this.formatUuid = formatUuid;
+       }
+
+
+       public ReplicationType getReplicationCase() {
+               return replicationCase;
+       }
+
+       
+       public void setReplicationCase(ReplicationType t) {
+               this.replicationCase = t;
+       }
+       public FqtnType getFqtnStyle() {
+               return fqtnStyle;
+       }
+
+       
+       public void setFqtnStyle(FqtnType t) {
+               this.fqtnStyle = t;
+       }
+
+       public String getGlobalMrURL() {
+               return globalMrURL;
+       }
+
+
+
+       public void setGlobalMrURL(String globalMrURL) {
+               this.globalMrURL = globalMrURL;
+       }
+
+
+
+       public String getVersion() {
+               return version;
+       }
+
+
+
+       public void setVersion(String version) {
+               this.version = version;
+       }
+
+
+
+       public String getPublisherRole() {
+               return publisherRole;
+       }
+       public void setPublisherRole(String publisherRole) {
+               this.publisherRole = publisherRole;
+       }
+       public String getSubscriberRole() {
+               return subscriberRole;
+       }
+       public void setSubscriberRole(String subscriberRole) {
+               this.subscriberRole = subscriberRole;
+       }
+       public String toProvJSON() {
+               StringBuilder str = new StringBuilder();
+               str.append("{ \"topicName\": \"");
+               str.append( this.getFqtn() );
+               str.append( "\", \"topicDescription\": \"");
+               str.append( this.getTopicDescription());
+               str.append( "\", \"partitionCount\": \"");
+               str.append( this.getPartitionCount());
+               str.append( "\", \"replicationCount\": \"");
+               str.append( this.getReplicationCount());
+               str.append( "\" } ");
+               
+               logger.info( str.toString() );
+               return str.toString();
+       }
+       @ApiModelProperty( hidden=true )
+       public byte[] getBytes() {
+               return toProvJSON().getBytes(StandardCharsets.UTF_8);
+       }
+
+
+       @Override
+       public boolean equals(Object o) {
+               if (this == o) {
+                       return true;
+               }
+               if (o == null || getClass() != o.getClass()) {
+                       return false;
+               }
+               Topic topic = (Topic) o;
+               return Objects.equal(fqtn, topic.fqtn) &&
+                       Objects.equal(topicName, topic.topicName) &&
+                       Objects.equal(tnxEnabled, topic.tnxEnabled) &&
+                       Objects.equal(owner, topic.owner);
+       }
+
+       @Override
+       public int hashCode() {
+               return Objects.hashCode(fqtn, topicName, tnxEnabled, owner);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilter.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilter.java
new file mode 100644 (file)
index 0000000..d8a7302
--- /dev/null
@@ -0,0 +1,141 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.http.HttpStatus;
+import org.onap.aaf.cadi.PropAccess;
+import org.onap.aaf.cadi.filter.CadiFilter;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+public class AAFAuthenticationFilter extends BaseLoggingClass implements Filter{
+
+    static final String CADI_PROPERTIES = "cadi.properties";
+    static final String CADI_AUTHN_FLAG = "enableCADI";
+
+    private boolean isCadiEnabled;
+    private CadiFilter cadiFilter;
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        DmaapConfig dmaapConfig = getConfig();
+        String flag = dmaapConfig.getProperty(CADI_AUTHN_FLAG, "false");
+        isCadiEnabled = "true".equalsIgnoreCase(flag);
+        initCadi(dmaapConfig);
+    }
+
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+        throws IOException, ServletException {
+
+        if(isCadiEnabled) {
+            cadiFilter.doFilter(servletRequest, servletResponse, filterChain);
+            updateResponseBody((HttpServletResponse)servletResponse);
+        } else {
+            filterChain.doFilter(servletRequest, servletResponse);
+        }
+    }
+
+    private void updateResponseBody(HttpServletResponse httpResponse)
+        throws IOException {
+        if(httpResponse.getStatus() == 401) {
+            String errorMsg = "invalid or no credentials provided";
+            errorLogger.error(errorMsg);
+            httpResponse.setContentType("application/json");
+            httpResponse.setCharacterEncoding("UTF-8");
+            httpResponse.getWriter().print(buildErrorResponse(errorMsg));
+            httpResponse.getWriter().flush();
+        }
+    }
+
+    private String buildErrorResponse(String msg) {
+        try {
+            return new ObjectMapper().writeValueAsString(new ApiError(HttpStatus.UNAUTHORIZED_401, msg, "Authentication"));
+        } catch (JsonProcessingException e) {
+            logger.warn("Could not serialize response entity: " + e.getMessage());
+            return "";
+        }
+    }
+
+
+    @Override
+    public void destroy() {
+        //nothing to cleanup
+    }
+
+    private void initCadi(DmaapConfig dmaapConfig) throws ServletException {
+        if(isCadiEnabled) {
+            try {
+                String cadiPropertiesFile = dmaapConfig.getProperty(CADI_PROPERTIES);
+                if(cadiPropertiesFile != null && !cadiPropertiesFile.isEmpty()) {
+                    cadiFilter = new CadiFilter(loadCadiProperties(cadiPropertiesFile));
+                } else {
+                    throw new ServletException("Cannot initialize CADI filter.CADI properties not available.");
+                }
+            } catch (ServletException e) {
+                errorLogger.error("CADI init error :" + e.getMessage());
+                throw e;
+            }
+        }
+    }
+
+    private PropAccess loadCadiProperties(String propertiesFilePath) throws ServletException {
+        try {
+            Properties props = new Properties();
+            props.load(new FileInputStream(propertiesFilePath));
+            return new PropAccess(props);
+        } catch (IOException e) {
+            String msg = "Could not load CADI properties file: " + propertiesFilePath;
+            errorLogger.error(msg, e);
+            throw new ServletException(msg);
+        }
+    }
+
+    DmaapConfig getConfig() {
+        return (DmaapConfig) DmaapConfig.getConfig();
+    }
+
+    //tests only
+    CadiFilter getCadiFilter() {
+        return cadiFilter;
+    }
+
+    void setCadiFilter(CadiFilter cadiFilter) {
+        this.cadiFilter = cadiFilter;
+    }
+
+    boolean isCadiEnabled() {
+        return isCadiEnabled;
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilter.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilter.java
new file mode 100644 (file)
index 0000000..779f71b
--- /dev/null
@@ -0,0 +1,115 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.http.HttpStatus;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.PermissionBuilder;
+
+public class AAFAuthorizationFilter extends BaseLoggingClass implements Filter {
+
+    static final String CADI_AUTHZ_FLAG = "enableCADI";
+    private boolean isCadiEnabled = false;
+
+    private PermissionBuilder permissionBuilder;
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        DmaapConfig dmaapConfig = getConfig();
+        isCadiEnabled = "true".equalsIgnoreCase(dmaapConfig.getProperty(CADI_AUTHZ_FLAG, "false"));
+        if(isCadiEnabled) {
+            permissionBuilder = new PermissionBuilder(dmaapConfig, getDmaapService());
+        }
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
+        throws IOException, ServletException {
+
+        if(isCadiEnabled) {
+            HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
+            permissionBuilder.updateDmaapInstance();
+            String permission = permissionBuilder.buildPermission(httpRequest);
+
+            if (httpRequest.isUserInRole(permission)) {
+                logger.info("User " + httpRequest.getUserPrincipal().getName() + " has permission " + permission);
+                filterChain.doFilter(servletRequest, servletResponse);
+            } else {
+                String msg = "User " + httpRequest.getUserPrincipal().getName() + " does not have permission " + permission;
+                errorLogger.error(msg);
+                ((HttpServletResponse) servletResponse).setStatus(HttpStatus.FORBIDDEN_403);
+                servletResponse.setContentType("application/json");
+                servletResponse.setCharacterEncoding("UTF-8");
+                servletResponse.getWriter().print(buildErrorResponse(msg));
+                servletResponse.getWriter().flush();
+            }
+        } else {
+            filterChain.doFilter(servletRequest, servletResponse);
+        }
+    }
+
+    @Override
+    public void destroy() {
+        //nothing to cleanup
+    }
+
+    DmaapConfig getConfig() {
+        return (DmaapConfig) DmaapConfig.getConfig();
+    }
+
+    DmaapService getDmaapService() {
+        return new DmaapService();
+    }
+
+    private String buildErrorResponse(String msg) {
+        try {
+            return new ObjectMapper().writeValueAsString(new ApiError(HttpStatus.FORBIDDEN_403, msg, "Authorization"));
+        } catch (JsonProcessingException e) {
+            logger.warn("Could not serialize response entity: " + e.getMessage());
+            return "";
+        }
+    }
+
+    PermissionBuilder getPermissionBuilder() {
+        return permissionBuilder;
+    }
+
+    void setPermissionBuilder(PermissionBuilder permissionBuilder) {
+        this.permissionBuilder = permissionBuilder;
+    }
+
+    void setCadiEnabled(boolean cadiEnabled) {
+        isCadiEnabled = cadiEnabled;
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/Authorization.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/Authorization.java
new file mode 100644 (file)
index 0000000..e8b05c6
--- /dev/null
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+import javax.ws.rs.NameBinding;
+
+// @Authorization annotation
+@NameBinding
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface Authorization {
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AuthorizationFilter.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/AuthorizationFilter.java
new file mode 100644 (file)
index 0000000..32e8845
--- /dev/null
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+
+import org.onap.dmaap.dbcapi.authentication.AuthenticationErrorException;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.service.ApiService;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+
+@Authorization
+public class AuthorizationFilter extends BaseLoggingClass implements ContainerRequestFilter   {
+
+       private static final String AAF_CADI_FLAG = "enableCADI";
+       private final ResponseBuilder responseBuilder = new ResponseBuilder();
+       private final boolean isCadiEnabled;
+
+
+       public AuthorizationFilter() {
+               DmaapConfig dmaapConfig = (DmaapConfig) DmaapConfig.getConfig();
+               String flag = dmaapConfig.getProperty(AAF_CADI_FLAG, "false");
+               isCadiEnabled = "true".equalsIgnoreCase(flag);
+       }
+
+       @Override
+       public void filter(ContainerRequestContext requestContext) {
+
+               if(!isCadiEnabled) {
+                       ApiService apiResp = new ApiService()
+                               .setAuth(requestContext.getHeaderString("Authorization"))
+                               .setUriPath(requestContext.getUriInfo().getPath())
+                               .setHttpMethod(requestContext.getMethod())
+                               .setRequestId(requestContext.getHeaderString("X-ECOMP-RequestID"));
+
+                       try {
+                               apiResp.checkAuthorization();
+                       } catch (AuthenticationErrorException ae) {
+                               errorLogger.error("Error", ae);
+                               requestContext.abortWith(responseBuilder.unauthorized(apiResp.getErr().getMessage()));
+                       } catch (Exception e) {
+                               errorLogger.error("Error", e);
+                               requestContext.abortWith(responseBuilder.unavailable());
+                       }
+               }
+       }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/BridgeResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/BridgeResource.java
new file mode 100644 (file)
index 0000000..299c48f
--- /dev/null
@@ -0,0 +1,191 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Modifications Copyright (C) 2018 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.BrTopic;
+import org.onap.dmaap.dbcapi.model.MirrorMaker;
+import org.onap.dmaap.dbcapi.service.MirrorMakerService;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+
+@Path("/bridge")
+@Api( value= "bridge", description = "Endpoint for retreiving MR Bridge metrics" )
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Authorization
+public class BridgeResource extends BaseLoggingClass {
+       
+       private MirrorMakerService mmService = new MirrorMakerService();
+       private ResponseBuilder responseBuilder = new ResponseBuilder();
+
+       @GET
+       @ApiOperation( value = "return BrTopic details", 
+       notes = "Returns array of  `BrTopic` objects. If source and target query params are specified, only report on that bridge.  "
+                       + "If detail param is true, list topics names, else just a count is returned.", 
+       response = BrTopic.class)
+@ApiResponses( value = {
+    @ApiResponse( code = 200, message = "Success", response = BrTopic.class),
+    @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+})
+       public Response getBridgedTopics(@QueryParam("mmagent") String mmagent,
+                                                                       @QueryParam("detail") Boolean detailFlag ){
+
+               if ( mmagent == null ) {
+                       return responseBuilder.success(getMMcounts(Boolean.TRUE.equals(detailFlag)));
+
+               }
+               logger.info( "getBridgeTopics():" + " mmagent=" + mmagent);
+
+               if ( ! Boolean.TRUE.equals(detailFlag)) {
+                       BrTopic brTopic = new BrTopic();
+                       
+                       // get topics between 2 bridged locations
+
+                       MirrorMaker mm = mmService.getMirrorMaker(mmagent);
+                       if ( mm == null ) {             
+                               return responseBuilder.notFound();
+                       } 
+                                       
+                       brTopic.setTopicCount( mm.getTopicCount() );
+                       brTopic.setBrSource( mm.getSourceCluster());
+                       brTopic.setBrTarget( mm.getTargetCluster());
+                       brTopic.setMmAgentName(mm.getMmName());
+                       
+                       logger.info( "topicCount [2 locations]: " + brTopic.getTopicCount() );
+               
+                       return responseBuilder.success(brTopic);
+               } else {        
+                       logger.info( "getBridgeTopics() detail:" + " mmagent=" + mmagent);
+                       // get topics between 2 bridged locations       
+                       MirrorMaker mm = mmService.getMirrorMaker(mmagent);
+                       if ( mm == null ) {             
+                               return responseBuilder.notFound();
+                       } 
+
+                       return responseBuilder.success(mm);
+               }
+       }
+       
+       private BrTopic[] getMMcounts( Boolean showDetail ) {
+               
+               List<String> mmList = mmService.getAllMirrorMakers();
+               int s = 1;
+               if ( showDetail ) {
+                       s = mmList.size() + 1;
+               }
+               BrTopic[] brTopic = new BrTopic[s];
+               
+               int totCnt = 0;
+               s = 0;
+               for( String key: mmList ) {
+                       int mCnt = 0;
+                       MirrorMaker mm = mmService.getMirrorMaker(key);
+                       if ( mm != null ) {
+                               mCnt = mm.getTopicCount();
+                       }
+                       logger.info( "Count for "+ key + ": " + mCnt);
+                       totCnt += mCnt;
+                       if (showDetail && mm!=null) {
+                               brTopic[s] =  new BrTopic();
+                               brTopic[s].setBrSource( mm.getSourceCluster());
+                               brTopic[s].setBrTarget(mm.getTargetCluster());
+                               brTopic[s].setMmAgentName(mm.getMmName());
+                               brTopic[s].setTopicCount(mm.getTopicCount());
+                               s++;
+                       }
+               }
+               
+               logger.info( "topicCount [all locations]: " + totCnt );
+               brTopic[s] =  new BrTopic();
+               brTopic[s].setBrSource("all");
+               brTopic[s].setBrTarget("all");
+               brTopic[s].setMmAgentName("n/a");
+               brTopic[s].setTopicCount(totCnt);
+               return brTopic;
+       }
+       
+       @PUT
+       @ApiOperation( value = "update MirrorMaker details", 
+               notes = "replace the topic list for a specific Bridge.  Use JSON Body for value to replace whitelist, "
+                               + "but if refreshFlag param is true, simply refresh using existing whitelist."
+                               + "If split param is true, spread whitelist over smaller mmagents.", 
+               response = MirrorMaker.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = BrTopic.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response putBridgedTopics(@QueryParam("mmagent") String mmagent,
+                                                                       @QueryParam("refresh") Boolean refreshFlag,
+                                                                       @QueryParam("split") Boolean splitFlag,
+                                                                       MirrorMaker newBridge ){
+               logger.info( "putBridgeTopics() mmagent:" +  mmagent );
+
+               if ( mmagent != null ) {                // put topics between 2 bridged locations
+                       
+                       MirrorMaker mm = mmService.getMirrorMaker(mmagent);
+                       if ( mm == null ) {             
+                               return responseBuilder.notFound();
+                       } 
+                       
+                       if ( splitFlag != null && splitFlag == true ) {
+                               mm = mmService.splitMM( mm );
+                       } else if ( refreshFlag == null  ||  refreshFlag == false ) {
+                               logger.info( "setting whitelist from message body containing mmName=" + newBridge.getMmName());
+                               if ( ! mmagent.equals(newBridge.getMmName()) ){
+                                       logger.error( "mmagent query param does not match mmName in body");
+                                       return responseBuilder.error(new ApiError(BAD_REQUEST.getStatusCode(),
+                                                       "mmagent query param does not match mmName in body"));
+                               }
+                               mm.setTopics( newBridge.getTopics() );
+                       } else {
+                               logger.info( "refreshing whitelist from memory");
+                       }
+                       mmService.updateMirrorMaker(mm);
+                       return responseBuilder.success(mm);
+               }
+
+               else {
+                       logger.error( "mmagent is required for PUT");
+                       return responseBuilder.error(new ApiError(BAD_REQUEST.getStatusCode(), "mmagent is required for PUT"));
+               }
+
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_NodeResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_NodeResource.java
new file mode 100644 (file)
index 0000000..f001136
--- /dev/null
@@ -0,0 +1,172 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dcae
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+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.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Node;
+import org.onap.dmaap.dbcapi.service.DR_NodeService;
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+import static javax.ws.rs.core.Response.Status.NO_CONTENT;
+
+@Path("/dr_nodes")
+@Api( value= "dr_nodes", description = "Endpoint for a Data Router Node server" )
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Authorization
+public class DR_NodeResource extends BaseLoggingClass {
+
+       private DR_NodeService dr_nodeService = new DR_NodeService();
+       private ResponseBuilder responseBuilder = new ResponseBuilder();
+       private RequiredChecker checker = new RequiredChecker();
+       
+       @GET
+       @ApiOperation( value = "return DR_Node details", 
+       notes = "Returns array of `DR_Node` object array.  Need to add filter by dcaeLocation.", 
+       response = DR_Node.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Node.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response getDr_Nodes() {
+               List<DR_Node> nodes = dr_nodeService.getAllDr_Nodes();
+
+               GenericEntity<List<DR_Node>> list = new GenericEntity<List<DR_Node>>(nodes) {
+        };
+        return responseBuilder.success(list);
+       }
+       
+       @POST
+       @ApiOperation( value = "return DR_Node details", 
+       notes = "create a `DR_Node` in a *dcaeLocation*.  Note that multiple `DR_Node`s may exist in the same `dcaeLocation`.", 
+       response = DR_Node.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Node.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response addDr_Node(DR_Node node) {
+
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "dcaeLocation", node.getDcaeLocationName());
+                       checker.required( "fqdn", node.getFqdn());
+               } catch ( RequiredFieldException rfe ) {
+                       return responseBuilder.error(new ApiError(BAD_REQUEST.getStatusCode(),
+                                       "missing required field", "dcaeLocation, fqdn"));
+               }
+               DR_Node nNode = dr_nodeService.addDr_Node(node, apiError);
+               if (apiError.is2xx()) {
+                       return responseBuilder.success(nNode);
+               }
+               return responseBuilder.error(apiError);
+       }
+       
+       @PUT
+       @ApiOperation( value = "return DR_Node details", 
+       notes = "Update a single `DR_Node` object.", 
+       response = DR_Node.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Node.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{fqdn}")
+       public Response updateDr_Node(@PathParam("fqdn") String name, DR_Node node) {
+
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "dcaeLocation", node.getDcaeLocationName());
+                       checker.required( "fqdn", node.getFqdn());
+               } catch ( RequiredFieldException rfe ) {
+                       return responseBuilder.error(new ApiError(BAD_REQUEST.getStatusCode(),
+                                       "missing required field", "dcaeLocation, fqdn"));
+               }
+               node.setFqdn(name);
+               DR_Node nNode = dr_nodeService.updateDr_Node(node, apiError);
+               if (apiError.is2xx()) {
+                       return responseBuilder.success(nNode);
+               }
+               return responseBuilder.error(apiError);
+       }
+       
+       @DELETE
+       @ApiOperation( value = "No Content", 
+       notes = "Delete a single `DR_Node` object.", 
+       response = DR_Node.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 204, message = "Success", response = DR_Node.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{fqdn}")
+       public Response deleteDr_Node( 
+                       @PathParam("fqdn") String name){
+
+
+               ApiError apiError = new ApiError();
+
+               dr_nodeService.removeDr_Node(name, apiError);
+               if (apiError.is2xx()) {
+                       return responseBuilder.success(NO_CONTENT.getStatusCode(), null);
+               }
+               return responseBuilder.error(apiError);
+       }
+
+       @GET
+       @ApiOperation( value = "return DR_Node details", 
+       notes = "Retrieve a single `DR_Node` object.", 
+       response = DR_Node.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Node.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{fqdn}")
+       public Response get(@PathParam("fqdn") String name) {
+
+               ApiError apiError = new ApiError();
+
+               DR_Node nNode = dr_nodeService.getDr_Node( name, apiError );
+               if (apiError.is2xx()) {
+                       return responseBuilder.success(nNode);
+               }
+               return responseBuilder.error(apiError);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_PubResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_PubResource.java
new file mode 100644 (file)
index 0000000..f512124
--- /dev/null
@@ -0,0 +1,252 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+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.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Pub;
+import org.onap.dmaap.dbcapi.model.Feed;
+import org.onap.dmaap.dbcapi.service.DR_PubService;
+import org.onap.dmaap.dbcapi.service.FeedService;
+
+
+@Path("/dr_pubs")
+@Api( value= "dr_pubs", description = "Endpoint for a Data Router client that implements a Publisher" )
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Authorization
+public class DR_PubResource extends BaseLoggingClass {
+
+       private DR_PubService dr_pubService = new DR_PubService();
+       private ResponseBuilder responseBuilder = new ResponseBuilder();
+       private RequiredChecker checker = new RequiredChecker();
+       
+       @GET
+       @ApiOperation( value = "return DR_Pub details", 
+       notes = "Returns array of  `DR_Pub` objects.  Add filter for feedId.", 
+       response = DR_Pub.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public  Response getDr_Pubs() {
+               logger.info( "Entry: GET /dr_pubs");
+               List<DR_Pub> pubs = dr_pubService.getAllDr_Pubs();
+
+               GenericEntity<List<DR_Pub>> list = new GenericEntity<List<DR_Pub>>(pubs) {
+        };
+        return responseBuilder.success(list);
+       }
+
+
+       @POST
+       @ApiOperation( value = "return DR_Pub details", 
+       notes = "create a DR Publisher in the specified environment.", 
+       response = DR_Pub.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response addDr_Pub(DR_Pub pub) {
+               ApiError apiError = new ApiError();
+               FeedService feeds = new FeedService();
+               Feed fnew = null;
+
+               logger.info( "Entry: POST /dr_pubs");
+
+               try {
+                       checker.required( "feedId", pub.getFeedId());
+               } catch ( RequiredFieldException rfe ) {
+                       try {
+                               checker.required( "feedName", pub.getFeedName());
+                       }catch ( RequiredFieldException rfe2 ) {
+                               logger.debug( rfe2.getApiError().toString() );
+                               return responseBuilder.error(rfe2.getApiError());
+                       }
+                       // if we found a FeedName instead of a FeedId then try to look it up.
+                       List<Feed> nfeeds =  feeds.getAllFeeds( pub.getFeedName(), pub.getFeedVersion(), "equals");
+                       if ( nfeeds.isEmpty() ) {
+                               apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                               apiError.setFields("feedName");
+                               return responseBuilder.error(apiError);
+                       }
+                       fnew = nfeeds.get(0);
+               }
+               try {
+                       checker.required( "dcaeLocationName", pub.getDcaeLocationName());
+               } catch ( RequiredFieldException rfe ) {
+                       logger.debug( rfe.getApiError().toString() );
+                       return responseBuilder.error(rfe.getApiError());
+               }
+
+
+               // we may have fnew already if located by FeedName
+               if ( fnew == null ) {
+                       fnew = feeds.getFeed(pub.getFeedId(), apiError);
+               }
+               if ( fnew == null ) {
+                       logger.info( "Specified feed " + pub.getFeedId() + " or " + pub.getFeedName() + " not known to Bus Controller");        
+                       return responseBuilder.error(apiError);
+               }
+
+               ArrayList<DR_Pub> pubs = fnew.getPubs();
+               logger.info( "num existing pubs before = " + pubs.size() );
+               
+               logger.info( "update feed");
+               pub.setNextPubId();
+               if ( pub.getUsername() == null ) {
+                       pub.setRandomUserName();
+               }
+               if ( pub.getUserpwd() == null ) {
+                       pub.setRandomPassword();
+               }
+               pubs.add( pub );
+               fnew.setPubs(pubs);
+               fnew = feeds.updateFeed(fnew, apiError);
+               
+               if (!apiError.is2xx()) {
+                       return responseBuilder.error(apiError);
+               }
+               pubs = fnew.getPubs();
+               logger.info( "num existing pubs after = " + pubs.size() );
+               
+               DR_Pub pnew = dr_pubService.getDr_Pub(pub.getPubId(), apiError);
+               return responseBuilder.success(Status.CREATED.getStatusCode(), pnew);
+       }
+       
+       @PUT
+       @ApiOperation( value = "return DR_Pub details", 
+       notes = "update a DR Publisher in the specified environment.  Update a `DR_Pub` object by pubId", 
+       response = DR_Pub.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{pubId}")
+       public Response updateDr_Pub(@PathParam("pubId") String name, DR_Pub pub) {
+               logger.info( "Entry: PUT /dr_pubs");
+               pub.setPubId(name);
+               DR_Pub res = dr_pubService.updateDr_Pub(pub);
+               return responseBuilder.success(res);
+       }
+       
+       @DELETE
+       @ApiOperation( value = "return DR_Pub details", 
+       notes = "delete a DR Publisher in the specified environment. Delete a `DR_Pub` object by pubId", 
+       response = DR_Pub.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 204, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{pubId}")
+       public Response deleteDr_Pub(@PathParam("pubId") String id){
+
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "pubId", id);
+               } catch ( RequiredFieldException rfe ) {
+                       return responseBuilder.error(rfe.getApiError());
+               }
+
+               DR_Pub pub =  dr_pubService.getDr_Pub(id, apiError);
+               if ( !apiError.is2xx()) {
+                       return responseBuilder.error(apiError);
+               }
+               FeedService feeds = new FeedService();
+               Feed fnew = feeds.getFeed(pub.getFeedId(), apiError);
+               if ( fnew == null ) {
+                       logger.info( "Specified feed " + pub.getFeedId() + " not known to Bus Controller");     
+                       return responseBuilder.error(apiError);
+               }
+               ArrayList<DR_Pub> pubs = fnew.getPubs();
+               if ( pubs.size() == 1 ) {
+                       apiError.setCode(Status.BAD_REQUEST.getStatusCode());
+                       apiError.setMessage( "Can't delete the last publisher of a feed");
+                       return responseBuilder.error(apiError);
+               }
+               
+               for( Iterator<DR_Pub> i = pubs.iterator(); i.hasNext(); ) {
+                       DR_Pub listItem = i.next();
+                       if ( listItem.getPubId().equals(id)) {
+                               i.remove();
+                       }
+               }
+               fnew.setPubs(pubs);
+               fnew = feeds.updateFeed(fnew,apiError);
+               if (!apiError.is2xx()) {
+                       return responseBuilder.error(apiError);
+               }
+               
+               dr_pubService.removeDr_Pub(id, apiError);
+               if (!apiError.is2xx()) {
+                       return responseBuilder.error(apiError);
+               }
+               return responseBuilder.success(Status.NO_CONTENT.getStatusCode(), null);
+       }
+
+       @GET
+       @ApiOperation( value = "return DR_Pub details", 
+       notes = "returns a DR Publisher in the specified environment. Gets a `DR_Pub` object by pubId", 
+       response = DR_Pub.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{pubId}")
+       public Response get(@PathParam("pubId") String id) {
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "feedId", id);
+               } catch ( RequiredFieldException rfe ) {
+                       return responseBuilder.error(rfe.getApiError());
+               }
+
+               DR_Pub pub =  dr_pubService.getDr_Pub(id, apiError);
+               if (!apiError.is2xx()) {
+                       return responseBuilder.error(apiError);
+               }
+               return responseBuilder.success(Status.OK.getStatusCode(), pub);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_SubResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DR_SubResource.java
new file mode 100644 (file)
index 0000000..2fa6ccd
--- /dev/null
@@ -0,0 +1,243 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import com.google.common.collect.Iterables;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+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.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Sub;
+import org.onap.dmaap.dbcapi.model.Feed;
+import org.onap.dmaap.dbcapi.service.DR_SubService;
+import org.onap.dmaap.dbcapi.service.FeedService;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import static javax.ws.rs.core.Response.Status.CREATED;
+
+
+@Path("/dr_subs")
+@Api( value= "dr_subs", description = "Endpoint for a Data Router client that implements a Subscriber" )
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Authorization
+public class DR_SubResource extends BaseLoggingClass {
+
+       private ResponseBuilder responseBuilder = new ResponseBuilder();
+       private RequiredChecker checker = new RequiredChecker();
+               
+       @GET
+       @ApiOperation( value = "return DR_Sub details", 
+       notes = "Returns array of  `DR_Sub` objects.  Add filter for feedId.", 
+       response = DR_Sub.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Sub.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response getDr_Subs() {
+               DR_SubService dr_subService = new DR_SubService();
+               List<DR_Sub> subs = dr_subService.getAllDr_Subs();
+
+               GenericEntity<List<DR_Sub>> list = new GenericEntity<List<DR_Sub>>(subs) {
+        };
+        return responseBuilder.success(list);
+       }
+               
+       @POST
+       @ApiOperation( value = "return DR_Sub details", 
+       notes = "Create a  `DR_Sub` object.  ", 
+       response = DR_Sub.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Sub.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response addDr_Sub(DR_Sub sub) {
+
+               ApiError apiError = new ApiError();
+               FeedService feeds = new FeedService();
+               Feed fnew = null;
+               try {
+                       checker.required( "feedId", sub.getFeedId());
+               } catch ( RequiredFieldException rfe ) {
+                       try {
+                               checker.required( "feedName", sub.getFeedName());
+                       }catch ( RequiredFieldException rfe2 ) {
+                               logger.debug( rfe2.getApiError().toString() );
+                               return responseBuilder.error(rfe2.getApiError());
+                       }
+                       // if we found a FeedName instead of a FeedId then try to look it up.
+                       List<Feed> nfeeds =  feeds.getAllFeeds( sub.getFeedName(), sub.getFeedVersion(), "equals");
+                       if ( nfeeds.isEmpty() ) {
+                               apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                               apiError.setFields("feedName");
+                               return responseBuilder.error(apiError);
+                       } else if (nfeeds.size() > 1) {
+                               logger.debug( "Attempt to match "+ sub.getFeedName() + " ver="+sub.getFeedVersion() + " matched " + nfeeds.size() );
+                               apiError.setCode(Status.CONFLICT.getStatusCode());
+                               apiError.setFields("feedName");
+                               return responseBuilder.error(apiError);
+                       }
+                       fnew = Iterables.getOnlyElement(nfeeds);
+               }
+                       
+               try {
+                       checker.required( "dcaeLocationName", sub.getDcaeLocationName());
+               } catch ( RequiredFieldException rfe ) {
+                       logger.debug( rfe.getApiError().toString() );
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               // we may have fnew already if located by FeedName
+               if ( fnew == null ) {
+                       fnew = feeds.getFeed( sub.getFeedId(), apiError);
+               }
+               if ( fnew == null ) {
+                       logger.warn( "Specified feed " + sub.getFeedId() + " or " + sub.getFeedName() + " not known to Bus Controller");
+                       apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                       return responseBuilder.error(apiError);
+               }
+               DR_SubService dr_subService = new DR_SubService( fnew.getSubscribeURL());
+               ArrayList<DR_Sub> subs = fnew.getSubs();
+               logger.info( "num existing subs before = " + subs.size() );
+               DR_Sub snew = dr_subService.addDr_Sub(sub, apiError);
+               if (!apiError.is2xx()) {
+                       return responseBuilder.error(apiError);
+               }
+               subs.add( snew );
+               logger.info( "num existing subs after = " + subs.size() );
+               
+               fnew.setSubs(subs);
+               logger.info( "update feed");
+               return responseBuilder.success(CREATED.getStatusCode(), snew);
+
+       }
+               
+       @PUT
+       @ApiOperation( value = "return DR_Sub details", 
+       notes = "Update a  `DR_Sub` object, selected by subId", 
+       response = DR_Sub.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Sub.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{subId}")
+       public Response updateDr_Sub(@PathParam("subId") String name, DR_Sub sub) {
+
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "subId", name);
+                       checker.required( "feedId", sub.getFeedId());
+                       checker.required( "dcaeLocationName", sub.getDcaeLocationName());
+       
+               } catch ( RequiredFieldException rfe ) {
+                       logger.debug( rfe.getApiError().toString() );
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               FeedService feeds = new FeedService();
+               Feed fnew = feeds.getFeed(sub.getFeedId(), apiError);
+               if ( fnew == null ) {
+                       logger.warn( "Specified feed " + sub.getFeedId() + " not known to Bus Controller");
+                       return responseBuilder.error(apiError);
+               }
+
+               DR_SubService dr_subService = new DR_SubService();
+               sub.setSubId(name);
+               DR_Sub nsub = dr_subService.updateDr_Sub(sub, apiError);
+               if ( nsub != null && nsub.isStatusValid() ) {
+                       return responseBuilder.success(nsub);
+               }
+               return responseBuilder.error(apiError);
+       }
+               
+       @DELETE
+       @ApiOperation( value = "return DR_Sub details", 
+       notes = "Delete a  `DR_Sub` object, selected by subId", 
+       response = DR_Sub.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Sub.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{subId}")
+       public Response deleteDr_Sub(@PathParam("subId") String id){
+
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "subId", id);
+               } catch ( RequiredFieldException rfe ) {
+                       logger.debug( rfe.getApiError().toString() );
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               DR_SubService dr_subService = new DR_SubService();
+               dr_subService.removeDr_Sub(id, apiError);
+               if (!apiError.is2xx() ) {
+                       return responseBuilder.error(apiError);
+               }
+               return responseBuilder.success(Status.NO_CONTENT.getStatusCode(), null );
+       }
+
+       @GET
+       @ApiOperation( value = "return DR_Sub details", 
+       notes = "Retrieve a  `DR_Sub` object, selected by subId", 
+       response = DR_Sub.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Sub.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{subId}")
+       public Response get(@PathParam("subId") String id) {
+
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "subId", id);
+               } catch ( RequiredFieldException rfe ) {
+                       logger.debug( rfe.getApiError().toString() );
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               DR_SubService dr_subService = new DR_SubService();
+               DR_Sub sub =  dr_subService.getDr_Sub(id, apiError);
+               if ( sub != null && sub.isStatusValid() ) {
+                       return responseBuilder.success(sub);
+               }
+               return responseBuilder.error(apiError);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResource.java
new file mode 100644 (file)
index 0000000..89c9b49
--- /dev/null
@@ -0,0 +1,153 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+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.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.service.DcaeLocationService;
+
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+import static javax.ws.rs.core.Response.Status.NO_CONTENT;
+
+
+@Path("/dcaeLocations")
+@Api( value= "dcaeLocations", description = "an OpenStack tenant purposed for OpenDCAE (i.e. where OpenDCAE components might be deployed)" )
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Authorization
+public class DcaeLocationResource extends BaseLoggingClass {
+       private DcaeLocationService locationService = new DcaeLocationService();
+       private ResponseBuilder responseBuilder = new ResponseBuilder();
+       
+       @GET
+       @ApiOperation( value = "return dcaeLocation details", 
+               notes = "Returns array of  `dcaeLocation` objects.  All objects managed by DMaaP are deployed in some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer*  (ecomp or edge).", 
+               response = DcaeLocation.class)
+    @ApiResponses( value = {
+        @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class),
+        @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+    })
+       public Response getDcaeLocations() {
+               List<DcaeLocation> locs = locationService.getAllDcaeLocations();
+
+               GenericEntity<List<DcaeLocation>> list = new GenericEntity<List<DcaeLocation>>(locs) {};
+        return responseBuilder.success(list);
+       }
+       
+       @POST
+       @ApiOperation( value = "return dcaeLocation details", 
+               notes = "Create some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer*  (ecomp or edge).", 
+               response = DcaeLocation.class)
+    @ApiResponses( value = {
+        @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class),
+        @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+    })
+       public Response addDcaeLocation(DcaeLocation location) {
+
+               if ( locationService.getDcaeLocation(location.getDcaeLocationName()) != null ) {
+                       return responseBuilder.error(new ApiError(Status.CONFLICT.getStatusCode(),
+                                       "dcaeLocation already exists", "dcaeLocation"));
+               }
+               DcaeLocation loc = locationService.addDcaeLocation(location);
+               return responseBuilder.success(Status.CREATED.getStatusCode(), loc);
+       }
+       
+       @PUT
+       @ApiOperation( value = "return dcaeLocation details", 
+               notes = "update the openStackAvailabilityZone of a dcaeLocation", 
+               response = DcaeLocation.class)
+    @ApiResponses( value = {
+        @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class),
+        @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+    })
+       @Path("/{locationName}")
+       public Response updateDcaeLocation( 
+                       @PathParam("locationName") String name, DcaeLocation location) {
+
+               location.setDcaeLocationName(name);
+               if ( locationService.getDcaeLocation(location.getDcaeLocationName()) == null ) {
+                       return responseBuilder.notFound();
+
+               }
+               DcaeLocation loc = locationService.updateDcaeLocation(location);
+               return responseBuilder.success(Status.CREATED.getStatusCode(), loc );
+       }
+       
+       @DELETE
+       @ApiOperation( value = "return dcaeLocation details", notes = "delete a dcaeLocation", response = DcaeLocation.class)
+    @ApiResponses( value = {
+        @ApiResponse( code = 204, message = "Success", response = DcaeLocation.class),
+        @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+    })
+       @Path("/{locationName}")
+       public Response deleteDcaeLocation( 
+                       @PathParam("locationName") String name
+                        ){
+               locationService.removeDcaeLocation(name);
+               return responseBuilder.success(NO_CONTENT.getStatusCode(), null);
+       }
+
+       @GET
+       @ApiOperation( value = "return dcaeLocation details", notes = "Returns a specific `dcaeLocation` object with specified tag", response = DcaeLocation.class)
+    @ApiResponses( value = {
+        @ApiResponse( code = 200, message = "Success", response = DcaeLocation.class),
+        @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+    })
+       @Path("/{locationName}")
+       public Response getDcaeLocation( 
+                       @PathParam("locationName") String name) {
+
+               DcaeLocation loc =  locationService.getDcaeLocation( name );
+               if ( loc == null ) {
+                       ApiError err = new ApiError();
+                               
+                       err.setCode(NOT_FOUND.getStatusCode());
+                       err.setMessage("dcaeLocation does not exist");
+                       err.setFields("dcaeLocation");
+                       
+                       return responseBuilder.error(err);
+               }
+
+               return responseBuilder.success(loc);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DmaapResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/DmaapResource.java
new file mode 100644 (file)
index 0000000..955cab7
--- /dev/null
@@ -0,0 +1,123 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+//
+// $Id$
+
+package org.onap.dmaap.dbcapi.resources;
+
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+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;
+
+
+@Path("/dmaap")
+@Api( value= "dmaap", description = "Endpoint for this instance of DMaaP object containing values for this OpenDCAE deployment" )
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Authorization
+public class DmaapResource extends BaseLoggingClass {
+
+
+       private DmaapService dmaapService = new DmaapService();
+       private ResponseBuilder responseBuilder = new ResponseBuilder();
+       private RequiredChecker checker = new RequiredChecker();
+
+       @GET
+       @ApiOperation( value = "return dmaap details", notes = "returns the `dmaap` object, which contains system wide configuration settings", response = Dmaap.class)
+    @ApiResponses( value = {
+        @ApiResponse( code = 200, message = "Success", response = Dmaap.class),
+        @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+    })
+
+       public Response getDmaap(@Context UriInfo uriInfo)  {
+               Dmaap d =  dmaapService.getDmaap();
+               return responseBuilder.success(d);
+       }
+
+       @POST
+       @ApiOperation( value = "return dmaap details", notes = "Create a new DMaaP set system wide configuration settings for the *dcaeEnvironment*.  Deprecated with introduction of persistence in 1610.", response = Dmaap.class)
+    @ApiResponses( value = {
+        @ApiResponse( code = 200, message = "Success", response = Dmaap.class),
+        @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+    })
+       public Response addDmaap( Dmaap obj ) {
+
+               try {
+                       validateRequiredFields(obj);
+               } catch( RequiredFieldException rfe ) {
+                       return responseBuilder.error(rfe.getApiError());
+               }
+
+               Dmaap d =  dmaapService.addDmaap(obj);
+               if ( d == null ) {
+                       return responseBuilder.notFound();
+
+               }
+
+               return responseBuilder.success(d);
+       }
+
+       @PUT
+       @ApiOperation( value = "return dmaap details", notes = "Update system settings for *dcaeEnvironment*.", response = Dmaap.class)
+    @ApiResponses( value = {
+        @ApiResponse( code = 200, message = "Success", response = Dmaap.class),
+        @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+    })
+       public Response updateDmaap(  Dmaap obj ) {
+
+               try {
+                       validateRequiredFields(obj);
+               } catch( RequiredFieldException rfe ) {
+                       return responseBuilder.error(rfe.getApiError());
+               }
+
+               Dmaap d =  dmaapService.updateDmaap(obj);
+               if ( d != null ) {
+                       return responseBuilder.success(d);
+               } else {
+                       return responseBuilder.notFound();
+               }
+       }
+
+       private void validateRequiredFields(Dmaap obj) throws RequiredFieldException {
+               checker.required( "dmaapName", obj.getDmaapName(), "^\\S+$" );  //no white space allowed in dmaapName
+               checker.required( "dmaapProvUrl", obj.getDrProvUrl());
+               checker.required( "topicNsRoot", obj.getTopicNsRoot());
+               checker.required( "bridgeAdminTopic", obj.getBridgeAdminTopic());
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/FeedResource.java
new file mode 100644 (file)
index 0000000..28bdb00
--- /dev/null
@@ -0,0 +1,259 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+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.QueryParam;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Pub;
+import org.onap.dmaap.dbcapi.model.Feed;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.service.FeedService;
+
+
+@Path("/feeds")
+@Api( value= "Feeds", description = "Endpoint for a Data Router Feed" )
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Authorization
+public class FeedResource extends BaseLoggingClass {
+
+       private ResponseBuilder responseBuilder = new ResponseBuilder();
+       private RequiredChecker checker = new RequiredChecker();
+
+       @GET
+       @ApiOperation( value = "return Feed details", 
+       notes = "Returns array of  `Feed` objects.", 
+       response = Feed.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = Feed.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response getFeeds(
+                       @QueryParam("feedName") String feedName,
+                       @QueryParam("version") String version,
+                       @QueryParam("match") String match) {
+
+               FeedService feedService = new FeedService();
+               List<Feed> nfeeds =  feedService.getAllFeeds( feedName, version, match );
+               GenericEntity<List<Feed>> list = new GenericEntity<List<Feed>>(nfeeds) {
+        };
+        return responseBuilder.success(list);
+       }
+       
+
+       
+       @POST
+       @ApiOperation( value = "return Feed details", 
+       notes = "Create a of  `Feed` object.", 
+       response = Feed.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = Feed.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response addFeed( 
+                       Feed feed,
+                       @QueryParam("useExisting") String useExisting) {
+
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "feedName", feed.getFeedName());
+                       checker.required( "feedVersion", feed.getFeedVersion());
+                       checker.required( "owner", feed.getOwner());
+                       checker.required( "asprClassification", feed.getAsprClassification());
+               } catch ( RequiredFieldException rfe ) {
+                       logger.debug( rfe.getApiError().toString() );
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               
+               
+               FeedService feedService = new FeedService();
+               Feed nfeed =  feedService.getFeedByName( feed.getFeedName(), feed.getFeedVersion(), apiError);
+               if ( nfeed == null ) {
+                       nfeed =  feedService.addFeed(feed, apiError);
+                       if ( nfeed != null ) {
+                               return responseBuilder.success(nfeed);
+                       } else {
+                               logger.error( "Unable to create: " + feed.getFeedName() + ":" + feed.getFeedVersion());
+
+                               return responseBuilder.error(apiError);
+                       }
+               } else if ( nfeed.getStatus() == DmaapObject_Status.DELETED ) {
+                       feed.setFeedId( nfeed.getFeedId());
+                       nfeed =  feedService.updateFeed(feed, apiError);
+                       if ( nfeed != null ) {
+                               return responseBuilder.success(nfeed);
+                       } else {
+                               logger.info( "Unable to update: " + feed.getFeedName() + ":" + feed.getFeedVersion());
+
+                               return responseBuilder.error(apiError);
+                       }
+               } else if ( (useExisting != null) && ("true".compareToIgnoreCase( useExisting ) == 0)) {
+                       return responseBuilder.success(nfeed);
+               }
+
+               apiError.setCode(Status.CONFLICT.getStatusCode());
+               return responseBuilder.error(apiError);
+       }
+       
+       @PUT
+       @ApiOperation( value = "return Feed details", 
+       notes = "Update a  `Feed` object, specified by id.", 
+       response = Feed.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = Feed.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{id}")
+       public Response updateFeed( 
+                       @PathParam("id") String id,
+                       Feed feed) {
+
+               FeedService feedService = new FeedService();
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "feedId", id);
+               } catch ( RequiredFieldException rfe ) {
+                       logger.debug( rfe.getApiError().toString() );
+                       return responseBuilder.error(rfe.getApiError());
+               }
+
+               Feed nfeed = feedService.getFeed(id, apiError);
+               if ( nfeed == null || nfeed.getStatus() == DmaapObject_Status.DELETED ) {
+                       return responseBuilder.notFound();
+               }
+       
+               //  we assume there is no updates allowed for pubs and subs objects via this api...             
+               // need to update any fields supported by PUT but preserve original field values. 
+               nfeed.setSuspended(feed.isSuspended());
+               nfeed.setFeedDescription(feed.getFeedDescription());
+               nfeed.setFormatUuid(feed.getFormatUuid());
+               
+               nfeed =  feedService.updateFeed(nfeed, apiError);
+               if ( nfeed != null ) {
+                       return responseBuilder.success(nfeed);
+               } else {
+                       logger.info( "Unable to update: " + feed.getFeedName() + ":" + feed.getFeedVersion());
+
+                       return responseBuilder.error(apiError);
+               }
+       }
+       
+       @DELETE
+       @ApiOperation( value = "return Feed details", 
+       notes = "Delete a  `Feed` object, specified by id.", 
+       response = Feed.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 204, message = "Success", response = Feed.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{id}")
+       public Response deleteFeed(@PathParam("id") String id){
+               ApiError apiError = new ApiError();
+
+               logger.debug( "Entry: DELETE  " + id);
+               FeedService feedService = new FeedService();
+               Feed nfeed =  feedService.getFeed(id, apiError);
+               if ( nfeed == null ) {
+                       apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                       return responseBuilder.error(apiError);
+               }
+               nfeed = feedService.removeFeed(nfeed, apiError);
+               if ( nfeed == null || nfeed.getStatus() == DmaapObject_Status.DELETED ) {
+                       return responseBuilder.success(Status.NO_CONTENT.getStatusCode(), null);
+               }
+               logger.info( "Unable to delete: " + id + ":" + nfeed.getFeedVersion());
+
+               return responseBuilder.error(apiError);
+       }
+
+       @GET
+       @ApiOperation( value = "return Feed details", 
+       notes = "Retrieve a  `Feed` object, specified by id.", 
+       response = Feed.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = DR_Pub.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{id}")
+       public Response getFeed(@PathParam("id") String id) {
+               ApiError apiError = new ApiError();
+
+               FeedService feedService = new FeedService();
+               Feed nfeed =  feedService.getFeed(id, apiError);
+               if ( nfeed == null ) {
+                       apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                       return responseBuilder.error(apiError);
+               }
+               return responseBuilder.success(nfeed);
+       }
+       
+       @PUT
+       @ApiOperation( value = "sync feeds to existing DR",
+       notes = "When Bus Controller is deployed after DR, then it is possible"
+                       + "that DR has previous provisioning data that needs to be imported"
+                       + "into Bus Controller.",
+       response = Feed.class )
+       @ApiResponses( value =  {
+                       @ApiResponse( code = 200, message = "Success", response = Feed.class),
+                       @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path( "/sync")
+       public Response syncFeeds (@QueryParam("hard") String hardParam) {
+               ApiError error = new ApiError();
+               
+               FeedService feedService = new FeedService();
+               boolean hard = false;
+               if (  hardParam != null && hardParam.equalsIgnoreCase("true")) {
+                       hard = true;
+               }
+               feedService.sync( hard, error );
+               if ( error.is2xx()) {
+                       List<Feed> nfeeds =  feedService.getAllFeeds();
+                       GenericEntity<List<Feed>> list = new GenericEntity<List<Feed>>(nfeeds) {
+                       };
+                       return responseBuilder.success(list);
+               }
+               return responseBuilder.error(error);
+       }
+       
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/InfoResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/InfoResource.java
new file mode 100644 (file)
index 0000000..bcb7ed2
--- /dev/null
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+//
+// $Id$
+
+package org.onap.dmaap.dbcapi.resources;
+
+
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+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.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+
+
+
+@Path("/info")
+@Api( value= "info", description = "Endpoint for this instance of DBCL.  Returns health info." )
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Authorization
+public class InfoResource extends BaseLoggingClass {
+
+
+       private ResponseBuilder responseBuilder = new ResponseBuilder();
+       
+       @GET
+       @ApiOperation( value = "return info details", notes = "returns the `info` object", response = Dmaap.class)
+    @ApiResponses( value = {
+        @ApiResponse( code = 200, message = "Success", response = Dmaap.class),
+        @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+    })
+
+       public Response getInfo(@Context UriInfo uriInfo)  {
+               return responseBuilder.success(204, null);
+       }
+       
+
+       
+       
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClientResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClientResource.java
new file mode 100644 (file)
index 0000000..80ee0a6
--- /dev/null
@@ -0,0 +1,208 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.MR_Client;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.service.MR_ClientService;
+import org.onap.dmaap.dbcapi.service.MR_ClusterService;
+import org.onap.dmaap.dbcapi.service.TopicService;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+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.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.util.List;
+
+import static javax.ws.rs.core.Response.Status.NO_CONTENT;
+
+
+@Path("/mr_clients")
+@Api( value= "MR_Clients", description = "Endpoint for a Message Router Client that implements a Publisher or a Subscriber" )
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Authorization
+public class MR_ClientResource extends BaseLoggingClass {
+
+       private MR_ClientService mr_clientService = new MR_ClientService();
+       private ResponseBuilder responseBuilder = new ResponseBuilder();
+       private RequiredChecker checker = new RequiredChecker();
+               
+       @GET
+       @ApiOperation( value = "return MR_Client details", 
+       notes = "Returns array of  `MR_Client` objects.", 
+       response = MR_Client.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = MR_Client.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response getMr_Clients() {
+               List<MR_Client> clients = mr_clientService.getAllMr_Clients();
+
+               GenericEntity<List<MR_Client>> list = new GenericEntity<List<MR_Client>>(clients) {
+        };
+        return responseBuilder.success(list);
+       }
+               
+       @POST
+       @ApiOperation( value = "Associate an MR_Client object to a Topic", 
+       notes = "Create a  `MR_Client` object."
+                       + "The `dcaeLocation` attribute is used to match an `MR_Cluster` object with the same value, with the intent of localizing message traffic."
+                       + "  In legacy implementation, the `clientRole` is granted appropriate permission in AAF."
+                       + "  Newer implementions may instead specify an AAF Identity, which will be added to the appropriate `Topic` role.", 
+       response = MR_Client.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = MR_Client.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response addMr_Client(MR_Client client) {
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "fqtn", client.getFqtn());
+                       checker.required( "dcaeLocationName", client.getDcaeLocationName());
+                       String s = client.getClientRole();
+                       if ( s == null ) {
+                               s = client.getClientIdentity();
+                       }
+                       checker.required( "clientRole or clientIdentity", s);
+                       checker.required( "action", client.getAction());
+
+               } catch ( RequiredFieldException rfe ) {
+                       logger.debug( rfe.getApiError().toString() );
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               MR_ClusterService clusters = new MR_ClusterService();
+
+               MR_Cluster cluster = clusters.getMr_Cluster(client.getDcaeLocationName(), apiError);
+               if ( cluster == null ) {
+
+                       apiError.setCode(Status.BAD_REQUEST.getStatusCode());
+                       apiError.setMessage( "MR_Cluster alias not found for dcaeLocation: " + client.getDcaeLocationName());
+                       apiError.setFields("dcaeLocationName");
+                       logger.warn(apiError.toString());
+                       return responseBuilder.error(apiError);
+               }
+
+               TopicService topics = new TopicService();
+
+               Topic t = topics.getTopic(client.getFqtn(), apiError);
+               if ( t == null ) {
+                       return responseBuilder.error(apiError);
+               }
+               MR_Client nClient =  mr_clientService.addMr_Client(client, t, apiError);
+               if (apiError.is2xx()) {
+                       t = topics.getTopic(client.getFqtn(), apiError);
+                       topics.checkForBridge(t, apiError);
+                       return responseBuilder.success(nClient);
+               }
+               else {
+                       return responseBuilder.error(apiError);
+               }
+       }
+               
+       @PUT
+       @ApiOperation( value = "Update an MR_Client object", 
+       notes = "Update a  `MR_Client` object, specified by clientId", 
+       response = MR_Client.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = MR_Client.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{clientId}")
+       public Response updateMr_Client(@PathParam("clientId") String clientId, MR_Client client) {
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "fqtn", client.getFqtn());
+                       checker.required( "dcaeLocationName", client.getDcaeLocationName());
+                       checker.required( "clientRole", client.getClientRole());
+                       checker.required( "action", client.getAction());
+
+               } catch ( RequiredFieldException rfe ) {
+                       logger.debug( rfe.getApiError().toString() );
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               client.setMrClientId(clientId);
+               MR_Client nClient = mr_clientService.updateMr_Client(client, apiError);
+               if (apiError.is2xx()) {
+                       return Response.ok(nClient)
+                               .build();
+               }
+               return Response.status(apiError.getCode())
+                               .entity(apiError)
+                               .build();
+       }
+               
+       @DELETE
+       @ApiOperation( value = "Delete an MR_Client object", 
+       notes = "Delete a  `MR_Client` object, specified by clientId", 
+       response = MR_Client.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 204, message = "Success", response = MR_Client.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{subId}")
+       public Response deleteMr_Client(@PathParam("subId") String id){
+               ApiError apiError = new ApiError();
+
+               mr_clientService.removeMr_Client(id, true, apiError);
+               if (apiError.is2xx()) {
+                       return responseBuilder.success(NO_CONTENT.getStatusCode(), null);
+               }
+               
+               return responseBuilder.error(apiError);
+       }
+
+       @GET
+       @ApiOperation( value = "return MR_Client details", 
+       notes = "Retrieve a  `MR_Client` object, specified by clientId", 
+       response = MR_Client.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = MR_Client.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{subId}")
+       public Response getMr_Client(@PathParam("subId") String id) {
+               ApiError apiError = new ApiError();
+
+               MR_Client nClient =  mr_clientService.getMr_Client(id, apiError);
+               if (apiError.is2xx()) {
+                       return responseBuilder.success(nClient);
+               }
+               return responseBuilder.error(apiError);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResource.java
new file mode 100644 (file)
index 0000000..0a361ff
--- /dev/null
@@ -0,0 +1,174 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+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.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.service.MR_ClusterService;
+
+
+@Path("/mr_clusters")
+@Api( value= "MR_Clusters", description = "Endpoint for a Message Router servers in a Cluster configuration" )
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Authorization
+public class MR_ClusterResource extends BaseLoggingClass {
+
+       private MR_ClusterService mr_clusterService = new MR_ClusterService();
+       private ResponseBuilder responseBuilder = new ResponseBuilder();
+       private RequiredChecker checker = new RequiredChecker();
+               
+       @GET
+       @ApiOperation( value = "return MR_Cluster details", 
+       notes = "Returns array of  `MR_Cluster` objects.", 
+       response = MR_Cluster.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response getMr_Clusters() {
+               List<MR_Cluster> clusters = mr_clusterService.getAllMr_Clusters();
+
+               GenericEntity<List<MR_Cluster>> list = new GenericEntity<List<MR_Cluster>>(clusters) {
+        };
+        return responseBuilder.success(list);
+       }
+               
+       @POST
+       @ApiOperation( value = "return MR_Cluster details", 
+       notes = "Create an  `MR_Cluster` object.", 
+       response = MR_Cluster.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response  addMr_Cluster(MR_Cluster cluster) {
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "dcaeLocationName", cluster.getDcaeLocationName());
+                       checker.required( "fqdn", cluster.getFqdn());
+               } catch( RequiredFieldException rfe ) {
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               MR_Cluster mrc =  mr_clusterService.addMr_Cluster(cluster, apiError);
+               if ( mrc != null && mrc.isStatusValid() ) {
+                       return responseBuilder.success(Status.CREATED.getStatusCode(), mrc);
+               }
+               return responseBuilder.error(apiError);
+
+       }
+               
+       @PUT
+       @ApiOperation( value = "return MR_Cluster details", 
+       notes = "Update an  `MR_Cluster` object, specified by clusterId.", 
+       response = MR_Cluster.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{clusterId}")
+       public Response updateMr_Cluster(@PathParam("clusterId") String clusterId, MR_Cluster cluster) {
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "fqdn", clusterId);
+                       checker.required( "dcaeLocationName", cluster.getDcaeLocationName());
+               } catch( RequiredFieldException rfe ) {
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               cluster.setDcaeLocationName(clusterId);
+               MR_Cluster mrc =  mr_clusterService.updateMr_Cluster(cluster, apiError);
+               if ( mrc != null && mrc.isStatusValid() ) {
+                       return responseBuilder.success(Status.CREATED.getStatusCode(), mrc);
+               }
+               return responseBuilder.error(apiError);
+       }
+               
+       @DELETE
+       @ApiOperation( value = "return MR_Cluster details", 
+       notes = "Delete an  `MR_Cluster` object, specified by clusterId.", 
+       response = MR_Cluster.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 204, message = "Success", response = MR_Cluster.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{clusterId}")
+       public Response deleteMr_Cluster(@PathParam("clusterId") String id){
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "fqdn", id);
+               } catch( RequiredFieldException rfe ) {
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               mr_clusterService.removeMr_Cluster(id, apiError);
+               if (apiError.is2xx()) {
+                       return responseBuilder.success(Status.NO_CONTENT.getStatusCode(), null);
+               } 
+               return responseBuilder.error(apiError);
+       }
+
+       @GET
+       @ApiOperation( value = "return MR_Cluster details", 
+       notes = "Retrieve an  `MR_Cluster` object, specified by clusterId.", 
+       response = MR_Cluster.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = MR_Cluster.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{clusterId}")
+       public Response getMR_Cluster(@PathParam("clusterId") String id) {
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "dcaeLocationName", id);
+               } catch( RequiredFieldException rfe ) {
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               MR_Cluster mrc =  mr_clusterService.getMr_Cluster(id, apiError);
+               if ( mrc != null && mrc.isStatusValid() ) {
+                       return responseBuilder.success(Status.CREATED.getStatusCode(), mrc);
+               }
+               return responseBuilder.error(apiError);
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequestTimeLogFilter.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequestTimeLogFilter.java
new file mode 100644 (file)
index 0000000..b2b98b6
--- /dev/null
@@ -0,0 +1,55 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * org.onap.dmaap\r
+ * ================================================================================\r
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+package org.onap.dmaap.dbcapi.resources;\r
+\r
+import com.att.eelf.configuration.EELFLogger;\r
+import java.time.Clock;\r
+import javax.ws.rs.container.ContainerRequestContext;\r
+import javax.ws.rs.container.ContainerRequestFilter;\r
+import javax.ws.rs.container.ContainerResponseContext;\r
+import javax.ws.rs.container.ContainerResponseFilter;\r
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;\r
+\r
+public class RequestTimeLogFilter extends BaseLoggingClass implements ContainerRequestFilter, ContainerResponseFilter {\r
+\r
+    private final EELFLogger log;\r
+    private Clock clock;\r
+\r
+    public RequestTimeLogFilter() {\r
+        this(auditLogger, Clock.systemDefaultZone());\r
+    }\r
+\r
+    RequestTimeLogFilter(EELFLogger logger, Clock clock) {\r
+        this.log = logger;\r
+        this.clock = clock;\r
+    }\r
+\r
+    @Override\r
+    public void filter(ContainerRequestContext requestContext) {\r
+        requestContext.setProperty("start", clock.millis());\r
+    }\r
+\r
+    @Override\r
+    public void filter(ContainerRequestContext requestContext, ContainerResponseContext containerResponseContext) {\r
+        long startTime = (long) requestContext.getProperty("start");\r
+        long elapsedTime = clock.millis() - startTime;\r
+        log.info("Request took {} ms", elapsedTime);\r
+    }\r
+}\r
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredChecker.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredChecker.java
new file mode 100644 (file)
index 0000000..52646aa
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2019 NOKIA Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.onap.dmaap.dbcapi.model.ApiError;
+
+public class RequiredChecker {
+
+    public void required(String name, Object val) throws RequiredFieldException {
+        if (val == null) {
+            throw new RequiredFieldException(new ApiError(BAD_REQUEST.getStatusCode(),
+                    "missing required field", name));
+        }
+    }
+
+    public void required(String name, String val, String expr) throws RequiredFieldException {
+
+        required(name, val);
+
+        if (expr != null && !expr.isEmpty()) {
+            Pattern pattern = Pattern.compile(expr);
+            Matcher matcher = pattern.matcher(val);
+            if (!matcher.find()) {
+                throw new RequiredFieldException(new ApiError(BAD_REQUEST.getStatusCode(),
+                        "value '" + val + "' violates regexp check '" + expr + "'", name));
+            }
+        }
+    }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredFieldException.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/RequiredFieldException.java
new file mode 100644 (file)
index 0000000..2968d18
--- /dev/null
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import org.onap.dmaap.dbcapi.model.ApiError;
+
+public class RequiredFieldException extends Exception {
+
+    private static final long serialVersionUID = 2L;
+
+    private final ApiError apiError;
+
+    public RequiredFieldException(ApiError apiError) {
+        super();
+        this.apiError = apiError;
+    }
+
+    public ApiError getApiError() {
+        return apiError;
+    }
+
+    @Override
+    public String toString() {
+        return "RequiredFieldException{" +
+                "apiError=" + apiError +
+                '}';
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/ResponseBuilder.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/ResponseBuilder.java
new file mode 100644 (file)
index 0000000..044e7c4
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2019 NOKIA Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+
+package org.onap.dmaap.dbcapi.resources;
+
+import static com.att.eelf.configuration.Configuration.MDC_RESPONSE_CODE;
+import static com.att.eelf.configuration.Configuration.MDC_RESPONSE_DESC;
+import static com.att.eelf.configuration.Configuration.MDC_STATUS_CODE;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+import static javax.ws.rs.core.Response.Status.SERVICE_UNAVAILABLE;
+import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
+
+import javax.ws.rs.core.Response;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.slf4j.MDC;
+
+public class ResponseBuilder extends BaseLoggingClass {
+
+    Response success(Object d) {
+        return buildSuccessResponse(d, Response.Status.OK.getStatusCode());
+    }
+
+    Response success(int code, Object d) {
+        return buildSuccessResponse(d, code);
+    }
+
+    Response error(ApiError err) {
+        return buildErrResponse(err);
+    }
+
+    Response unauthorized(String msg) {
+        return buildErrResponse(new ApiError(UNAUTHORIZED.getStatusCode(), msg, "Authorization"));
+    }
+
+    Response unavailable() {
+        return buildErrResponse(new ApiError(SERVICE_UNAVAILABLE.getStatusCode(),
+                "Request is unavailable due to unexpected condition"));
+    }
+
+    Response notFound() {
+        return buildErrResponse(new ApiError(NOT_FOUND.getStatusCode(),"Requested object not found"));
+    }
+
+    private Response buildSuccessResponse(Object d, int code) {
+        MDC.put(MDC_STATUS_CODE, "COMPLETE");
+        MDC.put(MDC_RESPONSE_DESC, "");
+        return buildResponse(d, code);
+    }
+
+    private Response buildErrResponse(ApiError err) {
+        MDC.put(MDC_STATUS_CODE, "ERROR");
+        MDC.put(MDC_RESPONSE_DESC, err.getMessage());
+
+        return buildResponse(err, err.getCode());
+    }
+
+    private Response buildResponse(Object obj, int code) {
+        MDC.put(MDC_RESPONSE_CODE, String.valueOf(code));
+
+        auditLogger.auditEvent("");
+        return Response.status(code)
+                .entity(obj)
+                .build();
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/TopicResource.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/resources/TopicResource.java
new file mode 100644 (file)
index 0000000..01926b7
--- /dev/null
@@ -0,0 +1,218 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+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.QueryParam;
+import javax.ws.rs.core.GenericEntity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.ReplicationType;
+import org.onap.dmaap.dbcapi.model.FqtnType;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.service.TopicService;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+import static javax.ws.rs.core.Response.Status.CREATED;
+
+@Path("/topics")
+@Api( value= "topics", description = "Endpoint for retreiving MR Topics" )
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Authorization
+public class TopicResource extends BaseLoggingClass {
+       private static FqtnType defaultTopicStyle;
+       private static String defaultPartitionCount;
+       private static String defaultReplicationCount;
+       private TopicService mr_topicService = new TopicService();
+       private ResponseBuilder responseBuilder = new ResponseBuilder();
+       private RequiredChecker checker = new RequiredChecker();
+       static final String UNSUPPORTED_PUT_MSG = "Method /PUT not supported for /topics";
+       
+       public TopicResource() {
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               defaultTopicStyle = FqtnType.Validator( p.getProperty("MR.topicStyle", "FQTN_LEGACY_FORMAT"));
+               defaultPartitionCount = p.getProperty( "MR.partitionCount", "2");
+               defaultReplicationCount = p.getProperty( "MR.replicationCount", "1");
+               
+               logger.info( "Setting defaultTopicStyle=" + defaultTopicStyle );
+       }
+               
+       @GET
+       @ApiOperation( value = "return Topic details", 
+       notes = "Returns array of  `Topic` objects.", 
+       response = Topic.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = Topic.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response getTopics() {
+               List<Topic> allTopics = mr_topicService.getAllTopics();
+               
+               GenericEntity<List<Topic>> list = new GenericEntity<List<Topic>>(allTopics) {
+                       };
+               return responseBuilder.success(list);
+               
+       }
+               
+       @POST
+       @ApiOperation( value = "Create a Topic object", 
+       notes = "Create  `Topic` object."
+                       + "For convenience, the message body may populate the `clients` array, in which case each entry will be added as an `MR_Client`."
+                       + "  Beginning in ONAP Dublin Release, dbcapi will create two AAF Roles by default, one each for the publisher and subscriber per topic."
+                       + "  MR_Clients can then specify an AAF Identity to be added to the appropriate default Role, avoiding the need to create Role(s) in advance.", 
+       response = Topic.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = Topic.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       public Response  addTopic(Topic topic, @QueryParam("useExisting") String useExisting) {
+               logger.info( "addTopic request: " + topic  + " useExisting=" + useExisting );
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "topicName", topic.getTopicName(), "^\\S+$" );  //no white space allowed in topicName
+                       checker.required( "topicDescription", topic.getTopicDescription());
+                       checker.required( "owner", topic.getOwner());
+               } catch( RequiredFieldException rfe ) {
+                       logger.error("Error", rfe.getApiError());
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               
+               ReplicationType t = topic.getReplicationCase();
+               if ( t == null || t == ReplicationType.REPLICATION_NOT_SPECIFIED ) {
+                       topic.setReplicationCase( mr_topicService.reviewTopic(topic));
+               } 
+               FqtnType ft = topic.getFqtnStyle();
+               if ( ft == null || ft == FqtnType.FQTN_NOT_SPECIFIED ) {
+                       logger.info( "setting defaultTopicStyle=" + defaultTopicStyle + " for topic " + topic.getTopicName() );
+                       topic.setFqtnStyle( defaultTopicStyle );
+               }
+               String pc = topic.getPartitionCount();
+               if ( pc == null ) {
+                       topic.setPartitionCount(defaultPartitionCount);
+               }
+               String rc = topic.getReplicationCount();
+               if ( rc == null ) {
+                       topic.setReplicationCount(defaultReplicationCount);
+               }
+               topic.setLastMod();
+               Boolean flag = false;
+               if (useExisting != null) {
+                       flag = "true".compareToIgnoreCase( useExisting ) == 0;
+               }
+               
+               Topic mrc =  mr_topicService.addTopic(topic, apiError, flag);
+               if ( mrc != null && apiError.is2xx() ) {
+                       return responseBuilder.success(CREATED.getStatusCode(), mrc);
+               }
+               return responseBuilder.error(apiError);
+       }
+       
+       @PUT
+       @ApiOperation( value = "return Topic details", 
+       notes = "Update a  `Topic` object, identified by topicId", 
+       response = Topic.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = Topic.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{topicId}")
+       public Response updateTopic(@PathParam("topicId") String topicId) {
+               ApiError apiError = new ApiError();
+
+               apiError.setCode(Status.BAD_REQUEST.getStatusCode());
+               apiError.setMessage(UNSUPPORTED_PUT_MSG);
+               
+               return responseBuilder.error(apiError);
+       }
+               
+       @DELETE
+       @ApiOperation( value = "return Topic details", 
+       notes = "Delete a  `Topic` object, identified by topicId", 
+       response = Topic.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 204, message = "Success", response = Topic.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{topicId}")
+       public Response deleteTopic(@PathParam("topicId") String id){
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "fqtn", id);
+               } catch( RequiredFieldException rfe ) {
+                       logger.error("Error", rfe.getApiError());
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               
+               mr_topicService.removeTopic(id, apiError);
+               if (apiError.is2xx()) {
+                       return responseBuilder.success(Status.NO_CONTENT.getStatusCode(), null);
+               } 
+               return responseBuilder.error(apiError);
+       }
+       
+
+       @GET
+       @ApiOperation( value = "return Topic details", 
+       notes = "Retrieve a  `Topic` object, identified by topicId", 
+       response = Topic.class)
+       @ApiResponses( value = {
+           @ApiResponse( code = 200, message = "Success", response = Topic.class),
+           @ApiResponse( code = 400, message = "Error", response = ApiError.class )
+       })
+       @Path("/{topicId}")
+       public Response getTopic(@PathParam("topicId") String id) {
+               logger.info("Entry: /GET " + id);
+               ApiError apiError = new ApiError();
+
+               try {
+                       checker.required( "topicName", id, "^\\S+$" );  //no white space allowed in topicName
+               } catch( RequiredFieldException rfe ) {
+                       logger.error("Error", rfe.getApiError());
+                       return responseBuilder.error(rfe.getApiError());
+               }
+               Topic mrc =  mr_topicService.getTopic(id, apiError);
+               if ( mrc == null ) {
+                       return responseBuilder.error(apiError);
+               }
+               return responseBuilder.success(mrc);
+               }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/ApplicationConfig.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/ApplicationConfig.java
new file mode 100644 (file)
index 0000000..2244b73
--- /dev/null
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.server;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.onap.dmaap.dbcapi.resources.RequestTimeLogFilter;
+import org.onap.dmaap.dbcapi.resources.AuthorizationFilter;
+
+public class ApplicationConfig extends ResourceConfig {
+       
+       /*
+        * Register JAX-RS application components
+        */
+       public ApplicationConfig() {
+               
+        register(AuthorizationFilter.class).
+                                       register(RequestTimeLogFilter.class);
+  
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CadiCertificateManager.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CadiCertificateManager.java
new file mode 100644 (file)
index 0000000..1da2bc4
--- /dev/null
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.server;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import org.onap.aaf.cadi.PropAccess;
+
+public class CadiCertificateManager extends CertificateManager {
+       private PropAccess propAccess;  
+       
+       CadiCertificateManager( Properties properties )  {
+               String cadiPropsFile = properties.getProperty("cadi.properties", "etc/org.onap.dmaa-bc.props");
+               logger.info( "using cadi properties in ", cadiPropsFile);
+               
+               propAccess = new PropAccess();
+               ready = true;
+               try {
+                       propAccess.load( new FileInputStream( cadiPropsFile ));
+               } catch ( IOException e ) {
+                       logger.error( "Failed to load props file: " + cadiPropsFile + "\n" +  e.getMessage());
+                       ready = false;
+               }
+               setKeyStoreType( "jks");
+               setKeyStoreFile( propAccess.getProperty("cadi_keystore") );
+               setKeyStorePassword( decryptPass( propAccess.getProperty("cadi_keystore_password_jks" ) ));
+
+               setTrustStoreType( "jks");
+               setTrustStoreFile( propAccess.getProperty("cadi_truststore" ) );
+               setTrustStorePassword( decryptPass( propAccess.getProperty("cadi_truststore_password" ) ));
+       }
+
+       private String decryptPass( String password ) {
+               String clear = null;
+               try {
+                       clear = propAccess.decrypt(password, false );
+               } catch (IOException e) {
+                       logger.error( "Failed to decrypt " + password + ": " + e.getMessage() );
+               }
+               return clear;
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertficateManagerFactory.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertficateManagerFactory.java
new file mode 100644 (file)
index 0000000..0bffd84
--- /dev/null
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.server;
+
+
+import java.util.Properties;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+public class CertficateManagerFactory extends BaseLoggingClass {
+       private final Properties dmaapConfig;
+
+    public CertficateManagerFactory() {
+        this((DmaapConfig) DmaapConfig.getConfig());
+    }
+
+    CertficateManagerFactory(Properties params) {
+        this.dmaapConfig = params;
+    }
+
+    public CertificateManager initCertificateManager() {
+        boolean useCadi = "cadi".equalsIgnoreCase(dmaapConfig.getProperty("CertificateManagement", "legacy"));
+        logger.info("CertificateManagerFactory: useCadi=" + useCadi);
+        
+        if ( useCadi ) {
+               return new CadiCertificateManager( dmaapConfig );
+        }
+        return new LegacyCertificateManager( dmaapConfig );
+    }
+
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertificateManager.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/CertificateManager.java
new file mode 100644 (file)
index 0000000..2772b92
--- /dev/null
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.server;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+
+public abstract class CertificateManager extends BaseLoggingClass{
+       
+       class cmAttribute {
+               private String type;
+               private String file;
+               private String password;
+               
+               private String getType() {
+                       return type;
+               }
+               private void setType(String certificateType) {
+                       this.type = certificateType;
+               }
+               private String getFile() {
+                       return file;
+               }
+               private void setFile(String keyStoreFile) {
+                       this.file = keyStoreFile;
+               }
+               private void setPassword( String pwd ) {
+                       this.password = pwd;
+               }
+               private String getPassword() {
+                       return password;
+               }
+       }
+
+       private cmAttribute keyStore;
+       private cmAttribute     trustStore;
+       protected boolean ready;
+
+       CertificateManager() {
+               keyStore = new cmAttribute();
+               trustStore = new cmAttribute();
+               ready = false;
+       }
+
+       public boolean isReady() {
+               return ready;
+       }
+       
+       public String getKeyStoreType() {
+               return keyStore.getType();
+       }
+       public void setKeyStoreType(String certificateType) {
+               this.keyStore.setType( certificateType) ;
+       }
+       public String getKeyStoreFile() {
+               return keyStore.getFile();
+       }
+       public void setKeyStoreFile(String keyStoreFile) {
+               this.keyStore.setFile(keyStoreFile);
+       }
+
+       public String getKeyStorePassword() {
+               return keyStore.getPassword();
+       }
+       public void setKeyStorePassword(String keyStorePassword) {
+               this.keyStore.setPassword(keyStorePassword);
+       }
+       public String getTrustStoreType() {
+               return trustStore.getType();
+       }
+       public void setTrustStoreType( String type ) {
+               this.trustStore.setType(type);
+       }
+       public String getTrustStoreFile() {
+               return trustStore.getFile();
+       }
+       public void setTrustStoreFile(String trustStoreFile) {
+               this.trustStore.setFile(trustStoreFile);
+       }
+       public String getTrustStorePassword() {
+               return trustStore.getPassword();
+       }
+       public void setTrustStorePassword(String trustStorePassword) {
+               this.trustStore.setPassword(trustStorePassword);
+       }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/JettyServer.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/JettyServer.java
new file mode 100644 (file)
index 0000000..52d7570
--- /dev/null
@@ -0,0 +1,174 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.server;
+
+import com.google.common.collect.Sets;
+import java.util.Properties;
+import javax.servlet.DispatcherType;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+/**
+ * A  Jetty server which supports:
+ *     - http and https (simultaneously for dev env)
+ *  - REST API context
+ *  - static html pages (for documentation).
+ */
+public class JettyServer extends BaseLoggingClass {
+
+    private static final CertificateManager certificateManager =
+        new CertficateManagerFactory(DmaapConfig.getConfig()).initCertificateManager();
+    private final Server server;
+
+
+    public Server getServer() {
+        return server;
+    }
+
+    public static CertificateManager getCertificateManager() {
+        return certificateManager;
+    }
+
+    public JettyServer(Properties params) {
+
+        server = new Server();
+        int httpPort = Integer.parseInt(params.getProperty("IntHttpPort", "80"));
+        int sslPort = Integer.parseInt(params.getProperty("IntHttpsPort", "443"));
+        boolean allowHttp = Boolean.parseBoolean(params.getProperty("HttpAllowed", "false"));
+        serverLogger.info("port params: http=" + httpPort + " https=" + sslPort);
+        serverLogger.info("allowHttp=" + allowHttp);
+
+        // HTTP Server
+        HttpConfiguration httpConfig = new HttpConfiguration();
+        httpConfig.setSecureScheme("https");
+        httpConfig.setSecurePort(sslPort);
+        httpConfig.setOutputBufferSize(32768);
+
+        try (ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfig))) {
+            httpConnector.setPort(httpPort);
+            httpConnector.setIdleTimeout(30000);
+
+            // HTTPS Server
+            HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
+            httpsConfig.addCustomizer(new SecureRequestCustomizer());
+            SslContextFactory sslContextFactory = new SslContextFactory.Server();
+            sslContextFactory.setWantClientAuth(true);
+
+            if ( ! certificateManager.isReady()) {
+               serverLogger.error("CertificateManager is not ready.  NOT starting https!");
+            } else {
+               setUpKeystore(sslContextFactory);
+               setUpTrustStore(sslContextFactory);
+          
+
+                   if (sslPort != 0) {
+                       try (ServerConnector sslConnector = new ServerConnector(server,
+                           new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
+                           new HttpConnectionFactory(httpsConfig))) {
+                           sslConnector.setPort(sslPort);
+                           server.addConnector(sslConnector);
+                           serverLogger.info("Starting sslConnector on port " + sslPort + " for https");
+                       }
+                   } else {
+                       serverLogger.info("NOT starting sslConnector because InHttpsPort param is " + sslPort );
+                   }
+            } 
+            if (allowHttp) {
+                serverLogger.info("Starting httpConnector on port " + httpPort);
+                server.addConnector(httpConnector);
+            } else {
+                serverLogger.info("NOT starting httpConnector because HttpAllowed param is " + allowHttp);
+            }
+        }
+
+        // Set context for servlet.  This is shared for http and https
+        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
+        context.setContextPath("/");
+        server.setHandler(context);
+
+        ServletHolder jerseyServlet = context
+            .addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/webapi/*");
+        jerseyServlet.setInitOrder(1);
+        jerseyServlet.setInitParameter("jersey.config.server.provider.packages", "org.onap.dmaap.dbcapi.resources");
+        jerseyServlet.setInitParameter("javax.ws.rs.Application", "org.onap.dmaap.dbcapi.server.ApplicationConfig");
+
+        // also serve up some static pages...
+        ServletHolder staticServlet = context.addServlet(DefaultServlet.class, "/*");
+        staticServlet.setInitParameter("resourceBase", "www");
+        staticServlet.setInitParameter("pathInfoOnly", "true");
+
+        registerAuthFilters(context);
+
+        try {
+
+            serverLogger.info("Starting jetty server");
+            String unitTest = params.getProperty("UnitTest", "No");
+            serverLogger.info("UnitTest=" + unitTest);
+            if (unitTest.equals("No")) {
+                server.start();
+                server.dumpStdErr();
+                server.join();
+            }
+        } catch (Exception e) {
+            errorLogger.error("Exception " + e);
+        } finally {
+            server.destroy();
+        }
+
+    }
+
+    private void registerAuthFilters(ServletContextHandler context) {
+        context.addFilter("org.onap.dmaap.dbcapi.resources.AAFAuthenticationFilter", "/webapi/*",
+            Sets.newEnumSet(Sets.newHashSet(DispatcherType.FORWARD, DispatcherType.REQUEST), DispatcherType.class));
+        context.addFilter("org.onap.dmaap.dbcapi.resources.AAFAuthorizationFilter", "/webapi/*",
+            Sets.newEnumSet(Sets.newHashSet(DispatcherType.FORWARD, DispatcherType.REQUEST), DispatcherType.class));
+    }
+
+    private void setUpKeystore(SslContextFactory sslContextFactory) {
+        String keystore = JettyServer.certificateManager.getKeyStoreFile();
+        logger.info("https Server using keystore at " + keystore);
+        sslContextFactory.setKeyStorePath(keystore);
+        sslContextFactory.setKeyStoreType(JettyServer.certificateManager.getKeyStoreType());
+        sslContextFactory.setKeyStorePassword(JettyServer.certificateManager.getKeyStorePassword());
+        sslContextFactory.setKeyManagerPassword(JettyServer.certificateManager.getKeyStorePassword());
+    }
+
+    private void setUpTrustStore(SslContextFactory sslContextFactory) {
+        String truststore = JettyServer.certificateManager.getTrustStoreFile();
+        logger.info("https Server using truststore at " + truststore);
+        sslContextFactory.setTrustStorePath(truststore);
+        sslContextFactory.setTrustStoreType(JettyServer.certificateManager.getTrustStoreType());
+        sslContextFactory.setTrustStorePassword(JettyServer.certificateManager.getTrustStorePassword());
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/LegacyCertificateManager.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/LegacyCertificateManager.java
new file mode 100644 (file)
index 0000000..bd54003
--- /dev/null
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.server;
+
+import java.util.Properties;
+
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+public class LegacyCertificateManager extends CertificateManager {
+
+       public LegacyCertificateManager(Properties properties ) {
+               setKeyStoreType( properties.getProperty("KeyStoreType", "jks") );
+               setKeyStoreFile( properties.getProperty("KeyStoreFile", "etc/keystore") );
+               setKeyStorePassword( properties.getProperty("KeyStorePassword", "changeit") );
+               
+               setTrustStoreFile( properties.getProperty("TrustStoreFile", "etc/org.onap.dmaap-bc.trust.jks") );
+               setTrustStoreType( properties.getProperty("TrustStoreType", "jks") );
+               setTrustStorePassword( properties.getProperty("TrustStorePassword", "changeit") );
+               ready = true;
+       }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/Main.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/server/Main.java
new file mode 100644 (file)
index 0000000..942fe6c
--- /dev/null
@@ -0,0 +1,93 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.server;
+import static com.att.eelf.configuration.Configuration.MDC_ALERT_SEVERITY;
+import static com.att.eelf.configuration.Configuration.MDC_INSTANCE_UUID;
+import static com.att.eelf.configuration.Configuration.MDC_SERVER_FQDN;
+import static com.att.eelf.configuration.Configuration.MDC_SERVER_IP_ADDRESS;
+import static com.att.eelf.configuration.Configuration.MDC_SERVICE_INSTANCE_ID;
+import static com.att.eelf.configuration.Configuration.MDC_TARGET_ENTITY;
+
+import java.net.InetAddress;
+import java.util.Properties;
+import java.util.UUID;
+import org.onap.dmaap.dbcapi.authentication.ApiPerms;
+import org.onap.dmaap.dbcapi.authentication.ApiPolicy;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.Singleton;
+import org.slf4j.MDC;
+
+public class Main extends BaseLoggingClass {
+
+    private static String provFQDN;
+    public static String getProvFQDN() {
+               return provFQDN;
+       }
+       public void setProvFQDN(String provFQDN) {
+               Main.provFQDN = provFQDN;
+       }
+       private Main() {
+    }
+    public static void main(String[] args) {
+        (new Main()).main();
+    }
+
+    private void main()  {
+    
+        MDC.clear();
+
+        MDC.put(MDC_SERVICE_INSTANCE_ID, "");
+        try {
+            MDC.put(MDC_SERVER_FQDN, InetAddress.getLocalHost().getHostName());
+            MDC.put(MDC_SERVER_IP_ADDRESS, InetAddress.getLocalHost().getHostAddress());
+        } catch (Exception e) {
+               errorLogger.error("Error while getting hostname or address", e);
+        }
+        MDC.put(MDC_INSTANCE_UUID, UUID.randomUUID().toString());
+        MDC.put(MDC_ALERT_SEVERITY, "0");
+        MDC.put(MDC_TARGET_ENTITY, "DCAE");
+
+        appLogger.info("Started.");
+        Properties parameters = DmaapConfig.getConfig();
+        setProvFQDN( parameters.getProperty("ProvFQDN", "ProvFQDN.notset.com"));
+
+               // for fresh installs, we may come up with no dmaap name so need to have a way for Controller to talk to us
+               Singleton<Dmaap> dmaapholder = DatabaseClass.getDmaap();
+               String name = dmaapholder.get().getDmaapName();
+               ApiPolicy apiPolicy = new ApiPolicy();
+               if ( apiPolicy.isPermissionClassSet() && (name == null || name.isEmpty())) {
+                       ApiPerms p = new ApiPerms();
+                       p.setBootMap();
+               }
+
+        try {
+               new JettyServer(parameters);
+        } catch (Exception e) {
+            errorLogger.error("Unable to start Jetty " + DmaapConfig.getConfigFileName(), e);
+            System.exit(1);
+        }
+
+    }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafPermissionService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafPermissionService.java
new file mode 100644 (file)
index 0000000..1997633
--- /dev/null
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import org.onap.dmaap.dbcapi.aaf.AafService;
+import org.onap.dmaap.dbcapi.aaf.AafUserRole;
+import org.onap.dmaap.dbcapi.aaf.DmaapGrant;
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.model.MR_Client;
+
+import static java.lang.String.format;
+
+class AafPermissionService extends BaseLoggingClass {
+
+    private static final String INSTANCE_PREFIX = ":topic.";
+    private final AafService aafService;
+    private final DmaapService dmaapService;
+
+    AafPermissionService(AafService aafService, DmaapService dmaapService) {
+        this.aafService = aafService;
+        this.dmaapService = dmaapService;
+    }
+
+    ApiError assignClientToRole(MR_Client client, String role) {
+        AafUserRole ur = new AafUserRole(client.getClientIdentity(), role);
+        int rc = aafService.addUserRole(ur);
+        if (rc != 201 && rc != 409) {
+            return handleErrorStatus(rc, client,
+                    format("Failed to add user %s to role %s", client.getClientIdentity(), role));
+        }
+        return handleOkStatus(client);
+    }
+
+    ApiError grantClientRolePerms(MR_Client client) {
+        return forEachClientAction(client, this::grantPermForClientRole);
+    }
+
+    private ApiError forEachClientAction(MR_Client client, PermissionUpdate permissionUpdate) {
+        try {
+            String instance = INSTANCE_PREFIX + client.getFqtn();
+
+            for (String action : client.getAction()) {
+                permissionUpdate.execute(client.getClientRole(), instance, action);
+            }
+
+        } catch (PermissionServiceException e) {
+            return handleErrorStatus(e.getCode(), client, e.getMessage());
+        }
+        return handleOkStatus(client);
+    }
+
+    private void grantPermForClientRole(String clientRole, String instance, String action) throws PermissionServiceException {
+        if (clientRole != null) {
+            DmaapPerm perm = new DmaapPerm(dmaapService.getTopicPerm(), instance, action);
+            DmaapGrant g = new DmaapGrant(perm, clientRole);
+            int code = aafService.addGrant(g);
+            if (code != 201 && code != 409) {
+                throw new PermissionServiceException(code, format("Grant of %s|%s|%s failed for %s",
+                        dmaapService.getTopicPerm(), instance, action, clientRole));
+            }
+        } else {
+            logger.warn("No Grant of {}|{}|{} because role is null ", dmaapService.getTopicPerm(), instance, action);
+        }
+    }
+
+    private ApiError handleErrorStatus(int code, MR_Client client, String message) {
+        ApiError apiError = new ApiError(code, message);
+        client.setStatus(DmaapObject_Status.INVALID);
+        logger.warn(apiError.getMessage());
+        return apiError;
+    }
+
+    private ApiError handleOkStatus(MR_Client client) {
+        client.setStatus(DmaapObject_Status.VALID);
+        return new ApiError(200, "OK");
+    }
+
+    private class PermissionServiceException extends Exception {
+        private final int code;
+        private final String message;
+
+        PermissionServiceException(int code, String message) {
+            this.code = code;
+            this.message = message;
+        }
+
+        public int getCode() {
+            return code;
+        }
+
+        @Override
+        public String getMessage() {
+            return message;
+        }
+    }
+
+    @FunctionalInterface
+    interface PermissionUpdate {
+        void execute(String clientRole, String instance, String action) throws PermissionServiceException;
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafTopicSetupService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/AafTopicSetupService.java
new file mode 100644 (file)
index 0000000..16ffa08
--- /dev/null
@@ -0,0 +1,218 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import org.onap.dmaap.dbcapi.aaf.AafNamespace;
+import org.onap.dmaap.dbcapi.aaf.AafRole;
+import org.onap.dmaap.dbcapi.aaf.AafService;
+import org.onap.dmaap.dbcapi.aaf.DmaapGrant;
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+import static java.lang.String.format;
+import static org.apache.commons.lang3.StringUtils.isNumeric;
+
+class AafTopicSetupService extends BaseLoggingClass {
+
+    private final AafService aafService;
+    private final DmaapService dmaapService;
+    private final DmaapConfig dmaapConfig;
+
+    AafTopicSetupService(AafService aafService, DmaapService dmaapService, DmaapConfig dmaapConfig) {
+        this.aafService = aafService;
+        this.dmaapService = dmaapService;
+        this.dmaapConfig = dmaapConfig;
+    }
+
+    ApiError aafTopicSetup(Topic topic) {
+
+        try {
+            String instance = ":topic." + topic.getFqtn();
+            String topicPerm = dmaapService.getTopicPerm();
+            DmaapPerm pubPerm = createPermission(topicPerm, instance, "pub");
+            DmaapPerm subPerm = createPermission(topicPerm, instance, "sub");
+            DmaapPerm viewPerm = createPermission(topicPerm, instance, "view");
+
+            // creating Topic Roles was not an original feature.
+            // For backwards compatibility, only do this if the feature is enabled.
+            // Also, if the namespace of the topic is a foreign namespace, (i.e. not the same as our root ns)
+            // then we likely don't have permission to create sub-ns and Roles so don't try.
+            if (createTopicRoles() && topic.getFqtn().startsWith(getTopicsNsRoot())) {
+                createNamespace(topic);
+
+                AafRole pubRole = createRole(topic, "publisher");
+                topic.setPublisherRole(pubRole.getFullyQualifiedRole());
+
+                AafRole subRole = createRole(topic, "subscriber");
+                topic.setSubscriberRole(subRole.getFullyQualifiedRole());
+
+                grantPermToRole(pubRole, pubPerm);
+                grantPermToRole(pubRole, viewPerm);
+
+                grantPermToRole(subRole, subPerm);
+                grantPermToRole(subRole, viewPerm);
+            }
+
+        } catch (TopicSetupException ex) {
+            logger.error("Exception in topic setup {}", ex.getMessage());
+            return new ApiError(ex.getCode(), ex.getMessage(), ex.getFields());
+        }
+        return okStatus();
+    }
+
+    ApiError aafTopicCleanup(Topic topic) {
+        try {
+            if (performCleanup()) {
+                String instance = ":topic." + topic.getFqtn();
+                String topicPerm = dmaapService.getTopicPerm();
+                removePermission(topicPerm, instance, "pub");
+                removePermission(topicPerm, instance, "sub");
+                removePermission(topicPerm, instance, "view");
+
+                if (createTopicRoles() && topic.getFqtn().startsWith(getTopicsNsRoot())) {
+                    removeNamespace(topic);
+                }
+            }
+        } catch (TopicSetupException ex) {
+            return new ApiError(ex.getCode(), ex.getMessage(), ex.getFields());
+        }
+        return okStatus();
+    }
+
+    private String getTopicsNsRoot() throws TopicSetupException {
+        String nsr = dmaapService.getDmaap().getTopicNsRoot();
+        if (nsr == null) {
+            throw new TopicSetupException(500,
+                    "Unable to establish AAF namespace root: (check /dmaap object)", "topicNsRoot");
+        }
+        return nsr;
+    }
+
+    private DmaapPerm createPermission(String permission, String instance, String action) throws TopicSetupException {
+        DmaapPerm perm = new DmaapPerm(permission, instance, action);
+        int rc = aafService.addPerm(perm);
+        if (rc != 201 && rc != 409) {
+            throw new TopicSetupException(500,
+                    format("Unexpected response from AAF: %d permission=%s instance=%s action=%s",
+                            rc, perm, instance, action));
+        }
+        return perm;
+    }
+
+    private void grantPermToRole(AafRole aafRole, DmaapPerm perm) throws TopicSetupException {
+        DmaapGrant g = new DmaapGrant(perm, aafRole.getFullyQualifiedRole());
+        int rc = aafService.addGrant(g);
+        if (rc != 201 && rc != 409) {
+            String message = format("Grant of %s failed for %s", perm.toString(), aafRole.getFullyQualifiedRole());
+            logger.warn(message);
+            throw new TopicSetupException(rc, message);
+        }
+    }
+
+    private void createNamespace(Topic topic) throws TopicSetupException {
+        AafNamespace ns = new AafNamespace(topic.getFqtn(), aafService.getIdentity());
+        int rc = aafService.addNamespace(ns);
+        if (rc != 201 && rc != 409) {
+            throw new TopicSetupException(500,
+                    format("Unexpected response from AAF: %d namespace=%s identity=%s",
+                            rc, topic.getFqtn(), aafService.getIdentity()));
+        }
+    }
+
+    private AafRole createRole(Topic topic, String roleName) throws TopicSetupException {
+        AafRole role = new AafRole(topic.getFqtn(), roleName);
+        int rc = aafService.addRole(role);
+        if (rc != 201 && rc != 409) {
+            throw new TopicSetupException(500,
+                    format("Unexpected response from AAF: %d topic=%s role=%s",
+                            rc, topic.getFqtn(), roleName));
+        }
+        return role;
+    }
+
+    private void removePermission(String permission, String instance, String action) throws TopicSetupException {
+        DmaapPerm perm = new DmaapPerm(permission, instance, action);
+        int rc = aafService.delPerm(perm, true);
+        if (rc != 200 && rc != 404) {
+            throw new TopicSetupException(500,
+                    format("Unexpected response from AAF: %d permission=%s instance=%s action=%s",
+                            rc, perm, instance, action));
+        }
+    }
+
+    private void removeNamespace(Topic topic) throws TopicSetupException {
+        AafNamespace ns = new AafNamespace(topic.getFqtn(), aafService.getIdentity());
+        int rc = aafService.delNamespace(ns, true);
+        if (rc != 200 && rc != 404) {
+            throw new TopicSetupException(500,
+                    format("Unexpected response from AAF: %d namespace=%s identity=%s",
+                            rc, topic.getFqtn(), aafService.getIdentity()));
+        }
+    }
+
+    private ApiError okStatus() {
+        return new ApiError(200, "OK");
+    }
+
+    private boolean createTopicRoles() {
+        return "true".equalsIgnoreCase(dmaapConfig.getProperty("aaf.CreateTopicRoles", "true"));
+    }
+
+    private boolean performCleanup() {
+        String deleteLevel = dmaapConfig.getProperty("MR.ClientDeleteLevel", "0");
+        if (!isNumeric(deleteLevel)) {
+            return false;
+        }
+        return Integer.valueOf(deleteLevel) >= 2;
+    }
+
+    private class TopicSetupException extends Exception {
+
+        private final int code;
+        private final String message;
+        private final String fields;
+
+        TopicSetupException(int code, String message) {
+            this(code, message, "");
+        }
+
+        TopicSetupException(int code, String message, String fields) {
+            this.code = code;
+            this.message = message;
+            this.fields = fields;
+        }
+
+        public int getCode() {
+            return code;
+        }
+
+        @Override
+        public String getMessage() {
+            return message;
+        }
+
+        public String getFields() {
+            return fields;
+        }
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/ApiService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/ApiService.java
new file mode 100644 (file)
index 0000000..ef1e6f4
--- /dev/null
@@ -0,0 +1,159 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import static com.att.eelf.configuration.Configuration.MDC_KEY_REQUEST_ID;
+import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME;
+
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+import org.onap.dmaap.dbcapi.authentication.ApiPolicy;
+import org.onap.dmaap.dbcapi.authentication.AuthenticationErrorException;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.RandomString;
+import org.slf4j.MDC;
+
+public class ApiService extends BaseLoggingClass {
+
+    private String apiNamespace;
+    private String uri;
+    private String uriPath;
+    private String method;
+    private String authorization;
+    private String requestId;
+    private ApiError err;
+    private ApiPolicy apiPolicy;
+    private CredentialsParser credentialsParser = new CredentialsParser();
+
+    public ApiService() {
+
+        err = new ApiError();
+        requestId = (new RandomString(10)).nextString();
+
+        if (apiNamespace == null) {
+            DmaapConfig p = (DmaapConfig) DmaapConfig.getConfig();
+            apiNamespace = p.getProperty("ApiNamespace", "org.openecomp.dmaapBC.api");
+            logger.info("config param usePE has been deprecated.  Use ApiPermission.Class property instead.");
+        }
+        apiPolicy = new ApiPolicy();
+
+        logger.info("apiNamespace=" + apiNamespace);
+    }
+
+    public ApiService setAuth(String auth) {
+        this.authorization = auth;
+        logger.info("setAuth:  authorization={} ", authorization);
+        return this;
+    }
+
+    private void setServiceName() {
+        String svcRequest = new String(this.method + " " + this.uriPath);
+        MDC.put(MDC_SERVICE_NAME, svcRequest);
+    }
+
+    public ApiService setHttpMethod(String httpMethod) {
+        this.method = httpMethod;
+        logger.info("setHttpMethod: method={} ", method);
+        setServiceName();
+        return this;
+    }
+
+    public ApiService setUriPath(String uriPath) {
+        this.uriPath = uriPath;
+        this.uri = setUriFromPath(uriPath);
+        logger.info("setUriPath: uriPath={} uri={}", uriPath, uri);
+        setServiceName();
+        return this;
+    }
+
+    private String setUriFromPath(String uriPath) {
+        int ch = uriPath.indexOf("/");
+        if (ch > 0) {
+            return ((String) uriPath.subSequence(0, ch));
+        } else {
+            return uriPath;
+        }
+    }
+
+    public ApiError getErr() {
+        return err;
+    }
+
+    public void checkAuthorization() throws Exception {
+
+        MDC.put(MDC_KEY_REQUEST_ID, requestId);
+
+        logger.info("request: uri={} method={} auth={}", uri, method, authorization);
+
+        if (uri == null || uri.isEmpty()) {
+            String errmsg = "No URI value provided ";
+            err.setMessage(errmsg);
+            logger.info(errmsg);
+            throw new AuthenticationErrorException();
+        }
+        if (method == null || method.isEmpty()) {
+            String errmsg = "No method value provided ";
+            err.setMessage(errmsg);
+            logger.info(errmsg);
+            throw new AuthenticationErrorException();
+        }
+        DmaapService dmaapService = new DmaapService();
+        Dmaap dmaap = dmaapService.getDmaap();
+        String env = dmaap.getDmaapName();
+
+        // special case during bootstrap of app when DMaaP environment may not be set.
+        // this allows us to authorize certain APIs used for initialization during this window.
+        if (env == null || env.isEmpty()) {
+            env = "boot";
+        }
+        if (!apiPolicy.isPermissionClassSet()) {
+            return;  // skip authorization if not enabled
+        }
+
+        Credentials credentials = credentialsParser.parse(authorization);
+        try {
+            DmaapPerm p = new DmaapPerm(apiNamespace + "." + uri, env, method);
+            apiPolicy.check(credentials.getId(), credentials.getPwd(), p);
+        } catch (AuthenticationErrorException ae) {
+            String errmsg =
+                "User " + credentials.getId() + " failed authentication/authorization for " + apiNamespace + "." + uriPath + " " + env
+                    + " " + method;
+            logger.info(errmsg);
+            err.setMessage(errmsg);
+            throw ae;
+
+        }
+    }
+
+    public ApiService setRequestId(String requestId) {
+        if (requestId == null || requestId.isEmpty()) {
+            this.requestId = (new RandomString(10)).nextString();
+            logger.warn("X-ECOMP-RequestID not set in HTTP Header.  Setting RequestId value to: " + this.requestId);
+        } else {
+            this.requestId = requestId;
+        }
+        MDC.put(MDC_KEY_REQUEST_ID, this.requestId);
+        return this;
+    }
+}
+
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/Credentials.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/Credentials.java
new file mode 100644 (file)
index 0000000..aee9b4f
--- /dev/null
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+public class Credentials {
+
+    private final String id;
+    private final String pwd;
+
+    Credentials(String id, String pwd) {
+        this.id = id;
+        this.pwd = pwd;
+    }
+
+    static Credentials empty() {
+        return new Credentials("", "");
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getPwd() {
+        return pwd;
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/CredentialsParser.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/CredentialsParser.java
new file mode 100644 (file)
index 0000000..4156d1b
--- /dev/null
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import javax.xml.bind.DatatypeConverter;
+
+class CredentialsParser {
+
+    Credentials parse(String authorizationHeader) {
+        if (authorizationHeader == null || authorizationHeader.isEmpty()) {
+            return Credentials.empty();
+        }
+
+        String credentials = authorizationHeader.substring("Basic".length()).trim();
+        byte[] decoded = DatatypeConverter.parseBase64Binary(credentials);
+        String decodedString = new String(decoded);
+        String[] actualCredentials = decodedString.split(":");
+        return new Credentials(actualCredentials[0], actualCredentials[1]);
+    }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_NodeService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_NodeService.java
new file mode 100644 (file)
index 0000000..b29beb9
--- /dev/null
@@ -0,0 +1,273 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dcae
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.Response.Status;
+import org.onap.dmaap.dbcapi.client.DrProvConnection;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Node;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+
+public class DR_NodeService extends BaseLoggingClass {
+       private  class DrProv {
+               String currentNodes;
+               String currentStaticNodes;
+               
+               private String getX( String X, ApiError apiError ) {
+                       
+               logger.info( "templog:getX at" + " 12.10.10" );
+                       DrProvConnection prov = new DrProvConnection();
+               logger.info( "templog:getX at" + " 12.10.12" );
+                       prov.makeNodesConnection( X );  
+               logger.info( "templog:getX at" + " 12.10.14" );
+                       String resp  = prov.doGetNodes(  apiError );
+               logger.info( "templog:getX at" + " 12.10.16" );
+                       logger.info( "rc=" + apiError.getCode() );
+               logger.info( "templog:getX at" + " 12.10.18" );
+                       return resp;
+               }
+               
+               private void setX( String X, String list, ApiError apiError ) {
+                       DrProvConnection prov = new DrProvConnection();
+                       prov.makeNodesConnection( X, list );    
+                       prov.doPutNodes( apiError );
+               }
+               
+               private String removeFromList( String aNode, String aList ) {
+                       String[] nodeList = aList.split("\\|");
+                       StringBuilder res = new StringBuilder();
+                       for ( String n: nodeList ) {
+                               logger.info( "compare existing node " + n + " vs " + aNode );
+                               if ( ! n.equals(aNode)) {
+                                       if (res.length() > 0 ) {
+                                               res.append( "|" );
+                                       }
+                                       res.append(n);
+                               }
+                       }
+                       logger.info( "result=" + res.toString() );
+                       return res.toString();
+               }
+               
+                boolean containsNode( String aNode , ApiError apiError ){
+       
+               logger.info( "templog:containsNode at" + " 12.10" );
+                       //DrProvConnection prov = new DrProvConnection();
+                       //prov.makeNodesConnection();   
+                       currentNodes = getX( "NODES", apiError );
+               logger.info( "templog:containsNode at" + " 12.12" );
+                       if ( ! apiError.is2xx() || currentNodes == null ) {
+               logger.info( "templog:containsNode at" + " 12.14" );
+                               return false;
+                       }
+               logger.info( "templog:containsNode at" + " 12.16" );
+                       logger.info( "NODES now=" + currentNodes );
+                       String[] nodeList = currentNodes.split("\\|");
+               logger.info( "templog:containsNode at" + " 12.17" );
+                       for( String n: nodeList ) {
+               logger.info( "templog:containsNode at" + " 12.18" );
+                               logger.info( "compare existing node " + n + " vs " + aNode );
+                               if ( n.equals(aNode) ) {
+                                       return true;
+                               }
+                       }
+                       return false;
+               }
+               
+                void addNode( String aNode, ApiError apiError ) {
+                       
+                       currentNodes = currentNodes + "|" + aNode;
+                       setX( "NODES", currentNodes, apiError );        
+
+                       
+               }
+               void removeNode( String aNode, ApiError apiError ) {
+                       currentNodes = removeFromList( aNode, currentNodes );
+                       setX( "NODES", currentNodes, apiError );                        
+               }
+
+               public boolean containsStaticNode(String aNode, ApiError apiError) {
+       
+                       //DrProvConnection prov = new DrProvConnection();
+                       //prov.makeNodesConnection();   
+                       currentStaticNodes = getX( "STATIC_ROUTING_NODES", apiError );
+                       if (! apiError.is2xx() || currentStaticNodes == null ) {
+                               return false;
+                       }
+                       logger.info( "STATIC_ROUTING_NODES now=" + currentNodes );
+                       String[] nodeList = currentStaticNodes.split("\\|");
+                       for( String n: nodeList ) {
+                               logger.info( "compare existing node " + n + " vs " + aNode );
+                               if ( n.equals(aNode) ) {
+                                       return true;
+                               }
+                       }
+                       return false;
+               }
+               
+
+               public void addStaticNode(String aNode, ApiError apiError) {
+                       currentStaticNodes = currentStaticNodes + "|" + aNode;
+                       setX( "STATIC_ROUTING_NODES", currentStaticNodes, apiError );
+               }
+               void removeStaticNode( String aNode, ApiError apiError ) {
+                       currentStaticNodes = removeFromList( aNode, currentStaticNodes );
+                       setX( "STATIC_ROUTING_NODES", currentStaticNodes, apiError );                   
+               }
+       }       
+
+       DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+       String unit_test = p.getProperty( "UnitTest", "No" );
+
+       private Map<String, DR_Node> dr_nodes = DatabaseClass.getDr_nodes();
+       
+       public Map<String, DR_Node> getDr_Nodes() {
+               return dr_nodes;
+       }
+       
+       public List<DR_Node> getAllDr_Nodes() {
+               return new ArrayList<DR_Node>(dr_nodes.values());
+       }
+       
+       public DR_Node getDr_Node( String fqdn, ApiError apiError ) {
+               DR_Node old = dr_nodes.get( fqdn );
+               if ( old == null ) {
+                       apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                       apiError.setFields( "fqdn");
+                       apiError.setMessage( "Node " + fqdn + " does not exist");
+                       return null;
+               }
+               apiError.setCode(200);
+               return old;
+       }
+
+       public DR_Node addDr_Node( DR_Node node, ApiError apiError ) {
+               String fqdn = node.getFqdn();
+               DR_Node old = dr_nodes.get( fqdn );
+               if ( old != null ) {
+                       apiError.setCode(Status.CONFLICT.getStatusCode());
+                       apiError.setFields( "fqdn");
+                       apiError.setMessage( "Node " + fqdn + " already exists");
+                       return null;
+               }
+               logger.info( "templog:addDr_Node at" + " 10" );
+               
+               DrProv drProv = new DrProv();
+               logger.info( "templog:addDr_Node at" + " 12" );
+
+               if ( ! drProv.containsNode( node.getFqdn(), apiError ) && apiError.is2xx() ) {
+                       logger.info( "templog:addDr_Node at" + " 15" );
+                       drProv.addNode( node.getFqdn(), apiError );
+               }
+               logger.info( "templog:addDr_Node at" + " 20" );
+               if ( ! apiError.is2xx() && ! unit_test.equals( "Yes" ) ) {
+                       return null;
+               }
+               logger.info( "templog:addDr_Node at" + " 30" );
+               DcaeLocationService locService = new DcaeLocationService();
+               if ( locService.isEdgeLocation( node.getDcaeLocationName()) && ! drProv.containsStaticNode( node.getFqdn(), apiError ) ) {
+                       if ( apiError.is2xx() ) {
+                               drProv.addStaticNode( node.getFqdn(), apiError );
+                       }
+               }
+               logger.info( "templog:addDr_Node at" + " 40" );
+               if ( ! apiError.is2xx() && ! unit_test.equals("Yes") ) {
+                       return null;
+               }
+               
+               logger.info( "templog:addDr_Node at" + " 50" );
+               node.setLastMod();
+               node.setStatus(DmaapObject_Status.VALID);
+               dr_nodes.put( node.getFqdn(), node );
+               logger.info( "templog:addDr_Node at" + " 60" );
+               apiError.setCode(200);
+               return node;
+       }
+       
+       public DR_Node updateDr_Node( DR_Node node, ApiError apiError ) {
+               DR_Node old = dr_nodes.get( node.getFqdn() );
+               if ( old == null ) {
+                       apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                       apiError.setFields( "fqdn");
+                       apiError.setMessage( "Node " + node.getFqdn() + " does not exist");
+                       return null;
+               }
+               node.setLastMod();
+               dr_nodes.put( node.getFqdn(), node );
+               apiError.setCode(200);
+               return node;
+       }
+       
+       public DR_Node removeDr_Node( String nodeName, ApiError apiError ) {
+               DR_Node old = dr_nodes.get( nodeName );
+               if ( old == null ) {
+                       apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                       apiError.setFields( "fqdn");
+                       apiError.setMessage( "Node " + nodeName + " does not exist");
+                       return null;
+               }
+               
+               DrProv drProv = new DrProv();
+               if ( drProv.containsNode( old.getFqdn(), apiError ) && apiError.is2xx() ) {
+                       drProv.removeNode( old.getFqdn(), apiError );
+               }
+               DcaeLocationService locService = new DcaeLocationService();
+               if ( locService.isEdgeLocation( old.getDcaeLocationName()) && drProv.containsStaticNode( old.getFqdn(), apiError ) ) {
+                       if ( apiError.is2xx()) {
+                               drProv.removeStaticNode( old.getFqdn(), apiError );
+                       }
+                       
+               }
+               
+               apiError.setCode(200);
+               return dr_nodes.remove(nodeName);
+       }               
+
+       public String getNodePatternAtLocation( String loc, boolean allowMult ) {
+               logger.info( "loc=" + loc );
+               if ( loc == null ) {
+                       return null;
+               }
+               StringBuilder str = new StringBuilder();
+               for( DR_Node node : dr_nodes.values() ) {
+                       if ( loc.equals( node.getDcaeLocationName()) ) {
+                               if ( str.length() > 0 ) {
+                                       str.append( ",");
+                               }
+                               str.append( node.getFqdn());
+                               if ( ! allowMult ) {
+                                       break;
+                               }
+                       }
+               }
+               logger.info( "returning " + str.toString() );
+               return str.toString();
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_PubService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_PubService.java
new file mode 100644 (file)
index 0000000..352330a
--- /dev/null
@@ -0,0 +1,147 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.Response.Status;
+import org.onap.dmaap.dbcapi.client.DrProvConnection;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Pub;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+
+public class DR_PubService  extends BaseLoggingClass{
+       
+       private Map<String, DR_Pub> dr_pubs = DatabaseClass.getDr_pubs();
+       private DR_NodeService nodeService = new DR_NodeService();
+       private static DrProvConnection prov;
+       
+       public DR_PubService() {
+               super();
+               prov = new DrProvConnection();
+       }
+
+       public Map<String, DR_Pub> getDr_Pubs() {                       
+               return dr_pubs;
+       }
+               
+       public List<DR_Pub> getAllDr_Pubs() {
+               return new ArrayList<DR_Pub>(dr_pubs.values());
+       }
+       
+       public ArrayList<DR_Pub> getDr_PubsByFeedId( String feedId ) {
+               ArrayList<DR_Pub> somePubs = new ArrayList<DR_Pub>();
+               for( DR_Pub pub : dr_pubs.values() ) {
+                       if ( feedId.equals(  pub.getFeedId()  )) {
+                               somePubs.add( pub );
+                       }
+               }
+                       
+               return somePubs;
+       }
+               
+       public DR_Pub getDr_Pub( String key, ApiError err ) {   
+               DR_Pub pub = dr_pubs.get( key );
+               if ( pub == null ) {
+                       err.setCode(Status.NOT_FOUND.getStatusCode());
+                       err.setFields( "pubId");
+                       err.setMessage("DR_Pub with pubId = " + key + " not found");
+               } else {
+                       err.setCode(Status.OK.getStatusCode());
+               }
+               return pub;
+       }
+       
+       private void addIngressRoute( DR_Pub pub, ApiError err ) {
+               
+               String nodePattern = nodeService.getNodePatternAtLocation( pub.getDcaeLocationName(), true );
+               if ( nodePattern != null && nodePattern.length() > 0 ) {
+                       logger.info( "creating ingress rule: pub " + pub.getPubId() + " on feed " + pub.getFeedId() + " to " + nodePattern);
+                       prov.makeIngressConnection( pub.getFeedId(), pub.getUsername(), "-", nodePattern);
+                       int rc = prov.doXgressPost(err);
+                       logger.info( "rc=" + rc + " error code=" + err.getCode() );
+                       
+                       if ( rc != 200 ) {
+                               switch( rc ) {
+                               case 403:
+                                       logger.error( "Not authorized for DR ingress API");
+                                       err.setCode(500);
+                                       err.setMessage("API deployment/configuration error - contact support");
+                                       err.setFields( "PROV_AUTH_ADDRESSES");
+                                       break;
+                               
+                               default: 
+                                       logger.info( DmaapbcLogMessageEnum.INGRESS_CREATE_ERROR, Integer.toString(rc),  pub.getPubId(), pub.getFeedId(), nodePattern);
+                               }
+                       }
+
+               }
+       }
+
+       public DR_Pub addDr_Pub( DR_Pub pub ) {
+               ApiError err = new ApiError();
+               if ( pub.getPubId() != null && ! pub.getPubId().isEmpty() ) {
+                       addIngressRoute( pub, err);
+                       if ( err.getCode() > 0 ) {
+                               pub.setStatus(DmaapObject_Status.INVALID);
+                       }
+                       pub.setLastMod();
+                       dr_pubs.put( pub.getPubId(), pub );
+                       return pub;
+               }
+               else {
+                       return null;
+               }
+       }
+               
+       public DR_Pub updateDr_Pub( DR_Pub pub ) {
+               if ( pub.getPubId().isEmpty()) {
+                       return null;
+               }
+               pub.setLastMod();
+               dr_pubs.put( pub.getPubId(), pub );
+               return pub;
+       }
+               
+       public DR_Pub removeDr_Pub( String pubId, ApiError err ) {
+               return removeDr_Pub( pubId, err, true );
+       }
+               
+       
+       public DR_Pub removeDr_Pub( String pubId, ApiError err, boolean hitDR ) {
+               DR_Pub pub =  dr_pubs.get( pubId );
+               if ( pub == null ) {
+                       err.setCode(Status.NOT_FOUND.getStatusCode());
+                       err.setFields( "pubId");
+                       err.setMessage( "pubId " + pubId + " not found");
+               } else {
+                       dr_pubs.remove(pubId);
+                       err.setCode(Status.OK.getStatusCode());
+               }
+               return pub;
+                               
+       }       
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_SubService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DR_SubService.java
new file mode 100644 (file)
index 0000000..0a583a0
--- /dev/null
@@ -0,0 +1,228 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.onap.dmaap.dbcapi.client.DrProvConnection;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Sub;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.RandomInteger;
+
+public class DR_SubService extends BaseLoggingClass {
+
+       private Map<String, DR_Sub> dr_subs = DatabaseClass.getDr_subs();
+       private DR_NodeService nodeService = new DR_NodeService();
+       private String provURL;
+       private static DrProvConnection prov;
+       
+       private String unit_test;
+       
+       
+       public DR_SubService(  ) {
+               logger.debug( "Entry: DR_SubService (with no args)" );
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               unit_test = p.getProperty( "UnitTest", "No" );
+       }       
+       public DR_SubService( String subURL ) {
+               logger.debug( "Entry: DR_SubService " + subURL );
+               provURL = subURL;
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               unit_test = p.getProperty( "UnitTest", "No" );
+       }
+       public Map<String, DR_Sub> getDR_Subs() {
+               logger.debug( "enter getDR_Subs()");
+               return dr_subs;
+       }
+               
+       public List<DR_Sub> getAllDr_Subs() {
+               logger.debug( "enter getAllDR_Subs()");
+               return new ArrayList<>(dr_subs.values());
+       }
+       
+       public ArrayList<DR_Sub> getDr_SubsByFeedId( String pubId ) {
+               ArrayList<DR_Sub> someSubs = new ArrayList<>();
+               for( DR_Sub sub : dr_subs.values() ) {
+                       if ( pubId.equals(  sub.getFeedId()  )) {
+                               someSubs.add( sub );
+                       }
+               }
+                       
+               return someSubs;
+       }
+       public DR_Sub getDr_Sub( String key, ApiError apiError ) {      
+               logger.debug( "enter getDR_Sub()");
+               DR_Sub sub = dr_subs.get( key );
+               if ( sub == null ) {
+                       apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                       apiError.setFields( "subId");
+                       apiError.setMessage("subId " + key + " not found");
+               } else {
+                       apiError.setCode(200);
+               }
+               return sub;
+       }
+
+       public DR_Sub addDr_Sub( DR_Sub sub, ApiError apiError ) {
+               logger.debug( "enter addDR_Subs()");
+               prov = new DrProvConnection();
+               prov.makeSubPostConnection( provURL );
+               String resp = prov.doPostDr_Sub( sub, apiError );
+               if ( "Yes".equals(unit_test) ) {
+                       resp = simulateResp( sub, "POST" );
+                       apiError.setCode(201);
+               }
+               logger.debug( "addDr_Sub resp=" + resp );
+
+               DR_Sub snew = null;
+
+               if ( resp != null ) {
+                       snew = new DR_Sub( resp );
+                       snew.setDcaeLocationName(sub.getDcaeLocationName());
+                       snew.setLastMod();
+                       addEgressRoute( snew, apiError );
+                       dr_subs.put( snew.getSubId(), snew );   
+                       apiError.setCode(201);
+               } else {
+                       apiError.setCode(400);
+               }
+               
+               return snew;
+       }
+
+       private void addEgressRoute( DR_Sub sub, ApiError err ) {
+               
+               String nodePattern = nodeService.getNodePatternAtLocation( sub.getDcaeLocationName(), false );
+               if ( nodePattern != null && nodePattern.length() > 0 ) {
+                       logger.info( "creating egress rule: sub " + sub.getSubId() + " on feed " + sub.getFeedId() + " to " + nodePattern);
+                       prov.makeEgressConnection( sub.getSubId(),  nodePattern);
+                       int rc = prov.doXgressPost(err);
+                       logger.info( "rc=" + rc + " error code=" + err.getCode() );
+                       
+                       if ( rc != 200 ) {
+                               switch( rc ) {
+                               case 403:
+                                       logger.error( "Not authorized for DR egress API");
+                                       err.setCode(500);
+                                       err.setMessage("API deployment/configuration error - contact support");
+                                       err.setFields( "PROV_AUTH_ADDRESSES");
+                                       break;
+                               
+                               default: 
+                                       logger.info( DmaapbcLogMessageEnum.EGRESS_CREATE_ERROR, Integer.toString(rc),  sub.getSubId(), sub.getFeedId(), nodePattern);
+                               }
+                       }
+
+               }
+       }
+       
+       public DR_Sub updateDr_Sub( DR_Sub obj, ApiError apiError ) {
+               logger.debug( "enter updateDR_Subs()");
+
+               DrProvConnection prov = new DrProvConnection();
+               prov.makeSubPutConnection( obj.getSubId() );
+               String resp = prov.doPutDr_Sub( obj, apiError );
+               if ( unit_test.equals( "Yes" ) ) {
+                       resp = simulateResp( obj, "PUT" );
+                       apiError.setCode(200);
+               }
+               logger.debug( "resp=" + resp );
+
+               DR_Sub snew = null;
+
+               if ( resp != null ) {
+                       snew = new DR_Sub( resp );
+                       snew.setDcaeLocationName(obj.getDcaeLocationName());
+                       snew.setLastMod();
+                       dr_subs.put( snew.getSubId(), snew );   
+                       apiError.setCode(200);
+               } else if ( apiError.is2xx()) {
+                       apiError.setCode(400);
+                       apiError.setMessage("unexpected empty response from DR Prov");
+               }
+               
+               return snew;
+       }
+               
+       public void removeDr_Sub( String key, ApiError apiError ) {
+               removeDr_Sub( key, apiError, true );
+               return;
+       }
+       
+       public void removeDr_Sub( String key, ApiError apiError, boolean hitDR ) {
+               logger.debug( "enter removeDR_Subs()");
+               
+               DR_Sub sub = dr_subs.get( key );
+               if ( sub == null ) {
+                       apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                       apiError.setFields( "subId");
+                       apiError.setMessage("subId " + key + " not found");
+               } else {
+                       if ( hitDR ) {
+                               DrProvConnection prov = new DrProvConnection();
+                               prov.makeSubPutConnection( key );
+                               String resp = prov.doDeleteDr_Sub( sub, apiError );
+                               logger.debug( "resp=" + resp );
+                       } else {
+                               apiError.setCode(200);
+                       }
+                       
+                       if ( apiError.is2xx() || unit_test.equals( "Yes" ) ) {
+                               dr_subs.remove(key);
+                       }
+               }
+
+               return;
+       }       
+
+       private String simulateResp( DR_Sub sub, String action ){
+               String server = "subscriber.onap.org";
+               String subid;
+               if ( action.equals( "POST" ) ) { 
+                       RandomInteger ran = new RandomInteger(10000);
+                       subid = Integer.toString( ran.next() );
+               } else if ( action.equals( "PUT" ) ) {
+                       subid = sub.getSubId();
+               } else {
+                       subid = "99";
+               }
+               String ret = String.format("{\"suspend\": false, \"delivery\": {\"url\": \"https://%s/delivery/%s\", \"user\": \"%s\", \"password\": \"%s\", \"use100\":  true}, \"metadataOnly\": false, \"groupid\": \"0\" , \"follow_redirect\": true, ", 
+                       server, subid, sub.getUsername(), sub.getUserpwd());
+               String links = String.format( "\"links\": {\"feed\": \"https://dr-prov/feedlog/%s\", \"self\": \"https://dr-prov/sub/%s\", \"log\": \"https://dr-prov/sublog/%s\" }", 
+                               sub.getFeedId(),
+                               subid,
+                               subid );
+               ret += links + "}";
+               logger.info( "DR_SubService:simulateResp=" + ret);
+
+               return ret;
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DcaeLocationService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DcaeLocationService.java
new file mode 100644 (file)
index 0000000..ad6c993
--- /dev/null
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public class DcaeLocationService {
+
+    private static final String DEFAULT_CENTRAL_LOCATION = "aCentralLocation"; // default value that is obvious to see is wrong
+    private final Map<String, DcaeLocation> dcaeLocations;
+
+    public DcaeLocationService() {
+        this(DatabaseClass.getDcaeLocations());
+    }
+
+    DcaeLocationService(Map<String, DcaeLocation> dcaeLocations) {
+        this.dcaeLocations = dcaeLocations;
+    }
+
+    public List<DcaeLocation> getAllDcaeLocations() {
+        return new ArrayList<>(dcaeLocations.values());
+    }
+
+    public DcaeLocation getDcaeLocation(String name) {
+        return dcaeLocations.get(name);
+    }
+
+    public DcaeLocation addDcaeLocation(DcaeLocation location) {
+        location.setLastMod();
+        location.setStatus(DmaapObject_Status.VALID);
+        dcaeLocations.put(location.getDcaeLocationName(), location);
+        return location;
+    }
+
+    public DcaeLocation updateDcaeLocation(DcaeLocation location) {
+        if (location.getDcaeLocationName().isEmpty()) {
+            return null;
+        }
+        location.setLastMod();
+        dcaeLocations.put(location.getDcaeLocationName(), location);
+        return location;
+    }
+
+    public DcaeLocation removeDcaeLocation(String locationName) {
+        return dcaeLocations.remove(locationName);
+    }
+
+    String getCentralLocation() {
+
+        Optional<DcaeLocation> firstCentralLocation =
+                dcaeLocations.values().stream().filter(DcaeLocation::isCentral).findFirst();
+
+        return firstCentralLocation.isPresent() ? firstCentralLocation.get().getDcaeLocationName() : DEFAULT_CENTRAL_LOCATION;
+    }
+
+    boolean isEdgeLocation(String aName) {
+        return dcaeLocations.get(aName) != null && !dcaeLocations.get(aName).isCentral();
+    }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/DmaapService.java
new file mode 100644 (file)
index 0000000..8789ac4
--- /dev/null
@@ -0,0 +1,310 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import java.util.ArrayList;
+import org.onap.dmaap.dbcapi.aaf.AafService;
+import org.onap.dmaap.dbcapi.aaf.AafServiceFactory;
+import org.onap.dmaap.dbcapi.aaf.DmaapGrant;
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType;
+import org.onap.dmaap.dbcapi.authentication.ApiPerms;
+import org.onap.dmaap.dbcapi.authentication.ApiPolicy;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.model.MR_Client;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.Singleton;
+
+public class DmaapService  extends BaseLoggingClass  {
+
+       
+       private Singleton<Dmaap> dmaapholder = DatabaseClass.getDmaap();
+       private static String noEnvironmentPrefix;
+       
+       
+       String topicFactory; // = "org.openecomp.dcae.dmaap.topicFactory";
+       String topicMgrRole; // = "org.openecomp.dmaapBC.TopicMgr";
+       
+       private boolean multiSite;
+       
+       
+       public DmaapService() {
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               topicFactory = p.getProperty("MR.TopicFactoryNS", "MR.topicFactoryNS.not.set");
+               topicMgrRole = p.getProperty("MR.TopicMgrRole", "MR.TopicMgrRole.not.set" );
+
+               multiSite = "true".equalsIgnoreCase(p.getProperty("MR.multisite", "true"));
+               noEnvironmentPrefix = p.getProperty( "AAF.NoEnvironmentPrefix", "org.onap");
+               
+               logger.info( "DmaapService settings: " + 
+                               " topicFactory=" + topicFactory +
+                               " topicMgrRole=" + topicMgrRole +
+                               
+                               " multisite=" + multiSite +
+                               " noEnvironmentPrefix=" + noEnvironmentPrefix
+                               );
+
+               Dmaap dmaap = dmaapholder.get();
+               logger.info( "DmaapService object values: " +
+                               " dmaapName=" + dmaap.getDmaapName() +
+                               " drProvURL=" + dmaap.getDrProvUrl() +
+                               " version="+ dmaap.getVersion()
+                               );
+               
+       }
+       
+       public Dmaap getDmaap() {
+               logger.info( "entering getDmaap()" );
+               return(dmaapholder.get());
+       }
+       
+       public Dmaap addDmaap( Dmaap nd ) {
+               
+               logger.info( "entering addDmaap()" );
+               Dmaap dmaap = dmaapholder.get();
+               if ( dmaap.getVersion().equals( "0")) {
+
+                       nd.setLastMod();
+                       dmaapholder.update(nd);
+                       
+                       AafService aaf = new AafServiceFactory().initAafService(ServiceType.AAF_Admin);
+                       ApiPolicy apiPolicy = new ApiPolicy();
+                       if ( apiPolicy.isPermissionClassSet() ) {
+                               ApiPerms p = new ApiPerms();
+                               p.setEnvMap();
+                       }
+                       boolean anythingWrong = false;
+                       
+                       if ( multiSite ) {
+                               anythingWrong = setTopicMgtPerms(  nd,  aaf ) || createMmaTopic();
+                       }
+                                       
+                       if ( anythingWrong ) {
+                               dmaap.setStatus(DmaapObject_Status.INVALID); 
+                       }
+                       else {
+                               dmaap.setStatus(DmaapObject_Status.VALID);  
+                       }
+                       dmaap.setLastMod();
+                       dmaapholder.update(dmaap);
+
+                       return dmaap;
+               
+               }
+               else { 
+                       return dmaap;
+               }
+       }
+       
+       public Dmaap updateDmaap( Dmaap nd ) {
+               logger.info( "entering updateDmaap()" );
+               
+               boolean anythingWrong = false;
+
+               Dmaap dmaap = dmaapholder.get();
+               
+               // some triggers for when we attempt to reprovision perms and MMA topic:
+               // - if the DMaaP Name changes
+               // - if the version is 0  (this is a handy test to force this processing by updating the DB)
+               // - if the object is invalid, reprocessing might fix it.
+               if ( ! dmaap.isStatusValid()  || ! nd.getDmaapName().equals(dmaap.getDmaapName()) || dmaap.getVersion().equals( "0") ) {
+                       nd.setLastMod();
+                       dmaapholder.update(nd);  //need to set this so the following perms will pick up any new vals.
+                       //dcaeTopicNs = dmaapholder.get().getTopicNsRoot();
+                       ApiPolicy apiPolicy = new ApiPolicy();
+                       if ( apiPolicy.isPermissionClassSet()) {
+                               ApiPerms p = new ApiPerms();
+                               p.setEnvMap();
+                       }
+                       AafService aaf = new AafServiceFactory().initAafService(ServiceType.AAF_Admin);
+                       if ( multiSite ) {
+                               anythingWrong = setTopicMgtPerms(  nd,  aaf ) || createMmaTopic();
+                       }
+               }
+                                       
+               if ( anythingWrong ) {
+                       nd.setStatus(DmaapObject_Status.INVALID); 
+               }
+               else {
+                       nd.setStatus(DmaapObject_Status.VALID);  
+               }
+               nd.setLastMod();
+               dmaapholder.update(nd);  // may need to update status...
+               return(dmaapholder.get());
+               
+       }
+       
+       public String getTopicPerm(){
+               Dmaap dmaap = dmaapholder.get();
+               return getTopicPerm( dmaap.getDmaapName() );
+       }
+       public String getTopicPerm( String val ) {
+               Dmaap dmaap = dmaapholder.get();
+               String nsRoot = dmaap.getTopicNsRoot();
+               if ( nsRoot == null ) { return null; }
+               
+               String t;
+               // in ONAP Casablanca, we assume no distinction of environments reflected in topic namespace
+               if ( nsRoot.startsWith(noEnvironmentPrefix) ) {
+                       t = nsRoot +  ".mr.topic";
+               } else {
+                       t = nsRoot + "." + val + ".mr.topic";
+               }
+               return t;
+       }
+       
+       public String getBridgeAdminFqtn(){
+               Dmaap dmaap = dmaapholder.get();
+               String topic = dmaap.getBridgeAdminTopic();
+               
+               // check if this is already an fqtn (contains a dot)
+               // otherwise build it
+               if ( topic.indexOf('.') < 0 ) {
+                       topic = dmaap.getTopicNsRoot() + "." + dmaap.getDmaapName() + "." + dmaap.getBridgeAdminTopic();
+               }
+               return( topic );
+       }
+
+       private boolean setTopicMgtPerms( Dmaap nd, AafService aaf ){
+               String[] actions = { "create", "destroy" };
+               String instance = ":" + nd.getTopicNsRoot() + "." + nd.getDmaapName() + ".mr.topic:" + nd.getTopicNsRoot() + "." + nd.getDmaapName();
+               
+               for( String action : actions ) {
+
+                       DmaapPerm perm = new DmaapPerm( topicFactory, instance, action );
+               
+                       int rc = aaf.addPerm( perm );
+                       if ( rc != 201 &&  rc != 409 ) {
+                               logger.error( "unable to add perm for "+ topicFactory + "|" + instance + "|" + action );
+                               return true;
+                       }
+
+                       DmaapGrant grant = new DmaapGrant( perm, topicMgrRole );
+                       rc = aaf.addGrant( grant );
+                       if ( rc != 201 && rc != 409 ) {
+                               logger.error( "unable to grant to " + topicMgrRole + " perm for "+ topicFactory + "|" + instance + "|" + action );
+                               return true;
+                       }
+               }
+               
+               String t = nd.getTopicNsRoot() +"." + nd.getDmaapName() + ".mr.topic";
+               String[] s = { "view", "pub", "sub" };
+               actions = s;
+               instance = "*";
+               
+               for( String action : actions ) {
+
+                       DmaapPerm perm = new DmaapPerm( t, instance, action );
+               
+                       int rc = aaf.addPerm( perm );
+                       if ( rc != 201 &&  rc != 409 ) {
+                               errorLogger.error( DmaapbcLogMessageEnum.AAF_UNEXPECTED_RESPONSE, Integer.toString(rc), "add perm", t + "|" + instance + "|" + action );
+                               return true;
+                       }
+
+                       DmaapGrant grant = new DmaapGrant( perm, topicMgrRole );
+                       rc = aaf.addGrant( grant );
+                       if ( rc != 201 && rc != 409 ) {
+                               errorLogger.error( DmaapbcLogMessageEnum.AAF_UNEXPECTED_RESPONSE, Integer.toString(rc), "grant to " + topicMgrRole + " perm ", topicFactory + "|" + instance + "|" + action );
+                               return true;
+                       }
+                               
+               }
+               return false;
+       }
+
+       public boolean testCreateMmaTopic() {
+
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               String unit_test = p.getProperty( "UnitTest", "No" );
+               if ( unit_test.equals( "Yes" ) ) {
+                       return createMmaTopic();
+               }
+               return false;
+       }
+       
+       // create the special topic for MMA provisioning.
+       // return true indicating a problem in topic creation, 
+       // else false means it was ok  (created or previously existed)
+       private boolean createMmaTopic() {
+               boolean rc = true;
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               Dmaap dmaap = dmaapholder.get();
+               
+               ArrayList<MR_Client> clients = new ArrayList<MR_Client>();
+               String[] actions = { "pub", "sub", "view" };
+               String centralMR = new DcaeLocationService().getCentralLocation();
+               if ( centralMR == null ) {
+                       return rc;
+               }
+               logger.info( "Location for " + dmaap.getBridgeAdminTopic() + " is " + centralMR );
+       
+               // first client is the Role used by Bus Controller to send messages to MMA
+               String provRole = p.getProperty("MM.ProvRole");
+               MR_Client nClient = new MR_Client();
+               nClient.setAction(actions);
+               nClient.setClientRole(provRole);
+               nClient.setDcaeLocationName(centralMR);
+               clients.add( nClient );
+       
+               // second client is the Role used by MMA to listen to messages from Bus Controller
+               String agentRole = p.getProperty("MM.AgentRole");
+               nClient = new MR_Client();
+               nClient.setAction(actions);
+               nClient.setClientRole(agentRole);
+               nClient.setDcaeLocationName(centralMR);
+               clients.add( nClient );
+       
+               // initialize Topic
+               Topic mmaTopic = new Topic().init();
+               mmaTopic.setTopicName(dmaap.getBridgeAdminTopic());
+               mmaTopic.setClients(clients);
+               mmaTopic.setOwner("BusController");
+               mmaTopic.setTopicDescription("topic reserved for MirrorMaker Administration");
+               mmaTopic.setTnxEnabled("false");
+               mmaTopic.setPartitionCount("1");  // a single partition should guarantee message order
+               
+               
+               ApiError err = new ApiError();
+               TopicService svc = new TopicService();
+               try {
+                       @SuppressWarnings("unused")
+                       Topic nTopic = svc.addTopic(mmaTopic, err, true);
+                       if ( err.is2xx() || err.getCode() == 409 ) {
+                               return false;
+                       }
+               } catch ( Exception e) {
+                       errorLogger.error( DmaapbcLogMessageEnum.UNEXPECTED_CONDITION, " while adding Topic: " + e.getMessage());
+               }
+               errorLogger.error( DmaapbcLogMessageEnum.TOPIC_CREATE_ERROR,  dmaap.getBridgeAdminTopic(), err.getFields(), err.getFields(), err.getMessage());
+               
+               return rc;
+               
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/FeedService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/FeedService.java
new file mode 100644 (file)
index 0000000..19b0267
--- /dev/null
@@ -0,0 +1,572 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.Response.Status;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.onap.dmaap.dbcapi.client.DrProvConnection;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Pub;
+import org.onap.dmaap.dbcapi.model.DR_Sub;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.model.Feed;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.RandomInteger;
+
+public class FeedService  extends BaseLoggingClass {
+       
+       private Map<String, Feed> feeds = DatabaseClass.getFeeds();
+       private Map<String, DR_Sub> dr_subs = DatabaseClass.getDr_subs();
+       private DR_PubService pubService = new DR_PubService();
+       private DR_SubService subService = new DR_SubService();
+       private DcaeLocationService dcaeLocations = new DcaeLocationService();
+       private String deleteHandling;
+       private String unit_test;
+       
+       public FeedService() {
+               logger.info( "new FeedService");
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               deleteHandling = p.getProperty("Feed.deleteHandling", "DeleteOnDR");
+               unit_test = p.getProperty( "UnitTest", "No" );
+
+       }
+       
+       public Map<String, Feed> getFeeds() {                   
+               return feeds;
+       }
+       
+       private void getSubObjects( Feed f ) {
+               ArrayList<DR_Pub> pubs = pubService.getDr_PubsByFeedId( f.getFeedId() );
+               f.setPubs(pubs);
+               ArrayList<DR_Sub> subs = subService.getDr_SubsByFeedId( f.getFeedId() );
+               f.setSubs(subs);        
+       }
+       
+       public List<Feed> getAllFeeds(){
+               return getAllFeeds(null, null, null);
+       }
+               
+       public List<Feed> getAllFeeds( String name, String ver, String match ) {
+               logger.info( "getAllFeeds: name=" + name + " ver=" + ver + " match=" + match);
+               ArrayList<Feed> fatFeeds = new ArrayList<Feed>();
+               for( Feed f:  feeds.values() ) {
+                       boolean keep = true;
+                       if ( name != null ) {
+                               if ( match != null && "startsWith".equals(match) ) {
+                                       if ( ! f.getFeedName().startsWith( name ) ) {
+                                               logger.info( "getAllFeeds: feedName=" + f.getFeedName() + " doesn't start with=" + name);
+                                               keep = false;
+                                       }
+                               } else if ( match != null && match.equals("contains") ) {
+                                       if ( ! f.getFeedName().contains( name ) ) {
+                                               logger.info( "getAllFeeds: feedName=" + f.getFeedName() + " doesn't contain=" + name);
+                                               keep = false;
+                                       }
+                               } else {
+                                       if ( ! f.getFeedName().equals( name ) ) {
+                                               logger.info( "getAllFeeds: feedName=" + f.getFeedName() + " doesn't equal=" + name);
+                                               keep = false;
+                                       }
+                               }
+
+                       }
+                       if ( keep && ver != null ) {
+                               if ( ! f.getFeedVersion().equals(ver)) {
+                                       logger.info( "getAllFeeds: feedVersion=" + f.getFeedName() + " doesn't match " + ver);
+                                       keep = false;
+                               } else {
+                                       logger.info( "getAllFeeds: feedVersion=" + f.getFeedName() + " matches " + ver);
+                               }
+                       }
+                                       
+                       if (keep){
+                               getSubObjects(f);
+                               fatFeeds.add(f);
+                       }
+               }
+               return fatFeeds;
+       }
+       
+       
+       private Feed _getFeed( String key, ApiError err, boolean flag ) {
+               Feed f = feeds.get( key );
+               if ( f != null && ( flag || f.getStatus() != DmaapObject_Status.DELETED ) ) {
+                       getSubObjects( f );
+               } else {
+                       err.setCode(Status.NOT_FOUND.getStatusCode());
+                       err.setMessage("feed not found");
+                       err.setFields("feedId=" + key );
+                       return null;
+               }
+               err.setCode(200);
+               return f;
+       }
+       public Feed getFeed( String key, ApiError err ) {
+               return _getFeed( key, err, false );
+       }
+       public Feed getFeedPure( String key, ApiError err ) {
+               return _getFeed( key, err, true );
+       }
+       
+       public Feed getFeedByName( String name, String ver, ApiError err ) {
+               for( Feed f:  feeds.values() ) {
+                       if ( f.getFeedName().equals( name ) && f.getFeedVersion().equals(ver) ) {
+                               getSubObjects(f);
+                               return f;
+                       }
+       
+               }
+               err.setCode(Status.NOT_FOUND.getStatusCode());
+               err.setMessage("feed not found");
+               err.setFields("feedName=" + name + " and ver=" + ver );
+               return null;
+       
+       }
+
+       private boolean savePubs( Feed f ) {
+               return savePubs( f, f );
+       }
+       // need to save the Pub objects independently and copy pubId from original request
+       private boolean savePubs( Feed fnew, Feed req ) {
+               // save any pubs
+               DR_PubService pubSvc = new DR_PubService();
+               ArrayList<DR_Pub> reqPubs = req.getPubs();
+               ArrayList<DR_Pub> newPubs = fnew.getPubs();
+               
+
+               
+               int nSize = newPubs.size();
+               int rSize = reqPubs.size();
+               logger.info( "reqPubs size=" + rSize + " newPubs size=" + nSize );
+               if ( nSize != rSize ) {
+                       errorLogger.error( "Resulting set of publishers do not match requested set of publishers " + nSize + " vs " + rSize );
+                       fnew.setStatus( DmaapObject_Status.INVALID);
+                       return false;
+               }
+               // NOTE: when i > 1 newPubs are in reverse order from reqPubs
+               for( int i = 0; i < reqPubs.size(); i++ ) {
+                       DR_Pub reqPub = reqPubs.get(i); 
+                       ApiError err = new ApiError();
+                       if ( pubSvc.getDr_Pub( reqPub.getPubId(), err ) == null ) {
+                               DR_Pub newPub = newPubs.get(nSize - i - 1);
+                               reqPub.setPubId(newPub.getPubId());
+                               reqPub.setFeedId(newPub.getFeedId());
+                               reqPub.setStatus(DmaapObject_Status.VALID);
+                               if ( reqPub.getDcaeLocationName() == null ) {
+                                       reqPub.setDcaeLocationName("notSpecified");
+                               }
+                               pubSvc.addDr_Pub( reqPub );
+                       }
+                       
+               }
+               
+               fnew.setPubs(reqPubs);
+               fnew.setStatus(DmaapObject_Status.VALID);
+               return true;
+
+       }
+       
+       private boolean saveSubs( Feed f ) {
+               return saveSubs( f, f );
+       }
+       // need to save the Sub objects independently
+       private boolean saveSubs( Feed fnew, Feed req ) {       
+               ArrayList<DR_Sub> subs = req.getSubs();
+               if ( subs == null || subs.size() == 0 ) {
+                       logger.info( "No subs specified");
+               } else {
+                       DR_SubService subSvc = new DR_SubService( fnew.getSubscribeURL() );
+                       ApiError err = new ApiError();
+                       for( int i = 0; i <  subs.size(); i++ ) {
+                               DR_Sub sub = subs.get(i);
+                               if ( subSvc.getDr_Sub( sub.getSubId(), err) == null ) {
+                                       subs.set( i,  subSvc.addDr_Sub(sub, err));
+                                       if ( ! err.is2xx())  {
+                                               logger.error( "i=" + i + " url=" + sub.getDeliveryURL() + " err=" + err.getCode() );
+                                               return false;
+                                       }
+                               }
+                               
+                       }
+                       fnew.setSubs(subs);
+               }
+
+
+               fnew.setStatus(DmaapObject_Status.VALID);
+               return true;
+
+       }
+
+       public  Feed addFeed( Feed req, ApiError err ) {
+
+               // at least 1 pub is required by DR, so create a default pub if none is specified
+               if ( req.getPubs().size() == 0 ) {
+                       logger.info( "No pubs specified - creating tmp pub");
+                       ArrayList<DR_Pub> pubs = new ArrayList<DR_Pub>();
+                       pubs.add( new DR_Pub( dcaeLocations.getCentralLocation())
+                                                               .setRandomUserName()
+                                                               .setRandomPassword());
+                       req.setPubs(pubs);
+               } 
+               
+
+               DrProvConnection prov = new DrProvConnection();
+               prov.makeFeedConnection();      
+               String resp = prov.doPostFeed( req, err );
+               if ( unit_test.equals( "Yes" ) ) {
+                       // assume resp is null, so need to simulate it
+                       resp = simulateResp( req, "POST" );
+               }
+               logger.info( "resp=" + resp );
+               if ( resp == null ) {
+                       switch( err.getCode() ) {
+                       case 400: 
+                               err.setFields( "feedName=" + req.getFeedName() + " + feedVersion=" + req.getFeedVersion() );
+                               break;
+                       case 403:
+                               err.setCode(500);
+                               err.setMessage("API deployment/configuration error - contact support");
+                               err.setFields( "PROV_AUTH_ADDRESSES");
+                               logger.error( "Prov response: 403. " + err.getMessage() + " regarding " + err.getFields() );
+                               break;
+                       default:
+                               err.setCode(500);
+                               err.setMessage( "Unexpected response from DR backend" );
+                               err.setFields("response");
+                       }
+                       return null;
+
+               }
+
+
+               Feed fnew = new Feed( resp );
+               logger.info( "fnew status is:" + fnew.getStatus() );
+               if ( ! fnew.isStatusValid()) {          
+                       err.setCode(500);
+                       err.setMessage( "Unexpected response from DR backend" );
+                       err.setFields("response");              
+                       return null;
+               }
+               
+               //saveChildren( fnew, req );
+               if ( ! savePubs( fnew, req ) || ! saveSubs( fnew, req ) ) {
+                       err.setCode(Status.BAD_REQUEST.getStatusCode());
+                       err.setMessage("Unable to save Pub or Sub objects");
+                       return null;
+               }
+               fnew.setFormatUuid(req.getFormatUuid());
+               fnew.setLastMod();
+               feeds.put( fnew.getFeedId(), fnew );
+               return fnew;
+       }
+               
+       public Feed updateFeed( Feed req, ApiError err ) {
+       
+               // at least 1 pub is required by DR, so create a default pub if none is specified
+               if ( req.getPubs().size() == 0 ) {
+                       logger.info( "No pubs specified - creating tmp pub");
+                       ArrayList<DR_Pub> pubs = new ArrayList<DR_Pub>();
+                       pubs.add( new DR_Pub( dcaeLocations.getCentralLocation())
+                                                               .setRandomUserName()
+                                                               .setRandomPassword());
+                       req.setPubs(pubs);
+               } 
+               
+               DrProvConnection prov = new DrProvConnection();
+               prov.makeFeedConnection( req.getFeedId() );
+               String resp = prov.doPutFeed( req, err );
+               if ( unit_test.equals( "Yes" ) ) {
+                       // assume resp is null, so need to simulate it
+                       resp = simulateResp( req, "PUT" );
+                       err.setCode(200);
+               }
+               logger.info( "resp=" + resp );
+               if ( resp == null ) {
+                       switch( err.getCode() ) {
+                       case 400: 
+                               err.setFields( "feedName=" + req.getFeedName() + " + feedVersion=" + req.getFeedVersion() );
+                               break;
+                       case 403:
+                               err.setCode(500);
+                               err.setMessage("API deployment/configuration error - contact support");
+                               err.setFields( "PROV_AUTH_ADDRESSES");
+                               break;
+                       default:
+                               err.setCode(500);
+                               err.setMessage( "Unexpected response from DR backend" );
+                               err.setFields("response");
+                       }
+                       return null;
+               }
+
+
+               Feed fnew = new Feed( resp );
+               logger.info( "fnew status is:" + fnew.getStatus() );
+               if ( ! fnew.isStatusValid()) {          
+                       err.setCode(500);
+                       err.setMessage( "Unexpected response from DR backend" );
+                       err.setFields("response");              
+                       return null;
+               }
+
+               if ( ! savePubs( fnew, req ) || ! saveSubs( fnew, req ) ) {
+                       err.setCode(Status.BAD_REQUEST.getStatusCode());
+                       err.setMessage("Unable to save Pub or Sub objects");
+                       return null;
+               }
+               fnew.setFormatUuid(req.getFormatUuid());
+               fnew.setLastMod();
+               feeds.put( fnew.getFeedId(), fnew );
+               return fnew;
+       }
+       
+       
+       //
+       // DR does not actually delete a feed, so we provide two behaviors:
+       // 1) clean up the feed by removing all subs and pubs, mark it here as DELETED.
+       //    then client can add it back if desired.
+       // 2) Call the DR Delete function.  Feed with the same name and version can never be added again
+       //
+       public Feed removeFeed( Feed req, ApiError err ) {
+               return removeFeed( req, err, true );
+       }
+       
+       public Feed removeFeed( Feed req, ApiError err, boolean hitDR ) {
+               
+               // strip pubs and subs from feed first no matter what
+               ArrayList<DR_Pub> pubs = pubService.getDr_PubsByFeedId( req.getFeedId() );
+               for( DR_Pub pub: pubs ) {
+                       pubService.removeDr_Pub(pub.getPubId(), err, hitDR);
+                       if ( ! err.is2xx()) {
+                               return req;
+                       }
+               }
+               ArrayList<DR_Sub> subs = subService.getDr_SubsByFeedId( req.getFeedId() );
+               for ( DR_Sub sub: subs ) {
+                       subService.removeDr_Sub(sub.getSubId(), err, hitDR);
+                       if ( ! err.is2xx()) {
+                               return req;
+                       }
+               }
+               
+               if ( ! hitDR ) {
+                       return feeds.remove(req.getFeedId());   
+               }
+       
+               if ( deleteHandling.equalsIgnoreCase("DeleteOnDR")) {
+                       DrProvConnection prov = new DrProvConnection();
+                       prov.makeFeedConnection( req.getFeedId() );
+                       String resp = prov.doDeleteFeed( req, err );
+                       if ( unit_test.equals( "Yes" ) ) {
+                               // assume resp is null, so need to simulate it
+                               resp = simulateDelResp( req );
+                       }
+                       logger.info( "resp=" + resp );
+                       if ( resp == null ) {
+                               switch( err.getCode() ) {
+                               case 400: 
+                                       err.setFields( "feedName=" + req.getFeedName() + " + feedVersion=" + req.getFeedVersion() );
+                                       break;
+                               case 403:
+                                       err.setCode(500);
+                                       err.setMessage("API deployment/configuration error - contact support");
+                                       err.setFields( "PROV_AUTH_ADDRESSES");
+                                       break;
+                               default:
+                                       err.setCode(500);
+                                       err.setMessage( "Unexpected response from DR backend" );
+                                       err.setFields("response");
+                               }
+                               return req;  // return back the requested feed - implies it wasn't removed
+                       }
+                       return feeds.remove(req.getFeedId());
+               } else {
+               
+                       logger.info( "Disable pubs for deleted feed - creating tmp pub");
+                       ArrayList<DR_Pub> tmppub = new ArrayList<DR_Pub>();
+                       tmppub.add( new DR_Pub( dcaeLocations.getCentralLocation())
+                                                               .setRandomUserName()
+                                                               .setRandomPassword());
+                       req.setPubs(tmppub);
+                       req.setSubs(null);
+                       Feed fnew = updateFeed( req, err );
+                       if ( ! err.is2xx()) {
+                               return req;
+                       }
+                       fnew.setStatus(DmaapObject_Status.DELETED);
+                       feeds.put( fnew.getFeedId(), fnew );
+                       return null;    
+               }
+
+               
+       }       
+       
+       
+       /*
+        * sync will retrieve current config from DR and add it to the DB
+        * when hard = true, then first git rid of current DR provisioning data (from the DB)
+        */
+       public void sync( boolean hard, ApiError err ) {
+       
+               if ( hard ) {
+                       
+                       ArrayList<Feed> flist = new ArrayList<Feed>(this.getAllFeeds());
+                       for ( Iterator<Feed> it = flist.iterator(); it.hasNext(); ) {
+                               Feed f = it.next();
+       
+                               @SuppressWarnings("unused")
+                               Feed old = removeFeed( f, err, false );
+                               if (! err.is2xx()) {
+                                       return;
+                               }
+                       }
+               }
+               
+               DrProvConnection prov = new DrProvConnection();
+               prov.makeDumpConnection();
+               String resp = prov.doGetDump( err );
+               if (! err.is2xx()) {
+                       return;
+               }
+               logger.debug("sync: resp from DR is: " + resp);
+               
+               JSONParser parser = new JSONParser();
+               JSONObject jsonObj;
+               try {
+                       jsonObj = (JSONObject) parser.parse( resp );
+               } catch ( ParseException pe ) {
+                       logger.error( "Error parsing provisioning data: " + resp );
+                       err.setCode(500);
+                       return;
+               }
+               
+               int i;
+
+               JSONArray feedsArray = (JSONArray) jsonObj.get( "feeds");
+               for( i = 0; i < feedsArray.size(); i++ ) {
+                       JSONObject entry = (JSONObject) feedsArray.get(i);
+                       Feed fnew = new Feed( entry.toJSONString() );
+                       
+                       logger.info( "fnew status is:" + fnew.getStatus() );
+                       if ( ! fnew.isStatusValid()) {          
+                               err.setCode(500);
+                               err.setMessage( "Unexpected response from DR backend" );
+                               err.setFields("response");              
+                               return;
+                       }
+                       
+                               if ( ! savePubs( fnew )  ) {
+                               err.setCode(Status.BAD_REQUEST.getStatusCode());
+                               err.setMessage("Unable to save Pub or Sub objects");
+                               return; 
+                       }
+                       fnew.setFormatUuid(fnew.getFormatUuid());
+                       fnew.setLastMod();
+                       feeds.put( fnew.getFeedId(), fnew );
+
+               }
+               
+               JSONArray subArray = (JSONArray) jsonObj.get( "subscriptions");
+               for( i = 0; i < subArray.size(); i++ ) {
+                       JSONObject entry = (JSONObject) subArray.get(i);
+                       DR_Sub snew = new DR_Sub( entry.toJSONString() );
+                       
+                       logger.info( "snew status is:" + snew.getStatus() );
+                       if ( ! snew.isStatusValid()) {          
+                               err.setCode(500);
+                               err.setMessage( "Unexpected response from DR backend" );
+                               err.setFields("response");              
+                               return;
+                       }
+                       
+                       dr_subs.put( snew.getSubId(), snew );
+
+               }
+               err.setCode(200);
+               return;
+               
+       }
+
+       private String simulateResp( Feed f, String action ){
+               String server = "localhost";
+               String feedid;
+               if ( action.equals( "POST" ) ) { 
+                       RandomInteger ran = new RandomInteger(10000);
+                       feedid = Integer.toString( ran.next() );
+               } else if ( action.equals( "PUT" ) ) {
+                       feedid = f.getFeedId();
+               } else {
+                       feedid = "99";
+               }
+               String ret = String.format( 
+"{\"suspend\":false,\"groupid\":0,\"description\":\"%s\",\"version\":\"1.0\",\"authorization\":",
+                       f.getFeedDescription() );
+
+               String endpoints = "{\"endpoint_addrs\":[],\"classification\":\"unclassified\",\"endpoint_ids\":[";
+               String sep = "";
+               for( DR_Pub pub: f.getPubs()) {
+                       endpoints +=  String.format( "%s{\"password\":\"%s\",\"id\":\"%s\"}", 
+                                       sep, pub.getUserpwd(), pub.getUsername() );
+                       sep = ",";
+                       
+               }
+               endpoints += "]},";
+               ret += endpoints;
+               
+               ret += String.format(
+               "\"name\":\"%s\",\"business_description\":\"\",\"publisher\":\"sim\",\"links\":{\"subscribe\":\"https://%s/subscribe/%s\",\"log\":\"https://%s/feedlog/%s\",\"publish\":\"https://%s/publish/%s\",\"self\":\"https://%s/feed/%s\"}}",
+
+                       f.getFeedName(),
+                       server, feedid,
+                       server, feedid,
+                       server, feedid,
+                       server, feedid
+                               );
+               logger.info( "simulateResp ret=" + ret );
+               return ret;
+       }
+       private String simulateDelResp( Feed f ){
+               String server = "localhost";
+               String feedid = f.getFeedId();
+               String ret = String.format( 
+"{\"suspend\":true,\"groupid\":0,\"description\":\"%s\",\"version\":\"1.0\",\"authorization\":{\"endpoint_addrs\":[],\"classification\":\"unclassified\",\"endpoint_ids\":[{\"password\":\"topSecret123\",\"id\":\"sim\"}]},\"name\":\"%s\",\"business_description\":\"\",\"publisher\":\"sim\",\"links\":{\"subscribe\":\"https://%s/subscribe/%s\",\"log\":\"https://%s/feedlog/%s\",\"publish\":\"https://%s/publish/%s\",\"self\":\"https://%s/feed/%s\"}}",
+               f.getFeedDescription(),
+               f.getFeedName(),
+               server, feedid,
+               server, feedid,
+               server, feedid,
+               server, feedid
+
+               );
+               return ret;
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClientService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClientService.java
new file mode 100644 (file)
index 0000000..bcf5408
--- /dev/null
@@ -0,0 +1,234 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType;
+import org.onap.dmaap.dbcapi.aaf.AafServiceFactory;
+import org.onap.dmaap.dbcapi.client.MrProvConnection;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.model.MR_Client;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+import javax.ws.rs.core.Response.Status;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class MR_ClientService extends BaseLoggingClass {
+
+    private static final String MR_CLIENT_ID = "mrClientId";
+    private int deleteLevel;
+    private Map<String, MR_Client> mr_clients = DatabaseClass.getMr_clients();
+    private Map<String, MR_Cluster> clusters = DatabaseClass.getMr_clusters();
+    private Map<String, DcaeLocation> locations = DatabaseClass.getDcaeLocations();
+    private DmaapService dmaap = new DmaapService();
+    private AafPermissionService aafPermissionService =
+            new AafPermissionService(new AafServiceFactory().initAafService(ServiceType.AAF_TopicMgr), dmaap);
+    private String centralCname;
+
+    public MR_ClientService() {
+        DmaapConfig p = (DmaapConfig) DmaapConfig.getConfig();
+
+        centralCname = p.getProperty("MR.CentralCname", "MRcname.not.set");
+        deleteLevel = Integer.valueOf(p.getProperty("MR.ClientDeleteLevel", "0"));
+    }
+
+    public List<MR_Client> getAllMr_Clients() {
+        return new ArrayList<>(mr_clients.values());
+    }
+
+    List<MR_Client> getAllMrClients(String fqtn) {
+        ArrayList<MR_Client> results = new ArrayList<>();
+        for (Map.Entry<String, MR_Client> entry : mr_clients.entrySet()) {
+            MR_Client client = entry.getValue();
+            if (fqtn.equals(client.getFqtn())) {
+                results.add(client);
+            }
+        }
+        return results;
+    }
+
+    List<MR_Client> getClientsByLocation(String location) {
+        List<MR_Client> results = new ArrayList<>();
+        for (Map.Entry<String, MR_Client> entry : mr_clients.entrySet()) {
+            MR_Client client = entry.getValue();
+            if (location.equals(client.getDcaeLocationName())) {
+                results.add(client);
+            }
+        }
+        return results;
+    }
+
+    public MR_Client getMr_Client(String key, ApiError apiError) {
+        MR_Client c = mr_clients.get(key);
+        if (c == null) {
+            apiError.setCode(Status.NOT_FOUND.getStatusCode());
+            apiError.setFields(MR_CLIENT_ID);
+            apiError.setMessage(MR_CLIENT_ID + " " + key + " not found");
+        } else {
+            apiError.setCode(200);
+        }
+        return c;
+    }
+
+    public MR_Client addMr_Client(MR_Client client, Topic topic, ApiError err) {
+        if (client.getDcaeLocationName().isEmpty()) {
+            logger.info("Client  dcaeLocation that doesn't exist or not specified");
+            return null;
+        }
+        // original style: clients specified Role.  This has precedence for backwards
+        //                 compatibility.
+        // ONAP style: clients specify Identity to be assigned to generated Role
+        String role = client.getClientRole();
+        if (role != null) {
+            updateApiError(err, aafPermissionService.grantClientRolePerms(client));
+        } else if (client.hasClientIdentity()) {
+            if (client.isSubscriber()) {
+                role = topic.getSubscriberRole();
+                updateApiError(err, aafPermissionService.assignClientToRole(client, role));
+            }
+            if (client.isPublisher()) {
+                role = topic.getPublisherRole();
+                updateApiError(err, aafPermissionService.assignClientToRole(client, role));
+            }
+        }
+        if (!client.isStatusValid()) {
+            return null;
+        }
+        String centralFqdn = null;
+        DcaeLocation candidate = locations.get(client.getDcaeLocationName());
+
+        MR_Cluster cluster = clusters.get(client.getDcaeLocationName());
+        if (cluster != null && candidate != null) {
+            if (candidate.isCentral() && !topic.getReplicationCase().involvesFQDN()) {
+                centralFqdn = centralCname;
+            }
+            client.setTopicURL(cluster.genTopicURL(centralFqdn, client.getFqtn()));
+            if (centralFqdn == null) {
+                client.setStatus(addTopicToCluster(cluster, topic, err));
+                if (!err.is2xx() && err.getCode() != 409) {
+                    topic.setFqtn(err.getMessage());
+                    return null;
+                }
+
+            } else {
+                MR_ClusterService clusters = new MR_ClusterService();
+                //  MM should only exist for edge-to-central
+                //  we use a cname for the central target (default resiliency with no replicationGroup set)
+                // but still need to provision topics on all central MRs
+                for (MR_Cluster central : clusters.getCentralClusters()) {
+                    client.setStatus(addTopicToCluster(central, topic, err));
+                    if (!err.is2xx() && err.getCode() != 409) {
+                        topic.setFqtn(err.getMessage());
+                        return null;
+                    }
+                }
+            }
+
+        } else {
+            logger.warn("Client references a dcaeLocation that doesn't exist:" + client.getDcaeLocationName());
+            client.setStatus(DmaapObject_Status.STAGED);
+        }
+
+        mr_clients.put(client.getMrClientId(), client);
+
+        err.setCode(200);
+
+        return client;
+    }
+
+    private DmaapObject_Status addTopicToCluster(MR_Cluster cluster, Topic topic, ApiError err) {
+
+        MrProvConnection prov = new MrProvConnection();
+        logger.info("POST topic " + topic.getFqtn() + " to cluster " + cluster.getFqdn() + " in loc " + cluster.getDcaeLocationName());
+        if (prov.makeTopicConnection(cluster)) {
+            prov.doPostTopic(topic, err);
+            logger.info("response code: " + err.getCode());
+            if (err.is2xx() || err.getCode() == 409) {
+                return DmaapObject_Status.VALID;
+            }
+        }
+        return DmaapObject_Status.INVALID;
+    }
+
+    public MR_Client updateMr_Client(MR_Client client, ApiError apiError) {
+        MR_Client c = mr_clients.get(client.getMrClientId());
+        if (c == null) {
+            apiError.setCode(Status.NOT_FOUND.getStatusCode());
+            apiError.setFields(MR_CLIENT_ID);
+            apiError.setMessage("mrClientId " + client.getMrClientId() + " not found");
+        } else {
+            apiError.setCode(200);
+        }
+        mr_clients.put(client.getMrClientId(), client);
+        return client;
+    }
+
+    public void removeMr_Client(String key, boolean updateTopicView, ApiError apiError) {
+        MR_Client client = mr_clients.get(key);
+        if (client == null) {
+            apiError.setCode(Status.NOT_FOUND.getStatusCode());
+            apiError.setFields(MR_CLIENT_ID);
+            apiError.setMessage("mrClientId " + key + " not found");
+            return;
+        } else {
+            apiError.setCode(200);
+        }
+
+        if (updateTopicView) {
+
+            TopicService topics = new TopicService();
+
+            Topic t = topics.getTopic(client.getFqtn(), apiError);
+            if (t != null) {
+                List<MR_Client> tc = t.getClients();
+                for (MR_Client c : tc) {
+                    if (c.getMrClientId().equals(client.getMrClientId())) {
+                        tc.remove(c);
+                        break;
+                    }
+                }
+                t.setClients(tc);
+                topics.updateTopic(t, apiError);
+            }
+
+        }
+
+        // remove from DB
+        if (deleteLevel >= 1) {
+            mr_clients.remove(key);
+        }
+    }
+
+    private void updateApiError(ApiError err, ApiError permissionServiceError) {
+        err.setCode(permissionServiceError.getCode());
+        err.setMessage(permissionServiceError.getMessage());
+        err.setFields(permissionServiceError.getFields());
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClusterService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MR_ClusterService.java
new file mode 100644 (file)
index 0000000..db6389e
--- /dev/null
@@ -0,0 +1,205 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.onap.dmaap.dbcapi.client.MrProvConnection;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.service.DcaeLocationService;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+public class MR_ClusterService extends BaseLoggingClass {
+
+       private Map<String, MR_Cluster> mr_clusters = DatabaseClass.getMr_clusters();
+       private boolean multiSite;
+       
+       public MR_ClusterService() {
+               logger.info( "new ClusterService");
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               multiSite = "true".equalsIgnoreCase(p.getProperty("MR.multisite", "true"));
+                               
+       }
+       
+       public Map<String, MR_Cluster> getMR_Clusters() {                       
+               return mr_clusters;
+       }
+               
+       public List<MR_Cluster> getAllMr_Clusters() {
+               return new ArrayList<MR_Cluster>(mr_clusters.values());
+       }
+               
+       public MR_Cluster getMr_Cluster( String key, ApiError apiError ) {                      
+               MR_Cluster mrc = mr_clusters.get( key );
+               if ( mrc == null ) {
+                       apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                       apiError.setFields( "dcaeLocationName");
+                       apiError.setMessage( "Cluster with dcaeLocationName " + key + " not found");
+               }
+               apiError.setCode(200);
+               return mrc;
+       }
+       public MR_Cluster getMr_ClusterByFQDN( String key ) {           
+               for( MR_Cluster cluster: mr_clusters.values() ) {
+                       if ( key.equals( cluster.getFqdn() ) ) {
+                               return cluster;
+                       }
+               }
+               return null;
+       }
+       
+       public MR_Cluster getMr_ClusterByLoc( String loc ) {
+               return mr_clusters.get( loc );
+       }
+       
+       public List<MR_Cluster> getCentralClusters() {
+               DcaeLocationService locations = new DcaeLocationService();
+               List<MR_Cluster> result = new ArrayList<MR_Cluster>();
+               for( MR_Cluster c: mr_clusters.values() ) {
+                       try {
+                               if ( locations.getDcaeLocation(c.getDcaeLocationName()).isCentral() ) {
+                                       result.add(c);
+                               }
+                       } catch ( NullPointerException npe ) {
+                               logger.warn( "Failed test isCentral for location:" + c.getDcaeLocationName() );
+                       }
+               }
+               return result;
+       }       
+       
+       // builds the set of unique cluster groups
+       public Set<String> getGroups() {
+               Set<String> result = new HashSet<String>();
+               for( MR_Cluster c: mr_clusters.values() ) {
+                       try {
+                               result.add(c.getReplicationGroup());
+                       } catch ( NullPointerException npe ) {
+                               logger.warn( "Failed to add Group for cluster:" + c.getDcaeLocationName() );
+                       }
+               }
+               return result;
+       }       
+
+
+       public MR_Cluster addMr_Cluster( MR_Cluster cluster, ApiError apiError ) {
+               logger.info( "Entry: addMr_Cluster");
+               MR_Cluster mrc = mr_clusters.get( cluster.getDcaeLocationName() );
+               if ( mrc != null ) {
+                       apiError.setCode(Status.CONFLICT.getStatusCode());
+                       apiError.setFields( "dcaeLocationName");
+                       apiError.setMessage( "Cluster with dcaeLocationName " + cluster.getDcaeLocationName() + " already exists");
+                       return null;
+               }
+               cluster.setLastMod();
+               cluster.setStatus( addTopicsToCluster( cluster, apiError ) );
+               mr_clusters.put( cluster.getDcaeLocationName(), cluster );
+               DcaeLocationService svc = new DcaeLocationService();
+               DcaeLocation loc = svc.getDcaeLocation( cluster.getDcaeLocationName() );
+               if ( loc != null && loc.isCentral() && multiSite ) {
+                       ApiError resp = TopicService.setBridgeClientPerms( cluster );
+                       if ( ! resp.is2xx() ) {
+                               logger.error( "Unable to provision Bridge to " + cluster.getDcaeLocationName() );
+                               cluster.setLastMod();
+                               cluster.setStatus(DmaapObject_Status.INVALID);
+                               mr_clusters.put( cluster.getDcaeLocationName(), cluster );
+                       }
+               }
+               apiError.setCode(200);
+               return cluster;
+       }
+               
+       public MR_Cluster updateMr_Cluster( MR_Cluster cluster, ApiError apiError ) {
+               MR_Cluster mrc = mr_clusters.get( cluster.getDcaeLocationName() );
+               if ( mrc == null ) {
+                       apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                       apiError.setFields( "dcaeLocationName");
+                       apiError.setMessage( "Cluster with dcaeLocationName " + cluster.getDcaeLocationName() + " not found");
+                       return null;
+               }
+               cluster.setLastMod();
+               cluster.setStatus( addTopicsToCluster( cluster, apiError ) );
+               mr_clusters.put( cluster.getDcaeLocationName(), cluster );
+               DcaeLocationService svc = new DcaeLocationService();
+               DcaeLocation loc = svc.getDcaeLocation( cluster.getDcaeLocationName() );
+               if ( loc == null ) {
+                       logger.error( "DcaeLocation not found for cluster in " + cluster.getDcaeLocationName() );
+                       cluster.setLastMod();
+                       cluster.setStatus(DmaapObject_Status.INVALID);
+                       mr_clusters.put( cluster.getDcaeLocationName(), cluster );
+               } else if ( loc.isCentral() && multiSite ) {
+                       ApiError resp = TopicService.setBridgeClientPerms( cluster );
+                       if ( ! resp.is2xx() ) {
+                               logger.error( "Unable to provision Bridge to " + cluster.getDcaeLocationName() );
+                               cluster.setLastMod();
+                               cluster.setStatus(DmaapObject_Status.INVALID);
+                               mr_clusters.put( cluster.getDcaeLocationName(), cluster );
+                       }
+               }
+               
+               apiError.setCode(200);
+               return cluster;
+       }
+               
+       public MR_Cluster removeMr_Cluster( String key, ApiError apiError ) {
+               MR_Cluster mrc = mr_clusters.get( key );
+               if ( mrc == null ) {
+                       apiError.setCode(Status.NOT_FOUND.getStatusCode());
+                       apiError.setFields( "dcaeLocationName");
+                       apiError.setMessage( "Cluster with dcaeLocationName " + key + " not found");
+                       return null;
+               }
+               apiError.setCode(200);
+               return mr_clusters.remove(key);
+       }       
+       
+       private DmaapObject_Status addTopicsToCluster( MR_Cluster cluster, ApiError err  ){
+               
+               TopicService ts = new TopicService();
+               MrProvConnection prov = new MrProvConnection();
+               List<Topic>  topics = ts.getAllTopicsWithoutClients();
+               for( Topic topic: topics ) {
+                       logger.info( "POST topic " + topic.getFqtn() + " to cluster " + cluster.getFqdn() + " in loc " + cluster.getDcaeLocationName());
+                       if ( prov.makeTopicConnection(cluster)) {
+                               String resp = prov.doPostTopic(topic, err);
+                               logger.info( "response code: " + err.getCode() );
+                               if ( ! err.is2xx() && ! (err.getCode() == 409) ) {
+                                       return DmaapObject_Status.INVALID;
+                               } 
+                       }
+               }
+               
+               return DmaapObject_Status.VALID;
+       }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MirrorMakerService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/MirrorMakerService.java
new file mode 100644 (file)
index 0000000..7c4b2ce
--- /dev/null
@@ -0,0 +1,255 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+
+
+
+
+
+
+
+//import org.openecomp.dmaapbc.aaf.AndrewDecryptor;
+import org.onap.dmaap.dbcapi.aaf.AafDecrypt;
+import org.onap.dmaap.dbcapi.client.MrTopicConnection;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.model.MirrorMaker;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.RandomInteger;
+
+public class MirrorMakerService extends BaseLoggingClass {
+       
+       private Map<String, MirrorMaker> mirrors = DatabaseClass.getMirrorMakers();
+       private static MrTopicConnection prov;
+       private static AafDecrypt decryptor;
+       
+       static final String PROV_USER_PROPERTY = "MM.ProvUserMechId";
+       static final String PROV_PWD_PROPERTY = "MM.ProvUserPwd";
+       static final String PROV_PWD_DEFAULT = "pwdNotSet";
+       static final String SOURCE_REPLICATION_PORT_PROPERTY = "MR.SourceReplicationPort";
+       static final String SOURCE_REPLICATION_PORT_DEFAULT = "9092";
+       static final String TARGET_REPLICATION_PORT_PROPERTY = "MR.TargetReplicationPort";
+       static final String TARGET_REPLICATION_PORT_DEFAULT = "2181";
+       
+       private static String provUser;
+       private static String provUserPwd;
+       private static String defaultProducerPort;
+       private static String defaultConsumerPort;
+       private static String centralFqdn;
+       private int maxTopicsPerMM;
+       private boolean mmPerMR;
+       
+       public MirrorMakerService() {
+               super();
+               decryptor = new AafDecrypt();
+               DmaapConfig p = (DmaapConfig)DmaapConfig.getConfig();
+               provUser = p.getProperty(PROV_USER_PROPERTY);
+               provUserPwd = decryptor.decrypt(p.getProperty( PROV_PWD_PROPERTY, PROV_PWD_DEFAULT ));
+               defaultProducerPort = p.getProperty( SOURCE_REPLICATION_PORT_PROPERTY, SOURCE_REPLICATION_PORT_DEFAULT );
+               defaultConsumerPort = p.getProperty( TARGET_REPLICATION_PORT_PROPERTY, TARGET_REPLICATION_PORT_DEFAULT );       
+               centralFqdn = p.getProperty("MR.CentralCname", "notSet");
+               maxTopicsPerMM = Integer.valueOf( p.getProperty( "MaxTopicsPerMM", "5"));
+               mmPerMR = "true".equalsIgnoreCase(p.getProperty("MirrorMakerPerMR", "true"));
+       }
+
+       // will create a MM on MMagent if needed
+       // will update the MMagent whitelist with all topics for this MM
+       public MirrorMaker updateMirrorMaker( MirrorMaker mm ) {
+               logger.info( "updateMirrorMaker");
+       
+               prov = new MrTopicConnection( provUser, provUserPwd );
+       
+               DmaapService dmaap = new DmaapService();
+               MR_ClusterService clusters = new MR_ClusterService();
+               MR_Cluster target_cluster = null;
+               String override = null;
+               
+               if ( ! mmPerMR ) {
+                       // in ECOMP, MM Agent is only deployed at central, so this case is needed for backwards compatibility
+                       //  we use a cname for the central MR cluster that is active, and provision on agent topic on that target
+                       // but only send 1 message so MM Agents can read it relying on kafka delivery
+                       for( MR_Cluster cluster: clusters.getCentralClusters() ) {
+
+                               target_cluster = cluster;
+                               override = centralFqdn;
+                               // we only want to send one message even if there are multiple central clusters
+                               break;
+                       
+                       } 
+               } else {
+                       // In ONAP deployment architecture, the MM Agent is deployed with each target MR
+                       target_cluster = clusters.getMr_ClusterByFQDN(mm.getTargetCluster());
+                       override = null;
+               }
+               
+               prov.makeTopicConnection(target_cluster, dmaap.getBridgeAdminFqtn(), override  );
+               ApiError resp = prov.doPostMessage(mm.createMirrorMaker( defaultConsumerPort, defaultProducerPort ));
+               if ( ! resp.is2xx() ) {
+
+                       errorLogger.error( DmaapbcLogMessageEnum.MM_PUBLISH_ERROR, "create MM", Integer.toString(resp.getCode()), resp.getMessage());
+                       mm.setStatus(DmaapObject_Status.INVALID);
+               } else {
+                       prov.makeTopicConnection(target_cluster, dmaap.getBridgeAdminFqtn(), override );
+                       resp = prov.doPostMessage(mm.getWhitelistUpdateJSON());
+                       if ( ! resp.is2xx()) {
+                               errorLogger.error( DmaapbcLogMessageEnum.MM_PUBLISH_ERROR,"MR Bridge", Integer.toString(resp.getCode()), resp.getMessage());
+                               mm.setStatus(DmaapObject_Status.INVALID);
+                       } else {
+                               mm.setStatus(DmaapObject_Status.VALID);
+                       }
+               }
+
+               mm.setLastMod();
+               return mirrors.put( mm.getMmName(), mm);
+       }
+       public MirrorMaker getMirrorMaker( String part1, String part2, int index ) {
+               String targetPart;
+
+               // original mm names did not have any index, so leave off index 0 for
+               // backwards compatibility
+               if ( index == 0 ) {
+                       targetPart = part2;
+               } else {
+                       targetPart = part2 + "_" + index;
+               }
+               logger.info( "getMirrorMaker using " + part1 + " and " + targetPart );
+               return mirrors.get(MirrorMaker.genKey(part1, targetPart));
+       }
+       public MirrorMaker getMirrorMaker( String part1, String part2 ) {
+               logger.info( "getMirrorMaker using " + part1 + " and " + part2 );
+               return mirrors.get(MirrorMaker.genKey(part1, part2));
+       }       
+       public MirrorMaker getMirrorMaker( String key ) {
+               logger.info( "getMirrorMaker using " + key);
+               return mirrors.get(key);
+       }
+       
+       
+       public void delMirrorMaker( MirrorMaker mm ) {
+               logger.info( "delMirrorMaker");
+               mirrors.remove(mm.getMmName());
+       }
+       
+       // TODO: this should probably return sequential values or get replaced by the MM client API
+       // but it should be sufficient for initial 1610 development
+       public static String genTransactionId() {
+               RandomInteger ri = new RandomInteger(100000);
+           int randomInt = ri.next();
+           return Integer.toString(randomInt);
+       }
+       public List<String> getAllMirrorMakers() {
+               List<String> ret = new ArrayList<String>();
+               for( String key: mirrors.keySet()) {
+                       ret.add( key );
+               }
+               
+               return ret;
+       }
+       
+       public MirrorMaker findNextMM( String source, String target, String fqtn ) {
+               int i = 0;
+               MirrorMaker mm = null;
+               while( mm == null ) {
+                       
+                       mm = this.getMirrorMaker( source, target, i);
+                       if ( mm == null ) {
+                               mm = new MirrorMaker(source, target, i);
+                       }
+                       if ( mm.getTopics().contains(fqtn) ) {
+                               break;
+                       }
+                       if ( mm.getTopicCount() >= maxTopicsPerMM ) {
+                               logger.info( "getNextMM: MM " + mm.getMmName() + " has " + mm.getTopicCount() + " topics.  Moving to next MM");
+                               i++;
+                               mm = null;
+                       }
+               }
+        
+               
+               return mm;
+       }
+
+       public MirrorMaker splitMM( MirrorMaker orig ) {
+               
+               String source = orig.getSourceCluster();
+               String target = orig.getTargetCluster();
+               
+               
+               ArrayList<String> whitelist = orig.getTopics();
+               while( whitelist.size() > maxTopicsPerMM ) {
+                       
+                       int last = whitelist.size() - 1;
+                       String topic = whitelist.get(last);
+                       whitelist.remove(last);
+                       MirrorMaker mm = this.findNextMM( source, target, "aValueThatShouldNotMatchAnything" );
+                       mm.addTopic(topic);     
+                       this.updateMirrorMaker(mm);
+               }
+               
+               orig.setTopics(whitelist);
+
+               return orig;
+               
+       }
+       
+       public static String getProvUser() {
+               return provUser;
+       }
+
+       public static void setProvUser(String provUser) {
+               MirrorMakerService.provUser = provUser;
+       }
+
+       public static String getProvUserPwd() {
+               return provUserPwd;
+       }
+
+       public static void setProvUserPwd(String provUserPwd) {
+               MirrorMakerService.provUserPwd = provUserPwd;
+       }
+
+       public static String getDefaultProducerPort() {
+               return defaultProducerPort;
+       }
+
+       public static void setDefaultProducerPort(String defaultProducerPort) {
+               MirrorMakerService.defaultProducerPort = defaultProducerPort;
+       }
+
+       public static String getDefaultConsumerPort() {
+               return defaultConsumerPort;
+       }
+
+       public static void setDefaultConsumerPort(String defaultConsumerPort) {
+               MirrorMakerService.defaultConsumerPort = defaultConsumerPort;
+       }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/TopicService.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/service/TopicService.java
new file mode 100644 (file)
index 0000000..009b745
--- /dev/null
@@ -0,0 +1,526 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType;
+import org.onap.dmaap.dbcapi.aaf.AafServiceFactory;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.logging.DmaapbcLogMessageEnum;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.model.MR_Client;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.model.MirrorMaker;
+import org.onap.dmaap.dbcapi.model.ReplicationType;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.Fqdn;
+import org.onap.dmaap.dbcapi.util.Graph;
+
+import javax.ws.rs.core.Response.Status;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class TopicService extends BaseLoggingClass {
+
+
+    // REF: https://wiki.web.att.com/pages/viewpage.action?pageId=519703122
+    private static String defaultGlobalMrHost;
+
+    private Map<String, Topic> mr_topics;
+
+    private static DmaapService dmaapSvc = new DmaapService();
+    private MR_ClientService clientService;
+    private MR_ClusterService clusters;
+    private DcaeLocationService locations;
+    private MirrorMakerService bridge;
+    private AafTopicSetupService aafTopicSetupService;
+
+    private static String centralCname;
+    private boolean strictGraph = true;
+    private boolean mmPerMR;
+
+
+    public TopicService() {
+        this(DatabaseClass.getTopics(), new MR_ClientService(), (DmaapConfig) DmaapConfig.getConfig(),
+                new MR_ClusterService(), new DcaeLocationService(), new MirrorMakerService(),
+                new AafTopicSetupService(
+                        new AafServiceFactory().initAafService(ServiceType.AAF_TopicMgr),
+                        dmaapSvc, (DmaapConfig) DmaapConfig.getConfig()));
+    }
+
+    TopicService(Map<String, Topic> mr_topics, MR_ClientService clientService, DmaapConfig p,
+                 MR_ClusterService clusters, DcaeLocationService locations, MirrorMakerService bridge, AafTopicSetupService aafTopicSetupService) {
+        this.mr_topics = mr_topics;
+        this.clientService = clientService;
+        defaultGlobalMrHost = p.getProperty("MR.globalHost", "global.host.not.set");
+        centralCname = p.getProperty("MR.CentralCname");
+        String unit_test = p.getProperty("UnitTest", "No");
+        if ("Yes".equals(unit_test)) {
+            strictGraph = false;
+        }
+        mmPerMR = "true".equalsIgnoreCase(p.getProperty("MirrorMakerPerMR", "true"));
+        logger.info("TopicService properties: CentralCname=" + centralCname +
+                "   defaultGlobarlMrHost=" + defaultGlobalMrHost +
+                " mmPerMR=" + mmPerMR);
+        this.clusters = clusters;
+        this.locations = locations;
+        this.bridge = bridge;
+        this.aafTopicSetupService = aafTopicSetupService;
+    }
+
+    public Map<String, Topic> getTopics() {
+        return mr_topics;
+    }
+
+    public List<Topic> getAllTopics() {
+        return getAllTopics(true);
+    }
+
+    public List<Topic> getAllTopicsWithoutClients() {
+        return getAllTopics(false);
+    }
+
+    private List<Topic> getAllTopics(Boolean withClients) {
+        ArrayList<Topic> topics = new ArrayList<>(mr_topics.values());
+        if (withClients) {
+            for (Topic topic : topics) {
+                topic.setClients(clientService.getAllMrClients(topic.getFqtn()));
+            }
+        }
+        return topics;
+    }
+
+    public Topic getTopic(String key, ApiError apiError) {
+        logger.info("getTopic: key=" + key);
+        Topic t = mr_topics.get(key);
+        if (t == null) {
+            apiError.setCode(Status.NOT_FOUND.getStatusCode());
+            apiError.setFields("fqtn");
+            apiError.setMessage("topic with fqtn " + key + " not found");
+            return null;
+        }
+        t.setClients(clientService.getAllMrClients(key));
+        apiError.setCode(Status.OK.getStatusCode());
+        return t;
+    }
+
+    public Topic addTopic(Topic topic, ApiError err, Boolean useExisting) {
+        logger.info("Entry: addTopic");
+        logger.info("Topic name=" + topic.getTopicName() + " fqtnStyle=" + topic.getFqtnStyle());
+        String nFqtn = topic.genFqtn();
+        logger.info("FQTN=" + nFqtn);
+        Topic pTopic = getTopic(nFqtn, err);
+        if (pTopic != null) {
+            String t = "topic already exists: " + nFqtn;
+            logger.info(t);
+            if (useExisting) {
+                err.setCode(Status.OK.getStatusCode());
+                return pTopic;
+            }
+            err.setMessage(t);
+            err.setFields("fqtn");
+            err.setCode(Status.CONFLICT.getStatusCode());
+            return null;
+        }
+        err.reset();  // err filled with NOT_FOUND is expected case, but don't want to litter...
+
+        topic.setFqtn(nFqtn);
+
+        ApiError topicSetupError = aafTopicSetupService.aafTopicSetup(topic);
+        updateApiError(err, topicSetupError);
+        if (err.getCode() >= 400) {
+            return null;
+        }
+
+        if (topic.getReplicationCase().involvesGlobal()) {
+            if (topic.getGlobalMrURL() == null) {
+                topic.setGlobalMrURL(defaultGlobalMrHost);
+            }
+            if (!Fqdn.isValid(topic.getGlobalMrURL())) {
+                logger.error("GlobalMR FQDN not valid: " + topic.getGlobalMrURL());
+                topic.setStatus(DmaapObject_Status.INVALID);
+                err.setCode(500);
+                err.setMessage("Value is not a valid FQDN:" + topic.getGlobalMrURL());
+                err.setFields("globalMrURL");
+
+                return null;
+            }
+        }
+
+
+        if (topic.getNumClients() > 0) {
+            ArrayList<MR_Client> clients = new ArrayList<MR_Client>(topic.getClients());
+
+
+            ArrayList<MR_Client> clients2 = new ArrayList<MR_Client>();
+            for (Iterator<MR_Client> it = clients.iterator(); it.hasNext(); ) {
+                MR_Client c = it.next();
+
+                logger.info("c fqtn=" + c.getFqtn() + " ID=" + c.getMrClientId() + " url=" + c.getTopicURL());
+                MR_Client nc = new MR_Client(c.getDcaeLocationName(), topic.getFqtn(), c.getClientRole(), c.getAction());
+                nc.setFqtn(topic.getFqtn());
+                nc.setClientIdentity(c.getClientIdentity());
+                logger.info("nc fqtn=" + nc.getFqtn() + " ID=" + nc.getMrClientId() + " url=" + nc.getTopicURL());
+                clients2.add(clientService.addMr_Client(nc, topic, err));
+                if (!err.is2xx()) {
+                    return null;
+                }
+            }
+
+            topic.setClients(clients2);
+        }
+
+        Topic ntopic = checkForBridge(topic, err);
+        if (ntopic == null) {
+            topic.setStatus(DmaapObject_Status.INVALID);
+            if (!err.is2xx()) {
+                return null;
+            }
+        }
+
+
+        mr_topics.put(nFqtn, ntopic);
+
+        err.setCode(Status.OK.getStatusCode());
+        return ntopic;
+    }
+
+
+    public Topic updateTopic(Topic topic, ApiError err) {
+        logger.info("updateTopic: entry");
+        logger.info("updateTopic: topic=" + topic);
+        logger.info("updateTopic: fqtn=" + topic.getFqtn());
+        if (topic.getFqtn().isEmpty()) {
+            return null;
+        }
+        logger.info("updateTopic: call checkForBridge");
+        Topic ntopic = checkForBridge(topic, err);
+        if (ntopic == null) {
+            topic.setStatus(DmaapObject_Status.INVALID);
+            if (!err.is2xx()) {
+                return null;
+            }
+        }
+        if (ntopic != null) {
+            logger.info("updateTopic: call put");
+            mr_topics.put(ntopic.getFqtn(), ntopic);
+        }
+        err.setCode(Status.OK.getStatusCode());
+        return ntopic;
+    }
+
+    public Topic removeTopic(String pubId, ApiError apiError) {
+        Topic topic = mr_topics.get(pubId);
+        if (topic == null) {
+            apiError.setCode(Status.NOT_FOUND.getStatusCode());
+            apiError.setMessage("Topic " + pubId + " does not exist");
+            apiError.setFields("fqtn");
+            return null;
+        }
+
+        ApiError topicSetupError = aafTopicSetupService.aafTopicCleanup(topic);
+        updateApiError(apiError, topicSetupError);
+        if (apiError.getCode() >= 400) {
+            return null;
+        }
+
+        ArrayList<MR_Client> clients = new ArrayList<MR_Client>(clientService.getAllMrClients(pubId));
+        for (Iterator<MR_Client> it = clients.iterator(); it.hasNext(); ) {
+            MR_Client c = it.next();
+            clientService.removeMr_Client(c.getMrClientId(), false, apiError);
+            if (!apiError.is2xx()) {
+                return null;
+            }
+        }
+        apiError.setCode(Status.OK.getStatusCode());
+        return mr_topics.remove(pubId);
+    }
+
+    public static ApiError setBridgeClientPerms(MR_Cluster node) {
+        DmaapConfig p = (DmaapConfig) DmaapConfig.getConfig();
+        String mmProvRole = p.getProperty("MM.ProvRole");
+        String mmAgentRole = p.getProperty("MM.AgentRole");
+        String[] Roles = {mmProvRole, mmAgentRole};
+        String[] actions = {"view", "pub", "sub"};
+        Topic bridgeAdminTopic = new Topic().init();
+        bridgeAdminTopic.setTopicName(dmaapSvc.getBridgeAdminFqtn());
+        bridgeAdminTopic.setTopicDescription("RESERVED topic for MirroMaker Provisioning");
+        bridgeAdminTopic.setOwner("DBC");
+
+        ArrayList<MR_Client> clients = new ArrayList<MR_Client>();
+        for (String role : Roles) {
+            MR_Client client = new MR_Client();
+            client.setAction(actions);
+            client.setClientRole(role);
+            client.setDcaeLocationName(node.getDcaeLocationName());
+            clients.add(client);
+        }
+        bridgeAdminTopic.setClients(clients);
+
+        TopicService ts = new TopicService();
+        ApiError err = new ApiError();
+        ts.addTopic(bridgeAdminTopic, err, true);
+
+        if (err.is2xx() || err.getCode() == 409) {
+            err.setCode(200);
+            return err;
+        }
+
+        errorLogger.error(DmaapbcLogMessageEnum.TOPIC_CREATE_ERROR, bridgeAdminTopic.getFqtn(), Integer.toString(err.getCode()), err.getFields(), err.getMessage());
+        return err;
+    }
+
+
+    public Topic checkForBridge(Topic topic, ApiError err) {
+        logger.info("checkForBridge: entry");
+        logger.info("fqtn=" + topic.getFqtn() + " replicatonType=" + topic.getReplicationCase());
+        if (topic.getReplicationCase() == ReplicationType.REPLICATION_NONE) {
+            topic.setStatus(DmaapObject_Status.VALID);
+            return topic;
+        }
+
+        boolean anythingWrong = false;
+
+        Set<String> groups = clusters.getGroups();
+        for (String g : groups) {
+            logger.info("buildBridge for " + topic.getFqtn() + " on group" + g);
+            anythingWrong |= buildBridge(topic, err, g);
+        }
+        if (anythingWrong) {
+            topic.setStatus(DmaapObject_Status.INVALID);
+            if (!err.is2xx()) {
+                return null;
+            }
+        } else {
+            topic.setStatus(DmaapObject_Status.VALID);
+        }
+        return topic;
+    }
+
+    private boolean buildBridge(Topic topic, ApiError err, String group) {
+        logger.info("buildBridge: entry");
+        boolean anythingWrong = false;
+        Graph graph;
+        logger.info("buildBridge: strictGraph=" + strictGraph);
+        if (group == null || group.isEmpty()) {
+            graph = new Graph(topic.getClients(), strictGraph);
+        } else {
+            graph = new Graph(topic.getClients(), strictGraph, group);
+        }
+        logger.info("buildBridge: graph=" + graph);
+        MR_Cluster groupCentralCluster = null;
+
+
+        if (graph.isEmpty()) {
+            logger.info("buildBridge: graph is empty.  return false");
+            return false;
+        } else if (group == null && topic.getReplicationCase().involvesFQDN()) {
+            logger.info("buildBridge: group is null and replicationCaseInvolvesFQDN. return false");
+            return false;
+        } else if (!graph.hasCentral()) {
+            logger.warn("Topic " + topic.getFqtn() + " wants to be " + topic.getReplicationCase() + " but has no central clients");
+            return true;
+        } else {
+            groupCentralCluster = clusters.getMr_ClusterByLoc(graph.getCentralLoc());
+        }
+        Collection<String> clientLocations = graph.getKeys();
+        for (String loc : clientLocations) {
+            logger.info("loc=" + loc);
+            DcaeLocation location = locations.getDcaeLocation(loc);
+            MR_Cluster cluster = clusters.getMr_ClusterByLoc(loc);
+            logger.info("cluster=" + cluster + " at " + cluster.getDcaeLocationName());
+            logger.info("location.isCentral()=" + location.isCentral() + " getCentralLoc()=" + graph.getCentralLoc());
+
+
+            String source = null;
+            String target = null;
+
+            /*
+             * Provision Edge to Central bridges...
+             */
+            if (!location.isCentral() && !graph.getCentralLoc().equals(cluster.getDcaeLocationName())) {
+                switch (topic.getReplicationCase()) {
+                    case REPLICATION_EDGE_TO_CENTRAL:
+                    case REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL:  // NOTE: this is for E2C portion only
+                        source = cluster.getFqdn();
+                        target = (mmPerMR) ? groupCentralCluster.getFqdn() : centralCname;
+                        logger.info("REPLICATION_EDGE_TO_CENTRAL: source=" + source + " target=" + target);
+                        break;
+                    case REPLICATION_CENTRAL_TO_EDGE:
+                    case REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE:  // NOTE: this is for C2E portion only
+                        source = (mmPerMR) ? groupCentralCluster.getFqdn() : centralCname;
+                        target = cluster.getFqdn();
+                        break;
+                    case REPLICATION_CENTRAL_TO_GLOBAL:
+                    case REPLICATION_GLOBAL_TO_CENTRAL:
+                    case REPLICATION_FQDN_TO_GLOBAL:
+                    case REPLICATION_GLOBAL_TO_FQDN:
+                        break;
+
+                    case REPLICATION_EDGE_TO_FQDN:
+                    case REPLICATION_EDGE_TO_FQDN_TO_GLOBAL:  // NOTE: this is for E2C portion only
+                        source = cluster.getFqdn();
+                        target = groupCentralCluster.getFqdn();
+                        break;
+                    case REPLICATION_FQDN_TO_EDGE:
+                    case REPLICATION_GLOBAL_TO_FQDN_TO_EDGE:  // NOTE: this is for F2E portion only
+                        source = groupCentralCluster.getFqdn();
+                        target = cluster.getFqdn();
+                        break;
+
+                    default:
+                        logger.error("Unexpected value for ReplicationType (" + topic.getReplicationCase() + ") for topic " + topic.getFqtn());
+                        anythingWrong = true;
+                        err.setCode(400);
+                        err.setFields("topic=" + topic.genFqtn() + " replicationCase="
+                                + topic.getReplicationCase());
+                        err.setMessage("Unexpected value for ReplicationType");
+                        continue;
+                }
+
+            } else if (location.isCentral() && graph.getCentralLoc().equals(cluster.getDcaeLocationName())) {
+                /*
+                 * Provision Central to Global bridges
+                 */
+                switch (topic.getReplicationCase()) {
+
+                    case REPLICATION_CENTRAL_TO_GLOBAL:
+                    case REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL:
+                        source = centralCname;
+                        target = topic.getGlobalMrURL();
+                        break;
+                    case REPLICATION_GLOBAL_TO_CENTRAL:
+                    case REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE:  // NOTE: this is for G2C portion only
+                        source = topic.getGlobalMrURL();
+                        target = centralCname;
+                        break;
+
+                    case REPLICATION_EDGE_TO_FQDN_TO_GLOBAL:  // NOTE: this is for E2F portion only
+                        source = groupCentralCluster.getFqdn();
+                        target = topic.getGlobalMrURL();
+                        break;
+
+                    case REPLICATION_FQDN_TO_GLOBAL:
+                        source = groupCentralCluster.getFqdn();
+                        target = topic.getGlobalMrURL();
+                        break;
+
+                    case REPLICATION_GLOBAL_TO_FQDN:
+                    case REPLICATION_GLOBAL_TO_FQDN_TO_EDGE:  // NOTE: this is for G2F portion only
+                        source = topic.getGlobalMrURL();
+                        target = groupCentralCluster.getFqdn();
+                        break;
+
+                    case REPLICATION_FQDN_TO_EDGE:
+                    case REPLICATION_EDGE_TO_FQDN:
+                    case REPLICATION_EDGE_TO_CENTRAL:
+                    case REPLICATION_CENTRAL_TO_EDGE:
+                        break;
+                    default:
+                        logger.error("Unexpected value for ReplicationType (" + topic.getReplicationCase() + ") for topic " + topic.getFqtn());
+                        anythingWrong = true;
+                        err.setCode(400);
+                        err.setFields("topic=" + topic.genFqtn() + " replicationCase="
+                                + topic.getReplicationCase());
+                        err.setMessage("Unexpected value for ReplicationType");
+                        continue;
+                }
+            } else {
+                logger.warn("dcaeLocation " + loc + " is neither Edge nor Central so no mmagent provisioning was done");
+                anythingWrong = true;
+                continue;
+            }
+            if (source != null && target != null) {
+                try {
+                    logger.info("Create a MM from " + source + " to " + target);
+                    MirrorMaker mm = bridge.findNextMM(source, target, topic.getFqtn());
+                    mm.addTopic(topic.getFqtn());
+                    bridge.updateMirrorMaker(mm);
+                } catch (Exception ex) {
+                    err.setCode(500);
+                    err.setFields("mirror_maker.topic");
+                    err.setMessage("Unexpected condition: " + ex);
+                    anythingWrong = true;
+                    break;
+                }
+            }
+
+
+        }
+        return anythingWrong;
+
+    }
+
+
+    /*
+     * Prior to 1707, we only supported EDGE_TO_CENTRAL replication.
+     * This was determined automatically based on presence of edge publishers and central subscribers.
+     * The following method is a modification of that original logic, to preserve some backwards compatibility,
+     * i.e. to be used when no ReplicationType is specified.
+     */
+
+    public ReplicationType reviewTopic(Topic topic) {
+
+
+        if (topic.getNumClients() > 1) {
+            Graph graph = new Graph(topic.getClients(), false);
+
+            String centralFqdn = new String();
+            if (graph.hasCentral()) {
+                DmaapConfig p = (DmaapConfig) DmaapConfig.getConfig();
+                centralFqdn = p.getProperty("MR.CentralCname");
+            }
+
+            Collection<String> locations = graph.getKeys();
+            for (String loc : locations) {
+                logger.info("loc=" + loc);
+                MR_Cluster cluster = clusters.getMr_ClusterByLoc(loc);
+                if (cluster == null) {
+                    logger.info("No MR cluster for location " + loc);
+                    continue;
+                }
+                if (graph.hasCentral() && !graph.getCentralLoc().equals(cluster.getDcaeLocationName())) {
+                    logger.info("Detected case for EDGE_TO_CENTRAL from " + cluster.getFqdn() + " to " + centralFqdn);
+                    return ReplicationType.REPLICATION_EDGE_TO_CENTRAL;
+
+                }
+
+            }
+        }
+
+        return ReplicationType.REPLICATION_NONE;
+    }
+
+    private void updateApiError(ApiError err, ApiError topicSetupError) {
+        err.setCode(topicSetupError.getCode());
+        err.setMessage(topicSetupError.getMessage());
+        err.setFields(topicSetupError.getFields());
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapConfig.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapConfig.java
new file mode 100644 (file)
index 0000000..e95ebab
--- /dev/null
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.util;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import java.io.*;
+import java.security.KeyStore;
+import java.util.*;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import org.onap.dmaap.dbcapi.server.CertificateManager;
+import org.onap.dmaap.dbcapi.server.JettyServer;
+
+public class DmaapConfig extends Properties    {
+
+       private static final EELFLogger logger = EELFManager.getInstance().getLogger(DmaapConfig.class);
+       private static final long serialVersionUID = 1L;
+       private static final String CONFIG_FILE_NAME = System.getProperty("ConfigFile", "/opt/app/config/conf/dmaapbc.properties");
+       private static final Properties config = new DmaapConfig();
+
+       public static Properties getConfig() {
+               return(config);
+       }
+       public static String getConfigFileName() {
+               return(CONFIG_FILE_NAME);
+       }
+       private DmaapConfig() {
+               try (InputStream is = new FileInputStream(CONFIG_FILE_NAME)){
+                       load(is);
+               } catch (Exception e) {
+                       logger.error("Unable to load configuration file " + CONFIG_FILE_NAME);
+                       System.exit(1);
+               }
+       }
+
+       public static SSLSocketFactory getSSLSocketFactory() {
+               SSLSocketFactory factory = null;
+               try {
+                       CertificateManager cm = JettyServer.getCertificateManager();
+                       String truststore = cm.getTrustStoreFile();
+                       KeyStore ts = KeyStore.getInstance(cm.getTrustStoreType());
+                       try (InputStream in = new FileInputStream(truststore)) {
+                               ts.load(in, cm.getTrustStorePassword().toCharArray());
+                       }
+                       TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+                       tmf.init(ts);
+                       TrustManager[] tm = tmf.getTrustManagers();
+                       SSLContext sslContext = SSLContext.getInstance("TLS");
+                       sslContext.init(null, tm, null);
+                       factory = sslContext.getSocketFactory();
+               } catch (Exception e) {
+                       logger.error("Exception thrown trying to get SSLSocketFactory: ", e);
+               }
+               return factory;
+       }
+       
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapTimestamp.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/DmaapTimestamp.java
new file mode 100644 (file)
index 0000000..f36df1c
--- /dev/null
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.util;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.Date;
+
+@XmlRootElement
+public class DmaapTimestamp {
+    private Date stamp;
+
+    public DmaapTimestamp() {
+        this(new Date());
+    }
+
+    DmaapTimestamp(Date stamp) {
+        this.stamp = new Date(stamp.getTime());
+    }
+
+    public void mark() {
+        stamp = new Date();
+    }
+
+    public Date getVal() {
+        return new Date(stamp.getTime());
+    }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Fqdn.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Fqdn.java
new file mode 100644 (file)
index 0000000..37715d3
--- /dev/null
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.util;
+
+import java.util.regex.Pattern;
+
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+
+public class Fqdn extends BaseLoggingClass {
+       // regexp value sourced from https://www.regextester.com/23
+       static String regexp = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$";
+       
+       
+       public static boolean isValid( String s ) {
+               appLogger.info( "Fqdn testing: " + s );
+               boolean b = false;
+               if ( s != null ) {
+                       b = Pattern.matches( regexp, s);
+               }
+               appLogger.info( "Fqdn isValid=" + b );
+               return b;
+       }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Graph.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Graph.java
new file mode 100644 (file)
index 0000000..ab40765
--- /dev/null
@@ -0,0 +1,132 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ *
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.util;
+
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.MR_Client;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.service.MR_ClusterService;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class Graph {
+       private HashMap<String, String> graph;
+       private boolean hasCentral;
+       
+       private Map<String, DcaeLocation> locations = DatabaseClass.getDcaeLocations();
+       
+       //TODO add to properties file
+       private static String centralDcaeLayerName = "central";
+
+       
+       public Graph(HashMap<String, String> graph) {
+               super();
+               this.graph = graph;
+       }
+
+       public Graph( List<MR_Client> clients, boolean strict ) {
+               if ( clients == null )
+                       return;
+               initGraph( clients, strict, "" );
+
+       }
+       public Graph( List<MR_Client> clients, boolean strict, String group ) {
+               if ( clients == null )
+                       return;
+               initGraph( clients, strict, group );
+       }
+       
+       private void initGraph(List<MR_Client> clients, boolean strict, String group ) {
+               MR_ClusterService clusters = new MR_ClusterService();
+               this.graph = new HashMap<>();
+               this.hasCentral = false;
+               for( MR_Client client: clients ) {
+                       if ( ! strict || client.isStatusValid()) {
+                               String loc = client.getDcaeLocationName();
+                               DcaeLocation dcaeLoc = locations.get(loc);
+                               if ( dcaeLoc == null ) continue;
+                               MR_Cluster c = clusters.getMr_ClusterByLoc(loc);
+                               if ( group != null &&  ! group.isEmpty() && ! group.equals(c.getReplicationGroup())) continue;
+                               
+                               for( String action : client.getAction() ){                      
+                                       if ( ! action.equals("view") && dcaeLoc != null ) {
+                                               String layer = dcaeLoc.getDcaeLayer();
+                                               if ( layer != null && layer.contains(centralDcaeLayerName) ) {
+                                                       this.hasCentral = true;
+                                               }
+                                               graph.put(loc, layer);
+                                       }
+                               }
+       
+                       }               
+               }               
+       }
+       
+       public HashMap<String, String> getGraph() {
+               return graph;
+       }
+
+       public void setGraph(HashMap<String, String> graph) {
+               this.graph = graph;
+       }
+       
+       public String put( String key, String val ) {
+               return graph.put(key, val);
+       }
+       
+       public String get( String key ) {
+               return graph.get(key);
+       }
+       
+       public Collection<String> getKeys() {
+               return graph.keySet();
+       }
+       public boolean hasCentral() {
+               return hasCentral;
+       }
+       public void setHasCentral(boolean hasCentral) {
+               this.hasCentral = hasCentral;
+       }
+       
+       public String getCentralLoc() {
+               if ( ! hasCentral ) {
+                       return null;
+               }
+               for( String loc : graph.keySet()) {
+                       if ( graph.get(loc).contains(centralDcaeLayerName)) {
+                               return loc;
+                       }
+               }
+               return null;
+       }
+       public boolean isEmpty() {
+               return graph.isEmpty();
+       }
+       
+       
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/PermissionBuilder.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/PermissionBuilder.java
new file mode 100644 (file)
index 0000000..d1f6b2b
--- /dev/null
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.util;
+
+import javax.servlet.http.HttpServletRequest;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+
+public class PermissionBuilder {
+
+    static final String API_NS_PROP = "ApiNamespace";
+    static final String DEFAULT_API_NS = "org.onap.dmaap-bc.api";
+    static final String BOOT_INSTANCE = "boot";
+    private static final String PERM_SEPARATOR = "|";
+    private static final String NS_SEPARATOR = ".";
+    private DmaapConfig dmaapConfig;
+    private DmaapService dmaapService;
+    private String instance;
+    private String apiNamespace;
+
+    public PermissionBuilder(DmaapConfig dmaapConfig, DmaapService dmaapService) {
+        this.dmaapConfig = dmaapConfig;
+        this.dmaapService = dmaapService;
+        initFields();
+    }
+
+    public synchronized void updateDmaapInstance() {
+        if(instance == null || instance.isEmpty() || instance.equalsIgnoreCase(BOOT_INSTANCE)) {
+            String dmaapName = getDmaapName();
+            instance = (dmaapName == null || dmaapName.isEmpty()) ? BOOT_INSTANCE : dmaapName;
+        }
+    }
+
+    public String buildPermission(HttpServletRequest httpRequest) {
+
+        StringBuilder sb = new StringBuilder(apiNamespace);
+        sb.append(NS_SEPARATOR)
+            .append(getPermissionType(httpRequest.getPathInfo()))
+            .append(PERM_SEPARATOR)
+            .append(instance)
+            .append(PERM_SEPARATOR)
+            .append(httpRequest.getMethod());
+        return sb.toString();
+    }
+
+
+    private void initFields() {
+        apiNamespace = dmaapConfig.getProperty(API_NS_PROP, DEFAULT_API_NS);
+        updateDmaapInstance();
+    }
+
+    private String getDmaapName() {
+        Dmaap dmaap = dmaapService.getDmaap();
+        return ( dmaap != null ) ? dmaap.getDmaapName() : BOOT_INSTANCE;
+    }
+
+    private String getPermissionType(String pathInfo) {
+        char pathSeparator = '/';
+        String[] pathSlices = pathInfo.split(String.valueOf(pathSeparator));
+        return pathSlices[1];
+    }
+
+    String getInstance() {
+        return instance;
+    }
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomInteger.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomInteger.java
new file mode 100644 (file)
index 0000000..1c9bc11
--- /dev/null
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.util;
+
+import java.util.Date;
+// source: http://www.javapractices.com/topic/TopicAction.do?Id=62
+// with some modifications
+import java.util.Random;
+
+public final class RandomInteger {
+       private static Random randomGenerator;
+       private int range;
+       
+       public RandomInteger( int r ) {
+               randomGenerator = new Random();
+               randomGenerator.setSeed((new Date()).getTime());
+               range = r;
+       }
+       
+       public int next(){
+               return randomGenerator.nextInt(range);
+       }
+
+}
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomString.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/RandomString.java
new file mode 100644 (file)
index 0000000..b889c1c
--- /dev/null
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.util;
+
+import java.util.Random;
+
+// source: http://stackoverflow.com/questions/41107/how-to-generate-a-random-alpha-numeric-string
+
+public class RandomString {
+
+         private static final char[] symbols;
+
+         static {
+           StringBuilder tmp = new StringBuilder();
+           for (char ch = '0'; ch <= '9'; ++ch)
+             tmp.append(ch);
+           for (char ch = 'a'; ch <= 'z'; ++ch)
+             tmp.append(ch);
+           symbols = tmp.toString().toCharArray();
+         }   
+
+         private final Random random = new Random();
+
+         private final char[] buf;
+
+         public RandomString(int length) {
+           if (length < 1)
+             throw new IllegalArgumentException("length < 1: " + length);
+           buf = new char[length];
+         }
+
+         public String nextString() {
+           for (int idx = 0; idx < buf.length; ++idx) 
+             buf[idx] = symbols[random.nextInt(symbols.length)];
+           return new String(buf);
+         }
+       }
diff --git a/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Singleton.java b/dmaap-bc/src/main/java/org/onap/dmaap/dbcapi/util/Singleton.java
new file mode 100644 (file)
index 0000000..3ef6a3d
--- /dev/null
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.util;
+
+public interface Singleton<T>  {
+       public T get();
+       public void init(T val);
+       public void update(T val);
+       public void remove();
+}
diff --git a/dmaap-bc/src/main/resources/docker-compose.yml b/dmaap-bc/src/main/resources/docker-compose.yml
deleted file mode 100644 (file)
index 41bc473..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-version: '2'
-services:
-  dbc-pg-primary:
-    image: crunchydata/crunchy-postgres:centos7-10.4-2.0.0
-    ports:
-    - "5432:5432"
-    environment:
-    - PG_MODE=master
-    - PG_PRIMARY_USER="dmaap_admin"
-    - PG_PRIMARY_PASSWORD=onapdemodb
-    - PG_USER="dmaap_admin"
-    - PG_PASSWORD=onapdemodb
-    - PG_ROOT_PASSWORD=onapdemodb
-    - PG_DATABASE="dmaap"
-    - PG_PRIMARY_PORT=5432
-
-  dmaap-bc:
-    image: nexus3.onap.org:10001/onap/dmaap/buscontroller:latest
-    ports:
-    - "30241:8080"
-    - "30242:8443"
-    volumes:
-    - /var/tmp/docker-databus-controller.conf:/opt/app/config/conf
-    depends_on:
-      - dbc-pg-primary
diff --git a/dmaap-bc/src/main/resources/docker-databus-controller.conf b/dmaap-bc/src/main/resources/docker-databus-controller.conf
deleted file mode 100644 (file)
index 7214adf..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-DMAAPBC_WAIT_TO_EXIT=Y
-DMAAPBC_PG_ENABLED=true
-DMAAPBC_PGHOST=dbc-pg-primary
-DMAAPBC_PGDBNAME=dmaap
-DMAAPBC_PGCRED=onapdemodb
-DMAAPBC_PGUSER=dmaap_admin
-DMAAPBC_MR_CNAME=message-router
-DMAAPBC_AAF_URL=https://aaf-authz/
-DMAAPBC_TOPICMGR_USER=m23456@dmaapbc.onap.org
-DMAAPBC_TOPICMGR_PWD=onapdemo
-DMAAPBC_ADMIN_USER=m12345@dmaapbc.onap.org
-DMAAPBC_ADMIN_PWD=onapdemo
\ No newline at end of file
similarity index 67%
rename from dmaap-bc/src/main/resources/Dockerfile
rename to dmaap-bc/src/main/resources/docker/Dockerfile
index 0f9e6a6..a801896 100644 (file)
@@ -4,6 +4,7 @@
 #  ===========================================================================
 #  Copyright Â© 2017 AT&T Intellectual Property. All rights reserved.
 #  Modifications Copyright (C) 2018 Nokia. All rights reserved.
+#  Modifications Copyright (C) 2021 Nordix Foundation.
 #  ===========================================================================
 #  Licensed under the Apache License, Version 2.0 (the "License");
 #  you may not use this file except in compliance with the License.
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 #  ============LICENSE_END====================================================
-#
-FROM library/maven:3.6-jdk-11
 
-MAINTAINER DMAAP Team
+FROM nexus3.onap.org:10001/onap/integration-java11:8.0.0
 
-COPY /opt /opt
+MAINTAINER DMAAP Team
 
 WORKDIR /opt/app/dmaapbc
 
-RUN apt-get update && \
-    apt-get install -y \
-       curl \
-       jq \
-       openssl \
-       net-tools \
-       wget \
-       procps \
-       bash
-
-#prepare certificate location for cadi
-RUN mkdir -p /opt/app/osaaf 
+COPY /opt /opt
+USER root
+RUN apk add --no-cache curl jq net-tools wget procps \
 
-RUN chmod +x bin/* && \
+&&  mkdir -p /opt/app/osaaf && \
+    chmod +x bin/* && \
     mkdir logs && \
-    mkdir www && \
     mkdir doc && \
-    mkdir config
-
-VOLUME /opt/app/dmaapbc/log
-
-RUN addgroup --system -gid 1001 onap \
-    && adduser --ingroup onap --system --disabled-password --no-create-home --uid 1000 dbc \
-    && chown -R dbc:onap /opt/
+    mkdir config && \
+    chown -R onap:onap /opt/
 
-USER dbc
+USER onap
 
-ENTRYPOINT ["sh", "./bin/dmaapbc", "deploy"]
+ENTRYPOINT ["sh", "bin/dmaapbc", "deploy"]
diff --git a/dmaap-bc/src/main/resources/misc/LocalKey b/dmaap-bc/src/main/resources/misc/LocalKey
new file mode 100644 (file)
index 0000000..38ede55
--- /dev/null
@@ -0,0 +1,27 @@
+7ntUvubggJ1h6AXwQENQScrnlqmLMno_583XufLsguAT11bnBk0DVLE2GtCZ0pNQzlR8I3PJ1_nZ
+UEVQs1G_qZzV-MHQZvz54solEp8dNUVji4JUzP7WiPuJdvCX8vvGLc8-jOVzEJ2DAGmV3gNp60_F
+jrKx7F7Dz-h94jWZ45rNn7-Re_BneSto6HiSj0DN_SKSNhE5z9Qf3tFyFLGIYmlQoxzbTYC38uN0
+FjAYuKz6W_pTLzyOjHNAagYwEjTUUU-ei-QA5pL20-oG3jSYGnj_V94kd8X5ncB1-nybUsy5OOvZ
+huCxf9hSetn2fpIszkRcuFxaxiwubpmEWp2L_zovhcRI1OMFPIIK0IckRHD1a5CpFVzR7P5L7LQk
+FErATsQkHsPS9BJN5wlj1EoIhA2uaELjXjmOqPQg76eyQqXXcMHRJTA6czbXPYfTQMQx1r2USC9o
+HdoLT4-so7zARZidmYmvPPT9qvNisK6BF4M32K-_s6YyJspSEB1MscNPujsD7zczIsBct7BTRoeZ
+CbtkskT_yFhQzdzdSMzFN_NJ7Yb9p3d1G4gSkj2rbA-BDybHHPij8k6-k-ipvi_T_LW9B_J8Jf5f
+aRclZqVgwwSG-mUKUyk9bI3cVc-1P2ICUmr59EjuauDAtlMQL-hnTJUs1rUerh4Q7d4XgrNLjLHY
+Oue8MEj24VSMl-f28DDIV1N6ODiBKDHUmdENsmlbqeNpzQxu7FoSbLu6gN6zDP8Jw4ck1NHEIv8H
+ecUf-hBGdB0HINClaV9X2ycafWcmRY-NCzX65cp7a1Rpl1kCEW5u79LLN28aJeTzmriewhy87hJf
+rAah8z3dHteIN1fuvRoGsFwZ4jKo6olFxcBOlpHQIW5JJ1roO1vQ2Dx_l-Foo7wV_AD127zCu7ci
+lYodnAOocKbhAub4sf90P3D2NMKb20e5CQrBSchtIEaD3G7J-vL5xYydLuLu5WipOdZuq3VhSSZm
+TZIR3Ya2QiZVokxKgH-N0gPDz-TimNV_MkUSCNsv2NxjBM7oPF3dzEHbHS5eue41_R4vqxFdTdva
+o0ASTFkARmmnMFBx1a73jmcoBBx-i4el8Rce7RvFWn1PALOnRsIQPy_Pgx0OE9_6eHfOSvyxbyMM
+0FwE5f77gO967tgc4LwqB1pzz2Uk7hfizLKNc6nrrgDxSb_9rDWwiE4rWw7WYcRKvRQ270lCH4FQ
+ezrPacnnK3cKM5L6brOyhbhiL4MnNX714L0K8C6TQnIVisQCLHwif40G_DSEWxICQ5V2DMzFn3JT
+PefaubHlqxRZVikNH71b_2ZRLEi84m5iUuy8Ir1s3W6xuyIdt-yKLnjgnLjOPPxTDC5G_xaXAAuM
+SkSOjvPzOArMUUnwYk68jAxXS2tBT8JN6OnglN8dHC-P24tYzfs4B4tMYJ1ibz6BUsQ6nYxsRUak
+4ZjRmo3UG0OFJbOY-f0ja6Q4pISe1IXmlM4Ly9QdCfeHyDV-7Fiud9V_zo92lpQwttwSpBvFoDYQ
+oePdA0zmCx6GIX_8L9e8a03hUx4aUtZ8C3Kf0PzOWTcjrV7nGb99ctjmRtfGw5GPWudH6CI3WFK2
+5wFDhrQSbRhzV4iQalYVPJQ7LO4WEi4EsBTRSiz074UpvkMV3UfMGDlpXAAq5rEjj-d5WbHhzbs8
+MGKzZLTfUz9lP4CME9AOwto_ey1ly3H-yaEgCpnshm-CZoSqVDmuFYM0QR_NcrqmSQ9ZKJEF_wTa
+TEAXNJ99CYE0ZLvU9FjgCqH1-q1zL7z3NLX1uFYazEZWGMZFPVD5XOcCtUlVyUz9KuAO9ARVyu5C
+7kzo_AFePtnsA_JUvvkauo6RwO6qhLJjZuSjvmiSdOAohiXUalDFjWVW8CMfgLF4PbRDklsAcsiT
+P0xUdyWJ5slu87f9PunXDwQZWNv6haTIhVX7bilCDpRPbTbmimmE_C2J7tgV2EvazD7o8V_jeu0g
+cnIpzRnaPG9l-uy2UKoxOXI4CSymcJoyV2xxC0SF7Q5quAhf3UGAdQUeFtHwxZtYiAMXLs06
\ No newline at end of file
diff --git a/dmaap-bc/src/main/resources/misc/PolicyEngineApi.properties.tmpl b/dmaap-bc/src/main/resources/misc/PolicyEngineApi.properties.tmpl
new file mode 100644 (file)
index 0000000..248b288
--- /dev/null
@@ -0,0 +1,36 @@
+#
+# ============LICENSE_START==========================================
+# org.onap.dmaap
+# ===================================================================
+# Copyright Â© 2018 AT&T Intellectual Property. All rights reserved.
+# ===================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END============================================
+# ECOMP is a trademark and service mark of AT&T Intellectual Property.
+#
+#
+
+cat <<!EOF
+#PDP URLs to which will be used by the Policy Engine API to take Decisions. There are multiple to support redundancy.
+#Format: PDP_URL{PriorityNumber} = URL, id, password.
+PDP_URL1 =  ${DMAAPBC_PDP1_URL:-https://host1.domain.notset.com:8081/pdp/} , ${DMAAPBC_PDP1_USER:-testpdp} , ${DMAAPBC_PDP1_PWD:-alpha123}
+PDP_URL2 =  ${DMAAPBC_PDP2_URL:-https://host2.domain.notset.com:8082/pdp/} , ${DMAAPBC_PDP2_USER:-testpdp} , ${DMAAPBC_PDP2_PWD:-alpha456}
+PAP_URL =   ${DMAAPBC_PAP_URL:-https://host3.domain.notset.com:9091/pap/}  , ${DMAAPBC_PAP_USER:-testpap} , ${DMAAPBC_PAP_PWD:-alpha123}
+
+
+
+CLIENT_ID=${DMAAPBC_TOPICMGR_USER:-mechIdNotSet@namespaceNotSet}
+ENVIRONMENT=${DMAAPBC_PE_AAF_ENV:-DEVL}
+
+
+!EOF
similarity index 62%
rename from dmaap-bc/misc/dmaapbc
rename to dmaap-bc/src/main/resources/misc/dmaapbc
index 15f2fd2..963d2d1 100644 (file)
@@ -4,6 +4,7 @@
 # org.onap.dmaap
 # ===================================================================
 # Copyright Â© 2018 AT&T Intellectual Property. All rights reserved.
+# Modifications copyright (C) 2021 Nordix Foundation.
 # ===================================================================
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # ============LICENSE_END============================================
 # ECOMP is a trademark and service mark of AT&T Intellectual Property.
 #
-#
 
 umask 0022
 TZ=GMT0
 COMPONENT=dmaapbc
 APP_ROOT=/opt/app/$COMPONENT
-USER=dbc
+USER=onap
 GROUP=onap
 export TZ
-PATH=/usr/local/openjdk-11/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+PATH=/opt/java/openjdk/bin:/usr/sbin:/usr/bin:/sbin:/bin
 
 export PATH
-CLASSPATH=`echo $APP_ROOT/etc $APP_ROOT/lib/*.jar | tr ' ' ':'` 
+CLASSPATH=`echo $APP_ROOT/etc $APP_ROOT/lib/*.jar | tr ' ' ':'`
 export CLASSPATH
 CONFIGMAP_ROOT=${CONFIGMAP_ROOT:-/opt/app/config}
 CONFIGMAP_PROPS=${CONFIGMAP_PROPS:-$CONFIGMAP_ROOT/conf/dmaapbc.properties}
 CONTAINER_CONFIG=$CONFIGMAP_ROOT/conf/buscontroller.env
-MAIN=org.onap.dmaap.dbcapi.server.Main 
+MAIN=org.onap.dmaap.dbcapi.server.Main
 
 authcheck() {
        set -x
@@ -75,8 +75,8 @@ config() {
        then
                echo "WARNING: Expected env file $CONTAINER_CONFIG not found. Default behaviors in effect"
                find $CONTAINER_ROOT -type f
-       else 
-           . $CONTAINER_CONFIG
+       else
+         . $CONTAINER_CONFIG
        fi
 
        if [ "$DMAAPBC_WAIT_TO_EXIT" != "Y" ]
@@ -85,27 +85,8 @@ config() {
                > $APP_ROOT/ok_to_exit
        else
                echo "Not creating $APP_ROOT/ok_to_exit"
-       fi      
-       
-       #. misc/havecert.tmpl > etc/havecert
-       #chmod +x etc/havecert
-       echo Check for certificate
-       TZ=GMT0
-       cd /opt/app/dmaapbc;
-       KEYSTORE=${DMAAPBC_KSTOREFILE:-etc/keystore}
-       echo "KEYSTORE=$KEYSTORE"
-       d=`dirname $KEYSTORE`
-       ls -l $d
-       if [ -f ${KEYSTORE} ]
-       then
-               echo "Goodness: Found ${KEYSTORE}"
-       else
-               EMSG="`date '+%F %T,000'` WARN Certificate file $KEYSTORE is missing"
-               echo $EMSG
-               echo $EMSG >>${DMAAPBC_LOGS:-logs}/dmaapbc.log
        fi
 
-
        # These files might be better provided in kubernetes configmaps
        # so if they are there, use them
        if [ -f $CONFIGMAP_PROPS ]
@@ -117,7 +98,7 @@ config() {
        fi
        if [ ! -f config/PolicyEngineApi.properties ]
        then
-               . misc/PolicyEngineApi.properties.tmpl > config/PolicyEngineApi.properties
+         . misc/PolicyEngineApi.properties.tmpl > config/PolicyEngineApi.properties
        fi
        set +x
 }
@@ -129,13 +110,6 @@ start() {
        cd $APP_ROOT
        pwd
 
-       if [ -f "$KEYSTORE" ]
-       then
-               echo >/dev/null
-       else
-               echo No certificate file available.  Cannot start
-               exit 0
-       fi
        PIDS=`pids`
        if [ "$PIDS" != "" ]
        then
@@ -144,11 +118,11 @@ start() {
        fi
        rm -f $APP_ROOT/etc/SHUTDOWN
 
+  java -classpath $CLASSPATH  $MAIN
+  dmaapjar="$APP_ROOT/lib/dmaap-bc.jar"
        # JVM flags
-#old line from Dockerfile...keep for reference only
-       FLAGS="-cp etc:lib/* -Dlog4j.configuration=etc/log4j.properties -DConfigFile=$PROPS  -Dlogback.configurationFile=etc/logback.xml -Dhttps.protocols=TLSv1.2 -Dhttps.cipherSuites=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"
-       #nohup java $FLAGS $MAIN </dev/null >/dev/null 2>&1 &
-       nohup java $FLAGS $MAIN </dev/null  &
+       FLAGS="-cp etc:lib/* -DConfigFile=$PROPS  -Dlogback.configurationFile=etc/logback.xml -Dhttps.protocols=TLSv1.2 -Dhttps.cipherSuites=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"
+       nohup java $FLAGS -jar $dmaapjar </dev/null  &
        sleep 5
        PIDS=`pids`
        set +x
@@ -181,46 +155,11 @@ status() {
        fi
 }
 
-init() {
-       echo "ENTER init"
-       if [ ! -d $CONFIGMAP_ROOT ]
-       then
-               echo $CONFIGMAP_ROOT does not exist
-               return
-       fi
-
-       #loop on get /dmaap until we get a good response to indicate other provisioning can continue
-       rc=999
-       while [ $rc != "200" ]
-       do
-               sleep 10
-               rc=`curl -s -o /dev/null -I -w "%{http_code}" -X GET -H "Content-Type: application/json" http://dmaap-bc:8080/webapi/dmaap`
-               echo "get dmaap response=${rc}"
-       done
-
-       cd $CONFIGMAP_ROOT
-       pwd
-       # order is important in this next list
-       for uri in dmaap dcaeLocations mr_clusters topics feeds
-       do
-               if [ -d ${uri} ]
-               then
-                       for j in `ls ${uri}/*.json`
-                       do
-                               echo "POST $j to $uri"
-                               rc=`curl -v -X POST -w "%{http_code}" -H "Content-Type: application/json" -d @${j} http://dmaap-bc:8080/webapi/${uri}`
-                               echo "response=$rc"
-                       done
-               fi
-       done
-}
-
 set -x
 case "$1" in
 'deploy')
        config
        start
-       #init
        wait
        ;;
 'start')
@@ -244,11 +183,11 @@ case "$1" in
 esac
                ls -l $APP_ROOT/logs/ONAP
                echo "------------ tail -100 error.log ---------------"
-               tail -100  $APP_ROOT/logs/ONAP/error.log
+               tail -n 1000  $APP_ROOT/logs/ONAP/error.log
                echo "------------ tail -100 server.log ---------------"
-               tail -100  $APP_ROOT/logs/ONAP/server.log
+               tail -n 1000  $APP_ROOT/logs/ONAP/server.log
                echo "------------ tail -100 application.log ---------------"
-               tail -100 $APP_ROOT/logs/ONAP/application.log
+               tail -n 1000 $APP_ROOT/logs/ONAP/application.log
 
                echo "Check $APP_ROOT/ok_to_exit"
                while [ ! -f $APP_ROOT/ok_to_exit ]
diff --git a/dmaap-bc/src/main/resources/misc/dmaapbc.properties.tmpl b/dmaap-bc/src/main/resources/misc/dmaapbc.properties.tmpl
new file mode 100755 (executable)
index 0000000..d013ca3
--- /dev/null
@@ -0,0 +1,222 @@
+cat <<!EOF
+#
+# ============LICENSE_START==========================================
+# org.onap.dmaap
+# ===================================================================
+# Copyright Â© 2018 AT&T Intellectual Property. All rights reserved.
+# ===================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END============================================
+# ECOMP is a trademark and service mark of AT&T Intellectual Property.
+#
+#
+#
+#      Configuration parameters fixed at startup for the DMaaP Bus Controller
+#
+# CSIT TESTING
+csit: ${DMAAPBC_CSIT:-No}
+
+#
+#      URI to retrieve dynamic DR configuration
+#
+ProvisioningURI:       ${DMAAPBC_INTURI:-/internal/prov}
+#
+#      Allow http access to API 
+#
+HttpAllowed:   ${DMAAPBC_HTTPALLOWED:-true}
+#
+#      The port number for http as seen within the server
+#
+IntHttpPort:   ${DMAAPBC_INT_HTTP_PORT:-8080}
+#
+#      The port number for https as seen within the server
+#   Set to 0 if no certificate is available yet...
+#
+IntHttpsPort:  ${DMAAPBC_INT_HTTPS_PORT:-8443}
+#
+#      The external port number for https taking port mapping into account
+#
+ExtHttpsPort:  ${DMAAPBC_EXT_HTTPS_PORT:-443}
+#
+#      The type of keystore for https
+#
+KeyStoreType:  jks
+#
+#      The path to the keystore for https
+#
+KeyStoreFile:  ${DMAAPBC_KSTOREFILE:-etc/keystore}
+#
+#      The password for the https keystore (remember to put password in "" and escape $ characters)
+#
+KeyStorePassword:      ${DMAAPBC_KSTOREPASS:-"Y@Y5f&gm?PAz,CVQL,lk[VAF"}
+#
+#      The password for the private key in the https keystore (remember to put password in "" and escape $ characters)
+#
+KeyPassword:   ${DMAAPBC_PVTKEYPASS:-"Y@Y5f&gm?PAz,CVQL,lk[VAF"}
+#
+#      The type of truststore for https
+#
+TrustStoreType:        jks
+#
+#      The path to the truststore for https
+#
+TrustStoreFile:        ${DMAAPBC_TSTOREFILE:-etc/org.onap.dmaap-bc.trust.jks}
+#
+#      The password for the https truststore (remember to put password in "" and escape $ characters)
+#
+TrustStorePassword:    ${DMAAPBC_TSTOREPASS:-"8b&R5%l\$l:@jSWz@FCs;rhY*"}
+#
+#      The path to the file used to trigger an orderly shutdown
+#
+QuiesceFile:   etc/SHUTDOWN
+#
+#      Enable postgress
+#
+UsePGSQL:      ${DMAAPBC_PG_ENABLED:-false}
+#
+#      The host for postgres access
+#
+DB.host:       ${DMAAPBC_PGHOST:-HostNotSet}
+#
+#      For postgres access
+#
+DB.cred:       ${DMAAPBC_PGCRED:-ValueNotSet}
+#
+#      Name of this environment
+#
+DmaapName:     ${DMAAPBC_INSTANCE_NAME:-demo}
+#
+#      Name of DR prov server
+#
+DR.provhost:   ${DMAAPBC_DRPROV_FQDN:-dcae-drps.domain.notset.com}
+#
+# handling of feed delete
+# DeleteOnDR - means use the DR API to DELETE a feed. (default for backwards compatibility)
+# SimulateDelete - means preserve the feed on DR (after cleaning it up), and mark as DELETED in DBCL.  Better for cfy environments
+Feed.deleteHandling: ${DMAAPBC_FEED_DELETE:-DeleteOnDR}
+
+################################################################################
+# MR Related Properties:
+#
+# ONAP Beijing and Casablanca are a single site deployment.
+MR.multisite:  false
+#
+#   Value of the CNAME DNS entry which resolves to the primary central MR cluster (when there are more than one central clusters).
+#   if there is only one MR cluster in an environment, set this to the DNS name for that cluster
+#
+MR.CentralCname:  ${DMAAPBC_MR_CNAME:-message-router}
+#
+#   MR Client Delete Level thoroughness:
+#   0 = don't delete
+#   1 = delete from persistent store
+#   2 = delete from persistent store (DB) and authorization store (AAF)
+MR.ClientDeleteLevel: 1
+#
+#   MR Topic Factory Namespace
+#
+MR.TopicFactoryNS: org.onap.dcae.dmaap.topicFactory
+#
+#   MR TopicMgr Role
+MR.TopicMgrRole:    org.onap.dmaap-bc-topic-mgr.client
+
+#   MR topic name style
+MR.topicStyle: FQTN_LEGACY_FORMAT
+
+#   MR topic ProjectID
+MR.projectID:  23456
+#
+# end of MR Related Properties
+################################################################################
+
+#
+#      The Role and credentials of the MirrorMaker Provisioner.  This is used by DMaaP Bus Controller to pub to the provisioning topic
+#   Not part of 1701
+#
+MM.ProvRole: ${DMAAPBC_MMPROV_ROLE:-org.onap.dmaap-bc-mm-prov.prov}
+MM.ProvUserMechId: ${DMAAPBC_MMPROV_ID:-dmaap-bc-mm-prov@dmaap-bc-mm-prov.onap.org}
+MM.ProvUserPwd: ${DMAAPBC_MMPROV_PWD:-demo123456!}
+#
+#      The Role of the MirrorMaker Agent. This is used by MM to sub to provisioning topic
+#
+MM.AgentRole: ${DMAAPBC_MMAGENT_ROLE:-org.onap.dmaap-bc-mm-prov.agent}
+#################
+#
+# CADI settings
+#
+# flag indication if CADI filtering is used
+enableCADI: ${DMAAPBC_ENABLE_CADI:-false}
+#
+# path to CADI properties
+cadi.properties: /opt/app/osaaf/local/org.onap.dmaap-bc.props
+
+#################
+# AAF Properties:
+UseAAF: ${DMAAPBC_USEAAF:-false}
+#
+# regarding password encryption:
+# In the dependencies that Maven retrieves (e.g., under dcae_dmaapbc/target/deps/ is a jar file cadi-core-version.jar.  Generate the key file with:
+#
+# java \u2013jar wherever/cadi-core-*.jar keygen keyfilename
+# chmod 400 keyfilename
+#
+# To encrypt a key:
+#
+# java \u2013jar wherever/cadi-core-*.jar digest password-to-encrypt keyfilename
+#
+# This will generate a string.  Put \u201Cenc:\u201D on the front of the string, and put the result in this properties file.
+#
+# Location of the Codec Keyfile which is used to decrypt passwords in this properties file before they are passed to AAF
+#
+# REF: https://wiki.domain.notset.com/display/cadi/CADI+Deployment
+#
+CredentialCodecKeyfile:        ${DMAAPBC_CODEC_KEYFILE:-etc/LocalKey}
+#
+# This overrides the Class used for Decryption.
+# This allows for a plugin encryption/decryption method if needed.
+# Call this Class for decryption at runtime.
+#AafDecryption.Class: com.company.proprietaryDecryptor
+
+#
+# This overrides the Class used for API Permission check.
+# This allows for a plugin policy check, if needed
+ApiPermission.Class: org.onap.dmaap.dbcapi.authentication.AllowAll
+
+# Namespace for URI values for API used to create AAF permissions
+# e.g. if ApiNamespace is X.Y..dmaapBC.api then for URI /topics we create an AAF perm X.Y..dmaapBC.api.topics
+ApiNamespace: ${DMAAPBC_API_NAMESPACE:-org.onap.dmaap-bc.api}
+#
+# URL of AAF environment to use.
+#
+aaf.URL:       ${DMAAPBC_AAF_URL:-https://aaf-onap-test.osaaf.org:8100}
+#
+# TopicMgr mechid@namespace
+#
+aaf.TopicMgrUser:      ${DMAAPBC_TOPICMGR_USER:-dmaap-bc-topic-mgr@dmaap-bc-topic-mgr.onap.org}
+#
+# TopicMgr password
+# 
+aaf.TopicMgrPassword:  ${DMAAPBC_TOPICMGR_PWD:-enc:l0ScEojNQiiKbbkuM6U1mtnrme69q960}
+#
+# Bus Controller Namespace Admin  mechid@namespace
+#
+aaf.AdminUser: ${DMAAPBC_ADMIN_USER:-aaf_admin@people.osaag.org}
+#
+# Bus Controller Namespace Admin password
+#
+aaf.AdminPassword:     ${DMAAPBC_ADMIN_PWD:-demo123456!}
+
+
+#
+# endof AAF Properties
+#################
+!EOF
similarity index 72%
rename from dmaap-bc/misc/log4j.properties.tmpl
rename to dmaap-bc/src/main/resources/misc/havecert.tmpl
index 2a30bf5..3d23c7b 100644 (file)
@@ -1,3 +1,4 @@
+#!/bin/bash
 #
 # ============LICENSE_START==========================================
 # org.onap.dmaap
 #
 #
 cat <<!EOF
-log4j.debug=FALSE
-log4j.rootLogger=INFO,Root
-
-log4j.appender.Root=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.Root.file=${DMAAPBC_LOGS:-logs}/buscontroller.log
-log4j.appender.Root.datePattern='.'yyyyMMdd
-log4j.appender.Root.append=true
-log4j.appender.Root.layout=org.apache.log4j.PatternLayout
-log4j.appender.Root.layout.ConversionPattern=%d %p %F %L %t %m%n
+echo Check for certificate
+TZ=GMT0
+cd /opt/app/dmaapbc;
+KEYSTORE=${DMAAPBC_KSTOREFILE:-etc/keystore}
+echo "KEYSTORE=$KEYSTORE"
+d=`dirname $KEYSTORE`
+ls -l $d
+if [ -f ${KEYSTORE} ]
+then
+       echo "Goodness: Found ${KEYSTORE}"
+       exit 0
+fi
+EMSG="`date '+%F %T,000'` WARN Certificate file $KEYSTORE is missing"
+echo $EMSG
+echo $EMSG >>${DMAAPBC_LOGS:-logs}/dmaapbc.log
+exit 1
 !EOF
diff --git a/dmaap-bc/src/main/resources/misc/logback.xml b/dmaap-bc/src/main/resources/misc/logback.xml
new file mode 100644 (file)
index 0000000..37a3af8
--- /dev/null
@@ -0,0 +1,348 @@
+
+<!--
+  ============LICENSE_START==========================================
+  org.onap.dmaap
+  ===================================================================
+  Copyright Â© 2018 AT&T Intellectual Property. All rights reserved.
+  ===================================================================
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+         http://www.apache.org/licenses/LICENSE-2.0
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  ============LICENSE_END============================================
+  ECOMP is a trademark and service mark of AT&T Intellectual Property.
+-->
+
+<configuration scan="true" scanPeriod="3 seconds">
+  <!--<jmxConfigurator /> -->
+  <!-- directory path for all other type logs -->
+  <property name="logDir" value="logs" />
+  <!--  specify the component name -->
+  <property name="componentName" value="ONAP"/>
+  
+  <!--  log file names -->
+  <property name="generalLogName" value="application" />
+  <property name="securityLogName" value="security" />
+  <property name="performanceLogName" value="performance" />
+  <property name="serverLogName" value="server" />
+  <property name="policyLogName" value="policy" />
+  <property name="errorLogName" value="error" />
+  <property name="metricsLogName" value="metrics" />
+  <property name="auditLogName" value="audit" />
+  <property name="debugLogName" value="debug" />
+  
+  <property name="defaultPattern" value="%date{ISO8601,UTC}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Timer}|%msg%n" />
+  
+  <property name="auditLoggerPattern" value="%X{BeginTimestamp}|%X{EndTimestamp}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{PartnerName}|%X{StatusCode}|%X{ResponseCode}|%X{ResponseDescription}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ElapsedTime}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Unused}|%X{ProcessKey}|%X{CustomField1}|%X{CustomField2}|%X{CustomField3}|%X{CustomField4}|%msg%n" />
+  <property name="metricsLoggerPattern" value="%X{BeginTimestamp}|%X{EndTimestamp}|%X{RequestId}|%X{ServiceInstanceId}|%thread|%X{VirtualServerName}|%X{ServiceName}|%X{PartnerName}|%X{TargetEntity}|%X{TargetServiceName}|%X{StatusCode}|%X{ResponseCode}|%X{ResponseDescription}|%X{InstanceUUID}|%.-5level|%X{AlertSeverity}|%X{ServerIPAddress}|%X{ElapsedTime}|%X{ServerFQDN}|%X{RemoteHost}|%X{ClassName}|%X{Unused}|%X{ProcessKey}|%X{TargetVirtualEntity}|%X{CustomField1}|%X{CustomField2}|%X{CustomField3}|%X{CustomField4}|%msg%n" />
+  <property name="errorLoggerPattern" value="%date{ISO8601,UTC}|%X{RequestId}|%thread|%X{ServiceName}|%X{PartnerName}|%X{TargetEntity}|%X{TargetServiceName}|%.-5level|%X{ErrorCode}|%X{ErrorDescription}|%msg%n" />
+  <property name="debugLoggerPattern" value="%date{ISO8601,UTC}|%X{RequestId}|%thread|%msg%n" />
+  
+  <property name="logDirectory" value="${logDir}/${componentName}" />
+  
+  <!-- Example evaluator filter applied against console appender -->
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>${defaultPattern}</pattern>
+    </encoder>
+  </appender>
+
+  <!-- ============================================================================ -->
+  <!-- EELF Appenders -->
+  <!-- ============================================================================ -->
+
+  <!-- The EELFAppender is used to record events to the general application 
+    log -->
+    
+    
+  <appender name="EELF"
+    class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${generalLogName}.log</file>
+    <rollingPolicy
+      class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+      <fileNamePattern>${logDirectory}/${generalLogName}.%i.log.zip
+      </fileNamePattern>
+      <minIndex>1</minIndex>
+      <maxIndex>9</maxIndex>
+    </rollingPolicy>
+    <triggeringPolicy
+      class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <maxFileSize>5MB</maxFileSize>
+    </triggeringPolicy>
+    <encoder>
+      <pattern>${defaultPattern}</pattern>
+    </encoder>
+  </appender>
+  
+  <appender name="asyncEELF" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELF" />
+  </appender>
+
+  <!-- EELF Security Appender. This appender is used to record security events 
+    to the security log file. Security events are separate from other loggers 
+    in EELF so that security log records can be captured and managed in a secure 
+    way separate from the other logs. This appender is set to never discard any 
+    events. -->
+  <appender name="EELFSecurity"
+    class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${securityLogName}.log</file>
+    <rollingPolicy
+      class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+      <fileNamePattern>${logDirectory}/${securityLogName}.%i.log.zip
+      </fileNamePattern>
+      <minIndex>1</minIndex>
+      <maxIndex>9</maxIndex>
+    </rollingPolicy>
+    <triggeringPolicy
+      class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <maxFileSize>5MB</maxFileSize>
+    </triggeringPolicy>
+    <encoder>
+      <pattern>${defaultPattern}</pattern>
+    </encoder>
+  </appender>
+  
+  <appender name="asyncEELFSecurity" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <discardingThreshold>0</discardingThreshold>
+    <appender-ref ref="EELFSecurity" />
+  </appender>
+
+  <!-- EELF Performance Appender. This appender is used to record performance 
+    records. -->
+  <appender name="EELFPerformance"
+    class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${performanceLogName}.log</file>
+    <rollingPolicy
+      class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+      <fileNamePattern>${logDirectory}/${performanceLogName}.%i.log.zip
+      </fileNamePattern>
+      <minIndex>1</minIndex>
+      <maxIndex>9</maxIndex>
+    </rollingPolicy>
+    <triggeringPolicy
+      class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <maxFileSize>5MB</maxFileSize>
+    </triggeringPolicy>
+    <encoder>
+      <pattern>${defaultPattern}</pattern>
+    </encoder>
+  </appender>
+  <appender name="asyncEELFPerformance" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELFPerformance" />
+  </appender>
+
+  <!-- EELF Server Appender. This appender is used to record Server related 
+    logging events. The Server logger and appender are specializations of the 
+    EELF application root logger and appender. This can be used to segregate Server 
+    events from other components, or it can be eliminated to record these events 
+    as part of the application root log. -->
+  <appender name="EELFServer"
+    class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${serverLogName}.log</file>
+    <rollingPolicy
+      class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+      <fileNamePattern>${logDirectory}/${serverLogName}.%i.log.zip
+      </fileNamePattern>
+      <minIndex>1</minIndex>
+      <maxIndex>9</maxIndex>
+    </rollingPolicy>
+    <triggeringPolicy
+      class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <maxFileSize>5MB</maxFileSize>
+    </triggeringPolicy>
+    <encoder>
+        <pattern>${defaultPattern}</pattern>
+    </encoder>
+  </appender>
+  <appender name="asyncEELFServer" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELFServer" />
+  </appender>
+
+  
+  <!-- EELF Policy Appender. This appender is used to record Policy engine 
+    related logging events. The Policy logger and appender are specializations 
+    of the EELF application root logger and appender. This can be used to segregate 
+    Policy engine events from other components, or it can be eliminated to record 
+    these events as part of the application root log. -->
+  <appender name="EELFPolicy"
+    class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${policyLogName}.log</file>
+    <rollingPolicy
+      class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+      <fileNamePattern>${logDirectory}/${policyLogName}.%i.log.zip
+      </fileNamePattern>
+      <minIndex>1</minIndex>
+      <maxIndex>9</maxIndex>
+    </rollingPolicy>
+    <triggeringPolicy
+      class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <maxFileSize>5MB</maxFileSize>
+    </triggeringPolicy>
+    <encoder>
+        <pattern>${defaultPattern}</pattern>
+    </encoder>
+  </appender>
+  <appender name="asyncEELFPolicy" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELFPolicy" />
+  </appender>
+  
+  
+  <!-- EELF Audit Appender. This appender is used to record audit engine 
+    related logging events. The audit logger and appender are specializations 
+    of the EELF application root logger and appender. This can be used to segregate 
+    Policy engine events from other components, or it can be eliminated to record 
+    these events as part of the application root log. -->
+    
+  <appender name="EELFAudit"
+    class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${auditLogName}.log</file>
+    <rollingPolicy
+      class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+      <fileNamePattern>${logDirectory}/${auditLogName}.%i.log.zip
+      </fileNamePattern>
+      <minIndex>1</minIndex>
+      <maxIndex>9</maxIndex>
+    </rollingPolicy>
+    <triggeringPolicy
+      class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <maxFileSize>5MB</maxFileSize>
+    </triggeringPolicy>
+    <encoder>
+    <pattern>${auditLoggerPattern}</pattern>
+    </encoder>
+  </appender>
+  <appender name="asyncEELFAudit" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELFAudit" />
+  </appender>
+
+<appender name="EELFMetrics"
+    class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${metricsLogName}.log</file>
+    <rollingPolicy
+      class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+      <fileNamePattern>${logDirectory}/${metricsLogName}.%i.log.zip
+      </fileNamePattern>
+      <minIndex>1</minIndex>
+      <maxIndex>9</maxIndex>
+    </rollingPolicy>
+    <triggeringPolicy
+      class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <maxFileSize>5MB</maxFileSize>
+    </triggeringPolicy>
+    <encoder>
+      <pattern>${metricsLoggerPattern}</pattern>
+    </encoder>
+  </appender>
+  
+  
+  <appender name="asyncEELFMetrics" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELFMetrics"/>
+  </appender>
+   
+  <appender name="EELFError"
+    class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${errorLogName}.log</file>
+    <rollingPolicy
+      class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+      <fileNamePattern>${logDirectory}/${errorLogName}.%i.log.zip
+      </fileNamePattern>
+      <minIndex>1</minIndex>
+      <maxIndex>9</maxIndex>
+    </rollingPolicy>
+    <triggeringPolicy
+      class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <maxFileSize>5MB</maxFileSize>
+    </triggeringPolicy>
+    <encoder>
+      <pattern>${errorLoggerPattern}</pattern>
+    </encoder>
+  </appender>
+  
+  <appender name="asyncEELFError" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELFError"/>
+  </appender>
+  
+   <appender name="EELFDebug"
+    class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <file>${logDirectory}/${debugLogName}.log</file>
+    <rollingPolicy
+      class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+      <fileNamePattern>${logDirectory}/${debugLogName}.%i.log.zip
+      </fileNamePattern>
+      <minIndex>1</minIndex>
+      <maxIndex>9</maxIndex>
+    </rollingPolicy>
+    <triggeringPolicy
+      class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+      <maxFileSize>5MB</maxFileSize>
+    </triggeringPolicy>
+    <encoder>
+      <pattern>${debugLoggerPattern}</pattern>
+    </encoder>
+  </appender>
+  
+  <appender name="asyncEELFDebug" class="ch.qos.logback.classic.AsyncAppender">
+    <queueSize>256</queueSize>
+    <appender-ref ref="EELFDebug" />
+    <includeCallerData>true</includeCallerData>
+  </appender>
+  
+  <!-- ============================================================================ -->
+  <!--  EELF loggers -->
+  <!-- ============================================================================ -->
+  <logger name="com.att.eelf" level="info" additivity="false">
+    <appender-ref ref="asyncEELF" />
+  </logger>
+  <logger name="com.att.eelf.security" level="info" additivity="false">
+    <appender-ref ref="asyncEELFSecurity" /> 
+  </logger>
+  <logger name="com.att.eelf.perf" level="info" additivity="false">
+    <appender-ref ref="asyncEELFPerformance" />
+  </logger>
+  <logger name="com.att.eelf.server" level="info" additivity="false">
+    <appender-ref ref="asyncEELFServer" />
+  </logger>
+  <logger name="com.att.eelf.policy" level="info" additivity="false">
+    <appender-ref ref="asyncEELFPolicy" />
+  </logger>
+
+  <logger name="com.att.eelf.audit" level="info" additivity="false">
+    <appender-ref ref="asyncEELFAudit" />
+  </logger>
+  
+  <logger name="com.att.eelf.metrics" level="info" additivity="false">
+        <appender-ref ref="asyncEELFMetrics" />
+  </logger>
+
+
+   <logger name="com.att.eelf.error" level="error" additivity="false">
+  <appender-ref ref="asyncEELFError" />
+  </logger>
+  
+   <logger name="com.att.eelf.debug" level="debug" additivity="false">
+        <appender-ref ref="asyncEELFDebug" />
+  </logger>
+
+
+  <root level="TRACE">
+    <appender-ref ref="asyncEELF" />
+  </root>
+
+</configuration>
+
diff --git a/dmaap-bc/src/main/resources/misc/schema_all.sql b/dmaap-bc/src/main/resources/misc/schema_all.sql
new file mode 100644 (file)
index 0000000..c294b09
--- /dev/null
@@ -0,0 +1,144 @@
+---
+-- ============LICENSE_START=======================================================
+-- OpenECOMP - org.openecomp.dmaapbc
+-- ================================================================================
+-- Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+-- ================================================================================
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+-- 
+--      http://www.apache.org/licenses/LICENSE-2.0
+-- 
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+-- ============LICENSE_END=========================================================
+---
+
+CREATE TABLE IF NOT EXISTS dcae_location       (
+       dcae_location_name      VARCHAR(100),
+       clli    VARCHAR(100),
+       dcae_layer      VARCHAR(100),
+       open_stack_availability_zone    VARCHAR(100),
+       last_mod        TIMESTAMP,
+       subnet          VARCHAR(100),
+  status               VARCHAR(100),
+       PRIMARY KEY(dcae_location_name)
+);
+CREATE TABLE IF NOT EXISTS dmaap       (
+       version VARCHAR(100),
+       topic_ns_root   VARCHAR(100),
+       dmaap_name      VARCHAR(100),
+       dr_prov_url     VARCHAR(200),
+       node_key        VARCHAR(100),
+       access_key_owner        VARCHAR(100),
+       last_mod        TIMESTAMP,
+       status          VARCHAR(100),
+       bridge_admin_topic      VARCHAR(100),
+       logging_url     VARCHAR(200)
+);
+CREATE TABLE IF NOT EXISTS dr_node     (
+       fqdn    VARCHAR(100),
+       dcae_location_name      VARCHAR(100),
+       host_name       VARCHAR(100),
+       version VARCHAR(100),
+       last_mod        TIMESTAMP,
+       status          VARCHAR(100),
+       PRIMARY KEY(fqdn)
+);
+CREATE TABLE IF NOT EXISTS dr_pub      (
+       dcae_location_name      VARCHAR(100),
+       username        VARCHAR(100),
+       userpwd VARCHAR(100),
+       feed_id VARCHAR(100),
+       pub_id  VARCHAR(100),
+       status  VARCHAR(100),
+       last_mod        TIMESTAMP,
+       PRIMARY KEY(pub_id)
+);
+CREATE TABLE IF NOT EXISTS dr_sub      (
+       owner   VARCHAR(100),
+       suspended       BOOLEAN,
+       status  VARCHAR(100),
+       use100  BOOLEAN,
+       dcae_location_name      VARCHAR(100),
+       username        VARCHAR(100),
+       userpwd VARCHAR(100),
+       feed_id VARCHAR(100),
+       delivery_u_r_l  VARCHAR(200),
+       log_u_r_l       VARCHAR(200),
+       sub_id  VARCHAR(100),
+       last_mod        TIMESTAMP,
+       guaranteed_delivery  BOOLEAN,
+       guaranteed_sequence  BOOLEAN,
+       privileged_subscriber  BOOLEAN,
+       decompress  BOOLEAN,
+       PRIMARY KEY(sub_id)
+);
+CREATE TABLE IF NOT EXISTS mr_client   (
+       dcae_location_name      VARCHAR(100),
+       fqtn    VARCHAR(100),
+       client_role     VARCHAR(100),
+       action  VARCHAR(300),
+       mr_client_id    VARCHAR(100),
+       status  VARCHAR(100),
+       topic_u_r_l     VARCHAR(200),
+       last_mod        TIMESTAMP,
+       client_identity varchar(100),
+       PRIMARY KEY(mr_client_id)
+);
+CREATE TABLE IF NOT EXISTS mr_cluster  (
+       last_mod        TIMESTAMP,
+       dcae_location_name      VARCHAR(100),
+       fqdn    VARCHAR(100),
+       topic_protocol  VARCHAR(100),
+       topic_port      VARCHAR(100),
+       status          VARCHAR(100),
+       replication_group       VARCHAR(100),
+       PRIMARY KEY(dcae_location_name)
+);
+CREATE TABLE IF NOT EXISTS feed        (
+       suspended       BOOLEAN,
+       subscribe_u_r_l VARCHAR(200),
+       feed_id VARCHAR(100),
+       feed_name       VARCHAR(100),
+       feed_version    VARCHAR(100),
+       feed_description        VARCHAR(1000),
+       owner   VARCHAR(100),
+       aspr_classification     VARCHAR(100),
+       publish_u_r_l   VARCHAR(200),
+       log_u_r_l       VARCHAR(200),
+       status  VARCHAR(100),
+       last_mod        TIMESTAMP,
+       format_uuid     VARCHAR(100),
+       PRIMARY KEY(feed_id)
+);
+CREATE TABLE IF NOT EXISTS topic       (
+       last_mod        TIMESTAMP,
+       fqtn    VARCHAR(100),
+       topic_name      VARCHAR(100),
+       topic_description       VARCHAR(1000),
+       tnx_enabled     VARCHAR(100),
+       owner   VARCHAR(100),
+       status  VARCHAR(100),
+       format_uuid     VARCHAR(100),
+       replication_case INT,
+       global_mr_u_r_l  VARCHAR(200),
+       partition_count VARCHAR(10) DEFAULT 2,
+  replication_count VARCHAR(10) DEFAULT 1,
+       publisher_role  VARCHAR(100),
+  subscriber_role VARCHAR(100),
+       PRIMARY KEY(fqtn)
+);
+CREATE TABLE IF NOT EXISTS mirror_maker        (
+       mm_name VARCHAR(100),
+       source_cluster  VARCHAR(100),
+       target_cluster  VARCHAR(100),
+       last_mod        TIMESTAMP,
+       status          VARCHAR(100),
+       topics          TEXT,
+  PRIMARY KEY(mm_name)
+);
diff --git a/dmaap-bc/src/main/webapp/HelloJetty.html b/dmaap-bc/src/main/webapp/HelloJetty.html
deleted file mode 100644 (file)
index 4d61636..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html>
-<!--
-  ============LICENSE_START=======================================================
-  OpenECOMP - org.openecomp.dmaapbc
-  ================================================================================
-  Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
-  ================================================================================
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-  
-       http://www.apache.org/licenses/LICENSE-2.0
-  
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  ============LICENSE_END=========================================================
-  -->
-
-<html>
-<head>
-<meta charset="ISO-8859-1">
-<title>Index</title>
-</head>
-<body>
-Hello Jetty!
-</body>
-</html>
diff --git a/dmaap-bc/src/main/webapp/WEB-INF/log4j2.xml b/dmaap-bc/src/main/webapp/WEB-INF/log4j2.xml
deleted file mode 100644 (file)
index bd7e9c5..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ============LICENSE_START=======================================================
-  Copyright (C) 2021 Nordix Foundation.
-  ================================================================================
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-
-  SPDX-License-Identifier: Apache-2.0
-  ============LICENSE_END=========================================================
-  -->
-
-<Configuration status="WARN">
-    <Appenders>
-        <!-- Console Appender -->
-        <Console name="STDOUT" target="SYSTEM_OUT">
-            <PatternLayout pattern="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
-        </Console>
-
-        <!-- Rolling File Appender -->
-        <RollingFile name="rollingFile">
-            <FileName>dmaapBC.log</FileName>
-            <FilePattern>${date:yyyy-MM}/dmaapBC-%d{yyyy-MM-dd}-%i.log.gz</FilePattern>
-            <PatternLayout>
-                <Pattern>[%d{HH:mm:ss:SSS}] - %-6p - %c.%M() - %m%n</Pattern>
-            </PatternLayout>
-            <Policies>
-                <SizeBasedTriggeringPolicy size="1000 KB"/>
-            </Policies>
-            <DefaultRolloverStrategy max="3"/>
-        </RollingFile>
-    </Appenders>
-    <Loggers>
-        <Logger name="org.openecomp.dcae.dmaapBC" level="debug" additivity="false">
-            <AppenderRef ref="rollingFile"/>
-        </Logger>
-        <Root level="ALL">
-            <AppenderRef ref="STDOUT"/>
-        </Root>
-    </Loggers>
-</Configuration>
\ No newline at end of file
diff --git a/dmaap-bc/src/main/webapp/WEB-INF/web.xml b/dmaap-bc/src/main/webapp/WEB-INF/web.xml
deleted file mode 100644 (file)
index 055fbf0..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ============LICENSE_START=======================================================
-  OpenECOMP - org.openecomp.dmaapbc
-  ================================================================================
-  Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
-  ================================================================================
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-  
-       http://www.apache.org/licenses/LICENSE-2.0
-  
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  ============LICENSE_END=========================================================
-  -->
-
-<!-- This web.xml file is not required when using Servlet 3.0 container,
-     see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
-<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
-  <servlet>
-    <servlet-name>Jersey Web Application</servlet-name>
-    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
-    <init-param>
-      <param-name>jersey.config.server.provider.packages</param-name>
-      <param-value>org.openecomp.dmaapBC</param-value>
-    </init-param>
-    <load-on-startup>1</load-on-startup>
-  </servlet>
-  <servlet-mapping>
-    <servlet-name>Jersey Web Application</servlet-name>
-    <url-pattern>/webapi/*</url-pattern>
-  </servlet-mapping>
-</web-app>
diff --git a/dmaap-bc/src/main/webapp/index.jsp b/dmaap-bc/src/main/webapp/index.jsp
deleted file mode 100644 (file)
index 3c20e06..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<%--
-  ============LICENSE_START=======================================================
-  org.onap.dcae
-  ================================================================================
-  Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
-  ================================================================================
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-  
-       http://www.apache.org/licenses/LICENSE-2.0
-  
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  ============LICENSE_END=========================================================
-  --%>
-
-<html>
-<body>
-    <h2>Jersey RESTful Web Application!</h2>
-    <p><a href="webapi/dmaap">Jersey resource</a>
-    <p>Visit <a href="http://jersey.java.net">Project Jersey website</a>
-    for more information on Jersey!
-</body>
-</html>
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafRoleTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafRoleTest.java
new file mode 100644 (file)
index 0000000..c53d8c6
--- /dev/null
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright 2019 IBM
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class AafRoleTest {
+
+    AafRole aafRole;
+
+    @Before
+    public void setUp() {
+        aafRole = new AafRole("testNs", "testRole");
+    }
+
+    @Test
+    public void testAafRole() {
+        aafRole.setNamespace("namespace");
+        aafRole.setRole("role");
+        assertEquals("namespace", aafRole.getNamespace());
+        assertEquals("role", aafRole.getRole());
+        assertEquals("namespace.role", aafRole.getFullyQualifiedRole());
+        assertNotNull(aafRole.toJSON());
+    }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafServiceFactoryTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafServiceFactoryTest.java
new file mode 100644 (file)
index 0000000..45ff2b1
--- /dev/null
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.onap.dmaap.dbcapi.aaf.AafService.ServiceType;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.BDDMockito.given;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AafServiceFactoryTest {
+
+    private static final String USE_AAF = "true";
+    private static final String AAF_URL = "https://aaf.url/api";
+    private static final String ADMIN_USER = "admin_user";
+    private static final String TOPIC_MANAGER = "topic_manager";
+    private static final String ADMIN_PASS = "admin_pass";
+    private static final String MANAGER_PASS = "manager_pass";
+    @Mock
+    private DmaapConfig dmaapConfig;
+    private AafServiceFactory aafServiceFactory;
+
+    @Before
+    public void setUp() throws Exception {
+        aafServiceFactory = new AafServiceFactory(dmaapConfig);
+    }
+
+    @Test
+    public void shouldBuildAafServiceForAafAdmin() {
+        givenDmaapConfig();
+
+        AafServiceImpl aafService = (AafServiceImpl) aafServiceFactory.initAafService(ServiceType.AAF_Admin);
+
+        assertEquals(ADMIN_USER, aafService.getIdentity());
+        assertEquals(AAF_URL, aafService.getAafUrl());
+        assertTrue(aafService.isUseAAF());
+    }
+
+    @Test
+    public void shouldBuildAafServiceForTopicManager() {
+        givenDmaapConfig();
+
+        AafServiceImpl aafService = (AafServiceImpl) aafServiceFactory.initAafService(ServiceType.AAF_TopicMgr);
+
+        assertEquals(TOPIC_MANAGER, aafService.getIdentity());
+        assertEquals(AAF_URL, aafService.getAafUrl());
+        assertTrue(aafService.isUseAAF());
+    }
+
+    @Test
+    public void shouldCorrectlyCreateCredentialsForAafAdmin() {
+        givenDmaapConfig();
+
+        AafServiceFactory.AafCred cred = aafServiceFactory.getCred(ServiceType.AAF_Admin);
+
+        assertEquals(ADMIN_USER, cred.getIdentity());
+        assertEquals(ADMIN_USER + ":" + new AafDecrypt().decrypt(ADMIN_PASS), cred.toString());
+    }
+
+    @Test
+    public void shouldCorrectlyCreateCredentialsForTopicManager() {
+        givenDmaapConfig();
+
+        AafServiceFactory.AafCred cred = aafServiceFactory.getCred(ServiceType.AAF_TopicMgr);
+
+        assertEquals(TOPIC_MANAGER, cred.getIdentity());
+        assertEquals(TOPIC_MANAGER + ":" + new AafDecrypt().decrypt(MANAGER_PASS), cred.toString());
+    }
+
+    private void givenDmaapConfig() {
+        given(dmaapConfig.getProperty("UseAAF", "false")).willReturn(USE_AAF);
+        given(dmaapConfig.getProperty("aaf.URL", "https://authentication.domain.netset.com:8100/proxy/")).willReturn(AAF_URL);
+        given(dmaapConfig.getProperty("aaf.AdminUser", "noMechId@domain.netset.com")).willReturn(ADMIN_USER);
+        given(dmaapConfig.getProperty("aaf.TopicMgrUser", "noMechId@domain.netset.com")).willReturn(TOPIC_MANAGER);
+        given(dmaapConfig.getProperty("aaf.AdminPassword", "notSet")).willReturn(ADMIN_PASS);
+        given(dmaapConfig.getProperty("aaf.TopicMgrPassword", "notSet")).willReturn(MANAGER_PASS);
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafServiceImplTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafServiceImplTest.java
new file mode 100644 (file)
index 0000000..ffd130e
--- /dev/null
@@ -0,0 +1,208 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.BDDMockito.then;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnitParamsRunner.class)
+public class AafServiceImplTest {
+
+    private static final String AAF_URL = "https://aaf.url/";
+    private static final String IDENTITY = "dmaap-bc@onap.org";
+    private static final boolean USE_AAF = true;
+    private static final int CREATED = 201;
+    private static final int OK = 200;
+    @Mock
+    private AafConnection aafConnection;
+    private AafServiceImpl aafService;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        given(aafConnection.postAaf(any(AafObject.class), anyString())).willReturn(CREATED);
+        given(aafConnection.delAaf(any(AafObject.class), anyString())).willReturn(OK);
+        aafService = new AafServiceImpl(USE_AAF, AAF_URL, IDENTITY, aafConnection);
+    }
+
+    @Test
+    public void shouldReturnCorrectIdentity() {
+
+        assertEquals(IDENTITY, aafService.getIdentity());
+    }
+
+    @Test
+    public void shouldAddPermission() {
+        DmaapPerm perm = new DmaapPerm("perm", "type", "action");
+
+        int status = aafService.addPerm(perm);
+
+        then(aafConnection).should().postAaf(perm, AAF_URL + "authz/perm");
+        assertEquals(CREATED, status);
+    }
+
+
+    @Test
+    public void shouldAddDmaapGrant() {
+        DmaapGrant grant = new DmaapGrant(new DmaapPerm("perm", "type", "action"), "roles");
+
+        int status = aafService.addGrant(grant);
+
+        then(aafConnection).should().postAaf(grant, AAF_URL + "authz/role/perm");
+        assertEquals(CREATED, status);
+    }
+
+    @Test
+    public void shouldAddUserRole() {
+        AafUserRole userRole = new AafUserRole("ident", "role");
+
+        int status = aafService.addUserRole(userRole);
+
+        then(aafConnection).should().postAaf(userRole, AAF_URL + "authz/userRole");
+        assertEquals(CREATED, status);
+    }
+
+    @Test
+    public void shouldAddRole() {
+        AafRole role = new AafRole("ns", "role");
+
+        int status = aafService.addRole(role);
+
+        then(aafConnection).should().postAaf(role, AAF_URL + "authz/role");
+        assertEquals(CREATED, status);
+    }
+
+    @Test
+    public void shouldAddNamespace() {
+        AafNamespace ns = new AafNamespace("ns", "ident");
+
+        int status = aafService.addNamespace(ns);
+
+        then(aafConnection).should().postAaf(ns, AAF_URL + "authz/ns");
+        assertEquals(CREATED, status);
+    }
+
+    @Test
+    public void shouldNotConnectToAafDuringCreate() {
+        aafService = new AafServiceImpl(false, AAF_URL, IDENTITY, aafConnection);
+        DmaapPerm perm = new DmaapPerm("perm", "type", "action");
+
+        int status = aafService.addPerm(perm);
+
+        verifyZeroInteractions(aafConnection);
+        assertEquals(CREATED, status);
+    }
+
+    @Test
+    @Parameters({"401", "403", "409", "200", "500"})
+    public void shouldHandleErrorDuringCreate(int aafServiceReturnedCode) {
+        given(aafConnection.postAaf(any(AafObject.class), anyString())).willReturn(aafServiceReturnedCode);
+        DmaapPerm perm = new DmaapPerm("perm", "type", "action");
+
+        int status = aafService.addPerm(perm);
+
+        assertEquals(aafServiceReturnedCode, status);
+    }
+
+    @Test
+    @Parameters({"401", "403", "404", "200", "500"})
+    public void shouldHandleErrorDuringDelete(int aafServiceReturnedCode) {
+        given(aafConnection.delAaf(any(AafObject.class), anyString())).willReturn(aafServiceReturnedCode);
+        DmaapPerm perm = new DmaapPerm("perm", "type", "action");
+
+        int status = aafService.delPerm(perm, false);
+
+        assertEquals(aafServiceReturnedCode, status);
+    }
+
+    @Test
+    public void shouldDeletePermission() {
+        DmaapPerm perm = new DmaapPerm("permName", "type", "action");
+
+        int status = aafService.delPerm(perm, false);
+
+        then(aafConnection).should().delAaf(any(AafEmpty.class), eq(AAF_URL + "authz/perm/permName/type/action"));
+        assertEquals(OK, status);
+    }
+
+    @Test
+    public void shouldDeletePermissionWithForce() {
+        DmaapPerm perm = new DmaapPerm("permName", "type", "action");
+
+        int status = aafService.delPerm(perm, true);
+
+        then(aafConnection).should().delAaf(any(AafEmpty.class), eq(AAF_URL + "authz/perm/permName/type/action?force=true"));
+        assertEquals(OK, status);
+    }
+
+    @Test
+    public void shouldDeleteNamespace() {
+        AafNamespace ns = new AafNamespace("nsName", "ident");
+
+        int status = aafService.delNamespace(ns, false);
+
+        then(aafConnection).should().delAaf(any(AafEmpty.class), eq(AAF_URL + "authz/ns/nsName"));
+        assertEquals(OK, status);
+    }
+
+    @Test
+    public void shouldDeleteNamespaceWithForce() {
+        AafNamespace ns = new AafNamespace("nsName", "ident");
+
+        int status = aafService.delNamespace(ns, true);
+
+        then(aafConnection).should().delAaf(any(AafEmpty.class), eq(AAF_URL + "authz/ns/nsName?force=true"));
+        assertEquals(OK, status);
+    }
+
+    @Test
+    public void shouldReturnExpectedCodeDuringPostWhenUseAffIsSetToFalse() {
+        aafService = new AafServiceImpl(false, AAF_URL, IDENTITY, aafConnection);
+        DmaapPerm perm = new DmaapPerm("perm", "type", "action");
+
+        int status = aafService.addPerm(perm);
+
+        assertEquals(CREATED, status);
+    }
+
+    @Test
+    public void shouldReturnExpectedCodeDuringDeleteWhenUseAffIsSetToFalse() {
+        aafService = new AafServiceImpl(false, AAF_URL, IDENTITY, aafConnection);
+        DmaapPerm perm = new DmaapPerm("perm", "type", "action");
+
+        int status = aafService.delPerm(perm, false);
+
+        assertEquals(OK, status);
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafUserRoleTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/aaf/AafUserRoleTest.java
new file mode 100644 (file)
index 0000000..88fff87
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 IBM Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Modifications Copyright (c) 2019 IBM
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.aaf;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class AafUserRoleTest {
+
+    AafUserRole aafUserRole;
+
+    @Before
+    public void setUp() {
+        aafUserRole = new AafUserRole("xyz", "admin");
+    }
+
+    @Test
+    public void testGetIdentity() {
+        aafUserRole.setIdentity("xyz");
+        assertEquals("xyz", aafUserRole.getIdentity());
+    }
+
+    @Test
+    public void testGetRole() {
+        aafUserRole.setRole("admin");
+        assertEquals("admin", aafUserRole.getRole());
+    }
+
+    @Test
+    public void toJSON() {
+        AafUserRole role = new AafUserRole("test", "admin");
+        assertThat(role.toJSON(), is(" { \"user\": \"test\", \"role\": \"admin\" }"));
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFishTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/AafLurAndFishTest.java
new file mode 100644 (file)
index 0000000..af6aba4
--- /dev/null
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.authentication;
+
+import static org.junit.Assert.assertTrue;
+
+import javax.ws.rs.core.Application;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AafLurAndFishTest extends JerseyTest {
+
+       @Before
+       public void setUp() throws Exception {
+               System.setProperty("ConfigFile", "src/test/resources/dmaapbc.properties");
+       }
+       
+       @Override
+       protected Application configure() {
+               return new ResourceConfig()
+                               .register( AafLurAndFish.class );
+       }
+       
+       @Test
+       public void constructorTest() {
+               try {
+               AafLurAndFish p = new AafLurAndFish();
+               } catch ( AuthenticationErrorException err ) {
+                       
+               }
+
+               assertTrue( true );
+       }       
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/AllowAllTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/AllowAllTest.java
new file mode 100644 (file)
index 0000000..9b6ee01
--- /dev/null
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.authentication;
+
+import org.junit.Test;
+
+import static org.junit.Assert.fail;
+
+public class AllowAllTest {
+
+    private AllowAll allowAll = new AllowAll();
+
+    @Test
+    public void check_shouldPassValidationForAllPerms() {
+        try {
+            allowAll.check(null, null, null);
+        } catch (Exception e) {
+            fail("No exception should be thrown");
+        }
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/ApiPermsTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/ApiPermsTest.java
new file mode 100644 (file)
index 0000000..ea749ce
--- /dev/null
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.authentication;
+
+import static org.junit.Assert.assertTrue;
+
+import javax.ws.rs.core.Application;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+
+import org.onap.dmaap.dbcapi.authentication.ApiPerms;
+
+
+public class ApiPermsTest extends JerseyTest {
+
+       @Override
+       protected Application configure() {
+               return new ResourceConfig()
+                               .register( ApiPerms.class );
+       }
+       
+       @Test
+       public void bootTest() {
+               ApiPerms p = new ApiPerms();
+               
+               p.setBootMap();
+               
+               p.setEnvMap();
+               
+
+               assertTrue( true );
+       }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/ApiPolicyTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/authentication/ApiPolicyTest.java
new file mode 100644 (file)
index 0000000..7b9fbb3
--- /dev/null
@@ -0,0 +1,82 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.authentication;
+
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+
+import java.util.Properties;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class ApiPolicyTest {
+
+    private Properties properties = new Properties();
+    private ApiPolicy apiPolicy;
+
+    @Test
+    public void check_shouldExecuteAuthorizationApi() throws Exception {
+        properties.put("ApiPermission.Class", "org.onap.dmaap.dbcapi.authentication.ApiPolicyTest$DummyApiAuthorization");
+        apiPolicy = new ApiPolicy(properties);
+
+        apiPolicy.check("mechId", "pwd", new DmaapPerm("api.perm", "*", "GET"));
+
+        assertTrue(((DummyApiAuthorization) apiPolicy.getPerm()).isCheckExecuted());
+    }
+
+    @Test
+    public void isPermissionClassSet_shouldReturnTrueForValidApiPermClass() {
+        properties.put("ApiPermission.Class", "org.onap.dmaap.dbcapi.authentication.ApiPolicyTest$DummyApiAuthorization");
+        apiPolicy = new ApiPolicy(properties);
+
+        assertTrue(apiPolicy.isPermissionClassSet());
+    }
+
+    @Test
+    public void isPermissionClassSet_shouldReturnFalseWhenPropertyIsNotSet() {
+        apiPolicy = new ApiPolicy(properties);
+
+        assertFalse(apiPolicy.isPermissionClassSet());
+    }
+
+    @Test
+    public void isPermissionClassSet_shouldReturnFalseWhenWrongClassIsSet() {
+        properties.put("ApiPermission.Class", "org.onap.dmaap.dbcapi.authentication.NotExisting");
+        apiPolicy = new ApiPolicy(properties);
+
+        assertFalse(apiPolicy.isPermissionClassSet());
+    }
+
+    public static class DummyApiAuthorization implements ApiAuthorizationCheckInterface {
+
+        private boolean checkExecuted = false;
+
+        @Override
+        public void check(String mechid, String pwd, DmaapPerm p) {
+            checkExecuted = true;
+        }
+
+        boolean isCheckExecuted() {
+            return checkExecuted;
+        }
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/client/DrProvConnectionTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/client/DrProvConnectionTest.java
new file mode 100644 (file)
index 0000000..4c44e0a
--- /dev/null
@@ -0,0 +1,136 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.client;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Pub;
+import org.onap.dmaap.dbcapi.model.DR_Sub;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.Feed;
+import org.onap.dmaap.dbcapi.service.DcaeLocationService;
+import org.onap.dmaap.dbcapi.service.MR_ClusterService;
+import org.onap.dmaap.dbcapi.service.TopicService;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+public class DrProvConnectionTest {
+
+       private static final String  fmt = "%24s: %s%n";
+       private static DmaapObjectFactory factory = new DmaapObjectFactory();
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       DrProvConnection ns;
+       MR_ClusterService mcs;
+       TopicService ts;
+
+       @Before
+       public void setUp() throws Exception {
+               ns = new DrProvConnection();
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.DrProvConnection", "get", "idNotSet@namespaceNotSet:pwdNotSet" ); 
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.DrProvConnection", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               String locname = "central-demo";
+
+               DcaeLocationService dls = new DcaeLocationService();
+               DcaeLocation loc = factory.genDcaeLocation( "central" );
+               dls.addDcaeLocation( loc );
+
+               ApiError err = new ApiError();
+               String[] hl = { "host1", "host2", "host3" };
+               ns.makeFeedConnection( );
+               ns.makeFeedConnection( "01" );
+               ns.makeSubPostConnection( "part0/part1/part2/part3/part4" );
+               ns.makeSubPutConnection( "44" );
+               ns.makeIngressConnection( "01", "aUser", "10.10.10.10", "aNode" );
+               ns.makeEgressConnection( "01", "aNode" );
+               ns.makeNodesConnection( "someVar" );
+               Feed feed = new Feed( "dgl feed 1" ,
+                                "v1.0",
+                                "dgl feed 1 for testing",
+                                "TEST",
+                                "unclassified"
+                    );
+            ArrayList<DR_Pub> pubs = new ArrayList<DR_Pub>();
+            pubs.add( new DR_Pub( "central-demo" ) );
+            feed.setPubs(pubs);
+
+               String resp = ns.doPostFeed( feed, err );
+               resp = ns.doPutFeed( feed, err );
+               resp = ns.doDeleteFeed( feed, err );
+
+               int i = ns.doXgressPost( err );
+
+               DR_Sub sub = factory.genDrSub( "central", feed.getFeedId() );
+               assertTrue( sub != null );
+               String sr = ns.doPostDr_Sub( sub, err );
+               /*
+                * TODO:
+                       - create a new DR_Sub based on a simulated response
+                       - update using ns.doPutDr_Sub( sub, err );
+                */
+       }
+
+       @Test
+       public void test4() {
+               ApiError err = new ApiError();
+               String resp = ns.doGetNodes( err );
+               ns.makeNodesConnection( "someVar", "host1|host2" );
+               resp = ns.doPutNodes( err );
+               try {
+                       InputStream is = new FileInputStream(new File("/src/test/resources/dmaapbc.properties"));
+                       String body = ns.bodyToString( is );
+               } catch ( FileNotFoundException fnfe ) {
+               }
+       }
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/client/MrProvConnectionTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/client/MrProvConnectionTest.java
new file mode 100644 (file)
index 0000000..0614cf9
--- /dev/null
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.client;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.service.DcaeLocationService;
+import org.onap.dmaap.dbcapi.service.MR_ClusterService;
+import org.onap.dmaap.dbcapi.service.TopicService;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+public class MrProvConnectionTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       MrProvConnection ns;
+       MR_ClusterService mcs;
+       TopicService ts;
+
+       @Before
+       public void setUp() throws Exception {
+               ns = new MrProvConnection();
+               ts = new TopicService();
+               mcs = new MR_ClusterService();
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.MrProvConnection", "get", "idNotSet@namespaceNotSet:pwdNotSet" ); 
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.MrProvConnection", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               String locname = "central-demo";
+
+               DcaeLocationService dls = new DcaeLocationService();
+               DcaeLocation loc = new DcaeLocation( "CLLI1234", "central-onap", locname, "aZone", "10.10.10.0/24" );
+               dls.addDcaeLocation( loc );
+
+               ApiError err = new ApiError();
+               
+               MR_Cluster cluster = new MR_Cluster( locname, "localhost", "http", "3904" );
+               mcs.addMr_Cluster( cluster, err );
+               ns.makeTopicConnection( cluster );
+               Topic topic = new Topic();
+               topic.setTopicName( "test5" );
+               String resp = ns.doPostTopic( topic, err );
+
+               try {
+                       InputStream is = new FileInputStream(new File("/src/test/resources/dmaapbc.properties"));
+                       String body = ns.bodyToString( is );
+               } catch ( FileNotFoundException fnfe ) {
+               }
+
+       }
+
+
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/client/MrTopicConnectionTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/client/MrTopicConnectionTest.java
new file mode 100644 (file)
index 0000000..af33ec6
--- /dev/null
@@ -0,0 +1,101 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.client;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.service.DcaeLocationService;
+import org.onap.dmaap.dbcapi.service.MR_ClusterService;
+import org.onap.dmaap.dbcapi.service.TopicService;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+public class MrTopicConnectionTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       MrTopicConnection ns;
+       MR_ClusterService mcs;
+       TopicService ts;
+
+       @Before
+       public void setUp() throws Exception {
+               ns = new MrTopicConnection( "aUser", "aPwd" );
+               ts = new TopicService();
+               mcs = new MR_ClusterService();
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.MrTopicConnection", "get", "idNotSet@namespaceNotSet:pwdNotSet" );        
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.MrTopicConnection", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               String locname = "central-demo";
+
+               DcaeLocationService dls = new DcaeLocationService();
+               DcaeLocation loc = new DcaeLocation( "CLLI1234", "central-onap", locname, "aZone", "10.10.10.0/24" );
+               dls.addDcaeLocation( loc );
+
+               ApiError err = new ApiError();
+               
+               MR_Cluster cluster = new MR_Cluster( locname, "localhost", "http", "3904");
+               mcs.addMr_Cluster( cluster, err );
+               ns.makeTopicConnection( cluster, "org.onap.dmaap.anInterestingTopic", "" );
+               String msg = "{ 'key': '1234', 'val': 'hello world' }";
+               ApiError e2 = ns.doPostMessage( msg );
+
+               try {
+                       InputStream is = new FileInputStream(new File("/src/test/resources/dmaapbc.properties"));
+                       String body = ns.bodyToString( is );
+               } catch ( FileNotFoundException fnfe ) {
+               }
+
+       }
+
+
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/DBFieldHandlerTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/DBFieldHandlerTest.java
new file mode 100644 (file)
index 0000000..f68b9ed
--- /dev/null
@@ -0,0 +1,110 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Modifications Copyright (c) 2019 IBM
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.database;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+import org.onap.dmaap.dbcapi.model.ReplicationType;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+public class DBFieldHandlerTest extends BaseLoggingClass {
+
+    private static final String fmt = "%24s: %s%n";
+
+    ReflectionHarness rh = new ReflectionHarness();
+
+    private static class TopicReplicationTypeHandler implements DBFieldHandler.SqlOp {
+        public Object get(ResultSet rs, int index) throws Exception {
+            int val = rs.getInt(index);
+
+            return (ReplicationType.valueOf(val));
+        }
+
+        public void set(PreparedStatement ps, int index, Object val) throws Exception {
+            if (val == null) {
+                ps.setInt(index, 0);
+                return;
+            }
+            @SuppressWarnings("unchecked")
+            ReplicationType rep = (ReplicationType) val;
+            ps.setInt(index, rep.getValue());
+        }
+    }
+
+    @Test
+    public void test1() {
+        // rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.MrTopicConnection", "get",
+        // "idNotSet@namespaceNotSet:pwdNotSet" );
+    }
+
+    @Test
+    public void test2() {
+        String v = "Validate";
+        // rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.MrTopicConnection", "set", v );
+    }
+
+    @Test
+    public void test3() {
+        try {
+            DBFieldHandler fh = new DBFieldHandler(String.class, "aString", 1);
+        } catch (Exception e) {
+            errorLogger.error("Error", e);
+        }
+    }
+
+    @Test
+    public void test4() {
+        try {
+            DBFieldHandler fh = new DBFieldHandler(String.class, "aString", 1, null);
+        } catch (Exception e) {
+            errorLogger.error("Error", e);
+        }
+    }
+
+    @Test
+    public void testfesc() {
+        String sampleString = "@xyz,ww;,";
+        String finalString = DBFieldHandler.fesc(sampleString);
+        assertEquals("@axyz@cww@s@c", finalString);
+    }
+
+    @Test
+    public void testfunesc() {
+        String sampleString = "@axyz@cww@s@c";
+        String convertedString = DBFieldHandler.funesc(sampleString);
+        assertEquals("@xyz,ww;,", convertedString);
+    }
+
+    @Test
+    public void testfescWithNull() {
+        String sampleString1 = DBFieldHandler.fesc(null);
+        String sampleString2 = DBFieldHandler.funesc(null);
+        assertNull(null, sampleString1);
+        assertNull(null, sampleString2);
+    }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/DBMapTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/DBMapTest.java
new file mode 100644 (file)
index 0000000..abd4aee
--- /dev/null
@@ -0,0 +1,84 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.database;
+
+import org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+import org.onap.dmaap.dbcapi.util.Singleton;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.util.*;
+
+public class DBMapTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+
+    private static Singleton<Dmaap> dmaap;
+    private static Map<String, DcaeLocation> dcaeLocations;
+
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               //rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.MrTopicConnection", "get", "idNotSet@namespaceNotSet:pwdNotSet" );      
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               //rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.MrTopicConnection", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               try {
+                dmaap = new DBSingleton<Dmaap>(Dmaap.class, "dmaap");
+                               Dmaap nd = new Dmaap.DmaapBuilder().createDmaap();
+                               dmaap.update(nd);
+               } catch (Exception e ) {
+               }
+               try {
+                dcaeLocations = new DBMap<DcaeLocation>(DcaeLocation.class, "dcae_location", "dcae_location_name");
+               } catch (Exception e ) {
+               }
+
+       }
+
+
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/DBSingletonTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/DBSingletonTest.java
new file mode 100644 (file)
index 0000000..003f250
--- /dev/null
@@ -0,0 +1,67 @@
+
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.database;
+
+import org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DBSingletonTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+
+       @Test
+       public void test3() {
+
+               try {
+                       DBSingleton<Dmaap> dmaap = new DBSingleton<Dmaap>(Dmaap.class, "dmaap");
+                       Dmaap d = new Dmaap.DmaapBuilder().createDmaap();
+                       dmaap.init( d );
+                       d = dmaap.get();
+                       d.setDmaapName( "foo" );
+                       dmaap.update( d );
+                       dmaap.remove();
+               } catch (Exception e ) {
+               }
+
+       }
+
+
+
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/LoadSchemaTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/LoadSchemaTest.java
new file mode 100644 (file)
index 0000000..99065d3
--- /dev/null
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.database;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+public class LoadSchemaTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       LoadSchema ls;
+
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.database.LoadSchema", "get", "idNotSet@namespaceNotSet:pwdNotSet" );
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.database.LoadSchema", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               LoadSchema.loadSchema();
+       }
+
+
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/TableHandlerTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/database/TableHandlerTest.java
new file mode 100644 (file)
index 0000000..978f3ad
--- /dev/null
@@ -0,0 +1,106 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.database;
+
+import org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.sql.*;
+
+public class TableHandlerTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+   private static class TopicReplicationTypeHandler implements DBFieldHandler.SqlOp {
+        public Object get(ResultSet rs, int index) throws Exception {
+            int val = rs.getInt(index);
+
+            return (ReplicationType.valueOf(val));
+        }
+        public void set(PreparedStatement ps, int index, Object val) throws Exception {
+            if (val == null) {
+                ps.setInt(index, 0);
+                return;
+            }
+            @SuppressWarnings("unchecked")
+            ReplicationType rep = (ReplicationType) val;
+            ps.setInt(index, rep.getValue());
+        }
+    }
+
+
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.MrTopicConnection", "get", "idNotSet@namespaceNotSet:pwdNotSet" );        
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               //rh.reflect( "org.onap.dmaap.dbcapi.aaf.client.MrTopicConnection", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               DBFieldHandler.SqlOp trth = new TopicReplicationTypeHandler();
+               TableHandler.setSpecialCase("topic", "replication_case", trth);
+
+               try {
+               ConnectionFactory cf = new ConnectionFactory();
+               TableHandler th = new TableHandler( cf, TopicReplicationTypeHandler.class, "foo", "bar" );
+               DBFieldHandler.SqlOp t = th.getSpecialCase( "foo", "bar" );
+               assert( trth == t );
+               } catch (Exception e ) {
+               }
+               try {
+
+               TableHandler th = new TableHandler( TopicReplicationTypeHandler.class, "foo", "bar" );
+               DBFieldHandler.SqlOp t = th.getSpecialCase( "foo", "bar" );
+               assert( trth == t );
+               } catch (Exception e ) {
+               }
+
+       }
+
+
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/BrTopicTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/BrTopicTest.java
new file mode 100644 (file)
index 0000000..11e7c85
--- /dev/null
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright 2019 IBM
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class BrTopicTest {
+
+    BrTopic brTopic;
+
+    @Before
+    public void setUp() {
+        brTopic = new BrTopic();
+    }
+
+    @Test
+    public void testGetBrSource() {
+        brTopic.setBrSource("brSource");
+        assertEquals("brSource", brTopic.getBrSource());
+    }
+
+    @Test
+    public void testGetBrTarget() {
+        brTopic.setBrTarget("brTarget");
+        assertEquals("brTarget", brTopic.getBrTarget());
+    }
+
+    @Test
+    public void testGetTopicCount() {
+        brTopic.setTopicCount(1);
+        assertEquals(1, brTopic.getTopicCount());
+    }
+
+    @Test
+    public void testGetMmAgentName() {
+        brTopic.setMmAgentName("Test");
+        assertEquals("Test", brTopic.getMmAgentName());
+    }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DRNodeTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DRNodeTest.java
new file mode 100644 (file)
index 0000000..ce23656
--- /dev/null
@@ -0,0 +1,86 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+public class DRNodeTest {
+       String f, d, h, v;
+
+       @Before
+       public void setUp() throws Exception {
+               v = "1";
+               f = "node01.onap.org";
+               h = "zlpdrns01.cloud.onap.org";
+               d = "central-onap";
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void testDRNodeClassDefaultConstructor() {
+
+               DR_Node t = new DR_Node();
+       
+               assertTrue( t.getFqdn() == null  );
+               assertTrue( t.getDcaeLocationName() == null  );
+               assertTrue( t.getHostName() == null  );
+               assertTrue( t.getVersion() == null  );
+       
+       }
+
+       @Test
+       public void testDRNodeClassConstructor() {
+
+               DR_Node t = new DR_Node( f, d, h, v );
+       
+               assertTrue( f.equals( t.getFqdn() ));
+               assertTrue( d.equals( t.getDcaeLocationName() ));
+               assertTrue( h.equals( t.getHostName() ));
+               assertTrue( v.equals( t.getVersion() ));
+       
+       }
+
+       @Test
+       public void testDRNodeClassSetters() {
+
+               DR_Node t = new DR_Node();
+
+               t.setFqdn( f );
+               assertTrue( f.equals( t.getFqdn() ));
+               t.setDcaeLocationName( d );
+               assertTrue( d.equals( t.getDcaeLocationName() ));
+               t.setHostName( h );
+               assertTrue( h.equals( t.getHostName() ));
+               t.setVersion( v );
+               assertTrue( v.equals( t.getVersion() ));
+       
+       }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DRPubTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DRPubTest.java
new file mode 100644 (file)
index 0000000..191f364
--- /dev/null
@@ -0,0 +1,90 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+public class DRPubTest {
+       String d, un, up, f, p;
+
+       @Before
+       public void setUp() throws Exception {
+               d = "central-onap";
+               un = "user1";
+               up = "secretW0rd";
+               f = "234";
+               p = "678";
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void testDRPubClassDefaultConstructor() {
+
+               DR_Pub t = new DR_Pub();
+       
+               assertTrue( t.getDcaeLocationName() == null  );
+               assertTrue( t.getUsername() == null  );
+               assertTrue( t.getUserpwd() == null  );
+               assertTrue( t.getFeedId() == null  );
+               assertTrue( t.getPubId() == null  );
+       
+       }
+
+       @Test
+       public void testDRPubClassConstructor() {
+
+               DR_Pub t = new DR_Pub( d, un, up, f, p );
+       
+               assertTrue( d.equals( t.getDcaeLocationName() ));
+               assertTrue( un.equals( t.getUsername() ));
+               assertTrue( up.equals( t.getUserpwd() ));
+               assertTrue( f.equals( t.getFeedId() ));
+               assertTrue( p.equals( t.getPubId() ));
+       }
+
+       @Test
+       public void testDRPubClassSetters() {
+
+               DR_Pub t = new DR_Pub();
+
+               t.setDcaeLocationName( d );
+               assertTrue( d.equals( t.getDcaeLocationName() ));
+               t.setUsername( un );
+               assertTrue( un.equals( t.getUsername() ));
+               t.setUserpwd( up );
+               assertTrue( up.equals( t.getUserpwd() ));
+               t.setFeedId( f );
+               assertTrue( f.equals( t.getFeedId() ));
+               t.setPubId( p );
+               assertTrue( p.equals( t.getPubId() ));
+       
+       }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DRSubTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DRSubTest.java
new file mode 100644 (file)
index 0000000..95cf9c9
--- /dev/null
@@ -0,0 +1,155 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+public class DRSubTest {
+       String d, un, up, f, du, lu, s, o;
+       Boolean u100, susp;
+
+       @Before
+       public void setUp() throws Exception {
+               d = "central-onap";
+               un = "user1";
+               up = "secretW0rd";
+               f = "22";
+               s = "sub123";
+               du = "sub.server.onap.org:8443/deliver/here";
+               lu = "https://drps.onap.org:8443/sublog/123";
+               u100 = true;
+               susp = false;
+               o = "joe";
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void testDRSubClassDefaultConstructor() {
+
+               DR_Sub t = new DR_Sub();
+       
+               assertTrue( t.getDcaeLocationName() == null  );
+               assertTrue( t.getUsername() == null  );
+               assertTrue( t.getUserpwd() == null  );
+               assertTrue( t.getFeedId() == null  );
+               assertTrue( t.getDeliveryURL() == null  );
+               assertTrue( t.getLogURL() == null  );
+               assertTrue( t.getSubId() == null  );
+               assertTrue( ! t.isUse100() );
+               assertTrue( ! t.isSuspended() );
+               assertTrue( t.getOwner() == null  );
+               assertTrue( t.isGuaranteedDelivery() == false );
+               assertTrue( t.isGuaranteedSequence() == false );
+               assertTrue( t.isPrivilegedSubscriber() == false );
+               assertTrue( t.isDecompress() == false );
+       }
+
+       @Test
+       public void testDRSubClassConstructor() {
+
+               DR_Sub t = new DR_Sub( d, un, up, f, du, lu, u100 );
+       
+               assertTrue( d.equals( t.getDcaeLocationName() ));
+               assertTrue( un.equals( t.getUsername() ));
+               assertTrue( up.equals( t.getUserpwd() ));
+               assertTrue( f.equals( t.getFeedId() ));
+               assertTrue( du.equals( t.getDeliveryURL() ) );
+               assertTrue( lu.equals( t.getLogURL() ) );
+               assertTrue( t.isUse100() );
+               assertTrue( ! t.isSuspended() );
+       }
+
+       @Test
+       public void testDRSubClassSetters() {
+
+               DR_Sub t = new DR_Sub();
+
+               t.setDcaeLocationName( d );
+               assertTrue( d.equals( t.getDcaeLocationName() ));
+               t.setUsername( un );
+               assertTrue( un.equals( t.getUsername() ));
+               t.setUserpwd( up );
+               assertTrue( up.equals( t.getUserpwd() ));
+               t.setFeedId( f );
+               assertTrue( f.equals( t.getFeedId() ));
+               t.setSubId( s );
+               assertTrue( s.equals( t.getSubId() ));
+               t.setDeliveryURL( du );
+               assertTrue( du.equals( t.getDeliveryURL() ) );
+               t.setLogURL( lu );
+               assertTrue( lu.equals( t.getLogURL() ) );
+               boolean v = true;
+               t.setGuaranteedDelivery( v );
+               assertTrue( t.isGuaranteedDelivery() == v );
+               t.setGuaranteedSequence(v);
+               assertTrue( t.isGuaranteedSequence() == v );
+               t.setPrivilegedSubscriber(v);
+               assertTrue( t.isPrivilegedSubscriber() == v );
+               t.setDecompress(v);
+               assertTrue( t.isDecompress() == v );
+       }
+
+       @Test
+       public void testJSONfromONAP() {
+       
+
+               DR_Sub s = new DR_Sub( d, un, up, f, du, lu, u100 );
+               String j = s.toProvJSON();
+
+               DR_Sub t = new DR_Sub( j );
+
+               assertTrue( un.equals( t.getUsername() ));
+               assertTrue( up.equals( t.getUserpwd() ));
+               //assertTrue( f.equals( t.getFeedId() ));
+               assertTrue( du.equals( t.getDeliveryURL() ) );
+               //assertTrue( lu.equals( t.getLogURL() ) );
+               assertTrue( ! t.isSuspended() );
+
+       }
+       
+       @Test
+       public void testJSONfromATT() {
+       
+
+               DR_Sub s = new DR_Sub( d, un, up, f, du, lu, u100 );
+
+               DR_Sub t = new DR_Sub( s.toProvJSONforATT() );
+
+               assertTrue( un.equals( t.getUsername() ));
+               assertTrue( up.equals( t.getUserpwd() ));
+               //assertTrue( f.equals( t.getFeedId() ));
+               assertTrue( du.equals( t.getDeliveryURL() ) );
+       //      assertTrue( lu.equals( t.getLogURL() ) );
+               assertTrue( ! t.isSuspended() );
+
+       }
+       
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DcaeLocationTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DcaeLocationTest.java
new file mode 100644 (file)
index 0000000..6652bb0
--- /dev/null
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+public class DcaeLocationTest {
+       String c, dl, dln, osz, s, edge;
+
+       @Before
+       public void setUp() throws Exception {
+               c = "ABCDE888NJ";
+               dl = "central-node";
+               edge = "local-node";
+               dln = "hollywood";
+               osz = "california";
+               s = "10.10.10.1";
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void testDcaeLocationDefaultConstructor() {
+
+               DcaeLocation t = new DcaeLocation();
+       
+               assertTrue( t.getClli() == null  );
+               assertTrue( t.getDcaeLayer() == null  );
+               assertTrue( t.getDcaeLocationName() == null  );
+               assertTrue( t.getOpenStackAvailabilityZone() == null  );
+               assertTrue( t.getSubnet() == null  );
+       
+       }
+
+       @Test
+       public void testDcaeLocationClassConstructor() {
+
+               DcaeLocation t = new DcaeLocation( c, dl, dln, osz, s );
+       
+               assertTrue( c.equals( t.getClli() ));
+               assertTrue( dl.equals( t.getDcaeLayer() ));
+               assertTrue( dln.equals( t.getDcaeLocationName() ));
+               assertTrue( osz.equals( t.getOpenStackAvailabilityZone() ));
+               assertTrue( s.equals( t.getSubnet() ));
+       }
+
+       @Test
+       public void testDmaapClassSetters() {
+
+               DcaeLocation t = new DcaeLocation();
+
+               t.setClli( c );
+               assertTrue( c.equals( t.getClli() ));
+               t.setDcaeLayer( dl );
+               assertTrue( dl.equals( t.getDcaeLayer() ));
+               assertTrue( t.isCentral() );
+               t.setDcaeLayer( edge );
+               assertTrue( edge.equals( t.getDcaeLayer() ));
+               assertTrue( t.isLocal() );
+               t.setDcaeLocationName( dln );
+               assertTrue( dln.equals( t.getDcaeLocationName() ));
+               t.setOpenStackAvailabilityZone( osz );
+               assertTrue( osz.equals( t.getOpenStackAvailabilityZone() ));
+               t.setSubnet( s );
+               assertTrue( s.equals( t.getSubnet() ));
+       }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DmaapTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/DmaapTest.java
new file mode 100644 (file)
index 0000000..cb0c22f
--- /dev/null
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DmaapTest {
+       private String ver, tnr, dn, dpu, lu, bat, nk, ako;
+
+       @Before
+       public void setUp() throws Exception {
+               ver = "1";
+               tnr = "org.onap.dmaap";
+               dn = "onap";
+               dpu = "https://drps.dmaap.onap.org:8081";
+               lu = "http://drps.dmaap.onap.org:8080/feedlog";
+               bat = "org.onap.dcae.dmaap.MM_AGENT_TOPIC";
+               nk = "foo";
+               ako = "bar";
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void testDmaapClassDefaultConstructor() {
+
+               Dmaap t = new Dmaap.DmaapBuilder().createDmaap();
+       
+               assertTrue( t.getVersion() == null  );
+               assertTrue( t.getTopicNsRoot() == null  );
+               assertTrue( t.getDmaapName() == null  );
+               assertTrue( t.getDrProvUrl() == null  );
+               assertTrue( t.getLoggingUrl() == null  );
+               assertTrue( t.getBridgeAdminTopic() == null  );
+               assertTrue( t.getNodeKey() == null  );
+               assertTrue( t.getAccessKeyOwner() == null  );
+       
+       }
+
+       @Test
+       public void testDmaapClassConstructor() {
+
+               Dmaap t = new Dmaap.DmaapBuilder().setVer(ver).setTnr(tnr).setDn(dn).setDpu(dpu).setLu(lu).setBat(bat).setNk(nk).setAko(ako).createDmaap();
+       
+               assertTrue( ver.equals( t.getVersion() ));
+               assertTrue( tnr.equals( t.getTopicNsRoot() ));
+               assertTrue( dn.equals( t.getDmaapName() ));
+               assertTrue( dpu.equals( t.getDrProvUrl() ));
+               assertTrue( lu.equals( t.getLoggingUrl() ));
+               assertTrue( bat.equals( t.getBridgeAdminTopic() ));
+               assertTrue( nk.equals( t.getNodeKey() ));
+               assertTrue( ako.equals( t.getAccessKeyOwner() ));
+       
+       }
+
+       @Test
+       public void testDmaapClassSetters() {
+
+               Dmaap t = new Dmaap.DmaapBuilder().createDmaap();
+
+               t.setVersion( ver );
+               assertTrue( ver.equals( t.getVersion() ));
+               t.setTopicNsRoot( tnr );
+               assertTrue( tnr.equals( t.getTopicNsRoot() ));
+               t.setDmaapName( dn );
+               assertTrue( dn.equals( t.getDmaapName() ));
+               t.setDrProvUrl( dpu );
+               assertTrue( dpu.equals( t.getDrProvUrl() ));
+               t.setLoggingUrl( lu );  
+               assertTrue( lu.equals( t.getLoggingUrl() ));
+               t.setBridgeAdminTopic( bat );
+               assertTrue( bat.equals( t.getBridgeAdminTopic() ));
+               t.setNodeKey( nk );
+               assertTrue( nk.equals( t.getNodeKey() ));
+               t.setAccessKeyOwner( ako );
+               assertTrue( ako.equals( t.getAccessKeyOwner() ));
+       
+       }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/FeedTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/FeedTest.java
new file mode 100644 (file)
index 0000000..4fdc9a1
--- /dev/null
@@ -0,0 +1,116 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import java.util.ArrayList;
+
+
+public class FeedTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       String n, v, d, o, a;
+
+       @Before
+       public void setUp() throws Exception {
+               System.setProperty("ConfigFile", "src/test/resources/dmaapbc.properties");
+               n = "Chicken Feed";
+               v = "1.0";
+               d = "A daily helping of chicken eating metrics";
+               o = "ab123";
+               a = "Unrestricted";
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.model.Feed", "get", null );  
+       
+       }
+
+       @Test
+       public void test2() {
+               Feed t = new Feed( n, v, d, o, a );
+
+               ArrayList<DR_Sub> subs = new ArrayList<DR_Sub>();
+               DR_Sub sub = new DR_Sub( "central", "user", "pwd", "22", "server.onap.org/deliv", "log.onap.org/logs", true );
+               subs.add( sub );
+               t.setSubs( subs );
+
+               assertTrue( n.equals( t.getFeedName() ));
+               assertTrue( v.equals( t.getFeedVersion() ));
+               assertTrue( d.equals( t.getFeedDescription() ));
+               assertTrue( o.equals( t.getOwner() ));
+               assertTrue( a.equals( t.getAsprClassification() ) );
+               assertTrue( ! t.isSuspended() );
+       }
+
+       @Test
+       public void test3() {
+
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.model.Feed", "set", v );
+       }
+
+       @Test
+       public void test4() {
+               String s = String.format( "{ \"%s\": \"%s\", \"%s\": \"%s\", \"%s\": \"%s\", \"%s\": \"%s\", \"%s\": false, \"%s\": { \"%s\": \"%s\", \"%s\": \"%s\", \"%s\": \"%s\" }, \"%s\": { \"%s\": \"%s\", \"%s\": [ { \"%s\": \"%s\", \"%s\": \"%s\" } ] } }",
+                               "name", n,
+                               "version", v,
+                               "description", d,
+                               "publisher", a,
+                               "suspend", 
+                               "links",
+                                       "publish", "https://feed.onap.org/publish/22",
+                                       "subscribe" , Feed.getSubProvURL( "22" ),
+                                       "log" , "https://feed.onap.org/log/22",
+                               "authorization",
+                                       "classification", a,
+                                       "endpoint_ids" , "id", "king", "password", "henry" );
+
+
+               Feed t = new Feed( s );
+
+               assertTrue( n.equals( t.getFeedName() ));
+               assertTrue( v.equals( t.getFeedVersion() ));
+               assertTrue( d.equals( t.getFeedDescription() ));
+               assertTrue( a.equals( t.getAsprClassification() ) );
+               assertTrue( ! t.isSuspended() );
+
+               String o = t.toString();
+
+       }
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/JUnitTestSuite.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/JUnitTestSuite.java
new file mode 100644 (file)
index 0000000..69e1b69
--- /dev/null
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import ch.qos.logback.classic.Logger;
+import junit.framework.TestSuite;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(Suite.class)
+@SuiteClasses({DmaapTest.class})
+public class JUnitTestSuite {
+
+    static Logger logger;
+
+    public static void main(String[] args) {
+        logger.info("Running the test suite");
+
+        TestSuite tstSuite = new TestSuite();
+        logger.info("Total Test Counts " + tstSuite.countTestCases());
+    }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/MRClientTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/MRClientTest.java
new file mode 100644 (file)
index 0000000..ba95a85
--- /dev/null
@@ -0,0 +1,111 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Modifications Copyright (C) 2019 IBM.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.model;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+public class MRClientTest {
+
+    private static final String fmt = "%24s: %s%n";
+
+    ReflectionHarness rh = new ReflectionHarness();
+
+    String d, t, f, c, m;
+
+    @Before
+    public void setUp() throws Exception {
+        d = "central-onap";
+        t = "org.onap.dmaap.interestingTopic";
+        f = "mrc.onap.org:3904/events/org.onap.dmaap.interestingTopic";
+        c = "publisher";
+        m = "m12345";
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void test1() {
+
+        // can't use simple reflection to test for null since null constructor
+        // initializes some fields.
+        // rh.reflect( "org.onap.dmaap.dbcapi.model.MR_Client", "get", null );
+        // so brute force instead...
+        String[] a = { "put", "view" };
+        MR_Client m = new MR_Client();
+
+        assertTrue(null == m.getDcaeLocationName());
+        assertTrue(null == m.getFqtn());
+        assertTrue(null == m.getClientRole());
+        assertTrue(null == m.getAction());
+
+    }
+
+    @Test
+    public void test2() {
+        String[] a = { "put", "view" };
+        MR_Client m = new MR_Client(d, f, c, a);
+
+        assertTrue(d.equals(m.getDcaeLocationName()));
+        assertTrue(f.equals(m.getFqtn()));
+        assertTrue(c.equals(m.getClientRole()));
+        String[] ma = m.getAction();
+        assertTrue(a.length == ma.length);
+        for (int i = 0; i < a.length; i++) {
+            assertTrue(a[i].equals(ma[i]));
+        }
+    }
+
+    @Test
+    public void test3() {
+
+        String v = "Validate";
+        rh.reflect("org.onap.dmaap.dbcapi.model.MR_Client", "set", v);
+    }
+
+    @Test
+    public void test4() {
+        MR_Client mrClient = new MR_Client();
+        String stringArray[] = { "test" };
+        mrClient.setAction(stringArray);
+        mrClient.hasAction("");
+        mrClient.setMrClientId("mrClientId");
+        mrClient.setTopicURL("testTopicURL");
+        mrClient.setClientIdentity("clientIdentity");
+
+        assertEquals("clientIdentity", mrClient.getClientIdentity());
+        assertEquals("testTopicURL", mrClient.getTopicURL());
+        assertEquals("mrClientId", mrClient.getMrClientId());
+        assertEquals(false, mrClient.isPublisher());
+        assertEquals(false, mrClient.isSubscriber());
+        assertEquals("test", mrClient.getAction()[0]);
+
+    }
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/MR_ClusterTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/MR_ClusterTest.java
new file mode 100644 (file)
index 0000000..2200627
--- /dev/null
@@ -0,0 +1,135 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+public class MR_ClusterTest {
+       String d, fqdn, repGrp, p1, p2, prot, p0;
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       @Before
+       public void setUp() throws Exception {
+               d = "central-onap";
+               fqdn = "mr.onap.org";
+               repGrp = "zeppelin";
+               prot = "http";
+               p0 = "3904";
+               p1 = "9092";
+               p2 = "2323";
+               
+       
+               
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void testMR_ClusterClassDefaultConstructor() {
+
+               MR_Cluster t = new MR_Cluster();
+       
+               assertTrue( t.getDcaeLocationName() == null  );
+               assertTrue( t.getFqdn() == null  );
+       
+       }
+
+       @Test
+       public void testMR_ClusterClassConstructor() {
+
+               MR_Cluster t = new MR_Cluster( d, fqdn, prot, p0);
+       
+               assertTrue( t.getDcaeLocationName() == d  );
+               assertTrue( t.getFqdn() == fqdn  );
+               assertTrue( t.getTopicProtocol() == prot );
+               assertTrue( t.getTopicPort() == p0 );
+               
+               // pass null params to trigger default settings
+                t = new MR_Cluster( d, fqdn, null, null );
+               
+               assertTrue( t.getDcaeLocationName() == d  );
+               assertTrue( t.getFqdn() == fqdn  );
+               assertTrue( t.getTopicProtocol() != null );
+               assertTrue( t.getTopicPort() != null );
+       }
+       
+       @Test
+       public void testMR_ClusterManyArgsClassConstructor() {
+
+               MR_Cluster t = new MR_Cluster( d, fqdn, prot, p0, repGrp, p1, p2 );
+       
+               assertTrue( t.getDcaeLocationName() == d  );
+               assertTrue( t.getFqdn() == fqdn  );
+               assertTrue( t.getTopicProtocol() == prot );
+               assertTrue( t.getTopicPort() == p0 );
+               assertTrue( t.getReplicationGroup() == repGrp  );
+               assertTrue( t.getSourceReplicationPort() == p1  );
+               assertTrue( t.getTargetReplicationPort() == p2 );
+               
+               // pass null params to trigger default settings
+               t = new MR_Cluster( d, fqdn, null, null, null, null, null );
+               
+               assertTrue( t.getDcaeLocationName() == d  );
+               assertTrue( t.getFqdn() == fqdn  );
+               assertTrue( t.getTopicProtocol() != null );
+               assertTrue( t.getTopicPort() != null );
+               assertTrue( t.getReplicationGroup() != null  );
+               assertTrue( t.getSourceReplicationPort() != null  );
+               assertTrue( t.getTargetReplicationPort() != null );
+       }
+
+       @Test
+       public void testw3() {
+
+               MR_Cluster t = new MR_Cluster();
+       
+               assertTrue( t.getDcaeLocationName() == null  );
+               assertTrue( t.getFqdn() == null  );
+
+               String override = "cluster2.onap.org";
+               String  topic2 = "org.onap.topic2";
+               String fqtn = t.genTopicURL( override, topic2 );        
+               assertTrue( fqtn.contains( override) && fqtn.contains(topic2));
+               
+               fqtn = t.genTopicURL( null, "org.onap.topic2" );
+               assertTrue(fqtn.contains(topic2));
+       }
+
+
+
+       @Test
+       public void testsetter() {
+
+               String v = "validate";
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.model.MR_Cluster", "set", v );       
+       
+       }
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/MirrorMakerTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/MirrorMakerTest.java
new file mode 100644 (file)
index 0000000..39de2be
--- /dev/null
@@ -0,0 +1,101 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import java.util.ArrayList;
+
+
+public class MirrorMakerTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.model.MirrorMaker", "get", null );   
+       
+       }
+       @Test
+       public void test2() {
+
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.model.MirrorMaker", "set", v );
+       }
+
+       @Test
+       public void test3() {
+               String f = "org.onap.interestingTopic";
+               String c1 =  "cluster1.onap.org";
+               String c2 =  "cluster2.onap.org";
+               MirrorMaker t = new MirrorMaker( c1, c2 );
+               String m = t.getMmName();
+
+               MirrorMaker.genKey( c1, c2 );
+
+               assertTrue( c1.equals( t.getSourceCluster() ));
+               assertTrue( c2.equals( t.getTargetCluster() ));
+       }
+
+
+       @Test
+       public void test4() {
+               String f = "org.onap.interestingTopic";
+               String c1 =  "cluster1.onap.org";
+               String c2 =  "cluster2.onap.org";
+               String p1 = "9092";
+               String p2 = "2081";
+               MirrorMaker t = new MirrorMaker( c1, c2 );
+               String m = t.getMmName();
+       
+
+               ArrayList<String> topics = new ArrayList<String>();
+               topics.add( f );
+               t.setTopics( topics );
+               t.addTopic( "org.onap.topic2" );
+
+               int i = t.getTopicCount();
+
+               String s = t.getWhitelistUpdateJSON();
+
+               s = t.createMirrorMaker(p1, p2);
+
+       }
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/TestRunner.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/TestRunner.java
new file mode 100644 (file)
index 0000000..16b48e3
--- /dev/null
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import ch.qos.logback.classic.Logger;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+import org.onap.dmaap.dbcapi.logging.BaseLoggingClass;
+
+public class TestRunner extends BaseLoggingClass {
+
+    private static Logger logger;
+
+    public static void main(String[] args) {
+
+        Result result = JUnitCore.runClasses(JUnitTestSuite.class);
+        for (Failure failure : result.getFailures()) {
+            logger.info(failure.toString());
+
+        }
+        logger.info(String.valueOf(result.wasSuccessful()));
+    }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/TopicTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/model/TopicTest.java
new file mode 100644 (file)
index 0000000..5da3aed
--- /dev/null
@@ -0,0 +1,87 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Modifications Copyright (c) 2019 IBM
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+public class TopicTest {
+
+    ReflectionHarness rh = new ReflectionHarness();
+
+    String f, t, d, e, o;
+
+    @Before
+    public void setUp() throws Exception {
+        f = "org.onap.dmaap.interestingTopic";
+        t = "interestingTopic";
+        d = "A so very interesting topic";
+        e = "Yes";
+        o = "m12345";
+    }
+
+    @After
+    public void tearDown() throws Exception {
+    }
+
+    @Test
+    public void test1() {
+        rh.reflect("org.onap.dmaap.dbcapi.model.Topic", "get", null);
+    }
+
+    @Test
+    public void test2() {
+        Topic obj = new Topic(f, t, d, e, o);
+        assertTrue(f.equals(obj.getFqtn()));
+        assertTrue(t.equals(obj.getTopicName()));
+        assertTrue(d.equals(obj.getTopicDescription()));
+        assertTrue(e.equals(obj.getTnxEnabled()));
+        assertTrue(o.equals(obj.getOwner()));
+    }
+
+    @Test
+    public void test3() {
+        String v = "Validate";
+        rh.reflect("org.onap.dmaap.dbcapi.model.Topic", "set", v);
+    }
+
+    @Test
+    public void getNumClientsHavingMRClientListNull() {
+        Topic obj = new Topic(f, t, d, e, o);
+        obj.setClients(null);
+        assertEquals(0, obj.getNumClients());
+    }
+
+    @Test
+    public void testTopicInitializationWithInvalidJsonString() {
+        String json = "{\"key\":\"value\"";
+        Topic obj = new Topic(json);
+        assertEquals(DmaapObject_Status.INVALID, obj.getStatus());
+    }
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilterTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthenticationFilterTest.java
new file mode 100644 (file)
index 0000000..76fe914
--- /dev/null
@@ -0,0 +1,195 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.onap.aaf.cadi.filter.CadiFilter;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AAFAuthenticationFilterTest {
+
+    @Spy
+    private AAFAuthenticationFilter filter;
+    @Mock
+    private FilterConfig filterConfig;
+    @Mock
+    private CadiFilter cadiFilterMock;
+    @Mock
+    private HttpServletRequest servletRequest;
+    @Mock
+    private HttpServletResponse servletResponse;
+    @Mock
+    private FilterChain filterChain;
+    @Mock
+    private DmaapConfig dmaapConfig;
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Before
+    public void setUp() throws Exception {
+        doReturn(dmaapConfig).when(filter).getConfig();
+    }
+
+    @Test
+    public void init_shouldNotInitializeCADI_whenAafIsNotUsed() throws Exception {
+        //given
+        doReturn("false").when(dmaapConfig).getProperty(eq(AAFAuthenticationFilter.CADI_AUTHN_FLAG), anyString());
+
+        //when
+        filter.init(filterConfig);
+
+        //then
+        assertFalse(filter.isCadiEnabled());
+        assertNull(filter.getCadiFilter());
+    }
+
+    @Test
+    public void doFilter_shouldSkipCADI_whenAafIsNotUsed() throws Exception {
+        //given
+        doReturn("false").when(dmaapConfig).getProperty(eq(AAFAuthenticationFilter.CADI_AUTHN_FLAG), anyString());
+        filter.init(filterConfig);
+        filter.setCadiFilter(cadiFilterMock);
+
+        //when
+        filter.doFilter(servletRequest, servletResponse, filterChain);
+
+        //then
+        verify(filterChain).doFilter(servletRequest,servletResponse);
+        verifyZeroInteractions(cadiFilterMock,servletRequest,servletResponse);
+    }
+
+    @Test
+    public void init_shouldFail_whenAafIsUsed_andCadiPropertiesHasNotBeenSet() throws Exception {
+        //given
+        doReturn("true").when(dmaapConfig).getProperty(eq(AAFAuthenticationFilter.CADI_AUTHN_FLAG), anyString());
+        doReturn("").when(dmaapConfig).getProperty(AAFAuthenticationFilter.CADI_PROPERTIES);
+
+        //then
+        thrown.expect(ServletException.class);
+        thrown.expectMessage("Cannot initialize CADI filter.CADI properties not available.");
+
+        //when
+        filter.init(filterConfig);
+    }
+
+    @Test
+    public void init_shouldFail_whenAafIsUsed_andInvalidCadiPropertiesSet() throws Exception {
+        //given
+        String invalidFilePath = "src/test/resources/notExisting.properties";
+        doReturn("true").when(dmaapConfig).getProperty(eq(AAFAuthenticationFilter.CADI_AUTHN_FLAG), anyString());
+        doReturn(invalidFilePath).when(dmaapConfig).getProperty(AAFAuthenticationFilter.CADI_PROPERTIES);
+
+        //then
+        thrown.expect(ServletException.class);
+        thrown.expectMessage("Could not load CADI properties file: "+invalidFilePath);
+
+        //when
+        filter.init(filterConfig);
+    }
+
+  /*
+   * See https://jira.onap.org/browse/DMAAP-1361  for why this is commented out
+    @Test
+    public void init_shouldInitializeCADI_whenAafIsUsed_andValidCadiPropertiesSet() throws Exception {
+        //given
+        doReturn("true").when(dmaapConfig).getProperty(eq(AAFAuthenticationFilter.CADI_AUTHN_FLAG), anyString());
+        doReturn("src/test/resources/cadi.properties").when(dmaapConfig).getProperty(AAFAuthenticationFilter.CADI_PROPERTIES);
+
+        //when
+        filter.init(filterConfig);
+
+        //then
+        assertTrue(filter.isCadiEnabled());
+        assertNotNull(filter.getCadiFilter());
+    }
+
+    @Test
+    public void doFilter_shouldUseCADIfilter_andAuthenticateUser_whenAAFisUsed_andUserIsValid() throws Exception{
+        //given
+        initCADIFilter();
+        doReturn(200).when(servletResponse).getStatus();
+
+        //when
+        filter.doFilter(servletRequest,servletResponse,filterChain);
+
+        //then
+        verify(cadiFilterMock).doFilter(servletRequest,servletResponse,filterChain);
+        verify(servletResponse).getStatus();
+        verifyNoMoreInteractions(servletResponse);
+        verifyZeroInteractions(filterChain, servletRequest);
+    }
+
+    @Test
+    public void doFilter_shouldUseCADIfilter_andReturnAuthenticationError_whenAAFisUsed_andUserInvalid() throws Exception{
+        //given
+        String errorResponseJson = "{\"code\":401,\"message\":\"invalid or no credentials provided\",\"fields\":\"Authentication\",\"2xx\":false}";
+        initCADIFilter();
+        doReturn(401).when(servletResponse).getStatus();
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        doReturn(pw).when(servletResponse).getWriter();
+
+        //when
+        filter.doFilter(servletRequest,servletResponse,filterChain);
+
+        //then
+        verify(cadiFilterMock).doFilter(servletRequest,servletResponse,filterChain);
+        verify(servletResponse).getStatus();
+        verify(servletResponse).setContentType("application/json");
+        verifyZeroInteractions(filterChain, servletRequest);
+        assertEquals(errorResponseJson, sw.toString());
+    }
+
+    private void initCADIFilter() throws Exception{
+        doReturn("true").when(dmaapConfig).getProperty(eq(AAFAuthenticationFilter.CADI_AUTHN_FLAG), anyString());
+        doReturn("src/test/resources/cadi.properties").when(dmaapConfig).getProperty(AAFAuthenticationFilter.CADI_PROPERTIES);
+        filter.init(filterConfig);
+        filter.setCadiFilter(cadiFilterMock);
+    }
+*/
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilterTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/AAFAuthorizationFilterTest.java
new file mode 100644 (file)
index 0000000..ba11b01
--- /dev/null
@@ -0,0 +1,172 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import com.sun.security.auth.UserPrincipal;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import org.onap.dmaap.dbcapi.util.PermissionBuilder;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AAFAuthorizationFilterTest {
+
+    @Spy
+    private AAFAuthorizationFilter filter;
+    @Mock
+    private FilterConfig filterConfig;
+    @Mock
+    private HttpServletRequest servletRequest;
+    @Mock
+    private HttpServletResponse servletResponse;
+    @Mock
+    private FilterChain filterChain;
+    @Mock
+    private DmaapConfig dmaapConfig;
+    @Mock
+    private PermissionBuilder permissionBuilder;
+    @Mock
+    private DmaapService dmaapService;
+
+    @Before
+    public void setUp() throws Exception {
+        filter.setPermissionBuilder(permissionBuilder);
+        doReturn(dmaapConfig).when(filter).getConfig();
+        doReturn(dmaapService).when(filter).getDmaapService();
+    }
+
+    @Test
+    public void init_shouldNotInitializePermissionBuilder_whenAAFnotUsed() throws Exception {
+        //given
+        filter.setPermissionBuilder(null);
+        configureAAFUsage(false);
+
+        //when
+        filter.init(filterConfig);
+
+        //then
+        assertNull(filter.getPermissionBuilder());
+    }
+
+    @Test
+    public void init_shouldInitializePermissionBuilder_whenAAFisUsed() throws Exception {
+        //given
+        filter.setPermissionBuilder(null);
+        configureAAFUsage(true);
+        //doReturn(provideEmptyInstance()).when(dmaapService).getDmaap();
+        when(dmaapService.getDmaap()).thenReturn(mock(Dmaap.class));
+
+        //when
+        filter.init(filterConfig);
+
+        //then
+        assertNotNull(permissionBuilder);
+    }
+
+    @Test
+    public void doFilter_shouldSkipAuthorization_whenAAFnotUsed() throws Exception {
+        //given
+        filter.setCadiEnabled(false);
+
+        //when
+        filter.doFilter(servletRequest,servletResponse,filterChain);
+
+        //then
+        verify(filterChain).doFilter(servletRequest,servletResponse);
+        verifyNoMoreInteractions(filterChain);
+        verifyZeroInteractions(permissionBuilder, servletRequest, servletResponse);
+    }
+
+    @Test
+    public void doFilter_shouldPass_whenUserHasPermissionToResourceEndpoint() throws Exception {
+        //given
+        String user = "johnny";
+        String permission = "org.onap.dmaap-bc.api.topics|mr|GET";
+        when(permissionBuilder.buildPermission(servletRequest)).thenReturn(permission);
+        configureServletRequest(permission, user, true);
+        filter.setCadiEnabled(true);
+
+        //when
+        filter.doFilter(servletRequest,servletResponse,filterChain);
+
+        //then
+        verify(filterChain).doFilter(servletRequest,servletResponse);
+        verify(permissionBuilder).updateDmaapInstance();
+        verifyZeroInteractions(servletResponse);
+    }
+
+    @Test
+    public void doFilter_shouldReturnError_whenUserDontHavePermissionToResourceEndpoint() throws Exception {
+        //given
+        String user = "jack";
+        String permission = "org.onap.dmaap-bc.api.topics|mr|GET";
+        when(permissionBuilder.buildPermission(servletRequest)).thenReturn(permission);
+        configureServletRequest(permission, user, false);
+        filter.setCadiEnabled(true);
+
+        String errorMsgJson = "{\"code\":403,\"message\":\"User "+user+" does not have permission "
+            + permission +"\",\"fields\":\"Authorization\",\"2xx\":false}";
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        when(servletResponse.getWriter()).thenReturn(pw);
+
+        //when
+        filter.doFilter(servletRequest,servletResponse,filterChain);
+
+        //then
+        verifyZeroInteractions(filterChain);
+        verify(permissionBuilder).updateDmaapInstance();
+        verify(servletResponse).setStatus(403);
+        assertEquals(errorMsgJson, sw.toString());
+    }
+
+    private void configureServletRequest(String permission, String user, boolean isUserInRole) {
+        when(servletRequest.getUserPrincipal()).thenReturn(new UserPrincipal(user));
+        when(servletRequest.isUserInRole(permission)).thenReturn(isUserInRole);
+    }
+
+    private void configureAAFUsage(Boolean isUsed) {
+        doReturn(isUsed.toString()).when(dmaapConfig).getProperty(eq(AAFAuthorizationFilter.CADI_AUTHZ_FLAG), anyString());
+    }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DR_NodeResourceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DR_NodeResourceTest.java
new file mode 100644 (file)
index 0000000..f131d8f
--- /dev/null
@@ -0,0 +1,236 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Node;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Response;
+
+import static javax.ws.rs.client.Entity.entity;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+
+public class DR_NodeResourceTest {
+
+    private static final DmaapObjectFactory DMAAP_OBJECT_FACTORY = new DmaapObjectFactory();
+    private static FastJerseyTestContainer testContainer;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        DatabaseClass.getDmaap().init(DMAAP_OBJECT_FACTORY.genDmaap());
+
+        testContainer = new FastJerseyTestContainer(new ResourceConfig()
+                .register(DR_NodeResource.class));
+        testContainer.init();
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        testContainer.destroy();
+        /*TODO: Cannot cleanup yet until still other Resources tests depends on the static DB content
+
+        DatabaseClass.clearDatabase();
+        DatabaseClass.getDmaap().remove();*/
+    }
+
+    @Test
+    public void getDr_Nodes_test() {
+        Response response = testContainer.target("dr_nodes").request().get(Response.class);
+        System.out.println("GET dr_subs response=" + response.getStatus());
+
+        assertEquals(200, response.getStatus());
+        assertTrue(response.hasEntity());
+    }
+
+    @Test
+    public void addDr_Node_shouldReturnError_whenNoLocationAndFqdnProvided() {
+        DR_Node node = new DR_Node(null, null, "hostName", "1.0");
+        Entity<DR_Node> requestedEntity = entity(node, APPLICATION_JSON);
+
+        Response response = testContainer.target("dr_nodes")
+                .request()
+                .post(requestedEntity, Response.class);
+
+        assertEquals(400, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("dcaeLocation, fqdn", responseError.getFields());
+    }
+
+    @Test
+    public void addDr_Node_shouldReturnError_whenDrNodeWithGiveFqdnAlreadyExists() {
+        DR_Node node = new DR_Node("fqdn", "location", "hostName", "1.0");
+
+        addDrNode(node);
+        Response response = addDrNode(node);
+
+        assertEquals(409, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("fqdn", responseError.getFields());
+        assertEquals("Node fqdn already exists", responseError.getMessage());
+    }
+
+    @Test
+    public void addDr_Node_shouldExecuteSuccessfully() {
+        DR_Node node = new DR_Node("fqdn", "location", "hostName", "1.0");
+
+        Response response = addDrNode(node);
+
+        assertEquals(200, response.getStatus());
+        assertTrue(response.hasEntity());
+        assertDrNodeExistInDB(response.readEntity(DR_Node.class));
+    }
+
+    @Test
+    public void updateDr_Node_shouldReturnError_whenNoLocationAndFqdnProvided() {
+        DR_Node node = new DR_Node(null, null, "hostName", "1.0");
+        Entity<DR_Node> requestedEntity = entity(node, APPLICATION_JSON);
+
+        Response response = testContainer.target("dr_nodes")
+                .path("fqdn")
+                .request()
+                .put(requestedEntity, Response.class);
+
+        assertEquals(400, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("dcaeLocation, fqdn", responseError.getFields());
+    }
+
+    @Test
+    public void updateDr_Node_shouldReturnError_whenDrNodeForUpdateDoesNotExistInDb() {
+        DR_Node node = new DR_Node("fqdn", "location", "hostName", "1.0");
+        Entity<DR_Node> requestedEntity = entity(node, APPLICATION_JSON);
+
+        Response response = testContainer.target("dr_nodes")
+                .path(node.getFqdn())
+                .request()
+                .put(requestedEntity, Response.class);
+
+        assertEquals(404, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("fqdn", responseError.getFields());
+        assertEquals("Node " + node.getFqdn() + " does not exist", responseError.getMessage());
+    }
+
+    @Test
+    public void updateDr_Node_ShouldExecuteSuccessfully() {
+        DR_Node toUpdate = new DR_Node("fqdn", "location", "hostName", "1.0");
+        Entity<DR_Node> requestedEntity = entity(toUpdate, APPLICATION_JSON);
+
+        addDrNode(new DR_Node("fqdn", "old_location", "old_hostName", "old_1.0"));
+        Response response = testContainer.target("dr_nodes")
+                .path(toUpdate.getFqdn())
+                .request()
+                .put(requestedEntity, Response.class);
+
+        assertEquals(200, response.getStatus());
+        assertTrue(response.hasEntity());
+        assertEquals(toUpdate, response.readEntity(DR_Node.class));
+    }
+
+    @Test
+    public void deleteDr_Node_shouldReturnError_whenDrNodeForDeleteDoesNotExistInDb() {
+        Response response = testContainer.target("dr_nodes")
+                .path("fqdn")
+                .request()
+                .delete();
+
+        assertEquals(404, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("fqdn", responseError.getFields());
+        assertEquals("Node fqdn does not exist", responseError.getMessage());
+    }
+
+    @Test
+    public void deleteDr_Node_shouldReturnError_whenNoExistingFqdnProvided() {
+        Response response = testContainer.target("dr_nodes")
+                .path("")
+                .request()
+                .delete();
+
+        assertEquals(405, response.getStatus());
+    }
+
+    @Test
+    public void deleteDr_Node_shouldExecuteSuccessfully() {
+        DR_Node node = new DR_Node("fqdn", "location", "hostName", "1.0");
+
+        addDrNode(node);
+        Response response = testContainer.target("dr_nodes")
+                .path("fqdn")
+                .request()
+                .delete();
+
+        assertEquals(204, response.getStatus());
+    }
+
+    @Test
+    public void getDr_Node_shouldReturnError_whenDrNodeForDeleteDoesNotExistInDb() {
+        Response response = testContainer.target("dr_nodes")
+                .path("fqdn")
+                .request()
+                .get();
+
+        assertEquals(404, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("fqdn", responseError.getFields());
+        assertEquals("Node fqdn does not exist", responseError.getMessage());
+    }
+
+    private Response addDrNode(DR_Node node) {
+        return testContainer.target("dr_nodes")
+                .request()
+                .post(entity(node, APPLICATION_JSON), Response.class);
+    }
+
+    private void assertDrNodeExistInDB(DR_Node created) {
+        Response response = testContainer.target("dr_nodes")
+                .path(created.getFqdn())
+                .request()
+                .get();
+        assertEquals(200, response.getStatus());
+        assertTrue(response.hasEntity());
+        assertEquals(created, response.readEntity(DR_Node.class));
+    }
+
+    @Before
+    public void cleanupDatabase() {
+        DatabaseClass.clearDatabase();
+    }
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DR_PubResourceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DR_PubResourceTest.java
new file mode 100644 (file)
index 0000000..340f362
--- /dev/null
@@ -0,0 +1,291 @@
+
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Pub;
+import org.onap.dmaap.dbcapi.model.Feed;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+
+public class DR_PubResourceTest {
+
+    private static final DmaapObjectFactory DMAAP_OBJECT_FACTORY = new DmaapObjectFactory();
+
+    private static final String DCAE_LOCATION_NAME = "central-onap";
+    private static final String USERNAME = "user1";
+    private static final String USRPWD = "secretW0rd";
+    private static final String FEED_ID = "someFakeFeedId";
+    private static final String PUB_ID = "0";
+    private static FastJerseyTestContainer testContainer;
+    private static TestFeedCreator testFeedCreator;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        //TODO: init is still needed here to assure that dmaap is not null
+        DatabaseClass.getDmaap().init(DMAAP_OBJECT_FACTORY.genDmaap());
+
+        testContainer = new FastJerseyTestContainer(new ResourceConfig()
+            .register(DmaapResource.class)
+            .register(DR_PubResource.class)
+            .register(FeedResource.class));
+
+        testContainer.init();
+        testFeedCreator = new TestFeedCreator(testContainer);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        testContainer.destroy();
+        /*TODO: Cannot cleanup yet until still other Resources tests depends on the static DB content
+
+        DatabaseClass.clearDatabase();
+        DatabaseClass.getDmaap().remove();*/
+    }
+
+    @Before
+    public void cleanupDatabase() {
+        DatabaseClass.clearDatabase();
+    }
+
+    @Test
+    public void getDr_Pub_test() {
+        Response resp = testContainer.target("dr_pubs").request().get(Response.class);
+        assertTrue(resp.getStatus() == 200);
+        assertTrue(resp.hasEntity());
+    }
+
+    @Test
+    public void addDr_Pub_shallReturnError_whenNoFeedIdAndFeedNameInPubProvided() {
+        //given
+        DR_Pub drPub = new DR_Pub(DCAE_LOCATION_NAME, USERNAME, USRPWD, null, PUB_ID);
+        Entity<DR_Pub> requestedEntity = Entity.entity(drPub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_pubs")
+            .request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(400, resp.getStatus());
+        ApiError responseError = resp.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("feedName", responseError.getFields());
+    }
+
+    @Test
+    public void addDr_Pub_shallReturnError_whenFeedNameProvided_butFeedNotExist() {
+        //given
+        DR_Pub drPub = new DR_Pub(DCAE_LOCATION_NAME, USERNAME, USRPWD, null, PUB_ID);
+        Entity<DR_Pub> requestedEntity = Entity.entity(drPub, MediaType.APPLICATION_JSON);
+        drPub.setFeedName("feed_name");
+
+
+        //when
+        Response resp = testContainer.target("dr_pubs")
+            .request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(404, resp.getStatus());
+        ApiError responseError = resp.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("feedName", responseError.getFields());
+
+    }
+
+    @Test
+    public void addDr_Pub_shallReturnError_whenFeedIdProvided_butFeedNotExist() {
+        //given
+        DR_Pub drPub = new DR_Pub(DCAE_LOCATION_NAME, USERNAME, USRPWD, FEED_ID, PUB_ID);
+        Entity<DR_Pub> requestedEntity = Entity.entity(drPub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_pubs")
+            .request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(404, resp.getStatus());
+        ApiError responseError = resp.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("feedId=" + FEED_ID, responseError.getFields());
+    }
+
+    @Test
+    public void addDr_Pub_shallExecuteSuccessfully_whenValidFeedIdProvided() {
+        //given
+        String feedId = assureFeedIsInDB();
+        DR_Pub drPub = new DR_Pub(DCAE_LOCATION_NAME, USERNAME, USRPWD, feedId);
+        Entity<DR_Pub> requestedEntity = Entity.entity(drPub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_pubs")
+            .request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(201, resp.getStatus());
+    }
+
+    @Test
+    public void addDr_Pub_shallExecuteSuccessfully_whenValidFeedNameProvided() {
+        //given
+        String feedName = "testFeed";
+        testFeedCreator.addFeed(feedName, "test feed");
+        DR_Pub drPub = new DR_Pub(DCAE_LOCATION_NAME, USERNAME, USRPWD, null, PUB_ID);
+        drPub.setFeedName(feedName);
+        Entity<DR_Pub> requestedEntity = Entity.entity(drPub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_pubs")
+            .request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(201, resp.getStatus());
+    }
+
+    @Test
+    public void updateDr_Pub_shallExecuteSuccessfully_whenAddingNewPublisher() {
+        //given
+        String pubId = "5";
+        DR_Pub drPub = new DR_Pub(DCAE_LOCATION_NAME, USERNAME, USRPWD, "feedId", PUB_ID);
+        Entity<DR_Pub> reqEntity2 = Entity.entity(drPub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_pubs")
+            .path(pubId)
+            .request()
+            .put(reqEntity2, Response.class);
+
+        //then
+        assertEquals(200, resp.getStatus());
+
+    }
+ /*//
+ //   When this test is included, the following error is generated:
+ Exception in thread "HTTP-Dispatcher" java.lang.AssertionError: State is not RESPONSE (REQUEST)
+    at jdk.httpserver/sun.net.httpserver.ServerImpl.responseCompleted(ServerImpl.java:814)
+    at jdk.httpserver/sun.net.httpserver.ServerImpl$Dispatcher.handleEvent(ServerImpl.java:297)
+    at jdk.httpserver/sun.net.httpserver.ServerImpl$Dispatcher.run(ServerImpl.java:356)
+    at java.base/java.lang.Thread.run(Thread.java:830)
+//  I can't figure it out, so created a Jira for now.  DMAAP-1358
+    @Test
+    public void updateDr_Pub_shallReturnError_whenPathIsWrong() {
+        //given
+        DR_Pub drPub = new DR_Pub(DCAE_LOCATION_NAME, USERNAME, USRPWD, FEED_ID, PUB_ID);
+        Entity<DR_Pub> reqEntity2 = Entity.entity(drPub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_pubs")
+            .path("")
+            .request()
+            .put(reqEntity2, Response.class);
+
+        //then
+        assertEquals(405, resp.getStatus());
+    }*/
+    @Test
+    public void deleteDr_Pub_shouldDeleteObjectWithSuccess() {
+        //given
+        String feedId = assureFeedIsInDB();
+        DR_Pub dr_pub = addPub(DCAE_LOCATION_NAME, USERNAME, USRPWD, feedId);
+
+        //when
+        Response resp = testContainer.target("dr_pubs")
+            .path(dr_pub.getPubId())
+            .request()
+            .delete();
+
+        //then
+        assertEquals("Shall delete publisher with success", 204, resp.getStatus());
+        assertFalse("No entity object shall be returned", resp.hasEntity());
+    }
+
+    @Test
+    public void deleteDr_Pub_shouldReturnErrorResponse_whenGivenPubIdNotFound() {
+        //given
+        String notExistingPubId = "6789";
+
+        //when
+        Response resp = testContainer.target("dr_pubs")
+            .path(notExistingPubId)
+            .request()
+            .delete();
+
+        //then
+        assertEquals("Shall return error, when trying to delete not existing publisher", 404, resp.getStatus());
+        ApiError responseError = resp.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("pubId", responseError.getFields());
+    }
+
+    @Test
+    public void get_shallReturnExistingObject() {
+        //given
+        String feedId = assureFeedIsInDB();
+        DR_Pub dr_Pub = addPub(DCAE_LOCATION_NAME, USERNAME, USRPWD, feedId);
+
+        //when
+        Response resp = testContainer.target("dr_pubs")
+                .path(dr_Pub.getPubId())
+                .request()
+                .get();
+
+        //then
+        assertEquals("Publisher shall be found", 200, resp.getStatus());
+        assertEquals("Retrieved object shall be equal to eh one put into DB", dr_Pub, resp.readEntity(DR_Pub.class));
+    }
+
+    private DR_Pub addPub(String d, String un, String up, String feedId) {
+        DR_Pub dr_pub = new DR_Pub(d, un, up, feedId, "");
+        Entity<DR_Pub> reqEntity2 = Entity.entity(dr_pub, MediaType.APPLICATION_JSON);
+        Response resp = testContainer.target("dr_pubs").request().post(reqEntity2, Response.class);
+        System.out.println("POST dr_pubs resp=" + resp.getStatus());
+        assertTrue(resp.getStatus() == 201);
+        dr_pub = resp.readEntity(DR_Pub.class);
+        return dr_pub;
+    }
+
+    private String assureFeedIsInDB() {
+        Feed feed = testFeedCreator.addFeed("PublisherTestFeed", "feed for DR_Pub testing");
+        assertNotNull("Feed shall be added into DB properly", feed);
+        return feed.getFeedId();
+    }
+
+
+}
+
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DR_SubResourceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DR_SubResourceTest.java
new file mode 100644 (file)
index 0000000..13b89ea
--- /dev/null
@@ -0,0 +1,434 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DR_Sub;
+import org.onap.dmaap.dbcapi.model.Feed;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+
+public class DR_SubResourceTest {
+
+    private static final DmaapObjectFactory DMAAP_OBJECT_FACTORY = new DmaapObjectFactory();
+
+    private static final String DCAE_LOCATION_NAME = "central-onap";
+    private static final String USERNAME = "user1";
+    private static final String USRPWD = "secretW0rd";
+    private static final String DELIVERY_URL = "https://subscriber.onap.org/delivery/id";
+    private static final String LOG_URL = "https://dr-prov/sublog/id";
+    private static final String DELIVERY_URL_TEMPLATE = "https://subscriber.onap.org/delivery/";
+    private static final String LOG_URL_TEMPLATE = "https://dr-prov/sublog/";
+    private static FastJerseyTestContainer testContainer;
+    private static TestFeedCreator testFeedCreator;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        //TODO: init is still needed here to assure that dmaap is not null
+        DatabaseClass.getDmaap().init(DMAAP_OBJECT_FACTORY.genDmaap());
+
+        testContainer = new FastJerseyTestContainer(new ResourceConfig()
+            .register(DR_SubResource.class)
+            .register(FeedResource.class));
+        testContainer.init();
+        testFeedCreator = new TestFeedCreator(testContainer);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        testContainer.destroy();
+        /*TODO: Cannot cleanup yet until still other Resources tests depends on the static DB content
+
+        DatabaseClass.clearDatabase();
+        DatabaseClass.getDmaap().remove();*/
+    }
+
+    @Before
+    public void cleanupDatabase() {
+        DatabaseClass.clearDatabase();
+    }
+
+    //TODO: figure out generic entity list unmarshall to check the entity list
+    @Test
+    public void getDr_Subs_test() {
+        Response resp = testContainer.target("dr_subs").request().get(Response.class);
+        System.out.println("GET dr_subs resp=" + resp.getStatus());
+
+        assertEquals(200, resp.getStatus());
+        assertTrue(resp.hasEntity());
+    }
+
+    @Test
+    public void addDr_Sub_shallReturnError_whenNoFeedIdAndFeedNameInSubProvided() {
+        //given
+        DR_Sub drSub = new DR_Sub(DCAE_LOCATION_NAME, USERNAME, USRPWD, null, DELIVERY_URL, LOG_URL, true);
+        Entity<DR_Sub> requestedEntity = Entity.entity(drSub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(400, resp.getStatus());
+        ApiError responseError = resp.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("feedName", responseError.getFields());
+    }
+
+    @Test
+    public void addDr_Sub_shallReturnError_whenFeedNameProvided_butFeedNotExist() {
+        //given
+        String notExistingFeedName = "notRealFead";
+        DR_Sub drSub = new DR_Sub(DCAE_LOCATION_NAME, USERNAME, USRPWD, null, DELIVERY_URL, LOG_URL, true);
+        drSub.setFeedName(notExistingFeedName);
+        Entity<DR_Sub> requestedEntity = Entity.entity(drSub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(404, resp.getStatus());
+        ApiError responseError = resp.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("feedName", responseError.getFields());
+    }
+
+    @Test
+    public void addDr_Sub_shallReturnError_whenFeedNameProvided_andManyFeedsWithTheSameNameInDB() {
+        //given
+        String notDistinctFeedName = "notDistinctFeedName";
+        Feed feed1 = new Feed(notDistinctFeedName, "1.0", "description", "dgl", "unrestricted");
+        Feed feed2 = new Feed(notDistinctFeedName, "2.0", "description", "dgl", "unrestricted");
+        DatabaseClass.getFeeds().put("1", feed1);
+        DatabaseClass.getFeeds().put("2", feed2);
+        DR_Sub drSub = new DR_Sub(DCAE_LOCATION_NAME, USERNAME, USRPWD, null, DELIVERY_URL, LOG_URL, true);
+        drSub.setFeedName(notDistinctFeedName);
+        Entity<DR_Sub> requestedEntity = Entity.entity(drSub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(409, resp.getStatus());
+        ApiError responseError = resp.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("feedName", responseError.getFields());
+    }
+
+    @Test
+    public void addDr_Sub_shallReturnError_whenFeedIdProvided_butFeedNotExist() {
+        //given
+        DR_Sub drSub = new DR_Sub(DCAE_LOCATION_NAME, USERNAME, USRPWD, "someFakeFeedId", DELIVERY_URL, LOG_URL, true);
+        Entity<DR_Sub> requestedEntity = Entity.entity(drSub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(404, resp.getStatus());
+        ApiError responseError = resp.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertTrue(responseError.getFields().contains("feedId"));
+    }
+
+    @Test
+    public void addDr_Sub_shallExecuteSuccessfully_whenValidFeedIdProvided() {
+        //given
+        String feedId = assureFeedIsInDB();
+        DR_Sub drSub = new DR_Sub(DCAE_LOCATION_NAME, USERNAME, USRPWD, feedId, DELIVERY_URL, LOG_URL, true);
+        Entity<DR_Sub> requestedEntity = Entity.entity(drSub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(201, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        DR_Sub created = resp.readEntity(DR_Sub.class);
+        assertSubscriptionExistInDB(created);
+    }
+
+    @Test
+    public void addDr_Sub_shallExecuteSuccessfully_whenValidFeedNameProvided() {
+        //given
+        String feedName = "testFeed";
+        testFeedCreator.addFeed(feedName, "test feed");
+        DR_Sub drSub = new DR_Sub(DCAE_LOCATION_NAME, USERNAME, USRPWD, null, DELIVERY_URL, LOG_URL, true);
+        drSub.setFeedName(feedName);
+        Entity<DR_Sub> requestedEntity = Entity.entity(drSub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(201, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        DR_Sub created = resp.readEntity(DR_Sub.class);
+        assertSubscriptionExistInDB(created);
+    }
+
+
+    @Test
+    public void updateDr_Sub_shallReturnError_whenNoFeedIdInSubProvided() {
+        //given
+        String subId = "1234";
+        DR_Sub drSub = new DR_Sub(DCAE_LOCATION_NAME, USERNAME, USRPWD, null, DELIVERY_URL, LOG_URL, true);
+        Entity<DR_Sub> requestedEntity = Entity.entity(drSub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .path(subId)
+            .request()
+            .put(requestedEntity, Response.class);
+
+        //then
+        assertEquals(400, resp.getStatus());
+        ApiError responseError = resp.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("feedId", responseError.getFields());
+    }
+
+    @Test
+    public void updateDr_Sub_shallReturnError_whenNoDCAELocationInSubProvided() {
+        //given
+        String subId = "1234";
+        DR_Sub drSub = new DR_Sub(null, USERNAME, USRPWD, "someFeedId", DELIVERY_URL, LOG_URL, true);
+        Entity<DR_Sub> requestedEntity = Entity.entity(drSub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .path(subId)
+            .request()
+            .put(requestedEntity, Response.class);
+
+        //then
+        assertEquals(400, resp.getStatus());
+        ApiError responseError = resp.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("dcaeLocationName", responseError.getFields());
+    }
+
+    @Test
+    public void updateDr_Sub_shallReturnError_whenFeedWithGivenIdInSubNotExists() {
+        //given
+        String subId = "1234";
+        DR_Sub drSub = new DR_Sub(DCAE_LOCATION_NAME, USERNAME, USRPWD, "someFeedId", DELIVERY_URL, LOG_URL, true);
+        Entity<DR_Sub> requestedEntity = Entity.entity(drSub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .path(subId)
+            .request()
+            .put(requestedEntity, Response.class);
+
+        //then
+        assertEquals(404, resp.getStatus());
+        assertNotNull(resp.readEntity(ApiError.class));
+    }
+
+    @Test
+    public void updateDr_Sub_shallReturnSuccess_whenAddingNewSubscription() {
+        //given
+        String subId = "31";
+        String feedId = assureFeedIsInDB();
+        DR_Sub drSub = new DR_Sub(DCAE_LOCATION_NAME, USERNAME, USRPWD, feedId, null, null, true);
+        Entity<DR_Sub> requestedEntity = Entity.entity(drSub, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .path(subId)
+            .request()
+            .put(requestedEntity, Response.class);
+
+        //then
+        assertEquals(200, resp.getStatus());
+
+        DR_Sub createdDrSub = resp.readEntity(DR_Sub.class);
+        assertNotNull(createdDrSub.getLastMod());
+        assertEquals(subId, createdDrSub.getSubId());
+        assertEquals(DCAE_LOCATION_NAME, createdDrSub.getDcaeLocationName());
+        assertEquals(USERNAME, createdDrSub.getUsername());
+        assertEquals(USRPWD, createdDrSub.getUserpwd());
+        assertEquals(DELIVERY_URL_TEMPLATE + subId, createdDrSub.getDeliveryURL());
+        assertEquals(LOG_URL_TEMPLATE + subId, createdDrSub.getLogURL());
+
+        assertSubscriptionExistInDB(createdDrSub);
+    }
+
+    @Test
+    public void updateDr_Sub_shallReturnSuccess_whenUpdatingExistingSubscription() {
+        //given
+        String feedId = assureFeedIsInDB();
+        DR_Sub existingDrSub = addSub(DCAE_LOCATION_NAME, USERNAME, USRPWD, feedId);
+        DR_Sub drSubUpdate = new DR_Sub("newDcaeLocationName", "newUserName", "newUserPwd", feedId, null, null, false);
+        Entity<DR_Sub> requestedEntity = Entity.entity(drSubUpdate, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .path(existingDrSub.getSubId())
+            .request()
+            .put(requestedEntity, Response.class);
+
+        //then
+        assertEquals(200, resp.getStatus());
+
+        DR_Sub updatedDrSub = resp.readEntity(DR_Sub.class);
+        assertNotNull(updatedDrSub.getLastMod());
+        assertEquals(existingDrSub.getSubId(), updatedDrSub.getSubId());
+        assertEquals(drSubUpdate.getDcaeLocationName(), updatedDrSub.getDcaeLocationName());
+        assertEquals(drSubUpdate.getUsername(), updatedDrSub.getUsername());
+        assertEquals(drSubUpdate.getUserpwd(), updatedDrSub.getUserpwd());
+        assertEquals(DELIVERY_URL_TEMPLATE + existingDrSub.getSubId(), updatedDrSub.getDeliveryURL());
+        assertEquals(LOG_URL_TEMPLATE + existingDrSub.getSubId(), updatedDrSub.getLogURL());
+
+        assertSubscriptionExistInDB(updatedDrSub);
+    }
+
+    @Test
+    public void deleteDr_Sub_shouldDeleteSubscriptionWithSuccess() {
+        //given
+        String feedId = assureFeedIsInDB();
+        DR_Sub dr_sub = addSub(DCAE_LOCATION_NAME, USERNAME, USRPWD, feedId);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .path(dr_sub.getSubId())
+            .request()
+            .delete();
+
+        //then
+        assertEquals("Shall delete subscription with success", 204, resp.getStatus());
+        assertFalse("No entity object shall be returned",resp.hasEntity());
+        assertSubscriptionNotExistInDB(dr_sub.getSubId());
+    }
+
+    @Test
+    public void deleteDr_Sub_shouldReturnErrorResponse_whenGivenSubIdNotFound() {
+        //given
+        String notExistingSubId = "6789";
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .path(notExistingSubId)
+            .request()
+            .delete();
+
+        //then
+        assertEquals("Shall return error, when trying to delete not existing subscription", 404, resp.getStatus());
+        assertNotNull(resp.readEntity(ApiError.class));
+    }
+
+    @Test
+    public void get_shallReturnExistingObject() {
+        //given
+        String feedId = assureFeedIsInDB();
+        DR_Sub dr_sub = addSub(DCAE_LOCATION_NAME, USERNAME, USRPWD, feedId);
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .path(dr_sub.getSubId())
+            .request()
+            .get();
+
+        //ten
+        assertEquals("Subscription shall be found",200, resp.getStatus());
+        assertEquals("Retrieved object shall be equal to eh one put into DB", dr_sub, resp.readEntity(DR_Sub.class));
+    }
+
+    @Test
+    public void get_shouldReturnError_whenSubWithIdNotFound() {
+        //given
+        String notExistingSubId = "1234";
+
+        //when
+        Response resp = testContainer.target("dr_subs")
+            .path(notExistingSubId)
+            .request()
+            .get();
+
+        //then
+        assertEquals("Subscription shall not be found", 404, resp.getStatus());
+        assertNotNull(resp.readEntity(ApiError.class));
+    }
+
+    private DR_Sub addSub(String d, String un, String up, String feedId) {
+        DR_Sub dr_sub = new DR_Sub(d, un, up, feedId,
+            "https://subscriber.onap.org/foo", "https://dr-prov/sublog", true);
+
+        Entity<DR_Sub> reqEntity2 = Entity.entity(dr_sub, MediaType.APPLICATION_JSON);
+        Response resp = testContainer.target("dr_subs").request().post(reqEntity2, Response.class);
+        System.out.println("POST dr_subs resp=" + resp.getStatus());
+        assertEquals(201, resp.getStatus());
+        dr_sub = resp.readEntity(DR_Sub.class);
+
+        return dr_sub;
+    }
+
+    private String assureFeedIsInDB() {
+        Feed feed = testFeedCreator.addFeed("SubscriberTestFeed", "feed for DR_Sub testing");
+        assertNotNull("Feed shall be added into DB properly", feed);
+        return feed.getFeedId();
+    }
+
+
+    private void assertSubscriptionNotExistInDB(String subId) {
+        assertEquals(404, testContainer.target("dr_subs")
+            .path(subId)
+            .request()
+            .get()
+            .getStatus());
+    }
+
+    private void assertSubscriptionExistInDB(DR_Sub sub) {
+        Response response = testContainer.target("dr_subs")
+            .path(sub.getSubId())
+            .request()
+            .get();
+        assertEquals(200, response.getStatus());
+        assertTrue(response.hasEntity());
+        assertEquals(sub, response.readEntity(DR_Sub.class));
+    }
+}
+
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResourceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DcaeLocationResourceTest.java
new file mode 100644 (file)
index 0000000..ff07927
--- /dev/null
@@ -0,0 +1,129 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.server.ResourceConfig;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.Path;
+import javax.ws.rs.GET;
+
+
+public class DcaeLocationResourceTest extends JerseyTest {
+
+       static DmaapObjectFactory factory = new DmaapObjectFactory();
+
+       @Override
+       protected Application configure() {
+               return new ResourceConfig( DcaeLocationResource.class );
+       }
+
+       private static final String  fmt = "%24s: %s%n";
+
+
+
+/*  may conflict with test framework! 
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+*/
+
+
+
+       @Test
+       public void GetTest() {
+               Response resp = target( "dcaeLocations").request().get( Response.class );
+               System.out.println( "GET feed resp=" + resp.getStatus() );
+
+               assertTrue( resp.getStatus() == 200 );
+       }
+       @Test
+       public void PostTest() {
+               DcaeLocation loc = factory.genDcaeLocation( "central" );
+               Entity<DcaeLocation> reqEntity = Entity.entity( loc, MediaType.APPLICATION_JSON );
+               Response resp = target( "dcaeLocations").request().post( reqEntity, Response.class );
+               System.out.println( "POST dcaeLocation resp=" + resp.getStatus() + " " + resp.readEntity( String.class ) );
+               if ( resp.getStatus() != 409 ) {
+                       assertTrue( resp.getStatus() == 201 );
+               }
+               
+               resp = target( "dcaeLocations").
+                               path( loc.getDcaeLocationName()).request().get( Response.class );
+               System.out.println( "GET feed resp=" + resp.getStatus() );
+
+               assertTrue( resp.getStatus() == 200 );
+       }
+
+       @Test
+       public void PutTest() {
+               DcaeLocation loc = factory.genDcaeLocation( "edge" );
+               Entity<DcaeLocation> reqEntity = Entity.entity( loc, MediaType.APPLICATION_JSON );
+               Response resp = target( "dcaeLocations").request().post( reqEntity, Response.class );
+               System.out.println( "POST dcaeLocation resp=" + resp.getStatus() + " " + resp.readEntity( String.class ) );
+               if ( resp.getStatus() != 409 ) {
+                       assertTrue( resp.getStatus() == 201 );
+               }
+
+               
+               loc.setClli("ATLCTYNJ9999");
+               reqEntity = Entity.entity( loc, MediaType.APPLICATION_JSON );
+               resp = target( "dcaeLocations").
+                               path( loc.getDcaeLocationName()).request().put( reqEntity, Response.class );
+               System.out.println( "PUT dcaeLocation resp=" + resp.getStatus() + " " + resp.readEntity( String.class ) );
+               assertTrue( resp.getStatus() == 201 );
+               
+       }
+
+       @Test
+       public void DelTest() {
+               DcaeLocation loc = factory.genDcaeLocation( "edge" );
+               Entity<DcaeLocation> reqEntity = Entity.entity( loc, MediaType.APPLICATION_JSON );
+               Response resp = target( "dcaeLocations").request().post( reqEntity, Response.class );
+               System.out.println( "POST dcaeLocation resp=" + resp.getStatus() + " " + resp.readEntity( String.class ) );
+               if ( resp.getStatus() != 409 ) {
+                       assertTrue( resp.getStatus() == 201 );
+               }
+               
+               resp = target( "dcaeLocations").
+                               path( loc.getDcaeLocationName()).request().delete( Response.class );
+               System.out.println( "DELETE dcaeLocation resp=" + resp.getStatus() + " " + resp.readEntity( String.class ) );
+               assertTrue( resp.getStatus() == 204 );
+       }
+
+
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DmaapResourceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/DmaapResourceTest.java
new file mode 100644 (file)
index 0000000..29ccb40
--- /dev/null
@@ -0,0 +1,92 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import static org.junit.Assert.assertTrue;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+
+
+public class DmaapResourceTest extends JerseyTest {
+
+       static DmaapObjectFactory factory = new DmaapObjectFactory();
+
+       @Override
+       protected Application configure() {
+               return new ResourceConfig( DmaapResource.class );
+               //return new ResourceConfig( HelloResource.class );
+       }
+
+       private static final String  fmt = "%24s: %s%n";
+
+
+
+/*  may conflict with test framework! 
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+*/
+
+
+
+       @Test
+       public void GetTest() {
+               Response resp = target( "dmaap").request().get( Response.class );
+               assertTrue( resp.getStatus() == 200 );
+       }
+       @Test
+       public void PostTest() {
+
+               Dmaap dmaap = factory.genDmaap();
+               Entity<Dmaap> reqEntity = Entity.entity( dmaap, MediaType.APPLICATION_JSON );
+               Response resp = target( "dmaap").request().post( reqEntity, Response.class );
+               System.out.println( resp.getStatus() );
+               assertTrue( resp.getStatus() == 200 );
+       }
+
+       @Test
+       public void PutTest() {
+
+               Dmaap dmaap = factory.genDmaap();
+               Entity<Dmaap> reqEntity = Entity.entity( dmaap, MediaType.APPLICATION_JSON );
+       
+               dmaap.setVersion( "2" );        
+               Response resp = target( "dmaap").request().put( reqEntity, Response.class );
+               System.out.println( resp.getStatus() );
+               assertTrue( resp.getStatus() == 200 );
+       }
+
+
+
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/FastJerseyTestContainer.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/FastJerseyTestContainer.java
new file mode 100644 (file)
index 0000000..8d38a9f
--- /dev/null
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import org.glassfish.jersey.test.JerseyTest;
+
+import javax.ws.rs.core.Application;
+
+class FastJerseyTestContainer extends JerseyTest {
+
+    FastJerseyTestContainer(Application jaxrsApplication) {
+        super(jaxrsApplication);
+    }
+
+    void init() throws Exception {
+        this.setUp();
+    }
+
+    void destroy() throws Exception {
+        this.tearDown();
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/FeedResourceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/FeedResourceTest.java
new file mode 100644 (file)
index 0000000..89dca8a
--- /dev/null
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+import org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.service.*;
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.util.*;
+import java.sql.*;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.server.ResourceConfig;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.Path;
+import javax.ws.rs.GET;
+
+
+public class FeedResourceTest extends JerseyTest {
+
+       @Override
+       protected Application configure() {
+               return new ResourceConfig( FeedResource.class );
+       }
+
+       private static final String  fmt = "%24s: %s%n";
+
+
+
+/*  may conflict with test framework! 
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+*/
+
+
+
+       @Test
+       public void GetTest() {
+               Response resp = target( "feeds").request().get( Response.class );
+               System.out.println( "GET feed resp=" + resp.getStatus() );
+
+               assertTrue( resp.getStatus() == 200 );
+       }
+       @Test
+       public void PostTest() {
+               Feed feed = new Feed( "aPostTest", "1.0", "a unit test", "dgl", "unrestricted" );
+               Entity<Feed> reqEntity = Entity.entity( feed, MediaType.APPLICATION_JSON );
+               Response resp = target( "feeds").request().post( reqEntity, Response.class );
+               System.out.println( "POST feed resp=" + resp.getStatus() );
+               assertTrue( resp.getStatus() == 200 );
+       }
+
+/*
+       @Test
+       public void PutTest() {
+               
+               Feed feed = new Feed( "aPutTest", "1.0", "a unit test", "dgl", "unrestricted" );
+               Entity<Feed> reqEntity = Entity.entity( feed, MediaType.APPLICATION_JSON );
+               Response resp = target( "feeds").request().post( reqEntity, Response.class );
+               System.out.println( "POST feed resp=" + resp.getStatus() );
+               String postResp = resp.readEntity( String.class );
+               System.out.println( "postResp=" + postResp );
+               Feed rFeed = new Feed( postResp );  getting a null pointer here
+               rFeed.setSuspended( true );
+               String target = new String ("feeds/" + rFeed.getFeedId() );
+               System.out.println( "PUT feed target=" + target );
+               reqEntity = Entity.entity( rFeed, MediaType.APPLICATION_JSON );
+               resp = target( target ).request().put( reqEntity, Response.class );
+               System.out.println( "PUT feed resp=" + resp.getStatus() );
+               assertTrue( resp.getStatus() == 200 );
+       }
+*/
+
+
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/InfoResourceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/InfoResourceTest.java
new file mode 100644 (file)
index 0000000..3f57f58
--- /dev/null
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+import org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.service.*;
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.util.*;
+import java.sql.*;
+
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.server.ResourceConfig;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.Path;
+import javax.ws.rs.GET;
+
+
+public class InfoResourceTest extends JerseyTest {
+
+       @Override
+       protected Application configure() {
+               return new ResourceConfig( InfoResource.class );
+       }
+
+       private static final String  fmt = "%24s: %s%n";
+
+
+
+/*  may conflict with test framework! 
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+*/
+
+
+
+       @Test
+       public void GetTest() {
+               Response resp = target( "info").request().get( Response.class );
+               assertTrue( resp.getStatus() == 204 );
+       }
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/MR_ClientResourceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/MR_ClientResourceTest.java
new file mode 100644 (file)
index 0000000..abe2e45
--- /dev/null
@@ -0,0 +1,304 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.MR_Client;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Response;
+
+import static javax.ws.rs.client.Entity.entity;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class MR_ClientResourceTest {
+
+    private static final DmaapObjectFactory DMAAP_OBJECT_FACTORY = new DmaapObjectFactory();
+    private static FastJerseyTestContainer testContainer;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        DatabaseClass.getDmaap().init(DMAAP_OBJECT_FACTORY.genDmaap());
+
+        testContainer = new FastJerseyTestContainer(new ResourceConfig()
+                .register(MR_ClientResource.class).register(MR_ClusterResource.class).register(TopicResource.class));
+        testContainer.init();
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        testContainer.destroy();
+        /*TODO: Cannot cleanup yet until still other Resources tests depends on the static DB content
+
+        DatabaseClass.getDmaap().remove();
+        DatabaseClass.clearDatabase();*/
+    }
+
+    @Test
+    public void addMr_Client_shouldReturnErrorWhenNoFqtnProvided() {
+        Entity<MR_Client> requestedEntity = entity(
+                new MR_Client("dcaeLocation", null, "clientRole", new String[]{"GET"}), APPLICATION_JSON);
+
+        Response response = testContainer.target("mr_clients")
+                .request()
+                .post(requestedEntity, Response.class);
+
+        assertEquals(400, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("fqtn", responseError.getFields());
+    }
+
+    @Test
+    public void addMr_Client_shouldReturnErrorWhenNoLocationProvided() {
+        Entity<MR_Client> requestedEntity = entity(
+                new MR_Client(null, "fqtn", "clientRole", new String[]{"GET"}), APPLICATION_JSON);
+
+        Response response = testContainer.target("mr_clients")
+                .request()
+                .post(requestedEntity, Response.class);
+
+        assertEquals(400, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("dcaeLocationName", responseError.getFields());
+    }
+
+    @Test
+    public void addMr_Client_shouldReturnErrorWhenNoClientRoleProvided() {
+        Entity<MR_Client> requestedEntity = entity(
+                new MR_Client("dcaeLocation", "fqtn", null, new String[]{"GET"}), APPLICATION_JSON);
+
+        Response response = testContainer.target("mr_clients")
+                .request()
+                .post(requestedEntity, Response.class);
+
+        assertEquals(400, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("clientRole or clientIdentity", responseError.getFields());
+    }
+
+    @Test
+    public void addMr_Client_shouldReturnErrorWhenNoActionsProvided() {
+        Entity<MR_Client> requestedEntity = entity(
+                new MR_Client("dcaeLocation", "fqtn", "clientRole", null), APPLICATION_JSON);
+
+        Response response = testContainer.target("mr_clients")
+                .request()
+                .post(requestedEntity, Response.class);
+
+        assertEquals(400, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("action", responseError.getFields());
+    }
+
+    @Test
+    public void addMr_Client_shouldReturnErrorWhereThereIsNoMrClusterAvailable() {
+        Entity<MR_Client> requestedEntity = entity(
+                new MR_Client("dcaeLocationName", "fqtn", "clientRole", new String[]{"GET"}), APPLICATION_JSON);
+
+        Response response = testContainer.target("mr_clients")
+                .request()
+                .post(requestedEntity, Response.class);
+
+        assertEquals(400, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("dcaeLocationName", responseError.getFields());
+    }
+
+    @Test
+    public void addMr_Client_shouldReturnErrorWhereThereIsNoTopicForFqtnAvailable() {
+        Entity<MR_Client> requestedEntity = entity(
+                new MR_Client("dcaeLocation", "fqtn", "clientRole", new String[]{"GET"}), APPLICATION_JSON);
+
+        createMrCluster(new MR_Cluster("dcaeLocation", "fqdn", "protocol", "port"));
+
+        Response response = testContainer.target("mr_clients")
+                .request()
+                .post(requestedEntity, Response.class);
+
+        assertEquals(404, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("fqtn", responseError.getFields());
+    }
+
+    @Test
+    public void addMr_Client_shouldAddMrClient() {
+        Entity<MR_Client> requestedEntity = entity(
+                new MR_Client("dcaeLocation2", "testTopic", "clientRole", new String[]{"GET"}), APPLICATION_JSON);
+
+        createMrCluster(new MR_Cluster("dcaeLocation2", "fqdn", "protocol", "port"));
+        createTopic("testTopic");
+
+        Response response = testContainer.target("mr_clients")
+                .request()
+                .post(requestedEntity, Response.class);
+
+        assertEquals(200, response.getStatus());
+        assertTrue(response.hasEntity());
+        assertMRClientExistInDB(response.readEntity(MR_Client.class));
+    }
+
+    @Test
+    public void deleteMr_Client_shouldDeleteMrClient() {
+        Entity<MR_Client> requestedEntity = entity(
+                new MR_Client("dcaeLocation3", "testTopic", "clientRole", new String[]{"GET"}), APPLICATION_JSON);
+        createMrCluster(new MR_Cluster("dcaeLocation3", "fqdn", "protocol", "port"));
+        createTopic("testTopic");
+
+        Response response = testContainer.target("mr_clients")
+                .request()
+                .post(requestedEntity, Response.class);
+        assertEquals(200, response.getStatus());
+
+        MR_Client mrClientEntity = response.readEntity(MR_Client.class);
+        Response deleteResponse = testContainer.target("mr_clients")
+                .path(mrClientEntity.getMrClientId())
+                .request()
+                .delete();
+
+        assertEquals(204, deleteResponse.getStatus());
+        assertMrClientNotExistInDB(mrClientEntity.getMrClientId());
+    }
+
+    @Test
+    public void deleteMr_Clients_shouldReturnMethodNotAllowedCodeWhenClientIdIsMissing() {
+        Response deleteResponse = testContainer.target("mr_clients")
+                .request()
+                .delete();
+
+        assertEquals(405, deleteResponse.getStatus());
+    }
+
+    @Test
+    public void getMr_Client_shouldReturnErrorWhenThereIsNoClient() {
+        Response response = testContainer.target("mr_clients")
+                .path("not_existing_id")
+                .request()
+                .get();
+
+        assertEquals(404, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("mrClientId", responseError.getFields());
+    }
+
+    @Test
+    public void updateMr_Client_shouldReturnErrorWhenNoFqtnProvided() {
+        MR_Client mrClient = new MR_Client("dcaeLocation", null, "clientRole", new String[]{"GET"});
+        Entity<MR_Client> requestedEntity = entity(mrClient, APPLICATION_JSON);
+
+        Response response = testContainer.target("mr_clients")
+                .path(mrClient.getMrClientId())
+                .request()
+                .put(requestedEntity, Response.class);
+
+        assertEquals(400, response.getStatus());
+        ApiError responseError = response.readEntity(ApiError.class);
+        assertNotNull(responseError);
+        assertEquals("fqtn", responseError.getFields());
+    }
+
+    @Test
+    public void updateMr_Client_shouldUpdate() {
+        Entity<MR_Client> requestedEntity = entity(
+                new MR_Client("dcaeLocation4", "testTopic", "clientRole", new String[]{"GET"}), APPLICATION_JSON);
+
+        createMrCluster(new MR_Cluster("dcaeLocation4", "fqdn", "protocol", "port"));
+        createTopic("testTopic");
+
+        Response response = testContainer.target("mr_clients")
+                .request()
+                .post(requestedEntity, Response.class);
+        MR_Client createdMrClient = response.readEntity(MR_Client.class);
+        createdMrClient.setDcaeLocationName("updatedDcaeLocation");
+
+
+        Response updateResponse = testContainer.target("mr_clients")
+                .path(createdMrClient.getMrClientId())
+                .request()
+                .put(requestedEntity, Response.class);
+
+        assertEquals(200, updateResponse.getStatus());
+        assertTrue(updateResponse.hasEntity());
+        assertMRClientExistInDB(updateResponse.readEntity(MR_Client.class));
+
+    }
+
+    @Test
+    public void getMr_Clients_test() {
+        Response response = testContainer.target("mr_clients").request().get(Response.class);
+        System.out.println("GET dr_subs response=" + response.getStatus());
+
+        assertEquals(200, response.getStatus());
+        assertTrue(response.hasEntity());
+    }
+
+
+    private void createMrCluster(MR_Cluster cluster) {
+        Response response = testContainer.target("mr_clusters")
+                .request()
+                .post(entity(cluster, APPLICATION_JSON), Response.class);
+        assertEquals(201, response.getStatus());
+    }
+
+    private void createTopic(String tname) {
+        Topic topic = new Topic();
+        topic.setFqtn(tname);
+        topic.setFqtn(tname);
+        DatabaseClass.getTopics().put(topic.getFqtn(), topic);
+    }
+
+    private void assertMRClientExistInDB(MR_Client created) {
+        Response response = testContainer.target("mr_clients")
+                .path(created.getMrClientId())
+                .request()
+                .get();
+        assertEquals(200, response.getStatus());
+        assertTrue(response.hasEntity());
+        MR_Client receivedMrClient = response.readEntity(MR_Client.class);
+        assertEquals(created.getFqtn(), receivedMrClient.getFqtn());
+        assertEquals(created.getDcaeLocationName(), receivedMrClient.getDcaeLocationName());
+    }
+
+    private void assertMrClientNotExistInDB(String clientId) {
+        assertEquals(404, testContainer.target("mr_clients")
+                .path(clientId)
+                .request()
+                .get()
+                .getStatus());
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResourceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/MR_ClusterResourceTest.java
new file mode 100644 (file)
index 0000000..9027f13
--- /dev/null
@@ -0,0 +1,284 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import static javax.ws.rs.client.Entity.entity;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+import org.eclipse.jetty.http.HttpStatus;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+
+public class MR_ClusterResourceTest {
+
+       private static final DmaapObjectFactory DMAAP_OBJECT_FACTORY = new DmaapObjectFactory();
+       private static FastJerseyTestContainer testContainer;
+       private static final String MR_CLUSTERS_TARGET = "mr_clusters";
+
+       @BeforeClass
+       public static void setUpClass() throws Exception {
+               DatabaseClass.getDmaap().init(DMAAP_OBJECT_FACTORY.genDmaap());
+
+               testContainer = new FastJerseyTestContainer(new ResourceConfig()
+                       .register(MR_ClusterResource.class).register(DcaeLocationResource.class));
+               testContainer.init();
+       }
+
+       @AfterClass
+       public static void tearDownClass() throws Exception {
+               testContainer.destroy();
+        /*TODO: Cannot cleanup yet until still other Resources tests depends on the static DB content
+
+        DatabaseClass.getDmaap().remove();
+        DatabaseClass.clearDatabase();*/
+       }
+
+       @Before
+       public void setUpClusterAndLocation() {
+               DatabaseClass.clearDatabase();
+       }
+
+       @Test
+       public void getMrClusters_shouldReturnEmptyList_whenNoMrClustersInDataBase() {
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).request().get(Response.class);
+
+               //then
+               assertEquals(HttpStatus.OK_200, resp.getStatus());
+               assertTrue(resp.hasEntity());
+
+               List<MR_Cluster> mrClusters = resp.readEntity(new GenericType<List<MR_Cluster>>() {
+               });
+               assertTrue(mrClusters.isEmpty());
+       }
+
+       @Test
+       public void addMrCluster_shouldReturnValidationError_whenDcaeLocationNameNotProvided() {
+               //given
+               Entity<MR_Cluster> requestEntity = entity(new MR_Cluster(), APPLICATION_JSON);
+
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).request().post(requestEntity, Response.class);
+
+               //then
+               assertEquals(HttpStatus.BAD_REQUEST_400, resp.getStatus());
+               assertTrue(resp.hasEntity());
+               ApiError errorObj = resp.readEntity(ApiError.class);
+               assertEquals("dcaeLocationName", errorObj.getFields());
+       }
+
+       @Test
+       public void addMrCluster_shouldReturnValidationError_whenFqdnNotProvided() {
+               //given
+               MR_Cluster mr_cluster = new MR_Cluster();
+               mr_cluster.setDcaeLocationName("central-cloud");
+               Entity<MR_Cluster> requestEntity = entity(mr_cluster, APPLICATION_JSON);
+
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).request().post(requestEntity, Response.class);
+
+               //then
+               assertEquals(HttpStatus.BAD_REQUEST_400, resp.getStatus());
+               assertTrue(resp.hasEntity());
+               ApiError errorObj = resp.readEntity(ApiError.class);
+               assertEquals("fqdn", errorObj.getFields());
+       }
+
+       @Test
+       public void addMrCluster_shouldAddMrClusterToDatabase() {
+               //given
+               MR_Cluster mrCluster = DMAAP_OBJECT_FACTORY.genMR_Cluster("edge");
+               Entity<MR_Cluster> requestEntity = entity(mrCluster, APPLICATION_JSON);
+
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).request().post(requestEntity, Response.class);
+
+               //then
+               assertEquals(HttpStatus.CREATED_201, resp.getStatus());
+               assertTrue(resp.hasEntity());
+               MR_Cluster respEntity = resp.readEntity(MR_Cluster.class);
+               assertTrue(respEntity.isStatusValid());
+       }
+
+       @Test
+       public void addMrCluster_shouldReturnInvalidMrCluster_whenClusterCannotBeAddedToDatabase() {
+               //given
+               MR_Cluster mrCluster = DMAAP_OBJECT_FACTORY.genMR_Cluster("central");
+               Entity<MR_Cluster> requestEntity = entity(mrCluster, APPLICATION_JSON);
+               prepareDcaeLocationForCentralCluster();
+
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).request().post(requestEntity, Response.class);
+
+               //then
+               assertEquals(HttpStatus.OK_200, resp.getStatus());
+               assertTrue(resp.hasEntity());
+               MR_Cluster respEntity = resp.readEntity(MR_Cluster.class);
+               assertFalse(respEntity.isStatusValid());
+       }
+
+       private void prepareDcaeLocationForCentralCluster() {
+               DcaeLocation centralDcaeLoc = DMAAP_OBJECT_FACTORY.genDcaeLocation("central");
+               centralDcaeLoc.setStatus(DmaapObject_Status.VALID);
+               DatabaseClass.getDcaeLocations().put(centralDcaeLoc.getDcaeLocationName(), centralDcaeLoc);
+       }
+
+       @Test
+       public void updateMrCluster_shouldReturnValidationError_whenDcaeLocationNameNotProvided() {
+               //given
+               Entity<MR_Cluster> requestEntity = entity(new MR_Cluster(), APPLICATION_JSON);
+
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).path("clusterId")
+                       .request().put(requestEntity, Response.class);
+
+               //then
+               assertEquals(HttpStatus.BAD_REQUEST_400, resp.getStatus());
+               assertTrue(resp.hasEntity());
+               ApiError errorObj = resp.readEntity(ApiError.class);
+               assertEquals("dcaeLocationName", errorObj.getFields());
+       }
+
+       @Test
+       public void updateMrCluster_shouldReturnApiError_whenMrClusterWithGivenIdNotFound() {
+               //given
+               MR_Cluster mr_cluster = new MR_Cluster();
+               mr_cluster.setDcaeLocationName("central-cloud");
+               Entity<MR_Cluster> requestEntity = entity(mr_cluster, APPLICATION_JSON);
+
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).path("notExistingMrCluster")
+                       .request().put(requestEntity, Response.class);
+
+               //then
+               assertEquals(HttpStatus.NOT_FOUND_404, resp.getStatus());
+               assertTrue(resp.hasEntity());
+               ApiError errorObj = resp.readEntity(ApiError.class);
+               assertEquals("dcaeLocationName", errorObj.getFields());
+       }
+
+       @Test
+       public void updateMrCluster_shouldUpdateClusterInDatabase() {
+               //given
+               String newReplicationGroup = "someNewReplicationGroup";
+               prepareDcaeLocationForEdgeCluster();
+               String clusterId = provideExistingEdgeMRClusterId();
+               MR_Cluster changedMrCluster = DMAAP_OBJECT_FACTORY.genMR_Cluster("edge");
+               changedMrCluster.setReplicationGroup(newReplicationGroup);
+               Entity<MR_Cluster> requestEntity = entity(changedMrCluster, APPLICATION_JSON);
+
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).path(clusterId)
+                       .request().put(requestEntity, Response.class);
+
+               //then
+               assertEquals(HttpStatus.CREATED_201, resp.getStatus());
+               assertTrue(resp.hasEntity());
+               MR_Cluster respEntity = resp.readEntity(MR_Cluster.class);
+               assertTrue(respEntity.isStatusValid());
+               assertEquals(newReplicationGroup, respEntity.getReplicationGroup());
+       }
+
+       private void prepareDcaeLocationForEdgeCluster() {
+               DcaeLocation edgeDcaeLoc = DMAAP_OBJECT_FACTORY.genDcaeLocation("edge");
+               edgeDcaeLoc.setStatus(DmaapObject_Status.VALID);
+               DatabaseClass.getDcaeLocations().put(edgeDcaeLoc.getDcaeLocationName(), edgeDcaeLoc);
+       }
+
+       private String provideExistingEdgeMRClusterId() {
+               MR_Cluster cluster = DMAAP_OBJECT_FACTORY.genMR_Cluster("edge");
+               cluster.setStatus(DmaapObject_Status.VALID);
+               DatabaseClass.getMr_clusters().put(cluster.getDcaeLocationName(), cluster);
+               return cluster.getDcaeLocationName();
+       }
+
+       @Test
+       public void deleteMr_Cluster_shouldReturnApiError_whenTryingToDeleteNotExistingMrCluster() {
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).path("notExistingClusterId")
+                       .request().delete(Response.class);
+
+               //then
+               assertEquals(HttpStatus.NOT_FOUND_404, resp.getStatus());
+               assertTrue(resp.hasEntity());
+               ApiError errorObj = resp.readEntity(ApiError.class);
+               assertEquals("dcaeLocationName", errorObj.getFields());
+       }
+
+       @Test
+       public void deleteMr_Cluster_shouldRemoveMrClusterFromDatabase() {
+               //given
+               String clusterId = provideExistingEdgeMRClusterId();
+
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).path(clusterId)
+                       .request().delete(Response.class);
+
+               //then
+               assertEquals(HttpStatus.NO_CONTENT_204, resp.getStatus());
+               assertFalse(resp.hasEntity());
+       }
+
+       @Test
+       public void getMr_Cluster_shouldReturnApiError_whenTryingToGetNotExistingMrCluster() {
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).path("notExistingClusterId")
+                       .request().get(Response.class);
+
+               //then
+               assertEquals(HttpStatus.OK_200, resp.getStatus());
+               assertTrue(resp.hasEntity());
+               ApiError errorObj = resp.readEntity(ApiError.class);
+               assertEquals("dcaeLocationName", errorObj.getFields());
+       }
+
+       @Test
+       public void getMr_Cluster_shouldReturnExistingMrCluster() {
+               //given
+               String clusterId = provideExistingEdgeMRClusterId();
+
+               //when
+               Response resp = testContainer.target(MR_CLUSTERS_TARGET).path(clusterId)
+                       .request().get(Response.class);
+
+               //then
+               assertEquals(HttpStatus.CREATED_201, resp.getStatus());
+               assertTrue(resp.hasEntity());
+               MR_Cluster mrCluster = resp.readEntity(MR_Cluster.class);
+               assertEquals(clusterId, mrCluster.getDcaeLocationName());
+       }
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/RequestTimeLogFilterTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/RequestTimeLogFilterTest.java
new file mode 100644 (file)
index 0000000..0c88c0c
--- /dev/null
@@ -0,0 +1,78 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * org.onap.dmaap\r
+ * ================================================================================\r
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+package org.onap.dmaap.dbcapi.resources;\r
+\r
+import static org.junit.Assert.assertNotNull;\r
+import static org.mockito.Matchers.anyString;\r
+import static org.mockito.Matchers.eq;\r
+import static org.mockito.Mockito.verify;\r
+import static org.mockito.Mockito.when;\r
+import com.att.eelf.configuration.EELFLogger;\r
+import java.time.Clock;\r
+import java.time.Instant;\r
+import java.time.ZoneId;\r
+import javax.ws.rs.container.ContainerRequestContext;\r
+import javax.ws.rs.container.ContainerResponseContext;\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+import org.junit.runner.RunWith;\r
+import org.mockito.Mock;\r
+import org.mockito.runners.MockitoJUnitRunner;\r
+\r
+@RunWith(MockitoJUnitRunner.class)\r
+public class RequestTimeLogFilterTest {\r
+\r
+    private Clock clock ;\r
+    private RequestTimeLogFilter requestTimeLogFilter;\r
+    public static final long START = 1L;\r
+    @Mock\r
+    private ContainerRequestContext requestContext;\r
+    @Mock\r
+    private ContainerResponseContext responseContext;\r
+    @Mock\r
+    private EELFLogger logger;\r
+\r
+\r
+    @Before\r
+    public void setup() {\r
+        clock = Clock.fixed(Instant.parse("1970-01-01T00:00:10Z"), ZoneId.systemDefault());\r
+        requestTimeLogFilter = new RequestTimeLogFilter(logger, clock);\r
+    }\r
+\r
+    @Test\r
+    public void shouldHaveDefaultConstructor() {\r
+        assertNotNull(new RequestTimeLogFilter());\r
+    }\r
+\r
+    @Test\r
+    public void filterShouldSetStartTimestampProperty() {\r
+        requestTimeLogFilter.filter(requestContext);\r
+        verify(requestContext).setProperty("start",clock.millis());\r
+    }\r
+\r
+    @Test\r
+    public void filterShouldPrintElapsedTime() {\r
+        when(requestContext.getProperty("start")).thenReturn(START);\r
+\r
+        requestTimeLogFilter.filter(requestContext, responseContext);\r
+\r
+        verify(logger).info(anyString(),eq(clock.millis() - START));\r
+    }\r
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/RequiredCheckerTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/RequiredCheckerTest.java
new file mode 100644 (file)
index 0000000..f07058b
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2019 NOKIA Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.onap.dmaap.dbcapi.model.ApiError;
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+import static org.junit.Assert.fail;
+
+public class RequiredCheckerTest {
+
+    private static final String NAME = "field_name";
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+    private RequiredChecker requiredChecker = new RequiredChecker();
+
+
+    @Test
+    public void required_shouldThrowExceptionWhenObjectIsNull() throws RequiredFieldException {
+        thrown.expect(RequiredFieldException.class);
+        thrown.expect(new ApiErrorMatcher(new ApiError(BAD_REQUEST.getStatusCode(),
+                "missing required field", NAME)));
+
+        requiredChecker.required(NAME, null);
+    }
+
+    @Test
+    public void required_shouldThrowExceptionWhenRegexValidationFailed() throws RequiredFieldException {
+        thrown.expect(RequiredFieldException.class);
+        thrown.expect(new ApiErrorMatcher(new ApiError(BAD_REQUEST.getStatusCode(),
+                "value 'with white space' violates regexp check '^\\S+$'", NAME)));
+
+        requiredChecker.required(NAME, "with white space", "^\\S+$");
+    }
+
+    @Test
+    public void required_shouldPassValidation() {
+        try {
+            requiredChecker.required(NAME, "value", "^\\S+$");
+        } catch (RequiredFieldException e) {
+            fail("No exception should be thrown");
+        }
+    }
+
+    class ApiErrorMatcher extends BaseMatcher {
+
+        private final ApiError expectedApiEror;
+
+        ApiErrorMatcher(ApiError expectedApiEror) {
+            this.expectedApiEror = expectedApiEror;
+        }
+
+        @Override
+        public boolean matches(Object exception) {
+            return expectedApiEror.equals(((RequiredFieldException) exception).getApiError());
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            description.appendText("Following ApiError is expected: ").appendValue(expectedApiEror);
+        }
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/RequiredFieldExceptionTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/RequiredFieldExceptionTest.java
new file mode 100644 (file)
index 0000000..d3dcc42
--- /dev/null
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Modifications Copyright (c) 2019 IBM
+ * ===================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.model.ApiError;
+
+public class RequiredFieldExceptionTest {
+    ApiError apiError;
+    RequiredFieldException requiredFieldException;
+    String expectedValue;
+
+    @Before
+    public void setUp() {
+        apiError = new ApiError(BAD_REQUEST.getStatusCode(), "value 'with white space' violates regexp check '^\\S+$'",
+                "field_name");
+
+        expectedValue = "RequiredFieldException{" + "apiError=" + apiError + '}';
+
+        requiredFieldException = new RequiredFieldException(apiError);
+    }
+
+    @Test
+    public void testRequiredFieldExceptionToString() {
+        assertEquals(expectedValue, requiredFieldException.toString());
+    }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/ResponseBuilderTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/ResponseBuilderTest.java
new file mode 100644 (file)
index 0000000..ff61d14
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PNF-REGISTRATION-HANDLER
+ * ================================================================================
+ * Copyright (C) 2019 NOKIA Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.resources;
+
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.model.ApiError;
+
+import javax.ws.rs.core.Response;
+
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+import static javax.ws.rs.core.Response.Status.SERVICE_UNAVAILABLE;
+import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
+import static org.junit.Assert.assertEquals;
+
+public class ResponseBuilderTest {
+
+    private static final String OBJECT = "Objcect";
+    private static final String MESSAGE = "msg";
+    private static final int CODE = 100;
+    private ResponseBuilder responseBuilder = new ResponseBuilder();
+
+    @Test
+    public void success_shouldCreateResponseWithOKStatusCode() {
+
+        Response response = responseBuilder.success(OBJECT);
+
+        assertEquals(OBJECT, response.getEntity());
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+    }
+
+    @Test
+    public void success_shouldCreateResponseWithDefinedStatusCode() {
+
+        Response response = responseBuilder.success(CODE, OBJECT);
+
+        assertEquals(OBJECT, response.getEntity());
+        assertEquals(CODE, response.getStatus());
+    }
+
+    @Test
+    public void unauthorized_shouldCreateCorrectResponse() {
+
+        ApiError error = new ApiError(UNAUTHORIZED.getStatusCode(), MESSAGE, "Authorization");
+        Response response = responseBuilder.unauthorized(MESSAGE);
+
+        assertEquals(error, response.getEntity());
+        assertEquals(error.getCode(), response.getStatus());
+    }
+
+    @Test
+    public void unavailable_shouldCreateCorrectResponse() {
+
+        ApiError error = new ApiError(SERVICE_UNAVAILABLE.getStatusCode(),
+                "Request is unavailable due to unexpected condition");
+        Response response = responseBuilder.unavailable();
+
+        assertEquals(error, response.getEntity());
+        assertEquals(error.getCode(), response.getStatus());
+    }
+
+    @Test
+    public void notFound_shouldCreateCorrectResponse() {
+        ApiError error = new ApiError(NOT_FOUND.getStatusCode(), "Requested object not found");
+        Response response = responseBuilder.notFound();
+
+        assertEquals(error, response.getEntity());
+        assertEquals(error.getCode(), response.getStatus());
+    }
+
+    @Test
+    public void error_shouldCreateCorrectResponse() {
+        ApiError error = new ApiError(CODE, "Some Error");
+        Response response = responseBuilder.error(error);
+
+        assertEquals(error, response.getEntity());
+        assertEquals(error.getCode(), response.getStatus());
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/TestFeedCreator.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/TestFeedCreator.java
new file mode 100644 (file)
index 0000000..e4dedb1
--- /dev/null
@@ -0,0 +1,49 @@
+/*-\r
+ * ============LICENSE_START=======================================================\r
+ * org.onap.dmaap\r
+ * ================================================================================\r
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.\r
+ * ================================================================================\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ============LICENSE_END=========================================================\r
+ */\r
+package org.onap.dmaap.dbcapi.resources;\r
+\r
+import static org.junit.Assert.assertTrue;\r
+\r
+import javax.ws.rs.client.Entity;\r
+import javax.ws.rs.core.MediaType;\r
+import javax.ws.rs.core.Response;\r
+import org.onap.dmaap.dbcapi.model.Feed;\r
+\r
+\r
+public class TestFeedCreator {\r
+\r
+\r
+    private final FastJerseyTestContainer testContainer;\r
+\r
+    public TestFeedCreator(FastJerseyTestContainer testContainer) {\r
+        this.testContainer = testContainer;\r
+    }\r
+\r
+    Feed addFeed(String name, String desc) {\r
+        Feed feed = new Feed(name, "1.0", desc, "dgl", "unrestricted");\r
+        Entity<Feed> reqEntity = Entity.entity(feed, MediaType.APPLICATION_JSON);\r
+        Response resp = testContainer.target("feeds").request().post(reqEntity, Response.class);\r
+        int rc = resp.getStatus();\r
+        System.out.println("POST feed resp=" + rc);\r
+        assertTrue(rc == 200 || rc == 409);\r
+        feed = resp.readEntity(Feed.class);\r
+        return feed;\r
+    }\r
+}\r
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/TopicResourceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/resources/TopicResourceTest.java
new file mode 100644 (file)
index 0000000..5b7c46d
--- /dev/null
@@ -0,0 +1,356 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.resources;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import java.util.List;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.database.DatabaseClass;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.model.FqtnType;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.model.ReplicationType;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+
+public class TopicResourceTest {
+
+    private static final DmaapObjectFactory DMAAP_OBJECT_FACTORY = new DmaapObjectFactory();
+    private static final String TOPICS_TARGET = "topics";
+
+    private static FastJerseyTestContainer testContainer;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        //TODO: init is still needed here to assure that dmaap is not null
+        DatabaseClass.getDmaap().init(DMAAP_OBJECT_FACTORY.genDmaap());
+
+        testContainer = new FastJerseyTestContainer(new ResourceConfig()
+            .register(TopicResource.class));
+        testContainer.init();
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        testContainer.destroy();
+    }
+
+    @Before
+    public void setUpClusterAndLocation() {
+        DatabaseClass.clearDatabase();
+
+        DcaeLocation centralDcaeLoc = DMAAP_OBJECT_FACTORY.genDcaeLocation("central");
+        centralDcaeLoc.setStatus(DmaapObject_Status.VALID);
+        DatabaseClass.getDcaeLocations().put(centralDcaeLoc.getDcaeLocationName(), centralDcaeLoc);
+
+        MR_Cluster cluster = DMAAP_OBJECT_FACTORY.genMR_Cluster("central");
+        cluster.setStatus(DmaapObject_Status.VALID);
+        DatabaseClass.getMr_clusters().put(cluster.getDcaeLocationName(), cluster);
+    }
+
+    @Test
+    public void getTopics_shouldReturnEmptyList_whenNoTopicsInDataBase() {
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).request().get(Response.class);
+
+        //then
+        assertEquals(HttpStatus.OK_200, resp.getStatus());
+        assertTrue(resp.hasEntity());
+
+        List<Topic> topics = resp.readEntity(new GenericType<List<Topic>>() {
+        });
+        assertTrue(topics.isEmpty());
+    }
+
+    @Test
+    public void getTopics_shouldReturnTopicsRegisteredInDataBase() {
+        //given
+        Topic topic1 = DMAAP_OBJECT_FACTORY.genSimpleTopic("testTopic1");
+        Topic topic2 = DMAAP_OBJECT_FACTORY.genSimpleTopic("testTopic2");
+        DatabaseClass.getTopics().put(topic1.getFqtn(), topic1);
+        DatabaseClass.getTopics().put(topic2.getFqtn(), topic2);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).request().get(Response.class);
+
+        //then
+        assertEquals(HttpStatus.OK_200, resp.getStatus());
+        assertTrue(resp.hasEntity());
+
+        List<Topic> topics = resp.readEntity(new GenericType<List<Topic>>() {
+        });
+        assertEquals(2, topics.size());
+        assertTrue(topics.contains(topic1));
+        assertTrue(topics.contains(topic2));
+    }
+
+    @Test
+    public void getTopics_shouldReturnValidationError_whenTopicNameIsInvalid() {
+        //given
+        String topicName = "wrong Topic Name";
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).path(topicName).request().get(Response.class);
+
+        //then
+        assertEquals(HttpStatus.BAD_REQUEST_400, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        ApiError errorObj = resp.readEntity(ApiError.class);
+        assertEquals("topicName", errorObj.getFields());
+    }
+
+    @Test
+    public void getTopic_shouldReturnError_whenRequestedTopicNotFound() {
+        //given
+        String topicName = "notExistingTopic";
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).path(topicName).request().get(Response.class);
+
+        //then
+        assertEquals(HttpStatus.NOT_FOUND_404, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        ApiError errorObj = resp.readEntity(ApiError.class);
+        assertEquals("fqtn", errorObj.getFields());
+    }
+
+    @Test
+    public void getTopic_shouldReturnTopicInformation_whenRequestedTopicExists() {
+        //given
+        Topic topic1 = DMAAP_OBJECT_FACTORY.genSimpleTopic("testTopic1");
+        DatabaseClass.getTopics().put(topic1.getFqtn(), topic1);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).path(topic1.getFqtn()).request().get(Response.class);
+
+        //then
+        assertEquals(HttpStatus.OK_200, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        Topic retrievedTopic = resp.readEntity(Topic.class);
+        assertEquals(topic1, retrievedTopic);
+    }
+
+
+    @Test
+    public void deleteTopic_shouldReturnError_whenTopicNotFound() {
+        //given
+        String topicName = "notExisting";
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).path(topicName).request().delete(Response.class);
+
+        //then
+        assertEquals(HttpStatus.NOT_FOUND_404, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        ApiError errorObj = resp.readEntity(ApiError.class);
+        assertEquals("fqtn", errorObj.getFields());
+    }
+
+    @Test
+    public void deleteTopic_shouldDeleteTopicFromDataBase_whenFound() {
+        //given
+        Topic topic = DMAAP_OBJECT_FACTORY.genSimpleTopic("testTopic");
+        DatabaseClass.getTopics().put(topic.getFqtn(), topic);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).path(topic.getFqtn()).request().delete(Response.class);
+
+        //then
+        assertEquals(HttpStatus.NO_CONTENT_204, resp.getStatus());
+        assertFalse(resp.hasEntity());
+    }
+
+    @Test
+    public void addTopic_shouldReturnValidationError_whenTopicNameIsInvalid() {
+        //given
+        Topic topic = DMAAP_OBJECT_FACTORY.genSimpleTopic("wrong topic name with spaces");
+        Entity<Topic> requestedEntity = Entity.entity(topic, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).request().post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(HttpStatus.BAD_REQUEST_400, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        ApiError errorObj = resp.readEntity(ApiError.class);
+        assertEquals("topicName", errorObj.getFields());
+    }
+
+    @Test
+    public void addTopic_shouldReturnValidationError_whenTopicDescriptionNotProvided() {
+        //given
+        Topic topic = DMAAP_OBJECT_FACTORY.genSimpleTopic("topicName");
+        topic.setTopicDescription(null);
+        Entity<Topic> requestedEntity = Entity.entity(topic, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).request().post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(HttpStatus.BAD_REQUEST_400, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        ApiError errorObj = resp.readEntity(ApiError.class);
+        assertEquals("topicDescription", errorObj.getFields());
+    }
+
+    @Test
+    public void addTopic_shouldReturnValidationError_whenTopicOwnerNotProvided() {
+        //given
+        Topic topic = DMAAP_OBJECT_FACTORY.genSimpleTopic("topicName");
+        topic.setOwner(null);
+        Entity<Topic> requestedEntity = Entity.entity(topic, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).request().post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(HttpStatus.BAD_REQUEST_400, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        ApiError errorObj = resp.readEntity(ApiError.class);
+        assertEquals("owner", errorObj.getFields());
+    }
+
+    @Test
+    public void addTopic_shouldReturnError_whenTopicAlreadyExist() {
+        //given
+        Topic topic = DMAAP_OBJECT_FACTORY.genSimpleTopic("topicName");
+        DatabaseClass.getTopics().put(topic.getFqtn(), topic);
+        Entity<Topic> requestedEntity = Entity.entity(topic, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).request().post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(HttpStatus.CONFLICT_409, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        ApiError errorObj = resp.readEntity(ApiError.class);
+        assertEquals("fqtn", errorObj.getFields());
+    }
+
+    @Test
+    public void addTopic_shouldReturnExistingTopic_whenTopicAlreadyExist_andUseExistingQueryParamUsed() {
+        //given
+        Topic topic = DMAAP_OBJECT_FACTORY.genSimpleTopic("topicName");
+        DatabaseClass.getTopics().put(topic.getFqtn(), topic);
+        Entity<Topic> requestedEntity = Entity.entity(topic, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).queryParam("useExisting", true).request()
+            .post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(HttpStatus.CREATED_201, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        assertEquals(topic, resp.readEntity(Topic.class));
+    }
+
+    @Test
+    public void addTopic_shouldReturnError_whenAddingTopicWithInvalidGlobalMRclusterHostname() {
+        Topic topic = DMAAP_OBJECT_FACTORY.genSimpleTopic("topicName");
+        topic.setReplicationCase(ReplicationType.REPLICATION_CENTRAL_TO_GLOBAL);
+        topic.setGlobalMrURL("some.invalid.Glob$al.M@R.ur)l");
+        Entity<Topic> requestedEntity = Entity.entity(topic, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).request().post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR_500, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        ApiError errorObj = resp.readEntity(ApiError.class);
+        assertEquals("globalMrURL", errorObj.getFields());
+    }
+
+    @Test
+    public void addTopic_shouldAddTopicWithDefaultOptionalValues_whenNotProvided() {
+        Topic topic = DMAAP_OBJECT_FACTORY.genSimpleTopic("topicName");
+        Entity<Topic> requestedEntity = Entity.entity(topic, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).request().post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(HttpStatus.CREATED_201, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        Topic createdTopic = resp.readEntity(Topic.class);
+        assertEquals(topic, createdTopic);
+        assertEquals(FqtnType.FQTN_LEGACY_FORMAT, createdTopic.getFqtnStyle());
+        assertEquals("2", createdTopic.getPartitionCount());
+        assertEquals("1", createdTopic.getReplicationCount());
+    }
+
+    @Test
+    public void addTopic_shouldAddTopicWithOriginalOptionalValues_whenProvided() {
+        Topic topic = DMAAP_OBJECT_FACTORY.genSimpleTopic("topicName");
+        topic.setFqtnStyle(FqtnType.FQTN_PROJECTID_FORMAT);
+        topic.setFqtn(topic.genFqtn());
+        topic.setPartitionCount("6");
+        topic.setReplicationCount("9");
+        Entity<Topic> requestedEntity = Entity.entity(topic, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).request().post(requestedEntity, Response.class);
+
+        //then
+        assertEquals(HttpStatus.CREATED_201, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        Topic createdTopic = resp.readEntity(Topic.class);
+        assertEquals(topic, createdTopic);
+        assertEquals(FqtnType.FQTN_PROJECTID_FORMAT, createdTopic.getFqtnStyle());
+        assertEquals("6", createdTopic.getPartitionCount());
+        assertEquals("9", createdTopic.getReplicationCount());
+    }
+
+    @Test
+    public void updateTopic_shouldReturnError_withInformationThatItIsNotSupported() {
+        //given
+        Topic topic = DMAAP_OBJECT_FACTORY.genSimpleTopic("topicName");
+        DatabaseClass.getTopics().put(topic.getFqtn(), topic);
+        topic.setOwner("newOwner");
+        Entity<Topic> requestedEntity = Entity.entity(topic, MediaType.APPLICATION_JSON);
+
+        //when
+        Response resp = testContainer.target(TOPICS_TARGET).path(topic.getFqtn()).request()
+            .put(requestedEntity, Response.class);
+
+        //then
+        assertEquals(HttpStatus.BAD_REQUEST_400, resp.getStatus());
+        assertTrue(resp.hasEntity());
+        ApiError errorObj = resp.readEntity(ApiError.class);
+        assertEquals(TopicResource.UNSUPPORTED_PUT_MSG, errorObj.getMessage());
+    }
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/server/JettyServerTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/server/JettyServerTest.java
new file mode 100644 (file)
index 0000000..35c9243
--- /dev/null
@@ -0,0 +1,79 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.server;
+
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.util.Properties;
+
+public class JettyServerTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+
+               JettyServer m = null;
+
+       @Before
+       public void setUp() throws Exception {
+               Properties p = DmaapConfig.getConfig();
+               try {
+                       m = new JettyServer(p);
+               } catch (Exception e ) {
+               }
+       }
+
+       @After
+       public void tearDown() throws Exception {
+               try {
+                       m.getServer().stop();
+               } catch (Exception e ) {
+               }
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.server.JettyServer", "get", null );  
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.server.JettyServer", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+       }
+
+
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/server/MainTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/server/MainTest.java
new file mode 100644 (file)
index 0000000..0e74f45
--- /dev/null
@@ -0,0 +1,79 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.server;
+
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MainTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       Main m;
+
+
+       @Before
+       public void setUp() throws Exception {
+               //m = new Main();
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.server.Main", "get", null ); 
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.server.Main", "set", v );
+
+       }
+
+/*
+       @Test
+       public void test3() {
+               String[] args = { "--help", "--version" };
+
+               try {
+                       m.main( args );
+               } catch (Exception e ) {
+               }
+
+       }
+*/
+
+
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/AafPermissionServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/AafPermissionServiceTest.java
new file mode 100644 (file)
index 0000000..716736e
--- /dev/null
@@ -0,0 +1,141 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.dmaap.dbcapi.aaf.AafService;
+import org.onap.dmaap.dbcapi.aaf.AafUserRole;
+import org.onap.dmaap.dbcapi.aaf.DmaapGrant;
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.MR_Client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.BDDMockito.then;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status.INVALID;
+import static org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status.VALID;
+
+@RunWith(JUnitParamsRunner.class)
+public class AafPermissionServiceTest {
+
+    private static final String ROLE = "dmaap.mr.demoTopic.publisher";
+    private static final String IDENTITY = "dmaap-bc@dmaap-bc.onap.org";
+    private static final String TOPIC_PERM = "org.onap.dmaap.mr.topic";
+    private static final String FQTN = "org.onap.dmaap.mr.demoTopic";
+    private static final String PUB_ACTION = "pub";
+    private static final int INTERNAL_SERVER_ERROR = 500;
+    @Mock
+    private AafService aafService;
+    @Mock
+    private DmaapService dmaapService;
+    @Mock
+    private MR_Client mrClient;
+    private AafPermissionService aafPermissionService;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        aafPermissionService = new AafPermissionService(aafService, dmaapService);
+        given(mrClient.getClientIdentity()).willReturn(IDENTITY);
+        given(mrClient.getFqtn()).willReturn(FQTN);
+        given(mrClient.getAction()).willReturn(new String[]{PUB_ACTION});
+        given(dmaapService.getTopicPerm()).willReturn(TOPIC_PERM);
+    }
+
+    @Test
+    @Parameters({"201", "409"})
+    public void shouldAssignClientToRole(int aafServiceReturnedCode) {
+        AafUserRole userRole = new AafUserRole(IDENTITY, ROLE);
+        given(aafService.addUserRole(userRole)).willReturn(aafServiceReturnedCode);
+
+        ApiError apiError = aafPermissionService.assignClientToRole(mrClient, ROLE);
+
+        then(aafService).should().addUserRole(userRole);
+        then(mrClient).should().setStatus(VALID);
+        assertOkStatus(apiError);
+    }
+
+    @Test
+    public void shouldReturnErrorStatusWhenClientWasNotAssignedToRole() {
+        AafUserRole userRole = new AafUserRole(IDENTITY, ROLE);
+        given(aafService.addUserRole(userRole)).willReturn(INTERNAL_SERVER_ERROR);
+
+        ApiError apiError = aafPermissionService.assignClientToRole(mrClient, ROLE);
+
+        then(mrClient).should().setStatus(INVALID);
+        assertErrorStatus(apiError, INTERNAL_SERVER_ERROR);
+    }
+
+    @Test
+    @Parameters({"201", "409"})
+    public void shouldGrantActionPermissionForClientRole(int aafServiceReturnedCode) {
+        DmaapGrant grant = new DmaapGrant(new DmaapPerm(TOPIC_PERM, ":topic." + FQTN, PUB_ACTION), ROLE);
+        given(mrClient.getClientRole()).willReturn(ROLE);
+        given(aafService.addGrant(grant)).willReturn(aafServiceReturnedCode);
+
+        ApiError apiError = aafPermissionService.grantClientRolePerms(mrClient);
+
+        then(aafService).should().addGrant(grant);
+        then(mrClient).should().setStatus(VALID);
+        assertOkStatus(apiError);
+    }
+
+    @Test
+    public void shouldReturnErrorStatusWhenPermissionWasNotGrantToRole() {
+        DmaapGrant grant = new DmaapGrant(new DmaapPerm(TOPIC_PERM, ":topic." + FQTN, PUB_ACTION), ROLE);
+        given(mrClient.getClientRole()).willReturn(ROLE);
+        given(aafService.addGrant(grant)).willReturn(INTERNAL_SERVER_ERROR);
+
+        ApiError apiError = aafPermissionService.grantClientRolePerms(mrClient);
+
+        then(mrClient).should().setStatus(INVALID);
+        assertErrorStatus(apiError, INTERNAL_SERVER_ERROR);
+    }
+
+    @Test
+    public void shouldReturnOkStatusWhenClientRoleIsNull() {
+        given(mrClient.getClientRole()).willReturn(null);
+
+        ApiError apiError = aafPermissionService.grantClientRolePerms(mrClient);
+
+        verifyZeroInteractions(aafService);
+        then(mrClient).should().setStatus(VALID);
+        assertOkStatus(apiError);
+    }
+
+    private void assertErrorStatus(ApiError apiError, int code) {
+        assertEquals(code, apiError.getCode());
+    }
+
+    private void assertOkStatus(ApiError apiError) {
+        assertTrue(apiError.is2xx());
+        assertEquals("OK", apiError.getMessage());
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/AafTopicSetupServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/AafTopicSetupServiceTest.java
new file mode 100644 (file)
index 0000000..0ca406a
--- /dev/null
@@ -0,0 +1,470 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.dmaap.dbcapi.aaf.AafNamespace;
+import org.onap.dmaap.dbcapi.aaf.AafRole;
+import org.onap.dmaap.dbcapi.aaf.AafService;
+import org.onap.dmaap.dbcapi.aaf.AafUserRole;
+import org.onap.dmaap.dbcapi.aaf.DmaapGrant;
+import org.onap.dmaap.dbcapi.aaf.DmaapPerm;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.BDDMockito.given;
+
+@RunWith(JUnitParamsRunner.class)
+public class AafTopicSetupServiceTest {
+
+    private static final int INTERNAL_SERVER_ERROR = 500;
+    private static final int NOT_FOUND = 404;
+    private static final int CREATED = 201;
+    private static final int OK = 200;
+    private static final String TOPIC_NS_ROOT = "org.onap.dmaap.mr";
+    private static final String TOPIC_PERM = "org.onap.dmaap.mr.topic";
+    private static final String TOPIC_FQTN = "org.onap.dmaap.mr.sample_topic";
+    private static final String IDENTITY = "dmaap-bc@dmaap-bc.onap.org";
+    private AafServiceStub aafService = new AafServiceStub();
+    @Mock
+    private DmaapService dmaapService;
+    @Mock
+    private DmaapConfig dmaapConfig;
+    private AafTopicSetupService aafTopicSetupService;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        Dmaap dmaap = new Dmaap();
+        dmaap.setTopicNsRoot(TOPIC_NS_ROOT);
+        given(dmaapService.getDmaap()).willReturn(dmaap);
+        given(dmaapService.getTopicPerm()).willReturn(TOPIC_PERM);
+        given(dmaapConfig.getProperty("aaf.CreateTopicRoles", "true")).willReturn("true");
+        given(dmaapConfig.getProperty("MR.ClientDeleteLevel", "0")).willReturn("2");
+        aafTopicSetupService = new AafTopicSetupService(aafService, dmaapService, dmaapConfig);
+    }
+
+    @Test
+    @Parameters({"201", "409"})
+    public void shouldCreatePublisherSubscriberViewerPermissions(int aafServiceReturnedCode) {
+        aafService.givenReturnCode(aafServiceReturnedCode);
+
+        aafTopicSetupService.aafTopicSetup(givenTopic(TOPIC_FQTN));
+
+        aafService.shouldAddPerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "pub"));
+        aafService.shouldAddPerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "sub"));
+        aafService.shouldAddPerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "view"));
+    }
+
+    @Test
+    public void shouldReturnOkStatusWhenNoError() {
+        aafService.givenReturnCode(201);
+
+        ApiError apiError = aafTopicSetupService.aafTopicSetup(givenTopic(TOPIC_FQTN));
+
+        assertOkStatus(apiError);
+    }
+
+    @Test
+    @Parameters({"201", "409"})
+    public void shouldAddNamespace(int aafServiceReturnedCode) {
+        aafService.givenReturnCode(aafServiceReturnedCode);
+        Topic topic = givenTopic(TOPIC_FQTN);
+
+        aafTopicSetupService.aafTopicSetup(topic);
+
+        AafNamespace namespace = new AafNamespace(TOPIC_FQTN, IDENTITY);
+        aafService.shouldAddNamespace(namespace);
+    }
+
+    @Test
+    @Parameters({"201", "409"})
+    public void shouldCretePublisherRoleAndSetItToTopic(int aafServiceReturnedCode) {
+        aafService.givenReturnCode(aafServiceReturnedCode);
+        Topic topic = givenTopic(TOPIC_FQTN);
+
+        aafTopicSetupService.aafTopicSetup(topic);
+
+        AafRole role = new AafRole(TOPIC_FQTN, "publisher");
+        aafService.shouldAddRole(role);
+        assertEquals(role.getFullyQualifiedRole(), topic.getPublisherRole());
+    }
+
+    @Test
+    @Parameters({"201", "409"})
+    public void shouldCreteSubscriberRoleAndSetItToTopic(int aafServiceReturnedCode) {
+        aafService.givenReturnCode(aafServiceReturnedCode);
+        Topic topic = givenTopic(TOPIC_FQTN);
+
+        aafTopicSetupService.aafTopicSetup(topic);
+
+        AafRole role = new AafRole(TOPIC_FQTN, "subscriber");
+        aafService.shouldAddRole(role);
+        assertEquals(role.getFullyQualifiedRole(), topic.getSubscriberRole());
+    }
+
+    @Test
+    @Parameters({"201", "409"})
+    public void shouldGrantPubAndViewPermissionToPublisherRole(int aafServiceReturnedCode) {
+        aafService.givenReturnCode(aafServiceReturnedCode);
+
+        aafTopicSetupService.aafTopicSetup(givenTopic(TOPIC_FQTN));
+
+        AafRole role = new AafRole(TOPIC_FQTN, "publisher");
+        DmaapPerm pubPerm = new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "pub");
+        DmaapPerm viewPerm = new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "view");
+        aafService.shouldAddGrant(new DmaapGrant(pubPerm, role.getFullyQualifiedRole()));
+        aafService.shouldAddGrant(new DmaapGrant(viewPerm, role.getFullyQualifiedRole()));
+    }
+
+    @Test
+    @Parameters({"201", "409"})
+    public void shouldGrantSubAndViewPermissionToSubscriberRole(int aafServiceReturnedCode) {
+        aafService.givenReturnCode(aafServiceReturnedCode);
+
+        aafTopicSetupService.aafTopicSetup(givenTopic(TOPIC_FQTN));
+
+        AafRole role = new AafRole(TOPIC_FQTN, "subscriber");
+        DmaapPerm subPerm = new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "sub");
+        DmaapPerm viewPerm = new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "view");
+        aafService.shouldAddGrant(new DmaapGrant(subPerm, role.getFullyQualifiedRole()));
+        aafService.shouldAddGrant(new DmaapGrant(viewPerm, role.getFullyQualifiedRole()));
+    }
+
+    @Test
+    public void shouldCreateOnlyPermissionsWhenCreateTopicRolesIsFalse() {
+        given(dmaapConfig.getProperty("aaf.CreateTopicRoles", "true")).willReturn("false");
+
+        aafTopicSetupService.aafTopicSetup(givenTopic(TOPIC_FQTN));
+
+        aafService.shouldAddPerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "pub"));
+        aafService.shouldAddPerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "sub"));
+        aafService.shouldAddPerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "view"));
+        aafService.shouldHaveNoNamespaceRolesAndGrantsAdded();
+    }
+
+    @Test
+    public void shouldCreateOnlyPermissionsWhenTopicFqtnDoesntStartWithNsRoot() {
+
+        String topicFqtn = "sample_topic";
+        aafTopicSetupService.aafTopicSetup(givenTopic(topicFqtn));
+
+        aafService.shouldAddPerm(new DmaapPerm(TOPIC_PERM, ":topic." + topicFqtn, "pub"));
+        aafService.shouldAddPerm(new DmaapPerm(TOPIC_PERM, ":topic." + topicFqtn, "sub"));
+        aafService.shouldAddPerm(new DmaapPerm(TOPIC_PERM, ":topic." + topicFqtn, "view"));
+        aafService.shouldHaveNoNamespaceRolesAndGrantsAdded();
+    }
+
+    @Test
+    public void shouldHandleExceptionWhenTopicSnRootIsNotDefined() {
+        Dmaap dmaap = new Dmaap();
+        dmaap.setTopicNsRoot(null);
+        given(dmaapService.getDmaap()).willReturn(dmaap);
+
+        ApiError apiError = aafTopicSetupService.aafTopicSetup(givenTopic(TOPIC_FQTN));
+
+        assertErrorStatus(apiError, INTERNAL_SERVER_ERROR);
+    }
+
+    @Test
+    public void shouldHandleExceptionWhenPermissionCreationWasFailed() {
+        aafService.givenAddPermStatus(NOT_FOUND);
+
+        ApiError apiError = aafTopicSetupService.aafTopicSetup(givenTopic(TOPIC_FQTN));
+
+        assertErrorStatus(apiError, INTERNAL_SERVER_ERROR);
+    }
+
+    @Test
+    public void shouldHandleExceptionWhenNamespaceCreationWasFailed() {
+        aafService.givenAddNamespaceStatus(NOT_FOUND);
+
+        ApiError apiError = aafTopicSetupService.aafTopicSetup(givenTopic(TOPIC_FQTN));
+
+        assertErrorStatus(apiError, INTERNAL_SERVER_ERROR);
+    }
+
+    @Test
+    public void shouldHandleExceptionWhenRoleCreationWasFailed() {
+        aafService.givenAddRoleStatus(NOT_FOUND);
+
+        ApiError apiError = aafTopicSetupService.aafTopicSetup(givenTopic(TOPIC_FQTN));
+
+        assertErrorStatus(apiError, INTERNAL_SERVER_ERROR);
+    }
+
+    @Test
+    public void shouldHandleExceptionWhenGrantPermToRoleWasFailed() {
+        aafService.givenAddGrantStatus(NOT_FOUND);
+
+        ApiError apiError = aafTopicSetupService.aafTopicSetup(givenTopic(TOPIC_FQTN));
+
+        assertErrorStatus(apiError, NOT_FOUND);
+    }
+
+    @Test
+    @Parameters({"200", "404"})
+    public void shouldremovePublisherSubscriberViewerPermissions(int aafServiceReturnedCode) {
+        aafService.givenReturnCode(aafServiceReturnedCode);
+
+        aafTopicSetupService.aafTopicCleanup(givenTopic(TOPIC_FQTN));
+
+        aafService.shouldRemovePerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "pub"));
+        aafService.shouldRemovePerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "sub"));
+        aafService.shouldRemovePerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "view"));
+    }
+
+    @Test
+    @Parameters({"200", "404"})
+    public void shouldRemoveNamespace(int aafServiceReturnedCode) {
+        aafService.givenReturnCode(aafServiceReturnedCode);
+        Topic topic = givenTopic(TOPIC_FQTN);
+
+        aafTopicSetupService.aafTopicCleanup(topic);
+
+        AafNamespace namespace = new AafNamespace(TOPIC_FQTN, IDENTITY);
+        aafService.shouldRemoveNamespace(namespace);
+    }
+
+    @Test
+    public void shouldRemoveOnlyPermissionsWhenCreateTopicRolesIsFalse() {
+        given(dmaapConfig.getProperty("aaf.CreateTopicRoles", "true")).willReturn("false");
+
+        aafTopicSetupService.aafTopicCleanup(givenTopic(TOPIC_FQTN));
+
+        aafService.shouldRemovePerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "pub"));
+        aafService.shouldRemovePerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "sub"));
+        aafService.shouldRemovePerm(new DmaapPerm(TOPIC_PERM, ":topic." + TOPIC_FQTN, "view"));
+        aafService.shouldNotRemoveNamespace();
+    }
+
+    @Test
+    public void shouldRemoveOnlyPermissionsWhenTopicFqtnDoesntStartWithNsRoot() {
+
+        String topicFqtn = "sample_topic";
+        aafTopicSetupService.aafTopicCleanup(givenTopic(topicFqtn));
+
+        aafService.shouldRemovePerm(new DmaapPerm(TOPIC_PERM, ":topic." + topicFqtn, "pub"));
+        aafService.shouldRemovePerm(new DmaapPerm(TOPIC_PERM, ":topic." + topicFqtn, "sub"));
+        aafService.shouldRemovePerm(new DmaapPerm(TOPIC_PERM, ":topic." + topicFqtn, "view"));
+        aafService.shouldNotRemoveNamespace();
+    }
+
+    @Test
+    public void shouldHandleExceptionWhenPermissionRemovalWasFailed() {
+        aafService.givenRemovePermStatus(INTERNAL_SERVER_ERROR);
+
+        ApiError apiError = aafTopicSetupService.aafTopicCleanup(givenTopic(TOPIC_FQTN));
+
+        assertErrorStatus(apiError, INTERNAL_SERVER_ERROR);
+    }
+
+    @Test
+    public void shouldHandleExceptionWhenNamespaceRemovalWasFailed() {
+        aafService.givenRemoveNamespaceStatus(INTERNAL_SERVER_ERROR);
+
+        ApiError apiError = aafTopicSetupService.aafTopicCleanup(givenTopic(TOPIC_FQTN));
+
+        assertErrorStatus(apiError, INTERNAL_SERVER_ERROR);
+    }
+
+    @Test
+    public void shouldNotPerformCleanupWhenDeleteLevelIsLessThanTwo() {
+        given(dmaapConfig.getProperty("MR.ClientDeleteLevel", "0")).willReturn("0");
+
+        ApiError apiError = aafTopicSetupService.aafTopicCleanup(givenTopic(TOPIC_FQTN));
+
+        aafService.shouldNotPerformCleanup();
+        assertOkStatus(apiError);
+    }
+
+    @Test
+    public void shouldNotPerformCleanupWhenDeleteLevelIsNotNumericValue() {
+        given(dmaapConfig.getProperty("MR.ClientDeleteLevel", "0")).willReturn("not number");
+
+        ApiError apiError = aafTopicSetupService.aafTopicCleanup(givenTopic(TOPIC_FQTN));
+
+        aafService.shouldNotPerformCleanup();
+        assertOkStatus(apiError);
+    }
+
+    private Topic givenTopic(String topicFqtn) {
+        Topic topic = new Topic();
+        topic.setFqtn(topicFqtn);
+        return topic;
+    }
+
+    private void assertOkStatus(ApiError apiError) {
+        assertTrue(apiError.is2xx());
+        assertEquals("OK", apiError.getMessage());
+    }
+
+    private void assertErrorStatus(ApiError apiError, int code) {
+        assertEquals(code, apiError.getCode());
+    }
+
+    private class AafServiceStub implements AafService {
+
+        private AafNamespace addedNamespace;
+        private AafNamespace removedNamespace;
+        private List<DmaapPerm> addedPerms = newArrayList();
+        private List<DmaapPerm> removedPerms = newArrayList();
+        private List<AafRole> addedRoles = newArrayList();
+        private List<DmaapGrant> addedGrants = newArrayList();
+        private int addNamespaceStatus = CREATED;
+        private int addGrantStatus = CREATED;
+        private int addRoleStatus = CREATED;
+        private int addPermStatus = CREATED;
+        private int removePermStatus = OK;
+        private int removeNamespaceStatus = OK;
+
+        @Override
+        public String getIdentity() {
+            return IDENTITY;
+        }
+
+        @Override
+        public int addPerm(DmaapPerm perm) {
+            this.addedPerms.add(perm);
+            return addPermStatus;
+        }
+
+        @Override
+        public int delPerm(DmaapPerm perm, boolean force) {
+            removedPerms.add(perm);
+            return removePermStatus;
+        }
+
+        @Override
+        public int addGrant(DmaapGrant grant) {
+            addedGrants.add(grant);
+            return addGrantStatus;
+        }
+
+        @Override
+        public int addUserRole(AafUserRole ur) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int addRole(AafRole role) {
+            this.addedRoles.add(role);
+            return addRoleStatus;
+        }
+
+        @Override
+        public int addNamespace(AafNamespace namespace) {
+            this.addedNamespace = namespace;
+            return addNamespaceStatus;
+        }
+
+        @Override
+        public int delNamespace(AafNamespace namespace, boolean force) {
+            this.removedNamespace = namespace;
+            return removeNamespaceStatus;
+        }
+
+        void givenReturnCode(int status) {
+            this.addNamespaceStatus = status;
+            this.addGrantStatus = status;
+            this.addRoleStatus = status;
+            this.addPermStatus = status;
+            this.removePermStatus = status;
+            this.removeNamespaceStatus = status;
+        }
+
+        void givenAddNamespaceStatus(int addNamespaceStatus) {
+            this.addNamespaceStatus = addNamespaceStatus;
+        }
+
+        void givenRemoveNamespaceStatus(int removeNamespaceStatus) {
+            this.removeNamespaceStatus = removeNamespaceStatus;
+        }
+
+        void givenAddGrantStatus(int addGrantStatus) {
+            this.addGrantStatus = addGrantStatus;
+        }
+
+        void givenAddRoleStatus(int addRoleStatus) {
+            this.addRoleStatus = addRoleStatus;
+        }
+
+        void givenAddPermStatus(int addPermStatus) {
+            this.addPermStatus = addPermStatus;
+        }
+
+        void givenRemovePermStatus(int removePermStatus) {
+            this.removePermStatus = removePermStatus;
+        }
+
+        void shouldAddPerm(DmaapPerm perm) {
+            assertTrue(addedPerms.contains(perm));
+        }
+
+        void shouldRemovePerm(DmaapPerm perm) {
+            assertTrue(removedPerms.contains(perm));
+        }
+
+        void shouldAddNamespace(AafNamespace namespace) {
+            assertEquals(namespace, this.addedNamespace);
+        }
+
+        void shouldRemoveNamespace(AafNamespace namespace) {
+            assertEquals(namespace, this.removedNamespace);
+        }
+
+        void shouldAddRole(AafRole role) {
+            assertTrue(addedRoles.contains(role));
+        }
+
+        void shouldAddGrant(DmaapGrant grant) {
+            assertTrue(addedGrants.contains(grant));
+        }
+
+        void shouldHaveNoNamespaceRolesAndGrantsAdded() {
+            assertNull(this.addedNamespace);
+            assertTrue(this.addedGrants.isEmpty());
+            assertTrue(this.addedRoles.isEmpty());
+        }
+
+        void shouldNotRemoveNamespace() {
+            assertNull(this.removedNamespace);
+        }
+
+        void shouldNotPerformCleanup() {
+            shouldNotRemoveNamespace();
+            assertTrue(removedPerms.isEmpty());
+        }
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/ApiServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/ApiServiceTest.java
new file mode 100644 (file)
index 0000000..c860e55
--- /dev/null
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ApiServiceTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       ApiService ds;
+
+       @Before
+       public void setUp() throws Exception {
+               ds = new ApiService();
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               //rh.reflect( "org.onap.dmaap.dbcapi.service.ApiService", "get", null );
+
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.service.ApiService", "set", v );
+
+       }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/CredentialsParserTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/CredentialsParserTest.java
new file mode 100644 (file)
index 0000000..ae5becc
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class CredentialsParserTest {
+
+    private CredentialsParser credentialsParser = new CredentialsParser();
+
+    @Test
+    public void parse_shouldReturnEmptyCredentialsWhenAuthorizationHeaderIsNull() {
+
+        Credentials credentials = credentialsParser.parse(null);
+
+        assertTrue(credentials.getId().isEmpty());
+        assertTrue(credentials.getPwd().isEmpty());
+    }
+
+    @Test
+    public void parse_shouldReturnEmptyCredentialsWhenAuthorizationHeaderIsEmpty() {
+
+        Credentials credentials = credentialsParser.parse("");
+
+        assertTrue(credentials.getId().isEmpty());
+        assertTrue(credentials.getPwd().isEmpty());
+    }
+
+    @Test
+    public void parse_shouldParseCorrectCredentials() {
+
+        Credentials credentials = credentialsParser.parse("Basic dXNlcjpwYXNzd29yZA==");
+
+        assertEquals("user", credentials.getId());
+        assertEquals("password", credentials.getPwd());
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/DR_NodeServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/DR_NodeServiceTest.java
new file mode 100644 (file)
index 0000000..307d5b5
--- /dev/null
@@ -0,0 +1,96 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import  org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.util.List;
+
+public class DR_NodeServiceTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+       static DmaapObjectFactory factory = new DmaapObjectFactory();
+
+       DR_NodeService ns;
+
+       @Before
+       public void setUp() throws Exception {
+               ns = new DR_NodeService();
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.service.DR_NodeService", "get", null );      
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.service.DR_NodeService", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               String f = "drsn01.onap.org";
+               String locname = "central-demo";
+
+               DcaeLocationService dls = new DcaeLocationService();
+               DcaeLocation loc = factory.genDcaeLocation( "central" ); 
+               dls.addDcaeLocation( loc );
+
+               ApiError err = new ApiError();
+               DR_Node node = new DR_Node( f, locname, "zplvm009.onap.org", "1.0.46" );
+               DR_Node n2 = ns.addDr_Node( node, err );        
+
+               if ( n2 != null ) {
+                       n2 = ns.getDr_Node( f,  err );
+               }
+
+               List<DR_Node> l = ns.getAllDr_Nodes();
+               if ( n2 != null ) {
+                       n2.setVersion( "1.0.47" );
+                       n2 = ns.updateDr_Node( n2, err );
+               }
+
+               n2 = ns.removeDr_Node( f,  err );
+                               
+
+       }
+
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/DcaeLocationServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/DcaeLocationServiceTest.java
new file mode 100644 (file)
index 0000000..e0b32a4
--- /dev/null
@@ -0,0 +1,144 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.DmaapObject;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class DcaeLocationServiceTest {
+
+    private static final String LOCATION_A = "locationA";
+    private static final String LOCATION_B = "locationB";
+    private DcaeLocationService locationService = new DcaeLocationService(new HashMap<>());
+
+    @Test
+    public void getAllDcaeLocations_shouldReturnEmptyCollection() {
+
+        List<DcaeLocation> allDcaeLocations = locationService.getAllDcaeLocations();
+
+        assertTrue(allDcaeLocations.isEmpty());
+    }
+
+    @Test
+    public void addDcaeLocation_shouldAddLocationToMap() {
+        DcaeLocation locationA = createDcaeLocation(LOCATION_A);
+
+        DcaeLocation addedLocation = locationService.addDcaeLocation(locationA);
+
+        assertEquals(locationA, locationService.getDcaeLocation(LOCATION_A));
+        assertSame(locationA, addedLocation);
+    }
+
+    @Test
+    public void addDcaeLocation_shouldSetStatusAndLastModDate() {
+        DcaeLocation locationA = createDcaeLocation(LOCATION_A);
+        Date creationDate = new Date(10);
+        locationA.setLastMod(creationDate);
+
+        DcaeLocation addedLocation = locationService.addDcaeLocation(locationA);
+
+        assertTrue(addedLocation.getLastMod().after(creationDate));
+        assertEquals(DmaapObject.DmaapObject_Status.VALID, addedLocation.getStatus());
+    }
+
+    @Test
+    public void updateDcaeLocation_shouldUpdateLocationAndLastModDate() {
+        DcaeLocation location = createDcaeLocation(LOCATION_A);
+        Date creationDate = new Date(10);
+        location.setLastMod(creationDate);
+        locationService.addDcaeLocation(location);
+
+        DcaeLocation updatedLocation = locationService.updateDcaeLocation(location);
+
+        assertTrue(updatedLocation.getLastMod().after(creationDate));
+        assertSame(location, updatedLocation);
+    }
+
+    @Test
+    public void updateDcaeLocation_shouldShouldReturnNullWhenLocationNameIsEmpty() {
+        DcaeLocation location = createDcaeLocation("");
+
+        DcaeLocation updatedLocation = locationService.updateDcaeLocation(location);
+
+        assertNull(updatedLocation);
+        assertTrue(locationService.getAllDcaeLocations().isEmpty());
+    }
+
+    @Test
+    public void removeDcaeLocation_shouldRemoveLocationFromService() {
+        locationService.addDcaeLocation(createDcaeLocation(LOCATION_A));
+
+        locationService.removeDcaeLocation(LOCATION_A);
+
+        assertTrue(locationService.getAllDcaeLocations().isEmpty());
+    }
+
+    @Test
+    public void getCentralLocation_shouldGetFirstCentralLocation() {
+        locationService.addDcaeLocation(createDcaeLocation(LOCATION_A, "layerA"));
+        locationService.addDcaeLocation(createDcaeLocation(LOCATION_B, "centralLayer"));
+
+        assertEquals(LOCATION_B, locationService.getCentralLocation());
+    }
+
+    @Test
+    public void getCentralLocation_shouldReturnDefaultCentralLocationNameWhenThereIsNoCentralLocation() {
+        locationService.addDcaeLocation(createDcaeLocation(LOCATION_A, "layerA"));
+
+        assertEquals("aCentralLocation", locationService.getCentralLocation());
+    }
+
+    @Test
+    public void isEdgeLocation_shouldReturnTrueForNotCentralLocation() {
+        locationService.addDcaeLocation(createDcaeLocation(LOCATION_A, "layerA"));
+        locationService.addDcaeLocation(createDcaeLocation(LOCATION_B, "centralLayer"));
+
+        assertTrue(locationService.isEdgeLocation(LOCATION_A));
+        assertFalse(locationService.isEdgeLocation(LOCATION_B));
+    }
+
+    @Test
+    public void isEdgeLocation_shouldReturnFalseWhenLocationDoesNotExist() {
+        locationService.addDcaeLocation(createDcaeLocation(LOCATION_A, "layerA"));
+
+        assertFalse(locationService.isEdgeLocation("not_existing_location"));
+    }
+
+    private DcaeLocation createDcaeLocation(String locationName) {
+        return createDcaeLocation(locationName, "dcaeLayer");
+    }
+
+    private DcaeLocation createDcaeLocation(String locationName, String dcaeLayer) {
+        return new DcaeLocation("clli", dcaeLayer, locationName, "openStackAvailabilityZone", "subnet");
+    }
+
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/DmaapServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/DmaapServiceTest.java
new file mode 100644 (file)
index 0000000..b8b660f
--- /dev/null
@@ -0,0 +1,90 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+public class DmaapServiceTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       DmaapService ds;
+
+       @Before
+       public void setUp() throws Exception {
+               ds = new DmaapService();
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               //rh.reflect( "org.onap.dmaap.dbcapi.service.DmaapService", "get", null );      
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.service.DmaapService", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               Dmaap nd = new Dmaap.DmaapBuilder().setVer("1").setTnr("org.onap.dmaap").setDn("onap-demo").setDpu("drps.demo.onap.org").setLu("").setBat("MMAGENT_TOPIC").setNk("").setAko("").createDmaap();
+               ds.addDmaap( nd );
+       }
+
+       @Test
+       public void test4() {
+               Dmaap d = ds.getDmaap();
+
+       }
+
+       @Test
+       public void test5() {
+               Dmaap nd = new Dmaap.DmaapBuilder().setVer("2").setTnr("org.onap.dmaap").setDn("onap-demo").setDpu("drps.demo.onap.org").setLu("").setBat("MMAGENT_TOPIC").setNk("").setAko("").createDmaap();
+               ds.updateDmaap( nd );
+
+       }
+
+       @Test
+       public void test6() {
+               String t = ds.getTopicPerm();
+               String t2 = ds.getTopicPerm( "val2" );
+               String t3 = ds.getBridgeAdminFqtn();
+
+               boolean b = ds.testCreateMmaTopic();
+
+       }
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/Dr_PubServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/Dr_PubServiceTest.java
new file mode 100644 (file)
index 0000000..2cfe475
--- /dev/null
@@ -0,0 +1,108 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import  org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.util.List;
+import java.util.ArrayList;
+
+public class Dr_PubServiceTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       DR_PubService ns;
+       FeedService fs;
+
+       @Before
+       public void setUp() throws Exception {
+               ns = new DR_PubService();
+               fs = new FeedService();
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.service.DR_PubService", "get", null );       
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.service.DR_PubService", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               String locname = "central-demo";
+
+               DcaeLocationService dls = new DcaeLocationService();
+               DcaeLocation loc = new DcaeLocation( "CLLI1234", "central-onap", locname, "aZone", "10.10.10.0/24" );
+               dls.addDcaeLocation( loc );
+
+               ApiError err = new ApiError();
+               Feed f = new Feed( "aTest", "1.0", "a unit test", "dgl", "unrestricted" );
+               f = fs.addFeed( f,      err );
+
+               assertTrue( f != null );
+               DR_Pub node = new DR_Pub( locname, "aUser", "aPwd", f.getFeedId(), "pubId01" );
+               DR_Pub n2 = ns.addDr_Pub( node );       
+               DR_Pub node2 = new DR_Pub( locname, "aUser", "aPwd", f.getFeedId() );
+               n2 = ns.addDr_Pub( node2 );     
+
+               if ( n2 != null ) {
+                       n2 = ns.getDr_Pub( n2.getPubId(),  err );
+               }
+
+               List<DR_Pub> l = ns.getAllDr_Pubs();
+               if ( n2 != null ) {
+                       n2 = ns.updateDr_Pub( n2 );
+               }
+
+               n2 = ns.removeDr_Pub( n2.getPubId(),  err );
+                       
+
+       }
+
+       @Test
+       public void test4() {
+               ArrayList<DR_Pub> l = ns.getDr_PubsByFeedId( "1" );
+
+
+       }
+
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/FeedServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/FeedServiceTest.java
new file mode 100644 (file)
index 0000000..478647b
--- /dev/null
@@ -0,0 +1,102 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import java.util.List;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.Feed;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+public class FeedServiceTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       FeedService ds;
+
+       @Before
+       public void setUp() throws Exception {
+               ds = new FeedService();
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.service.FeedService", "get", null ); 
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.service.FeedService", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               ApiError err = new ApiError();
+
+               Feed f = new Feed( "aTest", "1.0", "a unit test", "dgl", "unrestricted" );
+               f = ds.addFeed( f,      err );
+               System.out.println( "f=" + f );
+
+               ds.updateFeed( f, err );
+
+               ds.removeFeed( f, err );
+       }
+
+       @Test
+       public void test4() {
+               ApiError err = new ApiError();
+               Feed f = ds.getFeed( "aName", err );
+
+               f = ds.getFeedByName( "aName", "1.0", err );
+
+               f = ds.getFeedPure( "aName", err );
+       }
+
+       @Test
+       public void test5() {
+               List<Feed> f = ds.getAllFeeds( "aName", "1.0", "startsWith" );
+
+       }
+
+       
+       @Test 
+       public void syncTestHard() {
+               ApiError err = new ApiError();
+               ds.sync(  true, err );
+               
+               assert( 200 == err.getCode());
+       }
+
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MR_ClientServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MR_ClientServiceTest.java
new file mode 100644 (file)
index 0000000..e80697a
--- /dev/null
@@ -0,0 +1,135 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.DcaeLocation;
+import org.onap.dmaap.dbcapi.model.MR_Client;
+import org.onap.dmaap.dbcapi.model.MR_Cluster;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+public class MR_ClientServiceTest {
+
+       private static final String  fmt = "%24s: %s%n";
+       
+       private static DmaapObjectFactory factory = new DmaapObjectFactory();
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       private TopicService ts;
+       private MR_ClusterService mcs;
+       private MR_ClientService cls;
+       private DcaeLocationService dls;
+
+       private String f;
+       private String locname;
+
+       @Before
+       public void setUp() throws Exception {
+               ts = new TopicService();
+               mcs = new MR_ClusterService();
+               cls = new MR_ClientService();
+               f = "mrsn01.onap.org";
+               locname = "central-demo";
+
+               dls = new DcaeLocationService();
+               DcaeLocation loc = factory.genDcaeLocation( "central" );
+               dls.addDcaeLocation( loc );
+
+               ApiError err = new ApiError();
+               String[] h = { "zplvm009.onap.org", "zplvm007.onap.org", "zplvm008.onap.org" };
+               MR_Cluster node = factory.genMR_Cluster( "central" );
+               MR_Cluster n2 = mcs.addMr_Cluster( node, err ); 
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.service.MR_ClientService", "get", null );    
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.service.MR_ClientService", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               Topic topic = factory.genSimpleTopic( "test3" );
+               ApiError err = new ApiError();
+               Topic nTopic = ts.addTopic( topic, err, false );
+               if ( nTopic != null ) {
+                       assertTrue( nTopic.getTopicName().equals( topic.getTopicName() ));
+               }
+
+               MR_Client c = factory.genPublisher( "edge",  topic.getFqtn() );
+
+               c = cls.addMr_Client( c, topic, err );
+
+       }
+
+       @Test
+       public void test4() {
+               List<MR_Client> l = cls.getAllMr_Clients();
+
+               List<MR_Client> al = cls.getAllMrClients( "foo" );
+
+               List<MR_Client> al2 = cls.getClientsByLocation( "central" );
+       }
+
+       @Test
+       public void AddSubscriberToTopic() {
+               Topic topic = factory.genSimpleTopic( "test5" );
+               ApiError err = new ApiError();
+               Topic nTopic = ts.addTopic( topic, err, false );
+               if ( nTopic != null ) {
+                       assertTrue( nTopic.getTopicName().equals( topic.getTopicName() ));
+               }
+               MR_Client c = factory.genPublisher( "central", topic.getFqtn() );
+
+               c = cls.addMr_Client( c, topic, err );
+               assertTrue( c != null );
+
+               c = factory.genSubscriber( "central", topic.getFqtn() );
+               c = cls.addMr_Client( c, topic, err );
+               assertTrue( err.getCode() == 200 );
+
+               
+       }
+       
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MR_ClusterServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MR_ClusterServiceTest.java
new file mode 100644 (file)
index 0000000..8ae2667
--- /dev/null
@@ -0,0 +1,127 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import  org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.util.List;
+
+public class MR_ClusterServiceTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       MR_ClusterService ns;
+
+       @Before
+       public void setUp() throws Exception {
+               ns = new MR_ClusterService();
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.service.MR_ClusterService", "get", null );   
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.service.MR_ClusterService", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               String f = "mrsn01.onap.org";
+               String locname = "central-demo";
+
+               DcaeLocationService dls = new DcaeLocationService();
+               DcaeLocation loc = new DcaeLocation( "CLLI1234", "some-onap", locname, "aZone", "10.10.10.0/24" );
+               dls.addDcaeLocation( loc );
+
+               ApiError err = new ApiError();
+               String[] h = { "zplvm009.onap.org", "zplvm007.onap.org", "zplvm008.onap.org" };
+               MR_Cluster node = new MR_Cluster( locname, f,  "http", "3904");
+               MR_Cluster n2 = ns.addMr_Cluster( node, err );  
+
+               if ( n2 != null ) {
+                       n2 = ns.getMr_Cluster( f,  err );
+               }
+
+               List<MR_Cluster> l = ns.getAllMr_Clusters();
+               if ( n2 != null ) {
+                       n2 = ns.updateMr_Cluster( n2, err );
+               }
+
+               n2 = ns.removeMr_Cluster( f,  err );
+                               
+
+       }
+
+/*
+       @Test
+       public void test4() {
+               List<MR_Client> l = cls.getAllMr_Clients();
+
+               ArrayList<MR_Client> al = cls.getAllMrClients( "foo" );
+
+               ArrayList<MR_Client> al2 = cls.getClientsByLocation( "central" );
+       }
+
+       @Test
+       public void test5() {
+               Topic topic = new Topic();
+               ApiError err = new ApiError();
+               topic.setTopicName( "test3" );
+               topic.setFqtnStyle( FqtnType.Validator("none") );
+               topic.getFqtn();
+               Topic nTopic = ts.addTopic( topic, err );
+               if ( nTopic != null ) {
+                       assertTrue( nTopic.getTopicName().equals( topic.getTopicName() ));
+               }
+               String[] actions = { "pub", "view" };
+               MR_Client c = new MR_Client( "central-onap", "org.onap.dmaap.demo.interestingTopic2", "org.onap.clientApp.publisher", actions );
+
+               c = cls.addMr_Client( c, topic, err );
+               if ( c != null ) {
+                               actions[0] = "sub";
+                               c.setAction( actions );
+                               c = cls.updateMr_Client( c, err );
+                               assertTrue( err.getCode() == 200 );
+               }
+       }
+*/
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MirrorMakerServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MirrorMakerServiceTest.java
new file mode 100644 (file)
index 0000000..f247bad
--- /dev/null
@@ -0,0 +1,185 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import  org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.testframework.DmaapObjectFactory;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.util.List;
+import java.util.ArrayList;
+
+public class MirrorMakerServiceTest {
+
+       private static final String  fmt = "%24s: %s%n";
+       private static DmaapObjectFactory factory = new DmaapObjectFactory();
+       ReflectionHarness rh = new ReflectionHarness();
+
+       private MirrorMakerService mms;
+       private TopicService ts;
+       private MR_ClusterService mcs;
+       private MR_ClientService cls;
+       private DcaeLocationService dls;
+       
+       private Topic replicationTopic;
+
+
+       DmaapService ds;
+       String locname;
+
+       @Before
+       public void setUp() throws Exception {
+               mms = new MirrorMakerService();
+               ts = new TopicService();
+               assert( ts != null );
+               mcs = new MR_ClusterService();
+               assert( mcs != null );
+               Dmaap nd = factory.genDmaap();
+               ds = new DmaapService();
+               ds.addDmaap( nd );
+               ts = new TopicService();
+               mcs = new MR_ClusterService();
+               cls = new MR_ClientService();
+
+               dls = new DcaeLocationService();
+               DcaeLocation loc = factory.genDcaeLocation( "central" );
+               locname = loc.getDcaeLocationName();
+               dls.addDcaeLocation( loc );
+               loc = factory.genDcaeLocation( "edge");
+               dls.addDcaeLocation( loc );
+
+               ApiError err = new ApiError();
+               
+               MR_Cluster node = factory.genMR_Cluster( "central" );
+               mcs.addMr_Cluster( node, err);
+               node = factory.genMR_Cluster("edge" );
+               mcs.addMr_Cluster(node,  err);
+
+
+               String t = "org.onap.dmaap.bridgingTopic";
+               replicationTopic = factory.genSimpleTopic(t);
+               replicationTopic.setReplicationCase( ReplicationType.REPLICATION_EDGE_TO_CENTRAL );
+
+               String c = "publisher";
+               String[] a = { "sub", "view" };
+               MR_Client sub = factory.genMR_Client("central",  replicationTopic.getFqtn(), c, a );
+               String[] b = { "pub", "view" };
+               MR_Client pub = factory.genMR_Client( "edge", replicationTopic.getFqtn(), c, b );
+               ArrayList<MR_Client> clients = new ArrayList<MR_Client>();
+
+               clients.add( sub );
+               clients.add( pub );
+
+               replicationTopic.setClients( clients );
+
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+//     @Test
+//     public void test_getters() {
+//
+//
+//             rh.reflect( "org.onap.dmaap.dbcapi.service.MirrorMakerService", "get", null );  
+//     
+//     }
+
+       @Test
+       public void test_setters() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.service.MirrorMakerService", "set", v );
+
+       }
+
+       
+       
+       @Test
+       public void CreateMirrorMakerWithSingleTopic() {
+               ApiError err = new ApiError();
+
+
+               Topic nTopic = ts.addTopic(replicationTopic, err, true );
+
+               assertTrue( err.getCode() == 200 );
+               
+               List<String> mma = mms.getAllMirrorMakers();
+       }
+       
+       @Test
+       public void DeleteMirrorMakerWithSingleTopic() {
+
+               ApiError err = new ApiError();
+               Topic nTopic = ts.addTopic(replicationTopic, err, true );
+               replicationTopic.setTopicDescription("modified topic");
+               nTopic = ts.updateTopic( replicationTopic, err );
+
+               assertTrue( err.getCode() == 200 );
+
+               
+               List<String> mma = mms.getAllMirrorMakers();
+               
+               int nMM = mma.size();
+               assertTrue( nMM >= 1);
+               
+               String name = mma.get(0);
+               
+               MirrorMaker mm = mms.getMirrorMaker(name);
+               
+               mms.delMirrorMaker(mm);
+               
+               mma = mms.getAllMirrorMakers();
+               
+               assertTrue( mma.size() == (nMM-1) );
+       }
+       
+       @Test
+       public void SplitMirrorMakerWithSingleTopic() {
+
+               ApiError err = new ApiError();
+
+
+               Topic nTopic = ts.addTopic( replicationTopic, err, true );
+               replicationTopic.setTopicDescription("modified topic");
+               nTopic = ts.updateTopic( replicationTopic, err );
+
+
+               assertTrue( err.getCode() == 200 );
+               List<String> mma = mms.getAllMirrorMakers();
+               
+               int nMM = mma.size();
+               assertTrue( nMM >= 1);
+               
+               String name = mma.get(0);
+               
+               MirrorMaker mm = mms.getMirrorMaker(name);
+               
+               MirrorMaker mm2 = mms.splitMM(mm);      
+
+       }
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MirrorMakerServiceTestMockito.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/MirrorMakerServiceTestMockito.java
new file mode 100644 (file)
index 0000000..5beadc6
--- /dev/null
@@ -0,0 +1,97 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.onap.aaf.cadi.filter.CadiFilter;
+import org.onap.dmaap.dbcapi.model.MirrorMaker;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MirrorMakerServiceTestMockito {
+
+    @Spy
+    private MirrorMakerService service;
+
+    @Mock
+    private CadiFilter cadiFilterMock;
+    @Mock
+    private HttpServletRequest servletRequest;
+    @Mock
+    private HttpServletResponse servletResponse;
+   
+    @Mock
+    private DmaapConfig dmaapConfig;
+    
+    @Mock
+    private MirrorMaker mm = new MirrorMaker();
+
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Before
+    public void setUp() throws Exception {
+   
+    }
+
+    @Test
+    public void init_normalConstructor() throws Exception {
+        //given
+        
+
+        //when
+        
+
+        //then
+        assertEquals( MirrorMakerService.getProvUserPwd(), MirrorMakerService.PROV_PWD_DEFAULT);
+        assertEquals( MirrorMakerService.getDefaultConsumerPort(), MirrorMakerService.TARGET_REPLICATION_PORT_DEFAULT);
+        assertEquals( MirrorMakerService.getDefaultProducerPort(), MirrorMakerService.SOURCE_REPLICATION_PORT_DEFAULT);
+    }
+
+  // Todo: learn how to make more tests in Mockito
+
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/TopicServiceTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/service/TopicServiceTest.java
new file mode 100644 (file)
index 0000000..a37ce02
--- /dev/null
@@ -0,0 +1,305 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.service;
+
+import com.google.common.collect.ImmutableMap;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.onap.dmaap.dbcapi.model.ApiError;
+import org.onap.dmaap.dbcapi.model.MR_Client;
+import org.onap.dmaap.dbcapi.model.Topic;
+import org.onap.dmaap.dbcapi.util.DmaapConfig;
+
+import javax.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.collect.Lists.newArrayList;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+import static javax.ws.rs.core.Response.Status.OK;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.BDDMockito.then;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.onap.dmaap.dbcapi.model.ReplicationType.REPLICATION_GLOBAL_TO_FQDN;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TopicServiceTest {
+
+    private static final String TOPIC_FQTN = "topic_1";
+    private static final String GLOBAL_MR_HOST = "global.mr.host";
+    private TopicService topicService;
+    @Mock
+    private MR_ClientService clientService;
+    @Mock
+    private DmaapConfig dmaapConfig;
+    @Mock
+    private MR_ClusterService clusters;
+    @Mock
+    private DcaeLocationService locations;
+    @Mock
+    private MirrorMakerService bridge;
+    @Mock
+    private AafTopicSetupService aafTopicSetupService;
+
+    @Before
+    public void setUp() throws Exception {
+        given(dmaapConfig.getProperty("MR.globalHost", "global.host.not.set")).willReturn(GLOBAL_MR_HOST);
+        given(aafTopicSetupService.aafTopicSetup(any(Topic.class))).willReturn(new ApiError(200, "OK"));
+        given(aafTopicSetupService.aafTopicCleanup(any(Topic.class))).willReturn(new ApiError(200, "OK"));
+        createTopicService();
+    }
+
+    @Test
+    public void getTopics_shouldReturnTopicsReceivedDuringServiceCreation() {
+
+        ImmutableMap<String, Topic> topics = ImmutableMap.of(TOPIC_FQTN, new Topic());
+        topicService = new TopicService(topics, clientService, dmaapConfig, clusters, locations, bridge, aafTopicSetupService);
+
+        assertEquals(topics, topicService.getTopics());
+    }
+
+    @Test
+    public void getAllTopics_shouldReturnTopicsWithClients() {
+
+        ArrayList<MR_Client> mrClients = newArrayList(new MR_Client());
+        given(clientService.getAllMrClients(TOPIC_FQTN)).willReturn(mrClients);
+
+        List<Topic> allTopics = topicService.getAllTopics();
+
+        assertThat(getOnlyElement(allTopics), hasCorrectFqtn(TOPIC_FQTN));
+        assertEquals(mrClients, getOnlyElement(allTopics).getClients());
+    }
+
+    @Test
+    public void getAllTopicsWithoutClients_shouldReturnNoClients() {
+
+        List<Topic> allTopics = topicService.getAllTopicsWithoutClients();
+
+        assertThat(getOnlyElement(allTopics), hasCorrectFqtn(TOPIC_FQTN));
+        assertNull(getOnlyElement(allTopics).getClients());
+        verifyZeroInteractions(clientService);
+    }
+
+    @Test
+    public void getAllTopics_shouldCacheClients() {
+
+        ArrayList<MR_Client> mrClients = newArrayList(new MR_Client());
+        given(clientService.getAllMrClients(TOPIC_FQTN)).willReturn(mrClients);
+
+        topicService.getAllTopics();
+        List<Topic> allTopics = topicService.getAllTopicsWithoutClients();
+
+        assertThat(getOnlyElement(allTopics), hasCorrectFqtn(TOPIC_FQTN));
+        assertEquals(mrClients, getOnlyElement(allTopics).getClients());
+    }
+
+    @Test
+    public void getTopic_shouldReturnTopicByFqtn() {
+
+        ApiError apiError = new ApiError();
+        Topic topic = topicService.getTopic(TOPIC_FQTN, apiError);
+
+        assertThat(topic, hasCorrectFqtn(TOPIC_FQTN));
+        assertEquals(OK.getStatusCode(), apiError.getCode());
+    }
+
+    @Test
+    public void getTopic_shouldReturnTopicWithMrClients() {
+
+        ArrayList<MR_Client> mrClients = newArrayList(new MR_Client());
+        given(clientService.getAllMrClients(TOPIC_FQTN)).willReturn(mrClients);
+
+        Topic topic = topicService.getTopic(TOPIC_FQTN, new ApiError());
+
+        assertThat(topic, hasCorrectFqtn(TOPIC_FQTN));
+        assertEquals(mrClients, topic.getClients());
+    }
+
+    @Test
+    public void getTopic_shouldReturnError() {
+
+        ApiError apiError = new ApiError();
+        Topic topic = topicService.getTopic("not_existing", apiError);
+
+        assertNull(topic);
+        assertEquals(NOT_FOUND.getStatusCode(), apiError.getCode());
+    }
+
+    @Test
+    public void addTopic_shouldAddNewTopic() {
+        Topic newTopic = createTopic("");
+
+        ApiError apiError = new ApiError();
+        Topic addedTopic = topicService.addTopic(newTopic, apiError, true);
+
+        assertSame(newTopic, addedTopic);
+        assertEquals(OK.getStatusCode(), apiError.getCode());
+        assertNotNull(topicService.getTopic(addedTopic.getFqtn(), new ApiError()));
+    }
+
+    @Test
+    public void addTopic_shouldReturnErrorWhenTopicAlreadyExists() {
+        Topic newTopic = createTopic("");
+
+        ApiError apiError = new ApiError();
+        Topic addedTopic = topicService.addTopic(newTopic, apiError, false);
+        Topic secondAddedTopic = topicService.addTopic(addedTopic, apiError, false);
+
+        assertNull(secondAddedTopic);
+        assertEquals(Response.Status.CONFLICT.getStatusCode(), apiError.getCode());
+    }
+
+    @Test
+    public void addTopic_shouldAddTheSameTopicWhenUseExistingIsSet() {
+        Topic newTopic = createTopic("");
+
+        ApiError apiError = new ApiError();
+        Topic addedTopic = topicService.addTopic(newTopic, apiError, false);
+        Topic secondAddedTopic = topicService.addTopic(addedTopic, apiError, true);
+
+        assertSame(addedTopic, secondAddedTopic);
+        assertEquals(OK.getStatusCode(), apiError.getCode());
+        assertNotNull(topicService.getTopic(secondAddedTopic.getFqtn(), new ApiError()));
+    }
+
+
+    @Test
+    public void addTopic_shouldSetGlobalMrURL() {
+        Topic newTopic = createTopic(TOPIC_FQTN);
+        newTopic.setReplicationCase(REPLICATION_GLOBAL_TO_FQDN);
+
+        ApiError apiError = new ApiError();
+        Topic addedTopic = topicService.addTopic(newTopic, apiError, true);
+
+        assertEquals(OK.getStatusCode(), apiError.getCode());
+        assertEquals(GLOBAL_MR_HOST, addedTopic.getGlobalMrURL());
+    }
+
+    @Test
+    public void addTopic_shouldReturnErrorWhenGlobalMrURLIsInvalid() {
+        given(dmaapConfig.getProperty("MR.globalHost", "global.host.not.set")).willReturn("invalid@host");
+        createTopicService();
+        Topic newTopic = createTopic(TOPIC_FQTN);
+        newTopic.setReplicationCase(REPLICATION_GLOBAL_TO_FQDN);
+
+        ApiError apiError = new ApiError();
+        Topic addedTopic = topicService.addTopic(newTopic, apiError, true);
+
+        assertEquals(500, apiError.getCode());
+        assertNull(addedTopic);
+    }
+
+    @Test
+    public void removeTopic_shouldFailIfTopicDoesNotExist() {
+        ApiError apiError = new ApiError();
+
+        Topic removedTopic = topicService.removeTopic("not_existing_fqtn", apiError);
+
+        assertNull(removedTopic);
+        assertEquals(NOT_FOUND.getStatusCode(), apiError.getCode());
+        assertTrue(topicService.getTopics().containsKey(TOPIC_FQTN));
+    }
+
+    @Test
+    public void removeTopic_shouldExecuteAafCleanup() {
+        ApiError apiError = new ApiError();
+
+        Topic removedTopic = topicService.removeTopic(TOPIC_FQTN, apiError);
+
+        then(aafTopicSetupService).should().aafTopicCleanup(removedTopic);
+        assertEquals(OK.getStatusCode(), apiError.getCode());
+    }
+
+    @Test
+    public void removeTopic_shouldRemoveEachMrClientAssignedToTopic() {
+        ApiError apiError = new ApiError();
+        MR_Client mrClient = new MR_Client();
+        mrClient.setMrClientId("mrClientId");
+
+        given(clientService.getAllMrClients(TOPIC_FQTN)).willReturn(newArrayList(mrClient));
+
+        topicService.removeTopic(TOPIC_FQTN, apiError);
+
+        then(clientService).should().removeMr_Client(mrClient.getMrClientId(), false, apiError);
+        assertEquals(OK.getStatusCode(), apiError.getCode());
+    }
+
+    @Test
+    public void removeTopic_shouldRemoveTopicFromCache() {
+        ApiError apiError = new ApiError();
+
+        topicService.removeTopic(TOPIC_FQTN, apiError);
+
+        assertTrue(topicService.getTopics().isEmpty());
+        assertEquals(OK.getStatusCode(), apiError.getCode());
+    }
+
+    @Test
+    public void removeTopic_shouldFailIfAafCleanupWasFailed() {
+        ApiError apiError = new ApiError();
+        given(aafTopicSetupService.aafTopicCleanup(any(Topic.class))).willReturn(new ApiError(404, "sth went wrong"));
+
+        Topic removedTopic = topicService.removeTopic(TOPIC_FQTN, apiError);
+
+        assertNull(removedTopic);
+        assertEquals(404, apiError.getCode());
+        assertTrue(topicService.getTopics().containsKey(TOPIC_FQTN));
+    }
+
+    private void createTopicService() {
+        Map<String, Topic> mrTopics = new HashMap<>();
+        mrTopics.put(TOPIC_FQTN, createTopic(TOPIC_FQTN));
+        topicService = new TopicService(mrTopics, clientService, dmaapConfig, clusters, locations, bridge, aafTopicSetupService);
+    }
+
+    private Topic createTopic(String fqtn) {
+        return new Topic(fqtn, "name", "desc", "tnxEnabled", "owner");
+    }
+
+    public static Matcher<Topic> hasCorrectFqtn(final String fqtn) {
+        return new BaseMatcher<Topic>() {
+            public boolean matches(Object o) {
+                return fqtn.equals(((Topic) o).getFqtn());
+            }
+
+            public void describeTo(Description description) {
+                description.appendText("Topics should should be equal. Expected fqtn: ").appendValue(fqtn);
+            }
+        };
+    }
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/testframework/DmaapObjectFactory.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/testframework/DmaapObjectFactory.java
new file mode 100644 (file)
index 0000000..9d45a54
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.testframework;
+
+import org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.util.RandomInteger;
+
+public class DmaapObjectFactory {
+
+       /*
+        * we use localhost for most references so that connection attempts will resolve and not retry
+        * but still we expect that requests will fail.
+        */
+       private static final String  fmt = "%24s: %s%n";
+       private static final String     dmaap_name = "onap-ut";
+       private static final String dmaap_ver = "1";
+       private static final String dmaap_topic_root = "org.onap.dmaap";
+       private static final String dmaap_dr = "https://localhost:8443";
+       private static final String dmaap_log_url = "http://localhost:8080/log";
+       private static final String dmaap_mm_topic = "org.onap.dmaap.dcae.MM_AGENT_TOPIC";
+       private static final String central_loc = "SanFrancisco";
+       private static final String central_layer = "central-cloud";
+       private static final String central_clli = "SFCAL19240";
+       private static final String central_zone = "osaz01";
+       private static final String central_subnet = "10.10.10.0/24";
+       private static final String central_cluster_fqdn = "localhost";
+       private static final String pub_role = "org.onap.vnfapp.publisher";
+       private static final String sub_role = "org.onap.vnfapp.subscriber";
+       private static final String edge_loc = "Atlanta";
+       private static final String edge_layer = "edge-cloud";
+       private static final String edge_clli = "ATLGA10245";
+       private static final String edge_zone = "osaz02";
+       private static final String edge_subnet = "10.10.20.0/24";
+       private static final String edge_cluster_fqdn = "localhost";
+       private static final String[]hosts = { "host1", "host2", "host3" };
+       private static final String port = "3904";
+       private static final String prot = "http";
+
+       public Dmaap genDmaap() {
+               return new Dmaap.DmaapBuilder().setVer(dmaap_ver).setTnr(dmaap_topic_root).setDn(dmaap_name).setDpu(dmaap_dr).setLu(dmaap_log_url).setBat(dmaap_mm_topic).setNk("nk").setAko("ako").createDmaap();
+       }
+
+       public DcaeLocation genDcaeLocation( String layer ) {
+               if ( layer.contains( "edge" ) ) {
+                       return new DcaeLocation( edge_clli, edge_layer, edge_loc, edge_zone, edge_subnet );
+               }
+               return new DcaeLocation( central_clli, central_layer, central_loc, central_zone, central_subnet );
+       }
+
+
+       public MR_Cluster genMR_Cluster( String layer ) {
+               if ( layer.contains( "edge" ) ) {
+                       return new MR_Cluster( edge_loc, edge_cluster_fqdn,  prot, port );
+               }
+               return new MR_Cluster( central_loc, central_cluster_fqdn, prot, port );
+       }
+
+       public Topic genSimpleTopic( String tname ) {
+               Topic t = new Topic();
+               t.setTopicName( tname );
+        t.setFqtnStyle( FqtnType.Validator("none") );
+        t.setTopicDescription( "a simple Topic named " + tname );
+        t.setOwner( "ut");
+        t.setFqtn(t.genFqtn());
+               return t;
+       }
+
+       public MR_Client genMR_Client( String l, String f, String r, String[] a ) {
+               if ( l.contains( "edge" ) ) {
+                       return new MR_Client( edge_loc, f, r, a );
+               }
+               return new MR_Client( central_loc, f, r, a );
+       }
+
+       public MR_Client genPublisher( String layer, String fqtn ) {
+               String[] actions = { "pub", "view" };
+               return genMR_Client( layer, fqtn, pub_role, actions );
+       }
+       public MR_Client genSubscriber( String layer, String fqtn ) {
+               String[] actions = { "sub", "view" };
+               return genMR_Client( layer, fqtn, sub_role, actions );
+       }
+
+       public DR_Sub genDrSub( String l, String feed ) {
+        String un = "user1";
+        String up = "secretW0rd";
+        String du = "sub.server.onap.org:8443/deliver/here";
+        String lu = "https://drps.onap.org:8443/sublog/123";
+        boolean u100 = true;
+
+               if ( l.contains( "edge" ) ) {
+                       return new DR_Sub( edge_loc, un, up, feed, du, lu, u100 );
+               }
+               return new DR_Sub( central_loc, un, up, feed, du, lu, u100 );
+       }
+
+       public DR_Node genDR_Node( String l ) {
+        String version = "1.0.1";
+               RandomInteger ri = new RandomInteger( 1000 );
+               int i = ri.next();
+               String fqdn = String.format( "drns%d.onap.org", i );
+               String host = String.format( "host%d.onap.org", i );
+
+               if ( l.contains( "edge" ) ) {
+                       return new DR_Node( fqdn, edge_loc, host, version );
+               }
+               return new DR_Node( fqdn, central_loc, host, version );
+       }
+                               
+
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/testframework/ReflectionHarness.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/testframework/ReflectionHarness.java
new file mode 100644 (file)
index 0000000..be4b754
--- /dev/null
@@ -0,0 +1,169 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.testframework;
+
+import static java.lang.System.err;
+import static java.lang.System.out;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+public class ReflectionHarness {
+       private static final String  fmt = "%24s: %s%n";
+
+
+       // following 2 functions taken from: http://tutorials.jenkov.com/java-reflection/getters-setters.html
+       public static boolean isGetter(Method method){
+         if(!method.getName().startsWith("get"))      return false;
+         if(method.getParameterTypes().length != 0)   return false;  
+         if(void.class.equals(method.getReturnType())) return false;
+         return true;
+       }
+
+       public static boolean isSetter(Method method){
+         if(!method.getName().startsWith("set")) return false;
+         if(method.getParameterTypes().length != 1) return false;
+         return true;
+       }
+
+       private void testGetter( Class<?> c, Method m, Class<?>[] pType, String val ) {
+               out.format( fmt, "testGetter: Method Name", m.getName() );
+               Class retType = m.getReturnType();
+               out.format( fmt, "testGetter: Return Type ", retType );
+               out.format( fmt, "testGetter: val ", (val != null)?val:"null" );
+               assertTrue( pType.length == 0 );
+
+               try {
+                       Object t = c.newInstance();
+                       
+                               try {
+                                       m.setAccessible(true);
+                                       Object o = m.invoke( t );
+                                       
+                                       if( retType.equals( Class.forName( "java.lang.String" ) ) ) {
+                                               if ( val == null ) {
+                                                       out.format( fmt, "testGetter: expected null, got  ", (o != null)?o:"null" );
+                                                       assert( o == null );
+                                               } else {
+                                                       out.format( fmt, "testGetter: expected val, got  ", (o != null)?o:"null" );
+                                                       assert( o.equals( val ) );
+                                               }
+                                       } else {
+                                               out.format( fmt, "testGetter: " + m.getName() + " untested retType", retType );
+
+                                       }
+                       
+                               } catch (InvocationTargetException e ) {
+                                       Throwable cause = e.getCause();
+                                       err.format( "%s() returned %x%n", m.getName(), cause.getMessage() );
+                               }
+                                       
+               } catch (ClassNotFoundException nfe ){
+               nfe.printStackTrace();
+               } catch (IllegalArgumentException ae ) {
+                       ae.printStackTrace();
+               } catch (InstantiationException ie ) {
+                       ie.printStackTrace();
+               } catch (IllegalAccessException iae ) {
+                       iae.printStackTrace();
+               }
+       }
+
+       private void testSetter( Class<?> c, Method m, Class<?>[] pType ) {
+               //out.format( fmt, "testSetter: Method Name", m.getName() );
+               Class retType = m.getReturnType();
+               //out.format( fmt, "testSetter: Return Type ", retType );
+               //out.format( fmt, "testSetter: val ", (val != null)?val:"null" );
+               assertTrue( pType.length == 1 );
+
+               try {
+                       Object t = c.newInstance();
+                       
+                               try {
+                                       m.setAccessible(true);
+                                       //out.format( fmt, "testSetter: " + m.getName() + " to try pType", pType[0] );
+                                       if ( pType[0].equals( Class.forName( "java.lang.String" ) ) ) {
+                                               String val = "Validate123";
+                                               Object o = m.invoke( t, val );
+                                       } else if ( pType[0].equals( boolean.class ) ) { // note primitive class notation 
+                                               boolean b = true;
+                                               Object o = m.invoke( t, b );
+                                       } else {
+                                               out.format( fmt, "testSetter: " + m.getName() + " untested pType", pType[0] );
+                                       }
+                       
+                               } catch (InvocationTargetException e ) {
+                                       Throwable cause = e.getCause();
+                                       err.format( "%s() returned %x%n", m.getName(), cause.getMessage() );
+                               }
+                                       
+               } catch (ClassNotFoundException nfe ){
+               nfe.printStackTrace();
+               } catch (IllegalArgumentException ae ) {
+                       ae.printStackTrace();
+               } catch (InstantiationException ie ) {
+                       ie.printStackTrace();
+               } catch (IllegalAccessException iae ) {
+                       iae.printStackTrace();
+               }
+       }
+
+    public void reflect(String... args) {
+               try {
+           Class<?> c = Class.forName(args[0]);
+           Method[] allMethods = c.getDeclaredMethods();
+           String methodPrefix = args[1];
+           for (Method m : allMethods) {
+                       if (!m.getName().startsWith(methodPrefix)) {
+                       continue;
+                       }
+                       //out.format("%s%n", m.toGenericString());
+
+                       //out.format(fmt, "ReturnType", m.getReturnType());
+                       //out.format(fmt, "GenericReturnType", m.getGenericReturnType());
+
+                       Class<?>[] pType  = m.getParameterTypes();
+                       Type[] gpType = m.getGenericParameterTypes();
+                       for (int i = 0; i < pType.length; i++) {
+                       //out.format(fmt,"ParameterType", pType[i]);
+                       //out.format(fmt,"GenericParameterType", gpType[i]);
+                       }
+                       if ( isGetter( m ) ) {
+                               testGetter( c, m, pType , args[2]);
+                       } else if ( isSetter( m ) ) {
+                               testSetter( c, m, pType );
+                       }
+
+                       Class<?>[] xType  = m.getExceptionTypes();
+                       Type[] gxType = m.getGenericExceptionTypes();
+                       for (int i = 0; i < xType.length; i++) {
+                       //out.format(fmt,"ExceptionType", xType[i]);
+                       //out.format(fmt,"GenericExceptionType", gxType[i]);
+                       }
+           }
+
+        // production code should handle these exceptions more gracefully
+               } catch (ClassNotFoundException x) {
+               x.printStackTrace();
+               }
+    }
+}
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/DmaapConfigTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/DmaapConfigTest.java
new file mode 100644 (file)
index 0000000..6ef05c0
--- /dev/null
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.util;
+
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DmaapConfigTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       DmaapConfig g;
+
+
+       @Before
+       public void setUp() throws Exception {
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.util.DmaapConfig", "get", "" );      
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               rh.reflect( "org.onap.dmaap.dbcapi.util.DmaapConfig", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+
+               String f = g.getConfigFileName();
+       }
+
+
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/DmaapTimestampTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/DmaapTimestampTest.java
new file mode 100644 (file)
index 0000000..33d1d17
--- /dev/null
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.util;
+
+import org.junit.Test;
+
+import java.util.Date;
+
+import static org.junit.Assert.assertTrue;
+
+public class DmaapTimestampTest {
+
+    private DmaapTimestamp dmaapTimestamp = new DmaapTimestamp();
+
+    @Test
+    public void mark_shouldUpdateTimestamp() {
+        dmaapTimestamp = new DmaapTimestamp(new Date(10));
+        Date timestamp = dmaapTimestamp.getVal();
+
+        dmaapTimestamp.mark();
+
+        assertTrue(timestamp.before(dmaapTimestamp.getVal()));
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/FqdnTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/FqdnTest.java
new file mode 100644 (file)
index 0000000..7c7815f
--- /dev/null
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 IBM Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class FqdnTest {
+
+    @Test
+    public void testIsValid() {
+        assertTrue(Fqdn.isValid("www.ibm.com"));
+        assertFalse(Fqdn.isValid("testuser@ibm.com"));
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/GraphTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/GraphTest.java
new file mode 100644 (file)
index 0000000..770ee65
--- /dev/null
@@ -0,0 +1,102 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.util;
+
+import org.onap.dmaap.dbcapi.model.*;
+import org.onap.dmaap.dbcapi.service.*;
+import org.onap.dmaap.dbcapi.testframework.ReflectionHarness;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.util.*;
+
+public class GraphTest {
+
+       private static final String  fmt = "%24s: %s%n";
+
+       ReflectionHarness rh = new ReflectionHarness();
+
+       Graph g;
+
+
+       @Before
+       public void setUp() throws Exception {
+               HashMap<String, String> hm = new HashMap<String,String>();
+               g = new Graph( hm );
+       }
+
+       @After
+       public void tearDown() throws Exception {
+       }
+
+
+       @Test
+       public void test1() {
+
+
+               rh.reflect( "org.onap.dmaap.dbcapi.util.Graph", "get", "idNotSet@namespaceNotSet:pwdNotSet" );  
+       
+       }
+
+       @Test
+       public void test2() {
+               String v = "Validate";
+               //rh.reflect( "org.onap.dmaap.dbcapi.util.Graph", "set", v );
+
+       }
+
+       @Test
+       public void test3() {
+               String loc = "central-onap";
+               String[] actions = { "pub", "sub" };
+               DcaeLocationService dls = new DcaeLocationService();
+               DcaeLocation dl = new DcaeLocation( "CLLI123", "central-layer", loc, "aZone", "10.10.10.10" );
+               dls.addDcaeLocation( dl );
+               MR_Client mrc = new MR_Client();
+               mrc.setAction( actions );
+               List<MR_Client> cl = new ArrayList<MR_Client>();
+               cl.add( mrc );
+               cl.add( new MR_Client( loc, "aTopic", "ignore", actions ) );
+               
+               g = new Graph( cl, true );
+
+               HashMap<String, String> hm = new HashMap<String, String>();
+
+
+               String s = g.put( "aKey", "aVal" );
+               s = g.get( "aKey" );
+
+               s = g.getCentralLoc();          
+               g.setHasCentral( true );
+               g.hasCentral();
+
+               hm = g.getGraph();
+
+               Collection<String> k = g.getKeys();
+
+       }
+
+
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/PermissionBuilderTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/PermissionBuilderTest.java
new file mode 100644 (file)
index 0000000..8db9d2e
--- /dev/null
@@ -0,0 +1,164 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import javax.servlet.http.HttpServletRequest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.onap.dmaap.dbcapi.model.Dmaap;
+import org.onap.dmaap.dbcapi.model.DmaapObject.DmaapObject_Status;
+import org.onap.dmaap.dbcapi.service.DmaapService;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PermissionBuilderTest {
+
+    private static final String DMAAP_NAME = "mr";
+    private PermissionBuilder permissionBuilder;
+    @Mock
+    private DmaapConfig dmaapConfig;
+    @Mock
+    private DmaapService dmaapService;
+    @Mock
+    private HttpServletRequest request;
+
+
+    @Test
+    public void updateDmaapInstance_shouldSetBootInstance_whenDmaapIsNotInitialized() {
+        //given
+        doReturn(null).when(dmaapService).getDmaap();
+        permissionBuilder = new PermissionBuilder(dmaapConfig, dmaapService);
+
+        //when
+        permissionBuilder.updateDmaapInstance();
+
+        //then
+        assertEquals(PermissionBuilder.BOOT_INSTANCE, permissionBuilder.getInstance());
+    }
+
+    @Test
+    public void updateDmaapInstance_shouldSetBootInstance_whenDmaapIsInitializedWithDefaultInstance() {
+        //given
+        doReturn(provideDefaultInstance()).when(dmaapService).getDmaap();
+        permissionBuilder = new PermissionBuilder(dmaapConfig, dmaapService);
+
+        //when
+        permissionBuilder.updateDmaapInstance();
+
+        //then
+        assertEquals(PermissionBuilder.BOOT_INSTANCE, permissionBuilder.getInstance());
+    }
+
+    @Test
+    public void updateDmaapInstance_shouldSetRealInstance_whenDmaapServiceProvidesOne() {
+        //given
+        when(dmaapService.getDmaap()).thenReturn(provideDefaultInstance(), provideRealInstance(DMAAP_NAME));
+        permissionBuilder = new PermissionBuilder(dmaapConfig, dmaapService);
+
+        //when
+        permissionBuilder.updateDmaapInstance();
+
+        //then
+        assertEquals(DMAAP_NAME, permissionBuilder.getInstance());
+    }
+
+    @Test
+    public void updateDmaapInstance_shouldNotUpdateDmaapInstance_whenAlreadyInitializedWithRealInstance() {
+        //given
+        when(dmaapService.getDmaap()).thenReturn(provideRealInstance(DMAAP_NAME), provideRealInstance("newName"));
+        permissionBuilder = new PermissionBuilder(dmaapConfig, dmaapService);
+
+        //when
+        permissionBuilder.updateDmaapInstance();
+
+        //then
+        assertEquals(DMAAP_NAME, permissionBuilder.getInstance());
+        verify(dmaapService, atMost(1)).getDmaap();
+    }
+
+    @Test
+    public void buildPermission_shouldBuildPermissionWithBootInstance() {
+        //given
+        String path = "/dmaap";
+        String method = "GET";
+        initPermissionBuilder(path, method, provideDefaultInstance());
+
+        //when
+        String permission = permissionBuilder.buildPermission(request);
+
+        //then
+        assertEquals("org.onap.dmaap-bc.api.dmaap|boot|GET", permission);
+    }
+
+    @Test
+    public void buildPermission_shouldBuildPermissionWithRealInstance() {
+        //given
+        String path = "/dmaap";
+        String method = "GET";
+        initPermissionBuilder(path, method, provideRealInstance(DMAAP_NAME));
+
+        //when
+        String permission = permissionBuilder.buildPermission(request);
+
+        //then
+        assertEquals("org.onap.dmaap-bc.api.dmaap|mr|GET", permission);
+    }
+
+    @Test
+    public void buildPermission_shouldBuildPermissionWhenUrlContainsId() {
+        //given
+        String path = "/topics/topic_id_123";
+        String method = "GET";
+        initPermissionBuilder(path, method, provideRealInstance(DMAAP_NAME));
+
+        //when
+        String permission = permissionBuilder.buildPermission(request);
+
+        //then
+        assertEquals("org.onap.dmaap-bc.api.topics|mr|GET", permission);
+    }
+
+    private void initPermissionBuilder(String path, String method, Dmaap dmaapInstance) {
+        when(dmaapConfig.getProperty(PermissionBuilder.API_NS_PROP, PermissionBuilder.DEFAULT_API_NS))
+            .thenReturn(PermissionBuilder.DEFAULT_API_NS);
+        when(dmaapService.getDmaap()).thenReturn(dmaapInstance);
+        permissionBuilder = new PermissionBuilder(dmaapConfig, dmaapService);
+
+        when(request.getPathInfo()).thenReturn(path);
+        when(request.getMethod()).thenReturn(method);
+    }
+
+    private Dmaap provideDefaultInstance() {
+        return new Dmaap.DmaapBuilder().setVer("0").setTnr("").setDn("").setDpu("").setLu("").setBat("").setNk("").setAko("").createDmaap();
+    }
+
+    private Dmaap provideRealInstance(String dmaapName) {
+        Dmaap dmaap = new Dmaap.DmaapBuilder().setVer("1").setTnr("org.onap.dmaap").setDn(dmaapName).setDpu("https://dmaap-dr-prov:8443").setLu("").setBat("DCAE_MM_AGENT").setNk("").setAko("").createDmaap();
+        dmaap.setStatus(DmaapObject_Status.VALID);
+        return dmaap;
+    }
+
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/RandomIntegerTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/RandomIntegerTest.java
new file mode 100644 (file)
index 0000000..1184cdb
--- /dev/null
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.dmaap.dbcapi.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+public class RandomIntegerTest {
+
+    private static final int RANGE = 10;
+    private RandomInteger ri = new RandomInteger(RANGE);
+
+    @Test
+    public void next_shouldReturnIntegerFromGivenRange() {
+
+        int next = ri.next();
+
+        assertTrue(next >= 0 && next <= RANGE);
+    }
+
+}
+
diff --git a/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/RandomStringTest.java b/dmaap-bc/src/test/java/org/onap/dmaap/dbcapi/util/RandomStringTest.java
new file mode 100644 (file)
index 0000000..e549dc3
--- /dev/null
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * org.onap.dmaap
+ * ================================================================================
+ * Copyright (C) 2019 Nokia Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.dmaap.dbcapi.util;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class RandomStringTest {
+
+    private static final int LENGTH = 10;
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+    private RandomString randomString = new RandomString(LENGTH);
+
+    @Test
+    public void nextString_shouldReturnStringWithGivenLength() {
+
+        String nextString = randomString.nextString();
+
+        assertEquals(LENGTH, nextString.length());
+    }
+
+    @Test
+    public void nextString_shouldReturnAlphanumeric() {
+
+        String nextString = randomString.nextString();
+
+        assertTrue(nextString.matches("[a-z0-9]*"));
+    }
+
+    @Test
+    public void constructor_shouldThrowExceptionForNegativeLength() {
+
+        thrown.expect(IllegalArgumentException.class);
+
+        new RandomString(-1);
+    }
+}
\ No newline at end of file
diff --git a/dmaap-bc/src/test/resources/cadi.properties b/dmaap-bc/src/test/resources/cadi.properties
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/dmaap-bc/src/test/resources/dmaapbc.properties b/dmaap-bc/src/test/resources/dmaapbc.properties
new file mode 100644 (file)
index 0000000..5290032
--- /dev/null
@@ -0,0 +1,274 @@
+# Copyright Â© 2018  AT&T, Amdocs, Bell Canada Intellectual Property.  All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+#####################################################
+#
+# Hooks for specific environment configurations
+#
+#####################################################
+# Indicator for whether to use AAF for authentication
+#UseAAF: false
+
+# Stub out southbound calls for Unit Test cases to run.  e.g. not timeout
+# Comment out in other environments to get default (No)
+UnitTest: Yes
+
+
+#####################################################
+#
+# Settings for Southbound API: Datarouter
+#
+#####################################################
+
+# URI to retrieve dynamic DR configuration
+ProvisioningURI:  /internal/prov
+
+# indicator for handling feed delete:
+#  DeleteOnDR - means use the DR API to DELETE a feed.  (default for backwards compatibility)
+#  SimulateDelete - means preserve the feed on DR (after cleaning it up), and mark as DELETED in DBCL.  Better for cloudify environments.
+Feed.deleteHandling: DeleteOnDR
+
+###########################################################
+# The following properties default to match ONAP DR instance.
+# However, there are some non-ONAP DR instances that require other values.
+# Sets the X-DR-ON-BEHALF-OF HTTP Header value
+#DR.onBehalfHeader:
+# Value for the Content-Type Header in DR Feed API
+#DR.feedContentType:
+# Value for the Content-Type Header in DR Subscription API
+#DR.subContentType:
+#
+# END OF properties helpful for non-ONAP DR instance.
+############################################################
+
+#####################################################
+#
+# Settings for Soutbound API: Postgresql
+#
+#####################################################
+# flag indicates if we are using postgresql
+UsePGSQL: false
+
+# postgres host name
+# Need to connect to PG primary service, designated by service.name2
+DB.host: none
+
+# postgres schema name
+#DB.schema: {{ .Values.postgres.config.pgDatabase }}
+
+# postgres user name
+#DB.user: {{ .Values.postgres.config.pgUserName }}
+
+# postgres user password
+DB.cred: none
+
+
+#####################################################
+#
+# Settings for Soutbound API: Message Router
+#
+#####################################################
+# indicator for multi-site (locations) deployment.  Give clue to buscontroller whether
+# there is a need for message replication between edge and central.
+# ONAP Casablanca is a single site deployment
+MR.multisite: true
+
+# FQDN of primary message router.
+# In ONAP Casablanca, there is only 1 message router service, so use that.
+# In a multi-site, MR cluster deployment, use the CNAME DNS entry which resolves to the primary central MR
+MR.CentralCname: notSet.onap.org
+
+# Indicator for whether we want hostname verification on SSL connection to MR
+MR.hostnameVerify: false
+
+# MR Client Delete Level thoroughness:
+#  0 = don't delete
+#  1 = delete from persistent store
+#  2 = delete from persistent store (DB) and authorization store (AAF)
+MR.ClientDeleteLevel: 1
+
+# namespace of MR Topic Factory
+MR.TopicFactoryNS: org.onap.dmaap.mr.topicFactory
+
+# AAF Role assigned to Topic Manager Identity
+MR.TopicMgrRole: org.onap.dmaap-bc.TopicMgr
+
+# MR topic ProjectID (used in certain topic name generation formats)
+MR.projectID:  23456
+
+# Use Basic Authentication when provisioning topics
+#MR.authentication: basicAuth
+
+# MR topic name style (default is FQTN_LEGACY_FORMAT)
+MR.topicStyle: FQTN_LEGACY_FORMAT
+#
+# end of MR Related Properties
+################################################################################
+
+
+#####################################################
+#
+# Settings for Southbound API: CADI
+#
+#####################################################
+# path to cadi.properties
+#cadi.properties: /opt/app/osaaf/local/org.onap.dmaap-bc.props
+
+#####################################################
+#
+# Settings for Southbound API: AAF proxy
+#
+#####################################################
+# URL of the AAF server
+aaf.URL: https://localhost:8100/proxy
+
+# TopicMgr Identity
+aaf.TopicMgrUser: idNotSet@namespaceNotSet
+
+# Password for TopicMgr identity
+aaf.TopicMgrPassword: pwdNotSet
+
+# Buscontroller Admin Identity
+aaf.AdminUser: idNotSet@namespaceNotSet
+
+# Admin Password
+aaf.AdminPassword: pwdNotSet
+
+# Identity that is owner of any created namespaces for topics
+#aaf.NsOwnerIdentity: ownerNotSet@namespaceNotSet.org
+
+
+# this overrides the Class used for Decryption.
+# This allows for a plugin encryption/decryption method if needed.
+# Call this Class for decryption at runtime.
+#AafDecryption.Class: com.company.proprietaryDecryptor
+
+# location of the codec keyfile used to decrypt passwords in this properties file before they are passed to AAF
+# Not used in ONAP, but possibly used with Decryption override class.
+CredentialCodecKeyfile: etc/LocalKey
+
+#
+# endof AAF Properties
+####################################################
+
+
+#####################################################
+#
+# Settings for authorization of DBCAPI
+#
+#####################################################
+# Namespace for URI values for the API used to create AAF permissions
+# e.g. if ApiNamespace is X.Y.dmaapbc.api then for URI /mr_clients we create AAF perm X.Y.dmaapbc.api.mr_clients
+ApiNamespace: org.onap.dmaapBC.api
+
+# If API authorization is required, then implement a class to enforce it.
+# This overrides the Class used for API permission check.
+ApiPermission.Class: org.onap.dmaap.dbcapi.authentication.AllowAll
+
+#####################################################
+#
+# Settings for Southbound API: MirrorMaker provisioning
+#
+#####################################################
+# AAF Role of client publishing MM prov cmds
+MM.ProvRole: org.onap.dmaapBC.MMprov.prov
+
+# AAF identity when publishing MM prov cmds
+MM.ProvUserMechId: idNotSet@namespaceNotSet
+
+# pwd for Identity used to publish MM prov cmds
+MM.ProvUserPwd: pwdNotSet
+
+# AAF Role of MirrorMaker agent subscribed to prov cmds. 
+MM.AgentRole: org.onap.dmaapBC.MMagent.agent
+
+#####################################################
+#
+# Certificate Management
+#
+#####################################################
+
+# Indicates how we are expecting certificates to be provided:
+#  cadi - a set of artifacts will be downloaded from AAF at deployment time, and details will be in a cadi properties file
+#  legacy (default) - artifacts will be installed manually or some other way and details will be in this file
+CertificateManagement: legacy
+
+# When CertificateManagement is cadi, then this is where all the cadi properties will be.
+# Note that the cadi properties include where the cert is, and the encrypted passwords to read.
+cadi.properties: /opt/app/osaaf/local/org.onap.dmaap-bc.props
+
+###########################################################################################
+# When CertificateManagement is legacy, we need to provide more details about cert handling:
+#CertificateManagement: legacy
+# the type of keystore for https (for legacy CertificateManagment only)
+KeyStoreType: jks
+
+# path to the keystore file (for legacy CertificateManagment only)
+KeyStoreFile: etc/keystore
+
+# password for the https keystore (for legacy CertificateManagment only)
+KeyStorePassword:  changeit
+# password for the private key in the https keystore (for legacy CertificateManagment only)
+KeyPassword: changeit
+
+# type of truststore for https (for legacy CertificateManagment only)
+TrustStoreType: jks
+
+# path to the truststore for https (for legacy CertificateManagment only)
+TrustStoreFile: ${DMAAPBC_TSTOREFILE}
+
+# password for the https truststore (for legacy CertificateManagment only)
+TrustStorePassword: changeit
+#
+# END OF legacy CertificateManagement properties
+###########################################################################################
+
+
+#####################################################
+#
+# HTTP Server Configuration
+#
+#####################################################
+
+# Allow http access to dbcapi
+HttpAllowed: true
+
+# listen to http port within this container (server)
+IntHttpPort: 8080
+
+# listen to https port within this container (server)
+# set to 0 if no certificates are available.
+IntHttpsPort: 0
+
+
+inHttpsPort: 0
+
+#####################################################
+#
+# Deprecated properties
+#
+#####################################################
+# csit: stubs out some southbound APIs for csit  (deprecated)
+#csit: No
+# name of this DMaaP instance (deprecated)
+#DmaapName: onap-cit
+# external port number for https taking port mapping into account  (deprecated)
+#ExtHttpsPort: 443
+# path to the file used to trigger an orderly shutdown (deprecated)
+#QuiesceFile: etc/SHUTDOWN
+# FQDN of DR Prov Server (deprecated)
+#DR.provhost: localhost
+# root of topic namespace (decrecated)
+#topicNsRoot: org.onap.dcae.dmaap
diff --git a/docs/administration/administration.rst b/docs/administration/administration.rst
deleted file mode 100644 (file)
index b050a1f..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-.. This work is licensed under a Creative Commons Attribution 4.0 International License.
-.. http://creativecommons.org/licenses/by/4.0
-
-Administration
-==============
-
-
-Processes
----------
-NA
-
-Actions
--------
-NA
diff --git a/docs/apis/api-table.rst b/docs/apis/api-table.rst
new file mode 100644 (file)
index 0000000..402935e
--- /dev/null
@@ -0,0 +1,10 @@
+DMaaP Bus Controller REST API Download docs
+===========================================
+
+.. this table is provided to allow for download of API documentation in diff formats
+
+.. csv-table::
+  :header: "API name", "Swagger JSON"
+  :widths: 10,5
+
+   "DMaaP Buscontroller API", ":download:`link <swagger.json>`"
diff --git a/docs/apis/api.rst b/docs/apis/api.rst
new file mode 100644 (file)
index 0000000..7379556
--- /dev/null
@@ -0,0 +1,4926 @@
+DMaaP Bus Controller REST API 1.1.0
+===================================
+
+Description
+~~~~~~~~~~~
+
+    Bus Controller provides an API for OpenDCAE components which need to provision
+    underlying DMaaP technologies (Data Router and Message Router).
+    Primary clients for this API are anticipated to be the OpenDCAE
+    Controller, OpenDCAE Orchestrator, OpenDCAE Inventory and the
+    ECOMP Portal.
+
+    Objects managed by DMaaP are deployed in a dcaeLocation which is a
+    unique identifier for an OpenStack tenant for a dcaeLayer,
+    opendcae-central (aka ecomp) or opendcae-local-ntc (aka edge).
+
+    A dcaeEnvironment (e.g. FTL or prod) has a single DMaaP. A
+    DMaaP is managed by a one or more stateless DMaaP Bus
+    Controller(s), though Bus Controller relies on PGaaS for
+    persistence. Each DMaaP has a single instance of Data Router,
+    which has 1 or more DR_Nodes deployed at each dcaeLocation. DR
+    Clients of type DR_Pub generally publish to a DR_Node that is
+    local to its dcaeLocation. Routing for a Feed is determined by
+    the dcaelocation of its DR_Sub clients.
+
+    A DMaaP may have many Message Router instances. Each instance is
+    deployed as an MR_Cluster. One MR_Cluster is deployed at each
+    dcaeLocation. MR_Clients generally communicate to the
+    MR_Cluster at the same dcaeLocation. Replication of messages
+    between MR_Clusters is accomplished by MR Bridge, which is
+    provioned by DMaaP Bus Controller based on Topic attributes.
+
+    Therefore, the role of DMaaP Bus Controller is to support other
+    DCAE infrastructure components to dynamically provision DMaaP
+    services on behalf of DMaaP clients, and to assist in any
+    management or discovery activity of its clients.
+
+    A convention of this API is to return JSON responses per
+    OpenStack style.
+
+
+
+Contact Information
+~~~~~~~~~~~~~~~~~~~
+
+
+
+
+http://www.onap.org
+
+
+
+
+License
+~~~~~~~
+
+
+`Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_
+
+
+
+
+Base URL
+~~~~~~~~
+
+http://www.[host]:[port]/webapi
+https://www.[host]:[port]/webapi
+
+BRIDGE
+~~~~~~
+
+
+Endpoint for retreiving MR Bridge metrics
+
+
+
+
+
+GET ``/bridge``
+---------------
+
+
+Summary
++++++++
+
+return BrTopic details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Returns array of  `BrTopic` objects. If source and target query params are specified, only report on that bridge.  If detail param is true, list topics names, else just a count is returned.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        mmagent | query | No | string |  |  | 
+        detail | query | No | boolean |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`BrTopic <d_d71baea9d8e4e59bc395ef51f45dff1b>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "brSource": "somestring", 
+        "brTarget": "somestring", 
+        "mmAgentName": "somestring", 
+        "topicCount": 1
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+PUT ``/bridge``
+---------------
+
+
+Summary
++++++++
+
+update MirrorMaker details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    replace the topic list for a specific Bridge.  Use JSON Body for value to replace whitelist, but if refreshFlag param is true, simply refresh using existing whitelist.If split param is true, spread whitelist over smaller mmagents.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        mmagent | query | No | string |  |  | 
+        refresh | query | No | boolean |  |  | 
+        split | query | No | boolean |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`BrTopic <d_d71baea9d8e4e59bc395ef51f45dff1b>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "brSource": "somestring", 
+        "brTarget": "somestring", 
+        "mmAgentName": "somestring", 
+        "topicCount": 1
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+DCAELOCATIONS
+~~~~~~~~~~~~~
+
+
+an OpenStack tenant purposed for OpenDCAE (i.e. where OpenDCAE components might be deployed)
+
+
+
+
+
+POST ``/dcaeLocations``
+-----------------------
+
+
+Summary
++++++++
+
+return dcaeLocation details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Create some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer*  (ecomp or edge).
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "central": true, 
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "local": true, 
+        "openStackAvailabilityZone": "somestring", 
+        "status": "EMPTY", 
+        "subnet": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+DELETE ``/dcaeLocations/{locationName}``
+----------------------------------------
+
+
+Summary
++++++++
+
+return dcaeLocation details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    delete a dcaeLocation
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        locationName | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+successful operation
+
+
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "central": true, 
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "local": true, 
+        "openStackAvailabilityZone": "somestring", 
+        "status": "EMPTY", 
+        "subnet": "somestring"
+    }
+
+**204**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "central": true, 
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "local": true, 
+        "openStackAvailabilityZone": "somestring", 
+        "status": "EMPTY", 
+        "subnet": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/dcaeLocations/{locationName}``
+-------------------------------------
+
+
+Summary
++++++++
+
+return dcaeLocation details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Returns a specific `dcaeLocation` object with specified tag
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        locationName | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "central": true, 
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "local": true, 
+        "openStackAvailabilityZone": "somestring", 
+        "status": "EMPTY", 
+        "subnet": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/dcaeLocations``
+----------------------
+
+
+Summary
++++++++
+
+return dcaeLocation details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Returns array of  `dcaeLocation` objects.  All objects managed by DMaaP are deployed in some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer*  (ecomp or edge).
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "central": true, 
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "local": true, 
+        "openStackAvailabilityZone": "somestring", 
+        "status": "EMPTY", 
+        "subnet": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+PUT ``/dcaeLocations/{locationName}``
+-------------------------------------
+
+
+Summary
++++++++
+
+return dcaeLocation details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    update the openStackAvailabilityZone of a dcaeLocation
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        locationName | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DcaeLocation <d_47d80e451933beb623fcf5257867cbcb>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "central": true, 
+        "clli": "somestring", 
+        "dcaeLayer": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "local": true, 
+        "openStackAvailabilityZone": "somestring", 
+        "status": "EMPTY", 
+        "subnet": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+DMAAP
+~~~~~
+
+
+V2 Endpoint for this instance of DMaaP object containing values for this OpenDCAE deployment
+
+
+
+
+
+POST ``/dmaap_v2``
+------------------
+
+
+Summary
++++++++
+
+return dmaap details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Create a new DMaaP set system wide configuration settings for the *dcaeEnvironment*.  Deprecated with introduction of persistence in 1610.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "accessKeyOwner": "somestring", 
+        "bridgeAdminTopic": "somestring", 
+        "dmaapName": "somestring", 
+        "drProvUrl": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "loggingUrl": "somestring", 
+        "nodeKey": "somestring", 
+        "status": "EMPTY", 
+        "topicNsRoot": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+POST ``/dmaap``
+---------------
+
+
+Summary
++++++++
+
+return dmaap details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Create a new DMaaP set system wide configuration settings for the *dcaeEnvironment*.  Deprecated with introduction of persistence in 1610.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "accessKeyOwner": "somestring", 
+        "bridgeAdminTopic": "somestring", 
+        "dmaapName": "somestring", 
+        "drProvUrl": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "loggingUrl": "somestring", 
+        "nodeKey": "somestring", 
+        "status": "EMPTY", 
+        "topicNsRoot": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/dmaap_v2``
+-----------------
+
+
+Summary
++++++++
+
+return dmaap details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    returns the `dmaap` object, which contains system wide configuration settings
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "accessKeyOwner": "somestring", 
+        "bridgeAdminTopic": "somestring", 
+        "dmaapName": "somestring", 
+        "drProvUrl": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "loggingUrl": "somestring", 
+        "nodeKey": "somestring", 
+        "status": "EMPTY", 
+        "topicNsRoot": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/dmaap``
+--------------
+
+
+Summary
++++++++
+
+return dmaap details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    returns the `dmaap` object, which contains system wide configuration settings
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "accessKeyOwner": "somestring", 
+        "bridgeAdminTopic": "somestring", 
+        "dmaapName": "somestring", 
+        "drProvUrl": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "loggingUrl": "somestring", 
+        "nodeKey": "somestring", 
+        "status": "EMPTY", 
+        "topicNsRoot": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+PUT ``/dmaap_v2``
+-----------------
+
+
+Summary
++++++++
+
+return dmaap details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Update system settings for *dcaeEnvironment*.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "accessKeyOwner": "somestring", 
+        "bridgeAdminTopic": "somestring", 
+        "dmaapName": "somestring", 
+        "drProvUrl": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "loggingUrl": "somestring", 
+        "nodeKey": "somestring", 
+        "status": "EMPTY", 
+        "topicNsRoot": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+PUT ``/dmaap``
+--------------
+
+
+Summary
++++++++
+
+return dmaap details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Update system settings for *dcaeEnvironment*.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "accessKeyOwner": "somestring", 
+        "bridgeAdminTopic": "somestring", 
+        "dmaapName": "somestring", 
+        "drProvUrl": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "loggingUrl": "somestring", 
+        "nodeKey": "somestring", 
+        "status": "EMPTY", 
+        "topicNsRoot": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+DR_NODES
+~~~~~~~~
+
+
+Endpoint for a Data Router Node server
+
+
+
+
+
+POST ``/dr_nodes``
+------------------
+
+
+Summary
++++++++
+
+return DR_Node details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    create a `DR_Node` in a *dcaeLocation*.  Note that multiple `DR_Node`s may exist in the same `dcaeLocation`.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "hostName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "status": "EMPTY", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+DELETE ``/dr_nodes/{fqdn}``
+---------------------------
+
+
+Summary
++++++++
+
+No Content
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Delete a single `DR_Node` object.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        fqdn | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+successful operation
+
+
+Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "hostName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "status": "EMPTY", 
+        "version": "somestring"
+    }
+
+**204**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "hostName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "status": "EMPTY", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/dr_nodes/{fqdn}``
+------------------------
+
+
+Summary
++++++++
+
+return DR_Node details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Retrieve a single `DR_Node` object.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        fqdn | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "hostName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "status": "EMPTY", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/dr_nodes``
+-----------------
+
+
+Summary
++++++++
+
+return DR_Node details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Returns array of `DR_Node` object array.  Need to add filter by dcaeLocation.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "hostName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "status": "EMPTY", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+PUT ``/dr_nodes/{fqdn}``
+------------------------
+
+
+Summary
++++++++
+
+return DR_Node details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Update a single `DR_Node` object.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        fqdn | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Node <d_d15e2cee407536866c875375e3f705e0>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "hostName": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "status": "EMPTY", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+DR_PUBS
+~~~~~~~
+
+
+Endpoint for a Data Router client that implements a Publisher
+
+
+
+
+
+POST ``/dr_pubs``
+-----------------
+
+
+Summary
++++++++
+
+return DR_Pub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    create a DR Publisher in the specified environment.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "pubId": "somestring", 
+        "status": "EMPTY", 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+DELETE ``/dr_pubs/{pubId}``
+---------------------------
+
+
+Summary
++++++++
+
+return DR_Pub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    delete a DR Publisher in the specified environment. Delete a `DR_Pub` object by pubId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        pubId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+successful operation
+
+
+Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "pubId": "somestring", 
+        "status": "EMPTY", 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**204**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "pubId": "somestring", 
+        "status": "EMPTY", 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/dr_pubs/{pubId}``
+------------------------
+
+
+Summary
++++++++
+
+return DR_Pub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    returns a DR Publisher in the specified environment. Gets a `DR_Pub` object by pubId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        pubId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "pubId": "somestring", 
+        "status": "EMPTY", 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/dr_pubs``
+----------------
+
+
+Summary
++++++++
+
+return DR_Pub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Returns array of  `DR_Pub` objects.  Add filter for feedId.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "pubId": "somestring", 
+        "status": "EMPTY", 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+PUT ``/dr_pubs/{pubId}``
+------------------------
+
+
+Summary
++++++++
+
+return DR_Pub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    update a DR Publisher in the specified environment.  Update a `DR_Pub` object by pubId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        pubId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "pubId": "somestring", 
+        "status": "EMPTY", 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+DR_SUBS
+~~~~~~~
+
+
+Endpoint for a Data Router client that implements a Subscriber
+
+
+
+
+
+POST ``/dr_subs``
+-----------------
+
+
+Summary
++++++++
+
+return DR_Sub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Create a  `DR_Sub` object.  
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "dcaeLocationName": "somestring", 
+        "deliveryURL": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "status": "EMPTY", 
+        "subId": "somestring", 
+        "suspended": true, 
+        "use100": true, 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+DELETE ``/dr_subs/{subId}``
+---------------------------
+
+
+Summary
++++++++
+
+return DR_Sub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Delete a  `DR_Sub` object, selected by subId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        subId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "dcaeLocationName": "somestring", 
+        "deliveryURL": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "status": "EMPTY", 
+        "subId": "somestring", 
+        "suspended": true, 
+        "use100": true, 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/dr_subs/{subId}``
+------------------------
+
+
+Summary
++++++++
+
+return DR_Sub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Retrieve a  `DR_Sub` object, selected by subId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        subId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "dcaeLocationName": "somestring", 
+        "deliveryURL": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "status": "EMPTY", 
+        "subId": "somestring", 
+        "suspended": true, 
+        "use100": true, 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/dr_subs``
+----------------
+
+
+Summary
++++++++
+
+return DR_Sub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Returns array of  `DR_Sub` objects.  Add filter for feedId.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "dcaeLocationName": "somestring", 
+        "deliveryURL": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "status": "EMPTY", 
+        "subId": "somestring", 
+        "suspended": true, 
+        "use100": true, 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+PUT ``/dr_subs/{subId}``
+------------------------
+
+
+Summary
++++++++
+
+return DR_Sub details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Update a  `DR_Sub` object, selected by subId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        subId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "dcaeLocationName": "somestring", 
+        "deliveryURL": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "status": "EMPTY", 
+        "subId": "somestring", 
+        "suspended": true, 
+        "use100": true, 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+FEEDS
+~~~~~
+
+
+Endpoint for a Data Router Feed
+
+
+
+
+
+POST ``/feeds``
+---------------
+
+
+Summary
++++++++
+
+return Feed details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Create a of  `Feed` object.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        useExisting | query | No | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "asprClassification": "somestring", 
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "feedDescription": "somestring", 
+        "feedId": "somestring", 
+        "feedName": "somestring", 
+        "feedVersion": "somestring", 
+        "formatUuid": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "publishURL": "somestring", 
+        "pubs": [
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "status": "EMPTY", 
+        "subs": [
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "subscribeURL": "somestring", 
+        "suspended": true
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+DELETE ``/feeds/{id}``
+----------------------
+
+
+Summary
++++++++
+
+return Feed details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Delete a  `Feed` object, specified by id.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        id | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+successful operation
+
+
+Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "asprClassification": "somestring", 
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "feedDescription": "somestring", 
+        "feedId": "somestring", 
+        "feedName": "somestring", 
+        "feedVersion": "somestring", 
+        "formatUuid": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "publishURL": "somestring", 
+        "pubs": [
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "status": "EMPTY", 
+        "subs": [
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "subscribeURL": "somestring", 
+        "suspended": true
+    }
+
+**204**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "asprClassification": "somestring", 
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "feedDescription": "somestring", 
+        "feedId": "somestring", 
+        "feedName": "somestring", 
+        "feedVersion": "somestring", 
+        "formatUuid": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "publishURL": "somestring", 
+        "pubs": [
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "status": "EMPTY", 
+        "subs": [
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "subscribeURL": "somestring", 
+        "suspended": true
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/feeds/{id}``
+-------------------
+
+
+Summary
++++++++
+
+return Feed details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Retrieve a  `Feed` object, specified by id.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        id | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "feedId": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "pubId": "somestring", 
+        "status": "EMPTY", 
+        "username": "somestring", 
+        "userpwd": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/feeds``
+--------------
+
+
+Summary
++++++++
+
+return Feed details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Returns array of  `Feed` objects.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        feedName | query | No | string |  |  | 
+        version | query | No | string |  |  | 
+        match | query | No | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "asprClassification": "somestring", 
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "feedDescription": "somestring", 
+        "feedId": "somestring", 
+        "feedName": "somestring", 
+        "feedVersion": "somestring", 
+        "formatUuid": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "publishURL": "somestring", 
+        "pubs": [
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "status": "EMPTY", 
+        "subs": [
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "subscribeURL": "somestring", 
+        "suspended": true
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+PUT ``/feeds/{id}``
+-------------------
+
+
+Summary
++++++++
+
+return Feed details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Update a  `Feed` object, specified by id.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        id | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Feed <d_289ad39619725df26c9ff382d4c97c75>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "asprClassification": "somestring", 
+        "bytes": [
+            "somestring", 
+            "somestring"
+        ], 
+        "feedDescription": "somestring", 
+        "feedId": "somestring", 
+        "feedName": "somestring", 
+        "feedVersion": "somestring", 
+        "formatUuid": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "logURL": "somestring", 
+        "owner": "somestring", 
+        "publishURL": "somestring", 
+        "pubs": [
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "dcaeLocationName": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "pubId": "somestring", 
+                "status": "EMPTY", 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "status": "EMPTY", 
+        "subs": [
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }, 
+            {
+                "bytes": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "dcaeLocationName": "somestring", 
+                "deliveryURL": "somestring", 
+                "feedId": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "logURL": "somestring", 
+                "owner": "somestring", 
+                "status": "EMPTY", 
+                "subId": "somestring", 
+                "suspended": true, 
+                "use100": true, 
+                "username": "somestring", 
+                "userpwd": "somestring"
+            }
+        ], 
+        "subscribeURL": "somestring", 
+        "suspended": true
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+INFO
+~~~~
+
+
+Endpoint for this instance of DBCL.  Returns health info.
+
+
+
+
+
+GET ``/info``
+-------------
+
+
+Summary
++++++++
+
+return info details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    returns the `info` object
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Dmaap <d_4ea0e7758a1f8502222793e4a13b04f7>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "accessKeyOwner": "somestring", 
+        "bridgeAdminTopic": "somestring", 
+        "dmaapName": "somestring", 
+        "drProvUrl": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "loggingUrl": "somestring", 
+        "nodeKey": "somestring", 
+        "status": "EMPTY", 
+        "topicNsRoot": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+MR_CLIENTS
+~~~~~~~~~~
+
+
+Endpoint for a Message Router Client that implements a Publisher or a Subscriber
+
+
+
+
+
+POST ``/mr_clients``
+--------------------
+
+
+Summary
++++++++
+
+Associate an MR_Client object to a Topic
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Create a  `MR_Client` object.The `dcaeLocation` attribute is used to match an `MR_Cluster` object with the same value, with the intent of localizing message traffic.  In legacy implementation, the `clientRole` is granted appropriate permission in AAF.  Newer implementions may instead specify an AAF Identity, which will be added to the appropriate `Topic` role.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "action": [
+            "somestring", 
+            "somestring"
+        ], 
+        "clientIdentity": "somestring", 
+        "clientRole": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "fqtn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "mrClientId": "somestring", 
+        "status": "EMPTY", 
+        "topicURL": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+DELETE ``/mr_clients/{subId}``
+------------------------------
+
+
+Summary
++++++++
+
+Delete an MR_Client object
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Delete a  `MR_Client` object, specified by clientId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        subId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+successful operation
+
+
+Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "action": [
+            "somestring", 
+            "somestring"
+        ], 
+        "clientIdentity": "somestring", 
+        "clientRole": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "fqtn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "mrClientId": "somestring", 
+        "status": "EMPTY", 
+        "topicURL": "somestring"
+    }
+
+**204**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "action": [
+            "somestring", 
+            "somestring"
+        ], 
+        "clientIdentity": "somestring", 
+        "clientRole": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "fqtn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "mrClientId": "somestring", 
+        "status": "EMPTY", 
+        "topicURL": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/mr_clients``
+-------------------
+
+
+Summary
++++++++
+
+return MR_Client details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Returns array of  `MR_Client` objects.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "action": [
+            "somestring", 
+            "somestring"
+        ], 
+        "clientIdentity": "somestring", 
+        "clientRole": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "fqtn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "mrClientId": "somestring", 
+        "status": "EMPTY", 
+        "topicURL": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/mr_clients/{subId}``
+---------------------------
+
+
+Summary
++++++++
+
+return MR_Client details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Retrieve a  `MR_Client` object, specified by clientId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        subId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "action": [
+            "somestring", 
+            "somestring"
+        ], 
+        "clientIdentity": "somestring", 
+        "clientRole": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "fqtn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "mrClientId": "somestring", 
+        "status": "EMPTY", 
+        "topicURL": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+PUT ``/mr_clients/{clientId}``
+------------------------------
+
+
+Summary
++++++++
+
+Update an MR_Client object
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Update a  `MR_Client` object, specified by clientId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        clientId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "action": [
+            "somestring", 
+            "somestring"
+        ], 
+        "clientIdentity": "somestring", 
+        "clientRole": "somestring", 
+        "dcaeLocationName": "somestring", 
+        "fqtn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "mrClientId": "somestring", 
+        "status": "EMPTY", 
+        "topicURL": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+MR_CLUSTERS
+~~~~~~~~~~~
+
+
+Endpoint for a Message Router servers in a Cluster configuration
+
+
+
+
+
+POST ``/mr_clusters``
+---------------------
+
+
+Summary
++++++++
+
+return MR_Cluster details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Create an  `MR_Cluster` object.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
+        "status": "EMPTY", 
+        "targetReplicationPort": "somestring", 
+        "topicPort": "somestring", 
+        "topicProtocol": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+DELETE ``/mr_clusters/{clusterId}``
+-----------------------------------
+
+
+Summary
++++++++
+
+return MR_Cluster details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Delete an  `MR_Cluster` object, specified by clusterId.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        clusterId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+successful operation
+
+
+Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
+        "status": "EMPTY", 
+        "targetReplicationPort": "somestring", 
+        "topicPort": "somestring", 
+        "topicProtocol": "somestring"
+    }
+
+**204**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
+        "status": "EMPTY", 
+        "targetReplicationPort": "somestring", 
+        "topicPort": "somestring", 
+        "topicProtocol": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/mr_clusters/{clusterId}``
+--------------------------------
+
+
+Summary
++++++++
+
+return MR_Cluster details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Retrieve an  `MR_Cluster` object, specified by clusterId.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        clusterId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
+        "status": "EMPTY", 
+        "targetReplicationPort": "somestring", 
+        "topicPort": "somestring", 
+        "topicProtocol": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/mr_clusters``
+--------------------
+
+
+Summary
++++++++
+
+return MR_Cluster details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Returns array of  `MR_Cluster` objects.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
+        "status": "EMPTY", 
+        "targetReplicationPort": "somestring", 
+        "topicPort": "somestring", 
+        "topicProtocol": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+PUT ``/mr_clusters/{clusterId}``
+--------------------------------
+
+
+Summary
++++++++
+
+return MR_Cluster details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Update an  `MR_Cluster` object, specified by clusterId.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        clusterId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`MR_Cluster <d_eec7176a0080debe1b19c2dad2e97c24>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "dcaeLocationName": "somestring", 
+        "fqdn": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "replicationGroup": "somestring", 
+        "sourceReplicationPort": "somestring", 
+        "status": "EMPTY", 
+        "targetReplicationPort": "somestring", 
+        "topicPort": "somestring", 
+        "topicProtocol": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+TOPICS
+~~~~~~
+
+
+Endpoint for retreiving MR Topics
+
+
+
+
+
+POST ``/topics``
+----------------
+
+
+Summary
++++++++
+
+Create a Topic object
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Create  `Topic` object.For convenience, the message body may populate the `clients` array, in which case each entry will be added as an `MR_Client`.  Beginning in ONAP Dublin Release, dbcapi will create two AAF Roles by default, one each for the publisher and subscriber per topic.  MR_Clients can then specify an AAF Identity to be added to the appropriate default Role, avoiding the need to create Role(s) in advance.
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        useExisting | query | No | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "clients": [
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }, 
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }
+        ], 
+        "formatUuid": "somestring", 
+        "fqtn": "somestring", 
+        "fqtnStyle": "FQTN_NOT_SPECIFIED", 
+        "globalMrURL": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
+        "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
+        "status": "EMPTY", 
+        "subscriberRole": "somestring", 
+        "tnxEnabled": "somestring", 
+        "topicDescription": "somestring", 
+        "topicName": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+DELETE ``/topics/{topicId}``
+----------------------------
+
+
+Summary
++++++++
+
+return Topic details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Delete a  `Topic` object, identified by topicId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        topicId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+successful operation
+
+
+Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "clients": [
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }, 
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }
+        ], 
+        "formatUuid": "somestring", 
+        "fqtn": "somestring", 
+        "fqtnStyle": "FQTN_NOT_SPECIFIED", 
+        "globalMrURL": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
+        "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
+        "status": "EMPTY", 
+        "subscriberRole": "somestring", 
+        "tnxEnabled": "somestring", 
+        "topicDescription": "somestring", 
+        "topicName": "somestring", 
+        "version": "somestring"
+    }
+
+**204**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "clients": [
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }, 
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }
+        ], 
+        "formatUuid": "somestring", 
+        "fqtn": "somestring", 
+        "fqtnStyle": "FQTN_NOT_SPECIFIED", 
+        "globalMrURL": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
+        "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
+        "status": "EMPTY", 
+        "subscriberRole": "somestring", 
+        "tnxEnabled": "somestring", 
+        "topicDescription": "somestring", 
+        "topicName": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/topics/{topicId}``
+-------------------------
+
+
+Summary
++++++++
+
+return Topic details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Retrieve a  `Topic` object, identified by topicId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        topicId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "clients": [
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }, 
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }
+        ], 
+        "formatUuid": "somestring", 
+        "fqtn": "somestring", 
+        "fqtnStyle": "FQTN_NOT_SPECIFIED", 
+        "globalMrURL": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
+        "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
+        "status": "EMPTY", 
+        "subscriberRole": "somestring", 
+        "tnxEnabled": "somestring", 
+        "topicDescription": "somestring", 
+        "topicName": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+GET ``/topics``
+---------------
+
+
+Summary
++++++++
+
+return Topic details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Returns array of  `Topic` objects.
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "clients": [
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }, 
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }
+        ], 
+        "formatUuid": "somestring", 
+        "fqtn": "somestring", 
+        "fqtnStyle": "FQTN_NOT_SPECIFIED", 
+        "globalMrURL": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
+        "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
+        "status": "EMPTY", 
+        "subscriberRole": "somestring", 
+        "tnxEnabled": "somestring", 
+        "topicDescription": "somestring", 
+        "topicName": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+
+
+PUT ``/topics/{topicId}``
+-------------------------
+
+
+Summary
++++++++
+
+return Topic details
+
+Description
++++++++++++
+
+.. raw:: html
+
+    Update a  `Topic` object, identified by topicId
+
+Parameters
+++++++++++
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 15, 10, 10, 10, 20, 30
+
+        topicId | path | Yes | string |  |  | 
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Success
+
+
+Type: :ref:`Topic <d_2e99841971da81b9d240071b86bf168d>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "clients": [
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }, 
+            {
+                "action": [
+                    "somestring", 
+                    "somestring"
+                ], 
+                "clientIdentity": "somestring", 
+                "clientRole": "somestring", 
+                "dcaeLocationName": "somestring", 
+                "fqtn": "somestring", 
+                "lastMod": "2015-01-01T15:00:00.000Z", 
+                "mrClientId": "somestring", 
+                "status": "EMPTY", 
+                "topicURL": "somestring"
+            }
+        ], 
+        "formatUuid": "somestring", 
+        "fqtn": "somestring", 
+        "fqtnStyle": "FQTN_NOT_SPECIFIED", 
+        "globalMrURL": "somestring", 
+        "lastMod": "2015-01-01T15:00:00.000Z", 
+        "owner": "somestring", 
+        "partitionCount": "somestring", 
+        "publisherRole": "somestring", 
+        "replicationCase": "REPLICATION_NOT_SPECIFIED", 
+        "replicationCount": "somestring", 
+        "status": "EMPTY", 
+        "subscriberRole": "somestring", 
+        "tnxEnabled": "somestring", 
+        "topicDescription": "somestring", 
+        "topicName": "somestring", 
+        "version": "somestring"
+    }
+
+**400**
+^^^^^^^
+
+Error
+
+
+Type: :ref:`ApiError <d_a3a7580ce9d87225d7f62e6b67b4d036>`
+
+**Example:**
+
+.. code-block:: javascript
+
+    {
+        "code": 1, 
+        "fields": "somestring", 
+        "is2xx": true, 
+        "message": "somestring"
+    }
+
+
+
+  
+Data Structures
+~~~~~~~~~~~~~~~
+
+.. _d_a3a7580ce9d87225d7f62e6b67b4d036:
+
+ApiError Model Structure
+------------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        code | No | integer | int32 |  | 
+        fields | No | string |  |  | 
+        is2xx | No | boolean |  |  | 
+        message | No | string |  |  | 
+
+.. _d_d71baea9d8e4e59bc395ef51f45dff1b:
+
+BrTopic Model Structure
+-----------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        brSource | No | string |  |  | 
+        brTarget | No | string |  |  | 
+        mmAgentName | No | string |  |  | 
+        topicCount | No | integer | int32 |  | 
+
+.. _d_d15e2cee407536866c875375e3f705e0:
+
+DR_Node Model Structure
+-----------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        dcaeLocationName | No | string |  |  | 
+        fqdn | No | string |  |  | 
+        hostName | No | string |  |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
+        status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
+        version | No | string |  |  | 
+
+.. _d_e926d3fa8701e0cc9c8ed1761b3255cd:
+
+DR_Pub Model Structure
+----------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        dcaeLocationName | No | string |  |  | 
+        feedId | No | string |  |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
+        pubId | No | string |  |  | 
+        status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
+        username | No | string |  |  | 
+        userpwd | No | string |  |  | 
+
+.. _d_48cf328d246f41e1d11a09251b042f02:
+
+DR_Sub Model Structure
+----------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        bytes | No | array of string |  |  | 
+        dcaeLocationName | No | string |  |  | 
+        deliveryURL | No | string |  |  | 
+        feedId | No | string |  |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
+        logURL | No | string |  |  | 
+        owner | No | string |  |  | 
+        status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
+        subId | No | string |  |  | 
+        suspended | No | boolean |  |  | 
+        use100 | No | boolean |  |  | 
+        username | No | string |  |  | 
+        userpwd | No | string |  |  | 
+
+.. _d_47d80e451933beb623fcf5257867cbcb:
+
+DcaeLocation Model Structure
+----------------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        central | No | boolean |  |  | 
+        clli | No | string |  |  | 
+        dcaeLayer | No | string |  |  | 
+        dcaeLocationName | No | string |  |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
+        local | No | boolean |  |  | 
+        openStackAvailabilityZone | No | string |  |  | 
+        status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
+        subnet | No | string |  |  | 
+
+.. _d_4ea0e7758a1f8502222793e4a13b04f7:
+
+Dmaap Model Structure
+---------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        accessKeyOwner | No | string |  |  | 
+        bridgeAdminTopic | No | string |  |  | 
+        dmaapName | No | string |  |  | 
+        drProvUrl | No | string |  |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
+        loggingUrl | No | string |  |  | 
+        nodeKey | No | string |  |  | 
+        status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
+        topicNsRoot | No | string |  |  | 
+        version | No | string |  |  | 
+
+.. _d_289ad39619725df26c9ff382d4c97c75:
+
+Feed Model Structure
+--------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        asprClassification | No | string |  |  | 
+        bytes | No | array of string |  |  | 
+        feedDescription | No | string |  |  | 
+        feedId | No | string |  |  | 
+        feedName | No | string |  |  | 
+        feedVersion | No | string |  |  | 
+        formatUuid | No | string |  |  | 
+        lastMod | No | string | date-time |  | datestamp for last update to this object
+        logURL | No | string |  |  | 
+        owner | No | string |  |  | 
+        publishURL | No | string |  |  | 
+        pubs | No | array of :ref:`DR_Pub <d_e926d3fa8701e0cc9c8ed1761b3255cd>` |  |  | 
+        status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
+        subs | No | array of :ref:`DR_Sub <d_48cf328d246f41e1d11a09251b042f02>` |  |  | 
+        subscribeURL | No | string |  |  | 
+        suspended | No | boolean |  |  | 
+
+.. _d_56ff81dc98986e27074d9be2731e3f4c:
+
+MR_Client Model Structure
+-------------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        action | No | array of string |  |  | one or more actions from the set ('pub', 'sub', 'view') for which this client needs Permission
+        clientIdentity | No | string |  |  | an AAF identity to be associated to an appropriate topic Role
+        clientRole | No | string |  |  | an AAF Role to be granted an appropriate Permission.  If specified, takes precedence over clientIdentity, for backwards compatibility.
+        dcaeLocationName | No | string |  |  | a tag indicating a logical deployment site
+        fqtn | No | string |  |  | Fully Qualified Topic Name constructed by dbcapi
+        lastMod | No | string | date-time |  | datestamp for last update to this object
+        mrClientId | No | string |  |  | a unique identifier generated by dbcapi for this client
+        status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
+        topicURL | No | string |  |  | the URL for a MR instance - typically in the same dcaeLocation - that this client should use to access the topic
+
+.. _d_eec7176a0080debe1b19c2dad2e97c24:
+
+MR_Cluster Model Structure
+--------------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        dcaeLocationName | No | string |  |  | 
+        fqdn | No | string |  |  | 
+        lastMod | No | string | date-time |  | 
+        replicationGroup | No | string |  |  | 
+        sourceReplicationPort | No | string |  |  | 
+        status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
+        targetReplicationPort | No | string |  |  | 
+        topicPort | No | string |  |  | 
+        topicProtocol | No | string |  |  | 
+
+.. _d_08fb211d40d6deb9b6e04b000fd988e4:
+
+MirrorMaker Model Structure
+---------------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        lastMod | No | string | date-time |  | datestamp for last update to this object
+        mmName | No | string |  |  | 
+        sourceCluster | No | string |  |  | 
+        status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
+        targetCluster | No | string |  |  | 
+        topicCount | No | integer | int32 |  | 
+        topics | No | array of string |  |  | 
+        whitelistUpdateJSON | No | string |  |  | 
+
+.. _d_2e99841971da81b9d240071b86bf168d:
+
+Topic Model Structure
+---------------------
+
+.. csv-table::
+    :delim: |
+    :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+    :widths: 20, 10, 15, 15, 30, 25
+
+        clients | No | array of :ref:`MR_Client <d_56ff81dc98986e27074d9be2731e3f4c>` |  |  | an array of `MR_Client` objects associated to this `Topic`
+        formatUuid | No | string |  |  | a reference to an identifier that describes a data format used for this `Topic`
+        fqtn | No | string |  |  | Fully Qualified Topic Name constructed by dbcapi, following the rules for `fqtnStyle`
+        fqtnStyle | No | string |  | {'enum': ['FQTN_NOT_SPECIFIED', 'FQTN_LEGACY_FORMAT', 'FQTN_PROJECTID_FORMAT', 'FQTN_PROJECTID_VERSION_FORMAT']} | the construction rule for the `fqtn` field
+        globalMrURL | No | string |  |  | the URL of an outside MR instance
+        lastMod | No | string | date-time |  | datestamp for last update to this object
+        owner | No | string |  |  | a label used to identify who requested this `Topic` to be provisioned.  In the future this may be an AAF Identity.
+        partitionCount | No | string |  |  | the kafka attribute for specifying the number of partitions
+        publisherRole | No | string |  |  | a value generated by dbcapi, this AAF Role has permission to publish to this `Topic`
+        replicationCase | No | string |  | {'enum': ['REPLICATION_NOT_SPECIFIED', 'REPLICATION_NONE', 'REPLICATION_EDGE_TO_CENTRAL', 'REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL', 'REPLICATION_CENTRAL_TO_EDGE', 'REPLICATION_CENTRAL_TO_GLOBAL', 'REPLICATION_GLOBAL_TO_CENTRAL', 'REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE', 'REPLICATION_EDGE_TO_FQDN', 'REPLICATION_FQDN_TO_EDGE', 'REPLICATION_FQDN_TO_GLOBAL', 'REPLICATION_GLOBAL_TO_FQDN', 'REPLICATION_EDGE_TO_FQDN_TO_GLOBAL', 'REPLICATION_GLOBAL_TO_FQDN_TO_EDGE']} | An indicator for how this `Topic` should be replicated when there are more than one `MR_Cluster` instances
+        replicationCount | No | string |  |  | the kafka attribute for specifying replication within an `MR_Cluster` instance
+        status | No | string |  | {'enum': ['EMPTY', 'NEW', 'STAGED', 'VALID', 'INVALID', 'DELETED']} | 
+        subscriberRole | No | string |  |  | a value generated by dbcapi, this AAF Role has permission to subscribe to this `Topic`
+        tnxEnabled | No | string |  |  | 
+        topicDescription | No | string |  |  | a description of what this Topic is used for
+        topicName | No | string |  |  | the short name used by humans, and utilized to construct the `FQTN`
+        version | No | string |  |  | a hook for any versioning needed for managing a `Topic` over time
+
diff --git a/docs/apis/swagger.json b/docs/apis/swagger.json
new file mode 100644 (file)
index 0000000..ceb041f
--- /dev/null
@@ -0,0 +1,1871 @@
+{
+  "swagger" : "2.0",
+  "info" : {
+    "description" : "provides an API for OpenDCAE components which need to provision\n\t\t\t\t\t\t\t\t\tunderlying DMaaP technologies (Data Router and Message Router).\n\t\t\t\t\t\t\t\t\tPrimary clients for this API are anticipated to be the OpenDCAE\n\t\t\t\t\t\t\t\t\tController, OpenDCAE Orchestrator, OpenDCAE Inventory and the\n\t\t\t\t\t\t\t\t\tECOMP Portal.\n\n\t\t\t\t\t\t\t\t\tObjects managed by DMaaP are deployed in a dcaeLocation which is a\n\t\t\t\t\t\t\t\t\tunique identifier for an OpenStack tenant for a dcaeLayer,\n\t\t\t\t\t\t\t\t\topendcae-central (aka ecomp) or opendcae-local-ntc (aka edge).\n\n\t\t\t\t\t\t\t\t\tA dcaeEnvironment (e.g. FTL or prod) has a single DMaaP. A\n\t\t\t\t\t\t\t\t\tDMaaP is managed by a one or more stateless DMaaP Bus\n\t\t\t\t\t\t\t\t\tController(s), though Bus Controller relies on PGaaS for\n\t\t\t\t\t\t\t\t\tpersistence. Each DMaaP has a single instance of Data Router,\n\t\t\t\t\t\t\t\t\twhich has 1 or more DR_Nodes deployed at each dcaeLocation. DR\n\t\t\t\t\t\t\t\t\tClients of type DR_Pub generally publish to a DR_Node that is\n\t\t\t\t\t\t\t\t\tlocal to its dcaeLocation. Routing for a Feed is determined by\n\t\t\t\t\t\t\t\t\tthe dcaelocation of its DR_Sub clients.\n\n\t\t\t\t\t\t\t\t\tA DMaaP may have many Message Router instances. Each instance is\n\t\t\t\t\t\t\t\t\tdeployed as an MR_Cluster. One MR_Cluster is deployed at each\n\t\t\t\t\t\t\t\t\tdcaeLocation. MR_Clients generally communicate to the\n\t\t\t\t\t\t\t\t\tMR_Cluster at the same dcaeLocation. Replication of messages\n\t\t\t\t\t\t\t\t\tbetween MR_Clusters is accomplished by MR Bridge, which is\n\t\t\t\t\t\t\t\t\tprovioned by DMaaP Bus Controller based on Topic attributes.\n\n\t\t\t\t\t\t\t\t\tTherefore, the role of DMaaP Bus Controller is to support other\n\t\t\t\t\t\t\t\t\tDCAE infrastructure components to dynamically provision DMaaP\n\t\t\t\t\t\t\t\t\tservices on behalf of DMaaP clients, and to assist in any\n\t\t\t\t\t\t\t\t\tmanagement or discovery activity of its clients.\n\n\t\t\t\t\t\t\t\t\tA convention of this API is to return JSON responses per\n\t\t\t\t\t\t\t\t\tOpenStack style.",
+    "version" : "1.1.0",
+    "title" : "DMaaP Bus Controller REST API",
+    "termsOfService" : "http://www.apache.org/licenses/LICENSE-2.0",
+    "contact" : {
+      "url" : "http://www.onap.org"
+    },
+    "license" : {
+      "name" : "Licensed under the Apache License, Version 2.0",
+      "url" : "http://www.apache.org/licenses/LICENSE-2.0"
+    }
+  },
+  "host" : "www.[host]:[port]",
+  "basePath" : "/webapi",
+  "tags" : [ {
+    "name" : "Feeds",
+    "description" : "Endpoint for a Data Router Feed"
+  }, {
+    "name" : "MR_Clients",
+    "description" : "Endpoint for a Message Router Client that implements a Publisher or a Subscriber"
+  }, {
+    "name" : "MR_Clusters",
+    "description" : "Endpoint for a Message Router servers in a Cluster configuration"
+  }, {
+    "name" : "bridge",
+    "description" : "Endpoint for retreiving MR Bridge metrics"
+  }, {
+    "name" : "dcaeLocations",
+    "description" : "an OpenStack tenant purposed for OpenDCAE (i.e. where OpenDCAE components might be deployed)"
+  }, {
+    "name" : "dmaap",
+    "description" : "Endpoint for this instance of DMaaP object containing values for this OpenDCAE deployment"
+  }, {
+    "name" : "dr_nodes",
+    "description" : "Endpoint for a Data Router Node server"
+  }, {
+    "name" : "dr_pubs",
+    "description" : "Endpoint for a Data Router client that implements a Publisher"
+  }, {
+    "name" : "dr_subs",
+    "description" : "Endpoint for a Data Router client that implements a Subscriber"
+  }, {
+    "name" : "info",
+    "description" : "Endpoint for this instance of DBCL.  Returns health info."
+  }, {
+    "name" : "topics",
+    "description" : "Endpoint for retreiving MR Topics"
+  } ],
+  "schemes" : [ "http", "https" ],
+  "paths" : {
+    "/bridge" : {
+      "get" : {
+        "tags" : [ "bridge" ],
+        "summary" : "return BrTopic details",
+        "description" : "Returns array of  `BrTopic` objects. If source and target query params are specified, only report on that bridge.  If detail param is true, list topics names, else just a count is returned.",
+        "operationId" : "getBridgedTopics",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "mmagent",
+          "in" : "query",
+          "required" : false,
+          "type" : "string"
+        }, {
+          "name" : "detail",
+          "in" : "query",
+          "required" : false,
+          "type" : "boolean"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/BrTopic"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "put" : {
+        "tags" : [ "bridge" ],
+        "summary" : "update MirrorMaker details",
+        "description" : "replace the topic list for a specific Bridge.  Use JSON Body for value to replace whitelist, but if refreshFlag param is true, simply refresh using existing whitelist.If split param is true, spread whitelist over smaller mmagents.",
+        "operationId" : "putBridgedTopics",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "mmagent",
+          "in" : "query",
+          "required" : false,
+          "type" : "string"
+        }, {
+          "name" : "refresh",
+          "in" : "query",
+          "required" : false,
+          "type" : "boolean"
+        }, {
+          "name" : "split",
+          "in" : "query",
+          "required" : false,
+          "type" : "boolean"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/BrTopic"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/dcaeLocations" : {
+      "get" : {
+        "tags" : [ "dcaeLocations" ],
+        "summary" : "return dcaeLocation details",
+        "description" : "Returns array of  `dcaeLocation` objects.  All objects managed by DMaaP are deployed in some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer*  (ecomp or edge).",
+        "operationId" : "getDcaeLocations",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DcaeLocation"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "post" : {
+        "tags" : [ "dcaeLocations" ],
+        "summary" : "return dcaeLocation details",
+        "description" : "Create some `dcaeLocation` which is a unique identifier for an *OpenStack* tenant purposed for a *dcaeLayer*  (ecomp or edge).",
+        "operationId" : "addDcaeLocation",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DcaeLocation"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/dcaeLocations/{locationName}" : {
+      "get" : {
+        "tags" : [ "dcaeLocations" ],
+        "summary" : "return dcaeLocation details",
+        "description" : "Returns a specific `dcaeLocation` object with specified tag",
+        "operationId" : "getDcaeLocation",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "locationName",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DcaeLocation"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "put" : {
+        "tags" : [ "dcaeLocations" ],
+        "summary" : "return dcaeLocation details",
+        "description" : "update the openStackAvailabilityZone of a dcaeLocation",
+        "operationId" : "updateDcaeLocation",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "locationName",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DcaeLocation"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "delete" : {
+        "tags" : [ "dcaeLocations" ],
+        "summary" : "return dcaeLocation details",
+        "description" : "delete a dcaeLocation",
+        "operationId" : "deleteDcaeLocation",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "locationName",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "successful operation",
+            "schema" : {
+              "$ref" : "#/definitions/DcaeLocation"
+            }
+          },
+          "204" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DcaeLocation"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/dmaap" : {
+      "get" : {
+        "tags" : [ "dmaap" ],
+        "summary" : "return dmaap details",
+        "description" : "returns the `dmaap` object, which contains system wide configuration settings",
+        "operationId" : "getDmaap",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Dmaap"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "post" : {
+        "tags" : [ "dmaap" ],
+        "summary" : "return dmaap details",
+        "description" : "Create a new DMaaP set system wide configuration settings for the *dcaeEnvironment*.  Deprecated with introduction of persistence in 1610.",
+        "operationId" : "addDmaap",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Dmaap"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "put" : {
+        "tags" : [ "dmaap" ],
+        "summary" : "return dmaap details",
+        "description" : "Update system settings for *dcaeEnvironment*.",
+        "operationId" : "updateDmaap",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Dmaap"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/dr_nodes" : {
+      "get" : {
+        "tags" : [ "dr_nodes" ],
+        "summary" : "return DR_Node details",
+        "description" : "Returns array of `DR_Node` object array.  Need to add filter by dcaeLocation.",
+        "operationId" : "getDr_Nodes",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Node"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "post" : {
+        "tags" : [ "dr_nodes" ],
+        "summary" : "return DR_Node details",
+        "description" : "create a `DR_Node` in a *dcaeLocation*.  Note that multiple `DR_Node`s may exist in the same `dcaeLocation`.",
+        "operationId" : "addDr_Node",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Node"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/dr_nodes/{fqdn}" : {
+      "get" : {
+        "tags" : [ "dr_nodes" ],
+        "summary" : "return DR_Node details",
+        "description" : "Retrieve a single `DR_Node` object.",
+        "operationId" : "get",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "fqdn",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Node"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "put" : {
+        "tags" : [ "dr_nodes" ],
+        "summary" : "return DR_Node details",
+        "description" : "Update a single `DR_Node` object.",
+        "operationId" : "updateDr_Node",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "fqdn",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Node"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "delete" : {
+        "tags" : [ "dr_nodes" ],
+        "summary" : "No Content",
+        "description" : "Delete a single `DR_Node` object.",
+        "operationId" : "deleteDr_Node",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "fqdn",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "successful operation",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Node"
+            }
+          },
+          "204" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Node"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/dr_pubs" : {
+      "get" : {
+        "tags" : [ "dr_pubs" ],
+        "summary" : "return DR_Pub details",
+        "description" : "Returns array of  `DR_Pub` objects.  Add filter for feedId.",
+        "operationId" : "getDr_Pubs",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Pub"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "post" : {
+        "tags" : [ "dr_pubs" ],
+        "summary" : "return DR_Pub details",
+        "description" : "create a DR Publisher in the specified environment.",
+        "operationId" : "addDr_Pub",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Pub"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/dr_pubs/{pubId}" : {
+      "get" : {
+        "tags" : [ "dr_pubs" ],
+        "summary" : "return DR_Pub details",
+        "description" : "returns a DR Publisher in the specified environment. Gets a `DR_Pub` object by pubId",
+        "operationId" : "get",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "pubId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Pub"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "put" : {
+        "tags" : [ "dr_pubs" ],
+        "summary" : "return DR_Pub details",
+        "description" : "update a DR Publisher in the specified environment.  Update a `DR_Pub` object by pubId",
+        "operationId" : "updateDr_Pub",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "pubId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Pub"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "delete" : {
+        "tags" : [ "dr_pubs" ],
+        "summary" : "return DR_Pub details",
+        "description" : "delete a DR Publisher in the specified environment. Delete a `DR_Pub` object by pubId",
+        "operationId" : "deleteDr_Pub",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "pubId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "successful operation",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Pub"
+            }
+          },
+          "204" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Pub"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/dr_subs" : {
+      "get" : {
+        "tags" : [ "dr_subs" ],
+        "summary" : "return DR_Sub details",
+        "description" : "Returns array of  `DR_Sub` objects.  Add filter for feedId.",
+        "operationId" : "getDr_Subs",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Sub"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "post" : {
+        "tags" : [ "dr_subs" ],
+        "summary" : "return DR_Sub details",
+        "description" : "Create a  `DR_Sub` object.  ",
+        "operationId" : "addDr_Sub",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Sub"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/dr_subs/{subId}" : {
+      "get" : {
+        "tags" : [ "dr_subs" ],
+        "summary" : "return DR_Sub details",
+        "description" : "Retrieve a  `DR_Sub` object, selected by subId",
+        "operationId" : "get",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "subId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Sub"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "put" : {
+        "tags" : [ "dr_subs" ],
+        "summary" : "return DR_Sub details",
+        "description" : "Update a  `DR_Sub` object, selected by subId",
+        "operationId" : "updateDr_Sub",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "subId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Sub"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "delete" : {
+        "tags" : [ "dr_subs" ],
+        "summary" : "return DR_Sub details",
+        "description" : "Delete a  `DR_Sub` object, selected by subId",
+        "operationId" : "deleteDr_Sub",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "subId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Sub"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/feeds" : {
+      "get" : {
+        "tags" : [ "Feeds" ],
+        "summary" : "return Feed details",
+        "description" : "Returns array of  `Feed` objects.",
+        "operationId" : "getFeeds",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "feedName",
+          "in" : "query",
+          "required" : false,
+          "type" : "string"
+        }, {
+          "name" : "version",
+          "in" : "query",
+          "required" : false,
+          "type" : "string"
+        }, {
+          "name" : "match",
+          "in" : "query",
+          "required" : false,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Feed"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "post" : {
+        "tags" : [ "Feeds" ],
+        "summary" : "return Feed details",
+        "description" : "Create a of  `Feed` object.",
+        "operationId" : "addFeed",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "useExisting",
+          "in" : "query",
+          "required" : false,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Feed"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/feeds/sync" : {
+      "put" : {
+        "tags" : [ "Feeds" ],
+        "summary" : "sync feeds to existing DR",
+        "description" : "When Bus Controller is deployed after DR, then it is possiblethat DR has previous provisioning data that needs to be importedinto Bus Controller.",
+        "operationId" : "syncFeeds",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "hard",
+          "in" : "query",
+          "required" : false,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Feed"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/feeds/{id}" : {
+      "get" : {
+        "tags" : [ "Feeds" ],
+        "summary" : "return Feed details",
+        "description" : "Retrieve a  `Feed` object, specified by id.",
+        "operationId" : "getFeed",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "id",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/DR_Pub"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "put" : {
+        "tags" : [ "Feeds" ],
+        "summary" : "return Feed details",
+        "description" : "Update a  `Feed` object, specified by id.",
+        "operationId" : "updateFeed",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "id",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Feed"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "delete" : {
+        "tags" : [ "Feeds" ],
+        "summary" : "return Feed details",
+        "description" : "Delete a  `Feed` object, specified by id.",
+        "operationId" : "deleteFeed",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "id",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "successful operation",
+            "schema" : {
+              "$ref" : "#/definitions/Feed"
+            }
+          },
+          "204" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Feed"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/info" : {
+      "get" : {
+        "tags" : [ "info" ],
+        "summary" : "return info details",
+        "description" : "returns the `info` object",
+        "operationId" : "getInfo",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Dmaap"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/mr_clients" : {
+      "get" : {
+        "tags" : [ "MR_Clients" ],
+        "summary" : "return MR_Client details",
+        "description" : "Returns array of  `MR_Client` objects.",
+        "operationId" : "getMr_Clients",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Client"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "post" : {
+        "tags" : [ "MR_Clients" ],
+        "summary" : "Associate an MR_Client object to a Topic",
+        "description" : "Create a  `MR_Client` object.The `dcaeLocation` attribute is used to match an `MR_Cluster` object with the same value, with the intent of localizing message traffic.  In legacy implementation, the `clientRole` is granted appropriate permission in AAF.  Newer implementions may instead specify an AAF Identity, which will be added to the appropriate `Topic` role.",
+        "operationId" : "addMr_Client",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Client"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/mr_clients/{clientId}" : {
+      "put" : {
+        "tags" : [ "MR_Clients" ],
+        "summary" : "Update an MR_Client object",
+        "description" : "Update a  `MR_Client` object, specified by clientId",
+        "operationId" : "updateMr_Client",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "clientId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Client"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/mr_clients/{subId}" : {
+      "get" : {
+        "tags" : [ "MR_Clients" ],
+        "summary" : "return MR_Client details",
+        "description" : "Retrieve a  `MR_Client` object, specified by clientId",
+        "operationId" : "test",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "subId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Client"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "delete" : {
+        "tags" : [ "MR_Clients" ],
+        "summary" : "Delete an MR_Client object",
+        "description" : "Delete a  `MR_Client` object, specified by clientId",
+        "operationId" : "deleteMr_Client",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "subId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "successful operation",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Client"
+            }
+          },
+          "204" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Client"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/mr_clusters" : {
+      "get" : {
+        "tags" : [ "MR_Clusters" ],
+        "summary" : "return MR_Cluster details",
+        "description" : "Returns array of  `MR_Cluster` objects.",
+        "operationId" : "getMr_Clusters",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Cluster"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "post" : {
+        "tags" : [ "MR_Clusters" ],
+        "summary" : "return MR_Cluster details",
+        "description" : "Create an  `MR_Cluster` object.",
+        "operationId" : "addMr_Cluster",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Cluster"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/mr_clusters/{clusterId}" : {
+      "get" : {
+        "tags" : [ "MR_Clusters" ],
+        "summary" : "return MR_Cluster details",
+        "description" : "Retrieve an  `MR_Cluster` object, specified by clusterId.",
+        "operationId" : "getMR_Cluster",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "clusterId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Cluster"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "put" : {
+        "tags" : [ "MR_Clusters" ],
+        "summary" : "return MR_Cluster details",
+        "description" : "Update an  `MR_Cluster` object, specified by clusterId.",
+        "operationId" : "updateMr_Cluster",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "clusterId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Cluster"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "delete" : {
+        "tags" : [ "MR_Clusters" ],
+        "summary" : "return MR_Cluster details",
+        "description" : "Delete an  `MR_Cluster` object, specified by clusterId.",
+        "operationId" : "deleteMr_Cluster",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "clusterId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "successful operation",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Cluster"
+            }
+          },
+          "204" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/MR_Cluster"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/topics" : {
+      "get" : {
+        "tags" : [ "topics" ],
+        "summary" : "return Topic details",
+        "description" : "Returns array of  `Topic` objects.",
+        "operationId" : "getTopics",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Topic"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "post" : {
+        "tags" : [ "topics" ],
+        "summary" : "Create a Topic object",
+        "description" : "Create  `Topic` object.For convenience, the message body may populate the `clients` array, in which case each entry will be added as an `MR_Client`.  Beginning in ONAP Dublin Release, dbcapi will create two AAF Roles by default, one each for the publisher and subscriber per topic.  MR_Clients can then specify an AAF Identity to be added to the appropriate default Role, avoiding the need to create Role(s) in advance.",
+        "operationId" : "addTopic",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "useExisting",
+          "in" : "query",
+          "required" : false,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Topic"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    },
+    "/topics/{topicId}" : {
+      "get" : {
+        "tags" : [ "topics" ],
+        "summary" : "return Topic details",
+        "description" : "Retrieve a  `Topic` object, identified by topicId",
+        "operationId" : "getTopic",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "topicId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Topic"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "put" : {
+        "tags" : [ "topics" ],
+        "summary" : "return Topic details",
+        "description" : "Update a  `Topic` object, identified by topicId",
+        "operationId" : "updateTopic",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "topicId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Topic"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      },
+      "delete" : {
+        "tags" : [ "topics" ],
+        "summary" : "return Topic details",
+        "description" : "Delete a  `Topic` object, identified by topicId",
+        "operationId" : "deleteTopic",
+        "consumes" : [ "application/json" ],
+        "produces" : [ "application/json" ],
+        "parameters" : [ {
+          "name" : "topicId",
+          "in" : "path",
+          "required" : true,
+          "type" : "string"
+        } ],
+        "responses" : {
+          "200" : {
+            "description" : "successful operation",
+            "schema" : {
+              "$ref" : "#/definitions/Topic"
+            }
+          },
+          "204" : {
+            "description" : "Success",
+            "schema" : {
+              "$ref" : "#/definitions/Topic"
+            }
+          },
+          "400" : {
+            "description" : "Error",
+            "schema" : {
+              "$ref" : "#/definitions/ApiError"
+            }
+          }
+        }
+      }
+    }
+  },
+  "definitions" : {
+    "ApiError" : {
+      "type" : "object",
+      "properties" : {
+        "code" : {
+          "type" : "integer",
+          "format" : "int32"
+        },
+        "message" : {
+          "type" : "string"
+        },
+        "fields" : {
+          "type" : "string"
+        },
+        "is2xx" : {
+          "type" : "boolean"
+        }
+      }
+    },
+    "BrTopic" : {
+      "type" : "object",
+      "properties" : {
+        "brSource" : {
+          "type" : "string"
+        },
+        "brTarget" : {
+          "type" : "string"
+        },
+        "mmAgentName" : {
+          "type" : "string"
+        },
+        "topicCount" : {
+          "type" : "integer",
+          "format" : "int32"
+        }
+      }
+    },
+    "DR_Node" : {
+      "type" : "object",
+      "properties" : {
+        "lastMod" : {
+          "type" : "string",
+          "format" : "date-time",
+          "description" : "datestamp for last update to this object"
+        },
+        "status" : {
+          "type" : "string",
+          "enum" : [ "EMPTY", "NEW", "STAGED", "VALID", "INVALID", "DELETED" ]
+        },
+        "fqdn" : {
+          "type" : "string"
+        },
+        "dcaeLocationName" : {
+          "type" : "string"
+        },
+        "hostName" : {
+          "type" : "string"
+        },
+        "version" : {
+          "type" : "string"
+        }
+      }
+    },
+    "DR_Pub" : {
+      "type" : "object",
+      "properties" : {
+        "lastMod" : {
+          "type" : "string",
+          "format" : "date-time",
+          "description" : "datestamp for last update to this object"
+        },
+        "status" : {
+          "type" : "string",
+          "enum" : [ "EMPTY", "NEW", "STAGED", "VALID", "INVALID", "DELETED" ]
+        },
+        "dcaeLocationName" : {
+          "type" : "string"
+        },
+        "username" : {
+          "type" : "string"
+        },
+        "userpwd" : {
+          "type" : "string"
+        },
+        "feedId" : {
+          "type" : "string"
+        },
+        "pubId" : {
+          "type" : "string"
+        },
+        "feedName" : {
+          "type" : "string"
+        },
+        "feedVersion" : {
+          "type" : "string"
+        }
+      }
+    },
+    "DR_Sub" : {
+      "type" : "object",
+      "properties" : {
+        "lastMod" : {
+          "type" : "string",
+          "format" : "date-time",
+          "description" : "datestamp for last update to this object"
+        },
+        "status" : {
+          "type" : "string",
+          "enum" : [ "EMPTY", "NEW", "STAGED", "VALID", "INVALID", "DELETED" ]
+        },
+        "dcaeLocationName" : {
+          "type" : "string"
+        },
+        "username" : {
+          "type" : "string"
+        },
+        "userpwd" : {
+          "type" : "string"
+        },
+        "feedId" : {
+          "type" : "string"
+        },
+        "deliveryURL" : {
+          "type" : "string"
+        },
+        "logURL" : {
+          "type" : "string"
+        },
+        "subId" : {
+          "type" : "string"
+        },
+        "use100" : {
+          "type" : "boolean"
+        },
+        "suspended" : {
+          "type" : "boolean"
+        },
+        "owner" : {
+          "type" : "string"
+        },
+        "guaranteedDelivery" : {
+          "type" : "boolean"
+        },
+        "guaranteedSequence" : {
+          "type" : "boolean"
+        },
+        "privilegedSubscriber" : {
+          "type" : "boolean"
+        },
+        "decompress" : {
+          "type" : "boolean"
+        },
+        "feedName" : {
+          "type" : "string"
+        },
+        "feedVersion" : {
+          "type" : "string"
+        }
+      }
+    },
+    "DcaeLocation" : {
+      "type" : "object",
+      "properties" : {
+        "lastMod" : {
+          "type" : "string",
+          "format" : "date-time",
+          "description" : "datestamp for last update to this object"
+        },
+        "status" : {
+          "type" : "string",
+          "enum" : [ "EMPTY", "NEW", "STAGED", "VALID", "INVALID", "DELETED" ]
+        },
+        "clli" : {
+          "type" : "string"
+        },
+        "dcaeLayer" : {
+          "type" : "string"
+        },
+        "dcaeLocationName" : {
+          "type" : "string"
+        },
+        "openStackAvailabilityZone" : {
+          "type" : "string"
+        },
+        "subnet" : {
+          "type" : "string"
+        },
+        "local" : {
+          "type" : "boolean"
+        },
+        "central" : {
+          "type" : "boolean"
+        }
+      }
+    },
+    "Dmaap" : {
+      "type" : "object",
+      "properties" : {
+        "lastMod" : {
+          "type" : "string",
+          "format" : "date-time",
+          "description" : "datestamp for last update to this object"
+        },
+        "status" : {
+          "type" : "string",
+          "enum" : [ "EMPTY", "NEW", "STAGED", "VALID", "INVALID", "DELETED" ]
+        },
+        "version" : {
+          "type" : "string"
+        },
+        "topicNsRoot" : {
+          "type" : "string"
+        },
+        "dmaapName" : {
+          "type" : "string"
+        },
+        "drProvUrl" : {
+          "type" : "string"
+        },
+        "bridgeAdminTopic" : {
+          "type" : "string"
+        },
+        "loggingUrl" : {
+          "type" : "string"
+        },
+        "nodeKey" : {
+          "type" : "string"
+        },
+        "accessKeyOwner" : {
+          "type" : "string"
+        }
+      }
+    },
+    "Feed" : {
+      "type" : "object",
+      "properties" : {
+        "lastMod" : {
+          "type" : "string",
+          "format" : "date-time",
+          "description" : "datestamp for last update to this object"
+        },
+        "status" : {
+          "type" : "string",
+          "enum" : [ "EMPTY", "NEW", "STAGED", "VALID", "INVALID", "DELETED" ]
+        },
+        "feedId" : {
+          "type" : "string"
+        },
+        "feedName" : {
+          "type" : "string"
+        },
+        "feedVersion" : {
+          "type" : "string"
+        },
+        "feedDescription" : {
+          "type" : "string"
+        },
+        "owner" : {
+          "type" : "string"
+        },
+        "asprClassification" : {
+          "type" : "string"
+        },
+        "publishURL" : {
+          "type" : "string"
+        },
+        "subscribeURL" : {
+          "type" : "string"
+        },
+        "suspended" : {
+          "type" : "boolean"
+        },
+        "logURL" : {
+          "type" : "string"
+        },
+        "formatUuid" : {
+          "type" : "string"
+        },
+        "pubs" : {
+          "type" : "array",
+          "items" : {
+            "$ref" : "#/definitions/DR_Pub"
+          }
+        },
+        "subs" : {
+          "type" : "array",
+          "items" : {
+            "$ref" : "#/definitions/DR_Sub"
+          }
+        },
+        "bytes" : {
+          "type" : "array",
+          "items" : {
+            "type" : "string",
+            "format" : "byte"
+          }
+        }
+      }
+    },
+    "MR_Client" : {
+      "type" : "object",
+      "properties" : {
+        "lastMod" : {
+          "type" : "string",
+          "format" : "date-time",
+          "description" : "datestamp for last update to this object"
+        },
+        "status" : {
+          "type" : "string",
+          "enum" : [ "EMPTY", "NEW", "STAGED", "VALID", "INVALID", "DELETED" ]
+        },
+        "dcaeLocationName" : {
+          "type" : "string",
+          "description" : "a tag indicating a logical deployment site"
+        },
+        "topicURL" : {
+          "type" : "string",
+          "description" : "the URL for a MR instance - typically in the same dcaeLocation - that this client should use to access the topic"
+        },
+        "fqtn" : {
+          "type" : "string",
+          "description" : "Fully Qualified Topic Name constructed by dbcapi"
+        },
+        "clientRole" : {
+          "type" : "string",
+          "description" : "an AAF Role to be granted an appropriate Permission.  If specified, takes precedence over clientIdentity, for backwards compatibility."
+        },
+        "action" : {
+          "type" : "array",
+          "description" : "one or more actions from the set (\"pub\", \"sub\", \"view\") for which this client needs Permission",
+          "items" : {
+            "type" : "string"
+          }
+        },
+        "mrClientId" : {
+          "type" : "string",
+          "description" : "a unique identifier generated by dbcapi for this client"
+        },
+        "clientIdentity" : {
+          "type" : "string",
+          "description" : "an AAF identity to be associated to an appropriate topic Role"
+        }
+      }
+    },
+    "MR_Cluster" : {
+      "type" : "object",
+      "properties" : {
+        "lastMod" : {
+          "type" : "string",
+          "format" : "date-time"
+        },
+        "status" : {
+          "type" : "string",
+          "enum" : [ "EMPTY", "NEW", "STAGED", "VALID", "INVALID", "DELETED" ]
+        },
+        "dcaeLocationName" : {
+          "type" : "string"
+        },
+        "fqdn" : {
+          "type" : "string"
+        },
+        "topicProtocol" : {
+          "type" : "string"
+        },
+        "topicPort" : {
+          "type" : "string"
+        },
+        "replicationGroup" : {
+          "type" : "string"
+        },
+        "sourceReplicationPort" : {
+          "type" : "string"
+        },
+        "targetReplicationPort" : {
+          "type" : "string"
+        }
+      }
+    },
+    "MirrorMaker" : {
+      "type" : "object",
+      "properties" : {
+        "lastMod" : {
+          "type" : "string",
+          "format" : "date-time",
+          "description" : "datestamp for last update to this object"
+        },
+        "status" : {
+          "type" : "string",
+          "enum" : [ "EMPTY", "NEW", "STAGED", "VALID", "INVALID", "DELETED" ]
+        },
+        "sourceCluster" : {
+          "type" : "string"
+        },
+        "targetCluster" : {
+          "type" : "string"
+        },
+        "mmName" : {
+          "type" : "string"
+        },
+        "topics" : {
+          "type" : "array",
+          "items" : {
+            "type" : "string"
+          }
+        },
+        "whitelistUpdateJSON" : {
+          "type" : "string"
+        },
+        "topicCount" : {
+          "type" : "integer",
+          "format" : "int32"
+        }
+      }
+    },
+    "Topic" : {
+      "type" : "object",
+      "properties" : {
+        "lastMod" : {
+          "type" : "string",
+          "format" : "date-time",
+          "description" : "datestamp for last update to this object"
+        },
+        "status" : {
+          "type" : "string",
+          "enum" : [ "EMPTY", "NEW", "STAGED", "VALID", "INVALID", "DELETED" ]
+        },
+        "fqtn" : {
+          "type" : "string",
+          "description" : "Fully Qualified Topic Name constructed by dbcapi, following the rules for `fqtnStyle`"
+        },
+        "topicName" : {
+          "type" : "string",
+          "description" : "the short name used by humans, and utilized to construct the `FQTN`"
+        },
+        "topicDescription" : {
+          "type" : "string",
+          "description" : "a description of what this Topic is used for"
+        },
+        "tnxEnabled" : {
+          "type" : "string"
+        },
+        "owner" : {
+          "type" : "string",
+          "description" : "a label used to identify who requested this `Topic` to be provisioned.  In the future this may be an AAF Identity."
+        },
+        "formatUuid" : {
+          "type" : "string",
+          "description" : "a reference to an identifier that describes a data format used for this `Topic`"
+        },
+        "replicationCase" : {
+          "type" : "string",
+          "description" : "An indicator for how this `Topic` should be replicated when there are more than one `MR_Cluster` instances",
+          "enum" : [ "REPLICATION_NOT_SPECIFIED", "REPLICATION_NONE", "REPLICATION_EDGE_TO_CENTRAL", "REPLICATION_EDGE_TO_CENTRAL_TO_GLOBAL", "REPLICATION_CENTRAL_TO_EDGE", "REPLICATION_CENTRAL_TO_GLOBAL", "REPLICATION_GLOBAL_TO_CENTRAL", "REPLICATION_GLOBAL_TO_CENTRAL_TO_EDGE", "REPLICATION_EDGE_TO_FQDN", "REPLICATION_FQDN_TO_EDGE", "REPLICATION_FQDN_TO_GLOBAL", "REPLICATION_GLOBAL_TO_FQDN", "REPLICATION_EDGE_TO_FQDN_TO_GLOBAL", "REPLICATION_GLOBAL_TO_FQDN_TO_EDGE" ]
+        },
+        "globalMrURL" : {
+          "type" : "string",
+          "description" : "the URL of an outside MR instance"
+        },
+        "fqtnStyle" : {
+          "type" : "string",
+          "description" : "the construction rule for the `fqtn` field",
+          "enum" : [ "FQTN_NOT_SPECIFIED", "FQTN_LEGACY_FORMAT", "FQTN_PROJECTID_FORMAT", "FQTN_PROJECTID_VERSION_FORMAT" ]
+        },
+        "version" : {
+          "type" : "string",
+          "description" : "a hook for any versioning needed for managing a `Topic` over time"
+        },
+        "partitionCount" : {
+          "type" : "string",
+          "description" : "the kafka attribute for specifying the number of partitions"
+        },
+        "replicationCount" : {
+          "type" : "string",
+          "description" : "the kafka attribute for specifying replication within an `MR_Cluster` instance"
+        },
+        "publisherRole" : {
+          "type" : "string",
+          "description" : "a value generated by dbcapi, this AAF Role has permission to publish to this `Topic`"
+        },
+        "subscriberRole" : {
+          "type" : "string",
+          "description" : "a value generated by dbcapi, this AAF Role has permission to subscribe to this `Topic`"
+        },
+        "clients" : {
+          "type" : "array",
+          "description" : "an array of `MR_Client` objects associated to this `Topic`",
+          "items" : {
+            "$ref" : "#/definitions/MR_Client"
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
similarity index 77%
rename from docs/architecture/architecture.rst
rename to docs/architecture.rst
index 547e204..721146d 100644 (file)
@@ -8,13 +8,15 @@ Architecture
 
 Capabilities
 ------------
-Bus Controller is a RESTful web service used to provision DMaaP topics (on Message Router) and feeds (on Data Router), with associated authorization (on AAF).
+Bus Controller is a RESTful web service used to provision DMaaP topics on MR (Message Router)
+and feeds on DR (Data Router), with associated authorization via AAF.
 
 Usage Scenarios
 ---------------
 Bus Controller endpoints are used to provision:
-- a authorized topic on MR, and to create and grant permission for publishers and subscribers.
-- a feed on DR, with associated user authenticatio n.
+
+- an authorized topic on MR, and to create and grant permission for publishers and subscribers.
+- a feed on DR, with associated user authentication.
 
 .. blockdiag::
 
similarity index 99%
rename from docs/consumedapis/consumedapis.rst
rename to docs/consumedapis.rst
index c45851c..303fad5 100644 (file)
@@ -5,6 +5,7 @@ Consumed APIs
 ==============
 
 Bus Controller consumes the following APIs:
+
 1) the Message Router topic creation API
 2) the AAF authorization API to create and grant permissions
 3) the DR Provisioning API to create feeds and subscribers
similarity index 100%
rename from docs/delivery/delivery.rst
rename to docs/delivery.rst
diff --git a/docs/humaninterfaces/humaninterfaces.rst b/docs/humaninterfaces/humaninterfaces.rst
deleted file mode 100644 (file)
index f115731..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-.. This work is licensed under a Creative Commons Attribution 4.0 International License.
-.. http://creativecommons.org/licenses/by/4.0
-
-Human Interfaces
-================
-
-
-Buscontroller does not have human interfaces.
index 4e2ad15..5feae2f 100644 (file)
@@ -6,17 +6,16 @@ dmaap/buscontroller
 ===================
 
 .. toctree::
-   :maxdepth: 3
+   :maxdepth: 1
+
+   architecture.rst
+   configuration.rst
+   installation.rst
+   offeredapis.rst
+   consumedapis.rst
+   logging.rst
+   delivery.rst
+   security.rst
+   release-notes.rst
 
-   architecture/architecture.rst
-   release-notes/release-notes.rst
-   configuration/configuration.rst
-   humaninterfaces/humaninterfaces.rst
-   offeredapis/offeredapis.rst
-   Installation/Installation.rst
-   consumedapis/consumedapis.rst
-   administration/administration.rst
-   logging/logging.rst
-   delivery/delivery.rst
-   security/security.rst
 
similarity index 96%
rename from docs/Installation/Installation.rst
rename to docs/installation.rst
index 3a8e732..cee606c 100644 (file)
@@ -4,8 +4,6 @@
 Installation
 ============
 
-Environment
-===========
 Bus Controller is developed using Postgresql.  An embedded Jetty server is used to create the REST service.
 The service is packaged as a Docker container image.
 Helm charts for Bus Controller are part of the overall dmaap chart set.
@@ -25,6 +23,7 @@ Customizing the Bus Controller configuration
 
 The Bus Controller is highly configurable, but by default has settings that should work for a standard ONAP oom deployment.
 However, if some customization is desired, there are places to change behavior:
+
 1) The --namespace argument of the helm install step is also refernced to compose the topic namespace used.  i.e. the value is appended to org.onap.dmaap.   Since Message Router uses org.onap.dmaap.mr by default, we also use --namespace=mr.  But this can be changed to a value that matches a different deployment of MR.
 2) oom/kubernetes/dmaap/charts/dmaap-bus-controller/values.yaml  contains the set of tags used within the charts.  These can be modified if necessary.
 3) oom/kubernetes/dmaap/charts/dmaap-bus-controller/resources/config/buscontroller.env contains some environment settings for the container.  These can be modified.  For example, to indicate that AAF integration should be enabled, set USE_AAF=true in this file.
@@ -38,7 +37,7 @@ On Intel dev machine, in terminal (> indicates prompt) :
     > git clone https://gerrit.onap.org/r/dmaap/buscontroller
         - anonymous http, can't push changes
     > cd buscontroller
-    > mvn clean install -Pdocker
+    > mvn clean install -P docker
         - builds dmaap-bc and dbc-client images
 2) Run tests
     > cd dmaap-bc/src/main/resources/
@@ -56,7 +55,7 @@ On Arm:
     > git clone https://gerrit.onap.org/r/dmaap/buscontroller
         - anonymous http, can't push changes
     > cd buscontroller
-    > mvn clean install -Pdocker  -Ddocker.pull.registry=docker.io
+    > mvn clean install -P docker  -Ddocker.pull.registry=docker.io
         - ensure we pull Arm version of base image
 2) Run tests
     > cd dmaap-bc/src/main/resources/
diff --git a/docs/logging.rst b/docs/logging.rst
new file mode 100644 (file)
index 0000000..676a106
--- /dev/null
@@ -0,0 +1,19 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+   .. http://creativecommons.org/licenses/by/4.0
+
+Logging
+=======
+
+Where to Access Information
+---------------------------
+Bus Controller uses logback framework to generate logs found under logs/ONAP.
+Logs are organized into files:
+
+- application.log - contains general logs
+- error.log - contains errors
+- audit.log - contains transactions for audit trail
+- server.log - contains jetty server specific logging
+
+Error / Warning Messages
+------------------------
+Logging to error.log will distinguish critical errors from warnings.
diff --git a/docs/logging/logging.rst b/docs/logging/logging.rst
deleted file mode 100644 (file)
index aa6ae57..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-.. This work is licensed under a Creative Commons Attribution 4.0 International License.
-   .. http://creativecommons.org/licenses/by/4.0
-
-Logging
-=======
-
-.. note::
-   * This section is used to describe the informational or diagnostic messages emitted from
-     a software component and the methods or collecting them.
-
-   * This section is typically: provided for a platform-component and sdk; and
-     referenced in developer and user guides
-
-   * This note must be removed after content has been added.
-
-
-Where to Access Information
----------------------------
-Bus Controller uses logback framework to generate logs found under logs/ONAP.
-Logs are organized into files:
-application.log - contains general logs
-error.log - contains errors
-audit.log - contains transactions for audit trail
-server.log - contains jetty server specific logging
-
-Error / Warning Messages
-------------------------
-Logging to error.log will distinguish critical errors from warnings.
similarity index 61%
rename from docs/offeredapis/offeredapis.rst
rename to docs/offeredapis.rst
index f905f38..61991da 100644 (file)
@@ -5,4 +5,8 @@
 Offered APIs
 ==============
 
-Refer to Bus Controller API under https://onap.readthedocs.io/en/latest/guides/onap-developer/apiref/index.html
+.. toctree::
+   :maxdepth: 1
+
+   apis/api-table.rst
+   apis/api.rst
similarity index 92%
rename from docs/release-notes/release-notes.rst
rename to docs/release-notes.rst
index 42f7b2c..40dcb45 100644 (file)
@@ -3,8 +3,9 @@
 .. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING
 .. _release_notes:
 
-DMaaP Buscontroller Release Notes
-====================
+=============
+Release Notes
+=============
 
 .. note
 ..      * This Release Notes must be updated each time the team decides to Release new artifacts.
@@ -16,9 +17,9 @@ DMaaP Buscontroller Release Notes
 ..      * This note must be removed after content has been added.
 
 
-..      ===========================
-..      * * *    Honolulu    * * *
-..      ===========================
+
+Honolulu
+========
 
 
 
@@ -80,7 +81,7 @@ Security Notes
 References
 ----------
 
-For more information on the ONAP Guilin release, please see:
+For more information on the ONAP Honolulu release, please see:
 
 #. `ONAP Home Page`_
 #. `ONAP Documentation`_
@@ -95,14 +96,13 @@ For more information on the ONAP Guilin release, please see:
 
 
 Quick Links:
-- `DMAAP project page <https://wiki.onap.org/display/DW/DMaaP+Planning>`_
-- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/1751>`_
 
-..      ===========================
-..      * * *    GUILIN    * * *
-..      ===========================
+- `DMAAP project page <https://wiki.onap.org/display/DW/DMaaP+Planning>`_
 
+- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/2147>`_
 
+Guilin
+======
 
 Abstract
 --------
@@ -179,13 +179,13 @@ For more information on the ONAP Guilin release, please see:
 
 
 Quick Links:
+
 - `DMAAP project page <https://wiki.onap.org/display/DW/DMaaP+Planning>`_
-- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/1751>`_
 
-..      ===========================
-..      * * *    FRANKFURT    * * *
-..      ===========================
+- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/2147>`_
 
+Frankfurt
+=========
 
 
 Abstract
@@ -271,12 +271,13 @@ For more information on the ONAP Frankfurt release, please see:
 
 
 Quick Links:
+
 - `DMAAP project page <https://wiki.onap.org/display/DW/DMaaP+Planning>`_
-- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/1751>`_
 
-..      ==========================
-..      * * *     EL ALTO    * * *
-..      ==========================
+- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/2147>`_
+
+El Alto
+=======
 
 Version: 1.1.5 
 --------------
@@ -297,9 +298,12 @@ Version: 1.1.5
 DMAAP code has been formally scanned during build time using NexusIQ and all Critical vulnerabilities have been addressed, items that remain open have been assessed for risk and determined to be false positive. The DMAAP open Critical security vulnerabilities and their risk assessment have been documented as part of the `Dublin <https://wiki.onap.org/pages/viewpage.action?pageId=64003715>`_.
 
 Quick Links:
+
 - `DMAAP project page <https://wiki.onap.org/display/DW/DMaaP+Planning>`_
-- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/1751>`_
-- `El Alto Project Vulnerability Review Table for DMAAP <https://wiki.onap.org/pages/viewpage.action?pageId=71835817>`
+
+- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/2147>`_
+
+- `El Alto Project Vulnerability Review Table for DMAAP <https://wiki.onap.org/pages/viewpage.action?pageId=71835817>`_
 
 **Upgrade Notes**
        NA
@@ -327,8 +331,11 @@ Version: 1.1.5 (Dublin)
 DMAAP code has been formally scanned during build time using NexusIQ and all Critical vulnerabilities have been addressed, items that remain open have been assessed for risk and determined to be false positive. The DMAAP open Critical security vulnerabilities and their risk assessment have been documented as part of the `Dublin <https://wiki.onap.org/pages/viewpage.action?pageId=64003715>`_.
 
 Quick Links:
+
 - `DMAAP project page <https://wiki.onap.org/display/DW/DMaaP+Planning>`_
-- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/1751>`_
+
+- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/2147>`_
+
 - `Dublin Project Vulnerability Review Table for DMAAP <https://wiki.onap.org/pages/viewpage.action?pageId=64003715>`_
 
 **Upgrade Notes**
@@ -356,8 +363,11 @@ Version: 1.0.23
 DMAAP code has been formally scanned during build time using NexusIQ and all Critical vulnerabilities have been addressed, items that remain open have been assessed for risk and determined to be false positive. The DMAAP open Critical security vulnerabilities and their risk assessment have been documented as part of the `project <https://wiki.onap.org/pages/viewpage.action?pageId=28379799>`_.
 
 Quick Links:
+
 - `DMAAP project page <https://wiki.onap.org/display/DW/DMaaP+Planning>`_
-- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/1751>`_
+
+- `Passing Badge information for DMAAP <https://bestpractices.coreinfrastructure.org/en/projects/2147>`_
+
 - `Project Vulnerability Review Table for DMAAP <https://wiki.onap.org/pages/viewpage.action?pageId=28379799>`_
 
 **Upgrade Notes**
similarity index 99%
rename from docs/security/security.rst
rename to docs/security.rst
index 47a1736..aab2c3d 100644 (file)
@@ -13,7 +13,7 @@ Roles and Permissions
 | The roles and permissions are being provisioned to AAF instance during DMaaP BC instance initialization phase only when AAF is in use.
 | The default namespace in AAF for storing Bus Controller API roles and permissions is ``org.onap.dmaap-bc.api``.
 | Separate permission is created for every HTTP method on each DMaaP BC REST api endpoint.
-| Refer to :doc:`DMaaP Bus Controller REST API<../../../../dmaap/dbcapi.git/docs/api>` for comprehensive api information.
+| Refer to :ref:`offeredapis` for comprehensive api information.
 | Default name for DMaaP instance in ONAP is ``mr`` which is reflected in instance part of every created permission under DMaaP BC API.
 | Exception of above rule is for ``/dmaap`` endpoint where additionally set of permissions for ``boot`` instance is defined:
 
diff --git a/pom.xml b/pom.xml
index dd390fd..290b85b 100644 (file)
--- a/pom.xml
+++ b/pom.xml
   * limitations under the License. 
   * ============LICENSE_END==================================================== 
   * -->
- <project xmlns="http://maven.apache.org/POM/4.0.0"
-        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.onap.dmaap.buscontroller</groupId>
   <artifactId>parent</artifactId>
-   <version>${revision}</version>
+  <version>${revision}</version>
   <name>dmaap-buscontroller</name>
   <packaging>pom</packaging>
-  
+
   <parent>
-       <groupId>org.onap.oparent</groupId>
-       <artifactId>oparent</artifactId>
-      <version>3.2.0</version>
+    <groupId>org.onap.oparent</groupId>
+    <artifactId>oparent</artifactId>
+    <version>3.2.0</version>
   </parent>
 
   <properties>
     <revision>2.0.6-SNAPSHOT</revision>
     <multiproject.basedir>${basedir}</multiproject.basedir>
     <docker.maven.plugin.version>1.0.0</docker.maven.plugin.version>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <skip.docker.build>false</skip.docker.build>
     <skip.docker.tag>false</skip.docker.tag>
     <skip.docker.push>false</skip.docker.push>
-    <docker.skip.build>false</docker.skip.build>
     <docker.verbose>true</docker.verbose>
     <docker.apiVersion>2.2.54</docker.apiVersion>
+    <maven.compiler.source>11</maven.compiler.source>
+    <maven.compiler.target>11</maven.compiler.target>
+    <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath>
+    <docker.image.root>onap/dmaap/</docker.image.root>
+    <nexusproxy>https://nexus.onap.org</nexusproxy>
+    <docker.push.registry>nexus3.onap.org:10003</docker.push.registry>
+    <timestamp>${maven.build.timestamp}</timestamp>
+    <maven.build.timestamp.format>yyyyMMdd'T'HHmmss'Z'</maven.build.timestamp.format>
+    <sonar.language>java</sonar.language>
+    <sonar.skip>false</sonar.skip>
+    <sonar.surefire.reportsPath>${project.build.directory}/surefire-reports
+    </sonar.surefire.reportsPath>
+    <sonar.projectVersion>${project.version}</sonar.projectVersion>
+    <sitePath>/content/sites/site/org/onap/dmaap/buscontroller/${project.artifactId}/${revision}
+    </sitePath>
   </properties>
-
+  <modules>
+    <module>dmaap-bc</module>
+    <module>dbc-client</module>
+  </modules>
   <build>
     <plugins>
+    </plugins>
+    <pluginManagement>
+      <plugins>
         <plugin>
-            <groupId>io.fabric8</groupId>
-            <artifactId>docker-maven-plugin</artifactId>
-            <version>0.28.0</version>
-            <configuration>
-                <skipBuild>${skip.docker.build}</skipBuild>
-            </configuration>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <configuration>
+            <excludes>
+            </excludes>
+            <argLine>
+              ${surefireArgLine} --illegal-access=permit
+            </argLine>
+          </configuration>
         </plugin>
-    </plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-failsafe-plugin</artifactId>
+          <configuration>
+            <argLine>
+              --illegal-access=permit
+            </argLine>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>flatten-maven-plugin</artifactId>
+          <version>1.0.1</version>
+          <configuration>
+            <updatePomFile>true</updatePomFile>
+            <outputDirectory>target</outputDirectory>
+          </configuration>
+          <executions>
+            <execution>
+              <id>flatten</id>
+              <phase>process-resources</phase>
+              <goals>
+                <goal>flatten</goal>
+              </goals>
+            </execution>
+          </executions>
+        </plugin>
+        <plugin>
+          <artifactId>maven-checkstyle-plugin</artifactId>
+          <executions>
+            <execution>
+              <id>onap-java-style</id>
+              <configuration>
+                <consoleOutput>false</consoleOutput>
+              </configuration>
+            </execution>
+          </executions>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-compiler-plugin</artifactId>
+          <configuration>
+            <source>${maven.compiler.source}</source>
+            <target>${maven.compiler.target}</target>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-source-plugin</artifactId>
+          <version>2.2.1</version>
+          <executions>
+            <execution>
+              <id>attach-sources</id>
+              <goals>
+                <goal>jar-no-fork</goal>
+              </goals>
+            </execution>
+          </executions>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.gmaven</groupId>
+          <artifactId>gmaven-plugin</artifactId>
+          <version>${gmaven-plugin.version}</version>
+          <executions>
+            <execution>
+              <phase>validate</phase>
+              <goals>
+                <goal>execute</goal>
+              </goals>
+              <configuration>
+                <properties>
+                  <ver>${project.version}</ver>
+                </properties>
+                <!-- Setup image tags per https://wiki.onap.org/display/DW/Independent+Versioning+and+Release+Process#IndependentVersioningandReleaseProcess-StandardizedDockerTagging -->
+                <source>
+                  println 'ver: ' + project.properties['ver'];
+                  if (project.properties['ver'].endsWith("-SNAPSHOT")) {
+                    project.properties['dockertag1'] = project.properties['ver'] + "-latest";
+                    project.properties['dockertag2'] = project.properties['ver'] + "-" + project.properties['timestamp'];
+                  } else {
+                    project.properties['dockertag1'] = project.properties['ver'] + "-STAGING-latest";
+                    project.properties['dockertag2'] = project.properties['ver'] + "-STAGING-" + project.properties['timestamp'];
+                  }
+                  println 'docker tag 1: ' + project.properties['dockertag1'];
+                  println 'docker tag 2: ' + project.properties['dockertag2'];
+                </source>
+              </configuration>
+            </execution>
+          </executions>
+        </plugin>
+        <plugin>
+          <groupId>io.fabric8</groupId>
+          <artifactId>docker-maven-plugin</artifactId>
+          <version>${io.fabric8.version}</version>
+          <configuration>
+            <skipBuild>${docker.skip.build}</skipBuild>
+            <verbose>${docker.verbose}</verbose>
+            <apiVersion>${docker.apiVersion}</apiVersion>
+            <pullRegistry>${docker.pull.registry}</pullRegistry>
+            <pushRegistry>${docker.push.registry}</pushRegistry>
+          </configuration>
+        </plugin>
+        <plugin>
+          <groupId>org.codehaus.mojo</groupId>
+          <artifactId>properties-maven-plugin</artifactId>
+          <version>1.0.0</version>
+          <executions>
+            <execution>
+              <phase>validate</phase>
+              <goals>
+                <goal>read-project-properties</goal>
+              </goals>
+              <configuration>
+                <files>
+                  <file>../version.properties</file>
+                </files>
+              </configuration>
+            </execution>
+          </executions>
+        </plugin>
+      </plugins>
+    </pluginManagement>
   </build>
-
-  <modules>
-     <module>dmaap-bc</module>
-     <module>dbc-client</module>
-  </modules>
+  <pluginRepositories>
+    <pluginRepository>
+      <id>onap-plugin-snapshots</id>
+      <url>${onap.nexus.url}${snapshotNexusPath}</url>
+    </pluginRepository>
+  </pluginRepositories>
 </project>