From e02f72bcdafdb85fc93a3b21668569d59613ca07 Mon Sep 17 00:00:00 2001 From: prathameshmo Date: Mon, 22 Jul 2019 13:48:51 -0400 Subject: [PATCH] Fixed Kafka consumer behaviour on failed deserialization -Added ErrorHandlingDeserializer -Updated the integration test. Issue-ID: CCSDK-1514 Change-Id: I69112df850dfae2d4a3bd967b1dcfa541ea1523a Signed-off-by: prathameshmo --- .../selfservice/api/MessagingConfig.kt | 21 ++++++++++++++++----- .../selfservice/api/MessagingController.kt | 11 +++++------ .../api/messaginglib/MessagingControllerTest.kt | 10 ++++++---- .../test/resources/cba-for-kafka-integration.zip | Bin 20700 -> 0 bytes .../cba-for-kafka-integration_enriched.zip | Bin 0 -> 9781 bytes 5 files changed, 27 insertions(+), 15 deletions(-) delete mode 100644 ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/resources/cba-for-kafka-integration.zip create mode 100755 ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/resources/cba-for-kafka-integration_enriched.zip diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingConfig.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingConfig.kt index a04a79921..17e157d15 100644 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingConfig.kt +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingConfig.kt @@ -1,5 +1,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.selfservice.api +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.ObjectMapper import org.apache.kafka.clients.CommonClientConfigs import org.apache.kafka.clients.consumer.ConsumerConfig import org.apache.kafka.common.serialization.StringDeserializer @@ -7,10 +9,10 @@ import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInpu import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.kafka.annotation.EnableKafka import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory import org.springframework.kafka.core.ConsumerFactory import org.springframework.kafka.core.DefaultKafkaConsumerFactory +import org.springframework.kafka.support.serializer.ErrorHandlingDeserializer2 import org.springframework.kafka.support.serializer.JsonDeserializer @Configuration @@ -26,11 +28,20 @@ open class MessagingConfig { val configProperties = hashMapOf() configProperties[CommonClientConfigs.BOOTSTRAP_SERVERS_CONFIG] = bootstrapServers configProperties[ConsumerConfig.GROUP_ID_CONFIG] = groupId - configProperties[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java.name - configProperties[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = JsonDeserializer::class.java.name - configProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest") + configProperties[ConsumerConfig.AUTO_OFFSET_RESET_CONFIG] = "latest" + configProperties[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java + configProperties[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = ErrorHandlingDeserializer2::class.java + configProperties[ErrorHandlingDeserializer2.VALUE_DESERIALIZER_CLASS] = JsonDeserializer::class.java.name - return DefaultKafkaConsumerFactory(configProperties, StringDeserializer(), JsonDeserializer(ExecutionServiceInput::class.java)) + val deserializer = JsonDeserializer() + deserializer.setRemoveTypeHeaders(true) + deserializer.addTrustedPackages("*") + + val jsonDeserializer = JsonDeserializer(ExecutionServiceInput::class.java, + ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)) + + return DefaultKafkaConsumerFactory(configProperties, StringDeserializer(), + ErrorHandlingDeserializer2(jsonDeserializer)) } /** diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingController.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingController.kt index 1d219a83e..54cc0c129 100644 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingController.kt +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/MessagingController.kt @@ -18,6 +18,7 @@ package org.onap.ccsdk.cds.blueprintsprocessor.selfservice.api import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking import org.apache.commons.lang3.builder.ToStringBuilder +import org.apache.kafka.clients.consumer.ConsumerRecord import org.onap.ccsdk.cds.blueprintsprocessor.core.api.data.ExecutionServiceInput import org.onap.ccsdk.cds.blueprintsprocessor.message.service.BluePrintMessageLibPropertyService import org.slf4j.LoggerFactory @@ -39,17 +40,15 @@ open class MessagingController(private val propertyService: BluePrintMessageLibP } @KafkaListener(topics = ["\${blueprintsprocessor.messageclient.self-service-api.consumerTopic}"]) - open fun receive(input: ExecutionServiceInput) { - - log.info("Successfully received a message: {}", ToStringBuilder.reflectionToString(input)) + open fun receive(record: ConsumerRecord) { runBlocking { - log.info("Successfully received a message: {}", ToStringBuilder.reflectionToString(input)) + log.info("Successfully received a message: {}", ToStringBuilder.reflectionToString(record.value())) // Process the message. async { - processMessage(input) - } + processMessage(record.value()) + }.await() } } diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/messaginglib/MessagingControllerTest.kt b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/messaginglib/MessagingControllerTest.kt index f7459f522..602033ad9 100644 --- a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/messaginglib/MessagingControllerTest.kt +++ b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/selfservice/api/messaginglib/MessagingControllerTest.kt @@ -90,7 +90,7 @@ class MessagingControllerTest { @Autowired lateinit var webTestClient: WebTestClient - var receivedEvent: String? = null + var event: ExecutionServiceInput? = null @Before fun setup() { @@ -142,11 +142,13 @@ class MessagingControllerTest { log.info("test-sender sent message='{}'", ToStringBuilder.reflectionToString(input)) Thread.sleep(1000) + + assertNotNull(event) } @KafkaListener(topicPartitions = [TopicPartition(topic = "\${blueprintsprocessor.messageclient.self-service-api.topic}", partitionOffsets = [PartitionOffset(partition = "0", initialOffset = "0")])]) - fun receivedEventFromBluePrintProducer(event: ExecutionServiceInput) { - assertNotNull(event) + fun receivedEventFromBluePrintProducer(receivedEvent: ExecutionServiceInput) { + event = receivedEvent } private fun uploadBluePrint() { @@ -172,7 +174,7 @@ class MessagingControllerTest { } private fun loadCbaArchive():File { - return Paths.get("./src/test/resources/cba-for-kafka-integration.zip").toFile() + return Paths.get("./src/test/resources/cba-for-kafka-integration_enriched.zip").toFile() } @Configuration diff --git a/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/resources/cba-for-kafka-integration.zip b/ms/blueprintsprocessor/modules/inbounds/selfservice-api/src/test/resources/cba-for-kafka-integration.zip deleted file mode 100644 index 23070380c6b2264394060a316451d3a7dc9d0b0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20700 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

l@vBDJXznmB|`i6ee6k68yEK%w7PW0tb5> zCq*muQ(Ux4jJA$-26psn$&lm(Z018p)Cg{~9*+do0PH-RS)lzj*dAKD!S@!hKBmdW z531Zf546^5KDpW*^?bB|R<(~5??Ov4)!&{30GK9+n|RuNdLDN)KA&1}>@2Z5En$9E z;&aK+GsLb$i;sd?NaYwhw%kF95_ZI#MDx@C6&?1H5A~LJ75Zi*oB4EgTrpVGp)2E( zr`<};Lr?niE3jY)r0@wognq;19XHYx0|prprUYdfBRB-lI-S?!RVD?tZBWzLd2jWf&0S2_HrO~fu_12 zIwId8+!v?a4}WF%CC&R9M?l(1iZ?BQ zOal-#9Daa!pqh9ts9Ohg{>&#m$)d60g3KJN>_}9vK{V3TkeP?)CfTvJ1EICbi!tfF zvALozeAC5x1|Q)QT1L6z+t=K-VR?mNbqaTX)>TUnW{RM*eGS$7KRzgb>NG&*FT1Jk z;(hjk_6fXI<1mG8FWpBT5$Npi^&g7EpASj>haRXTfiF+a z&|3NR)Hl-`abI>$7<1uHjJYHcs9&8VXo{+ktUENVp1Z9kAJ|TSe_EZFFjxMsBsn9a z(EI~aQG_x|&pg@Fenka*qDfo^zh@_Ru_q6n%EbB=-CnrIghW@SKHCGy(9hW1l(B5f zZhz`!8>QwYCe0>Wz)}9$dcMiB2#u1a!|=U5XSyB#7{$3#z58^Soeh%h(iQUIN_a?T zUZO9M3{}@AsG^v`r@|;=S06B^Sc61u+&44SLfCHi*~E@Qb>*V8N_H0&qXFf|bJ5+g zO8%gFBTpUb>&W?UN{Xx1yfhZG7os2;I}b zthTPPYVwdNDUA9pO<;E$D_PbQsCKMf9#7BG_tdjkxFE=R)-?;e3gD<+# z7m1#ch@Fcjg}m!z6>ff#TVQO|*gJ}(C2`!VE&$sELnosRu5_8I8gQD4uce4sRzVTC zFU9^^scTM{)8|{Oo&Y+eWSspy>V|Mk_)REKU|cjzpompuOPakbNQD&6P2~r|AVJI* z(u^TSUxlEKYk{{h%tnIS9Mg2EnHhLohZ@OG-~xRd+{wkvXLWU*bUzsmJaf>|ji(&Z z_ejtmAUNAr;l<|GFQDA>*^vCAH94~lAWh4(r54A0Gfz}x6#dFJ%D#i&93=m}mea8l z&k$_~eoLM-S$0DSbGCnQ9F`-3JaiN7PW$gos{d4^*j}tKB$;gU*&8t2$zG;?) za~r#-`h9x@hi}kF+Th)j*lg%TIN8pygt*BBfC>El(eFKN`G%gNWs%aK1;((JM~_*L z8ogAiW+{#hPo?}?9wG2cv?!QeVw~S#J;HO7Nj5rK7qcpARw;mcsm3tJzyEu?o%e=t zRg$N$e_(!?MV{g77Hw2F5z@=F)zQcU%*wcz$w5b2-x72Aj`t(PBao%jF}m?=I+@0< zhPF|`AJB67Kz8wYZqkbUjzSZa@dOddt6m*rnBmNZ&o zv5-{ZdpXjd`1gtj&7j8iI_N;W(4UR}zF^}>+^l4Oo^j5acpk#c(d{_0Y#pda1@Fk! zX1HWmJ~W=HZJK8M#-r0Zk9X`hDE9D|G94n4&WS+=Vdm4HBVhwSe!|Q&XgqFv@PJy?7p$-^;4MtG<`nTr2zCKh&@GzC`BZjFa5>$(5P%Zx5 zS5^LXhJ=3ANlh6t!VdHt_{Ixn%PVs=hIqTI%NXLThD<&X^`;uHHZcu<)Zbr&b}+~H zWMix(DoDI|ui;|1c0d>|uF6!lrUo@K4<{+5-UXG=978TMHUO_n*&e|*3tu||sy-24 zTR(Q2%*BBs2aJ@cx;F&6^3>Iae(Oro>stSYmSSd$tZ^SZ=5 z(d^B5lgPtcJY@70)SVuC$VH~ZqWpSI*0IfPF5I9;l96nsTRm+#R;QeLtQ|*NKOGT6 zXypiwDoeM18ufea83^8>JvKe6?C>ri9B;Y^G(whBm1HSi_XkmuE79vJc0J2=%)_rV z{i30P5?d=)GL6F}RsP#9I~kP#r+r-;nYbqFiN`ydh3Ig&R~X$tIoCd64+mz_Xm%`& zO>Y59#rNb$(^!XW^Mp-1)#RoFb=T|klN>`l$P|@%nMl)O*lYqA>s}=ikPh>l1|}I{ zlD#V_&o^d5GVnRcogDn~u(nFS+id4{HblPs0>ZiCA?S7 zN&-jbGUPIvVep6R^v7w~+mDwRUEpKwtSsDl&VPTiG~(9vH1%863^{pDUQ$C)8dNYu zove=GoT}bVI}!IN*3tX|n zHcovMXvxHG!@a1a{d1B|w3JeTTM!?J`P(2>m$z#lNeSr@@3tU?0N>Y=a#@d*tmmMu z%dYU=uEQmhZ}_IEJD+RYl1O5&a*o#{ZTl+vvBpllg?Iee%S;UWOXvC$mD=E+_9kkR zI)=G=WfVsafD8s6z`Y{_Z#aI!*^AjVVH|d6xW4`%3@^Z6CicvTGQ?=oM+lWr<}i#2 zNjYV2jqSejNIU%Fi=pzFLk0jI|Bdp*AU38JX5wI5YltgULRsqax85^!6T?^z!Jz5g zg%ff_e@xa<%aJe{urt$^&VrCY(j`u>Ldzf^F?2JVCN5BM;X}~ZB{ z$dmjqNmWElafJ_9wZ|RQSa{NRQCoZEIpC7>i5N7KN4~L}Z2Dotox7g4&!hKA;UHhp zsTC7TY%L!EA{9MPikp8*9*U@^mFYZ-(Djz7{>LaLuj;u_C6PJWMqO;;n7-pSedMMv zveH0RpktlyPsV_Y1IvmUR2qHLt&X-FZg*m~PfKTRJi1Lrs2|!c4J(paR*#P#v>$!j zzp}ozg_pWY7d#!FPRsVb%|u|0v$bt}UPvwH@X2QMuS4n7f$sKB;dG7%VfD=cd0q! zNlxh3ix=6L=7*e+*Z~Whnf?eB`EbP>(ha-dn*@iB*h~%mU~J7G_npQ0U%9e2R);Gn zV-;Ck2Q()o5+mnBQ;_~(ZI7YriQq!d%=Fk7=*t4|F7F&d9W8?cRX)I7#_@Z7%U0aNWLTaqQQ{< z##V-0?48jK|iOM*2x??}EFFr@!tcft~3oyR+(0Oo%X|IvqpCBr&?cjQM{ zFy#O6|6p0LF5ex?>6Ufp)BQi5A1oKv@w?+T-RJ(>68W>ghNZ!}X?L_z?0d8PZ{8X# z5Y`sE0}23Pfd9=TgGIvHK6l8k(4V>dk;#8Y{AYg;OM|sz?r0{s_h@fS|D51IB@x!{ zxI=0_yodaorT!-_tebJi9lX!|b8-GDcGw2~4#$VTM zdwCK4C-RSm1{MU{(AW3jhEB literal 0 HcmV?d00001 -- 2.16.6