50
50
import org .springframework .beans .factory .annotation .Autowired ;
51
51
import org .springframework .context .annotation .Bean ;
52
52
import org .springframework .context .annotation .Configuration ;
53
+ import org .springframework .messaging .MessageHeaders ;
53
54
import org .springframework .messaging .handler .annotation .Header ;
54
55
import org .springframework .pulsar .annotation .EnablePulsar ;
55
56
import org .springframework .pulsar .core .DefaultPulsarProducerFactory ;
68
69
import org .springframework .pulsar .reactive .core .ReactiveMessageConsumerBuilderCustomizer ;
69
70
import org .springframework .pulsar .reactive .core .ReactivePulsarConsumerFactory ;
70
71
import org .springframework .pulsar .reactive .listener .ReactivePulsarListenerTests .BasicListenersTestCases .BasicListenersTestCasesConfig ;
72
+ import org .springframework .pulsar .reactive .listener .ReactivePulsarListenerTests .PulsarHeadersCustomObjectMapperTest .PulsarHeadersCustomObjectMapperTestConfig ;
71
73
import org .springframework .pulsar .reactive .listener .ReactivePulsarListenerTests .PulsarHeadersTest .PulsarListenerWithHeadersConfig ;
72
74
import org .springframework .pulsar .reactive .listener .ReactivePulsarListenerTests .StreamingListenerTestCases .StreamingListenerTestCasesConfig ;
73
75
import org .springframework .pulsar .reactive .listener .ReactivePulsarListenerTests .SubscriptionTypeTests .WithDefaultType .WithDefaultTypeConfig ;
74
76
import org .springframework .pulsar .reactive .listener .ReactivePulsarListenerTests .SubscriptionTypeTests .WithSpecificTypes .WithSpecificTypesConfig ;
75
77
import org .springframework .pulsar .reactive .support .MessageUtils ;
76
78
import org .springframework .pulsar .support .PulsarHeaders ;
79
+ import org .springframework .pulsar .support .header .JsonPulsarHeaderMapper ;
77
80
import org .springframework .pulsar .test .model .UserPojo ;
78
81
import org .springframework .pulsar .test .model .UserRecord ;
82
+ import org .springframework .pulsar .test .model .json .UserRecordDeserializer ;
79
83
import org .springframework .test .context .ContextConfiguration ;
80
84
import org .springframework .test .util .ReflectionTestUtils ;
81
85
import org .springframework .util .ObjectUtils ;
82
86
87
+ import com .fasterxml .jackson .databind .ObjectMapper ;
88
+ import com .fasterxml .jackson .databind .module .SimpleModule ;
83
89
import reactor .core .publisher .Flux ;
84
90
import reactor .core .publisher .Mono ;
85
91
@@ -541,40 +547,67 @@ Mono<Void> listenString(String ignored) {
541
547
class PulsarHeadersTest {
542
548
543
549
static CountDownLatch simpleListenerLatch = new CountDownLatch (1 );
550
+ static CountDownLatch simpleListenerPojoLatch = new CountDownLatch (1 );
544
551
static CountDownLatch pulsarMessageListenerLatch = new CountDownLatch (1 );
545
552
static CountDownLatch springMessagingMessageListenerLatch = new CountDownLatch (1 );
546
553
547
554
static AtomicReference <String > capturedData = new AtomicReference <>();
548
555
static AtomicReference <MessageId > messageId = new AtomicReference <>();
549
556
static AtomicReference <String > topicName = new AtomicReference <>();
550
557
static AtomicReference <String > fooValue = new AtomicReference <>();
558
+ static AtomicReference <Object > pojoValue = new AtomicReference <>();
551
559
static AtomicReference <byte []> rawData = new AtomicReference <>();
552
560
553
561
@ Test
554
562
void simpleListenerWithHeaders () throws Exception {
555
- MessageId messageId = pulsarTemplate .newMessage ("hello-simple-listener" )
563
+ var topic = "rplt-simpleListenerWithHeaders" ;
564
+ var msg = "hello-%s" .formatted (topic );
565
+ MessageId messageId = pulsarTemplate .newMessage (msg )
556
566
.withMessageCustomizer (messageBuilder -> messageBuilder .property ("foo" , "simpleListenerWithHeaders" ))
557
- .withTopic ("simpleListenerWithHeaders" )
567
+ .withTopic (topic )
558
568
.send ();
559
569
assertThat (simpleListenerLatch .await (10 , TimeUnit .SECONDS )).isTrue ();
560
- assertThat (capturedData .get ()).isEqualTo ("hello-simple-listener" );
561
- assertThat (PulsarHeadersTest .messageId .get ()).isEqualTo (messageId );
562
- assertThat (topicName .get ()).isEqualTo ("persistent://public/default/simpleListenerWithHeaders" );
563
- assertThat (fooValue .get ()).isEqualTo ("simpleListenerWithHeaders" );
564
- assertThat (rawData .get ()).isEqualTo ("hello-simple-listener" .getBytes (StandardCharsets .UTF_8 ));
570
+ assertThat (PulsarHeadersTest .messageId ).hasValue (messageId );
571
+ assertThat (topicName ).hasValue ("persistent://public/default/%s" .formatted (topic ));
572
+ assertThat (capturedData ).hasValue (msg );
573
+ assertThat (rawData ).hasValue (msg .getBytes (StandardCharsets .UTF_8 ));
574
+ assertThat (fooValue ).hasValue ("simpleListenerWithHeaders" );
575
+ }
576
+
577
+ @ Test
578
+ void simpleListenerWithPojoHeader () throws Exception {
579
+ var topic = "rplt-simpleListenerWithPojoHeader" ;
580
+ var msg = "hello-%s" .formatted (topic );
581
+ // In order to send complex headers (pojo) must manually map and set each
582
+ // header as follows
583
+ var user = new UserRecord ("that" , 100 );
584
+ var headers = new HashMap <String , Object >();
585
+ headers .put ("user" , user );
586
+ var headerMapper = JsonPulsarHeaderMapper .builder ().build ();
587
+ var mappedHeaders = headerMapper .toPulsarHeaders (new MessageHeaders (headers ));
588
+ MessageId messageId = pulsarTemplate .newMessage (msg )
589
+ .withMessageCustomizer (messageBuilder -> mappedHeaders .forEach (messageBuilder ::property ))
590
+ .withTopic (topic )
591
+ .send ();
592
+ assertThat (simpleListenerPojoLatch .await (10 , TimeUnit .SECONDS )).isTrue ();
593
+ assertThat (PulsarHeadersTest .messageId ).hasValue (messageId );
594
+ assertThat (topicName ).hasValue ("persistent://public/default/%s" .formatted (topic ));
595
+ assertThat (pojoValue ).hasValue (user );
596
+ assertThat (capturedData ).hasValue (msg );
597
+ assertThat (rawData ).hasValue (msg .getBytes (StandardCharsets .UTF_8 ));
565
598
}
566
599
567
600
@ Test
568
601
void pulsarMessageListenerWithHeaders () throws Exception {
569
602
MessageId messageId = pulsarTemplate .newMessage ("hello-pulsar-message-listener" )
570
603
.withMessageCustomizer (
571
604
messageBuilder -> messageBuilder .property ("foo" , "pulsarMessageListenerWithHeaders" ))
572
- .withTopic ("pulsarMessageListenerWithHeaders" )
605
+ .withTopic ("rplt- pulsarMessageListenerWithHeaders" )
573
606
.send ();
574
607
assertThat (pulsarMessageListenerLatch .await (10 , TimeUnit .SECONDS )).isTrue ();
575
608
assertThat (capturedData .get ()).isEqualTo ("hello-pulsar-message-listener" );
576
609
assertThat (PulsarHeadersTest .messageId .get ()).isEqualTo (messageId );
577
- assertThat (topicName .get ()).isEqualTo ("persistent://public/default/pulsarMessageListenerWithHeaders" );
610
+ assertThat (topicName .get ()).isEqualTo ("persistent://public/default/rplt- pulsarMessageListenerWithHeaders" );
578
611
assertThat (fooValue .get ()).isEqualTo ("pulsarMessageListenerWithHeaders" );
579
612
assertThat (rawData .get ()).isEqualTo ("hello-pulsar-message-listener" .getBytes (StandardCharsets .UTF_8 ));
580
613
}
@@ -584,13 +617,13 @@ void springMessagingMessageListenerWithHeaders() throws Exception {
584
617
MessageId messageId = pulsarTemplate .newMessage ("hello-spring-messaging-message-listener" )
585
618
.withMessageCustomizer (
586
619
messageBuilder -> messageBuilder .property ("foo" , "springMessagingMessageListenerWithHeaders" ))
587
- .withTopic ("springMessagingMessageListenerWithHeaders" )
620
+ .withTopic ("rplt- springMessagingMessageListenerWithHeaders" )
588
621
.send ();
589
622
assertThat (springMessagingMessageListenerLatch .await (10 , TimeUnit .SECONDS )).isTrue ();
590
623
assertThat (capturedData .get ()).isEqualTo ("hello-spring-messaging-message-listener" );
591
624
assertThat (PulsarHeadersTest .messageId .get ()).isEqualTo (messageId );
592
625
assertThat (topicName .get ())
593
- .isEqualTo ("persistent://public/default/springMessagingMessageListenerWithHeaders" );
626
+ .isEqualTo ("persistent://public/default/rplt- springMessagingMessageListenerWithHeaders" );
594
627
assertThat (fooValue .get ()).isEqualTo ("springMessagingMessageListenerWithHeaders" );
595
628
assertThat (rawData .get ())
596
629
.isEqualTo ("hello-spring-messaging-message-listener" .getBytes (StandardCharsets .UTF_8 ));
@@ -600,8 +633,9 @@ void springMessagingMessageListenerWithHeaders() throws Exception {
600
633
@ Configuration
601
634
static class PulsarListenerWithHeadersConfig {
602
635
603
- @ ReactivePulsarListener (subscriptionName = "simple-listener-with-headers-sub" ,
604
- topics = "simpleListenerWithHeaders" , consumerCustomizer = "subscriptionInitialPositionEarliest" )
636
+ @ ReactivePulsarListener (topics = "rplt-simpleListenerWithHeaders" ,
637
+ subscriptionName = "rplt-simple-listener-with-headers-sub" ,
638
+ consumerCustomizer = "subscriptionInitialPositionEarliest" )
605
639
Mono <Void > simpleListenerWithHeaders (String data , @ Header (PulsarHeaders .MESSAGE_ID ) MessageId messageId ,
606
640
@ Header (PulsarHeaders .TOPIC_NAME ) String topicName , @ Header (PulsarHeaders .RAW_DATA ) byte [] rawData ,
607
641
@ Header ("foo" ) String foo ) {
@@ -614,8 +648,23 @@ Mono<Void> simpleListenerWithHeaders(String data, @Header(PulsarHeaders.MESSAGE_
614
648
return Mono .empty ();
615
649
}
616
650
617
- @ ReactivePulsarListener (subscriptionName = "pulsar-message-listener-with-headers-sub" ,
618
- topics = "pulsarMessageListenerWithHeaders" ,
651
+ @ ReactivePulsarListener (topics = "rplt-simpleListenerWithPojoHeader" ,
652
+ subscriptionName = "simpleListenerWithPojoHeader-sub" ,
653
+ consumerCustomizer = "subscriptionInitialPositionEarliest" )
654
+ Mono <Void > simpleListenerWithPojoHeader (String data , @ Header (PulsarHeaders .MESSAGE_ID ) MessageId messageId ,
655
+ @ Header (PulsarHeaders .TOPIC_NAME ) String topicName , @ Header (PulsarHeaders .RAW_DATA ) byte [] rawData ,
656
+ @ Header ("user" ) UserRecord user ) {
657
+ capturedData .set (data );
658
+ PulsarHeadersTest .messageId .set (messageId );
659
+ PulsarHeadersTest .topicName .set (topicName );
660
+ pojoValue .set (user );
661
+ PulsarHeadersTest .rawData .set (rawData );
662
+ simpleListenerPojoLatch .countDown ();
663
+ return Mono .empty ();
664
+ }
665
+
666
+ @ ReactivePulsarListener (subscriptionName = "rplt-pulsar-message-listener-with-headers-sub" ,
667
+ topics = "rplt-pulsarMessageListenerWithHeaders" ,
619
668
consumerCustomizer = "subscriptionInitialPositionEarliest" )
620
669
Mono <Void > pulsarMessageListenerWithHeaders (Message <String > data ,
621
670
@ Header (PulsarHeaders .MESSAGE_ID ) MessageId messageId ,
@@ -630,8 +679,8 @@ Mono<Void> pulsarMessageListenerWithHeaders(Message<String> data,
630
679
return Mono .empty ();
631
680
}
632
681
633
- @ ReactivePulsarListener (subscriptionName = "pulsar-message-listener-with-headers-sub" ,
634
- topics = "springMessagingMessageListenerWithHeaders" ,
682
+ @ ReactivePulsarListener (subscriptionName = "rplt- pulsar-message-listener-with-headers-sub" ,
683
+ topics = "rplt- springMessagingMessageListenerWithHeaders" ,
635
684
consumerCustomizer = "subscriptionInitialPositionEarliest" )
636
685
Mono <Void > springMessagingMessageListenerWithHeaders (org .springframework .messaging .Message <String > data ,
637
686
@ Header (PulsarHeaders .MESSAGE_ID ) MessageId messageId ,
@@ -650,6 +699,62 @@ Mono<Void> springMessagingMessageListenerWithHeaders(org.springframework.messagi
650
699
651
700
}
652
701
702
+ @ Nested
703
+ @ ContextConfiguration (classes = PulsarHeadersCustomObjectMapperTestConfig .class )
704
+ class PulsarHeadersCustomObjectMapperTest {
705
+
706
+ private static final String TOPIC = "rplt-listenerWithPojoHeader-custom" ;
707
+
708
+ private static final CountDownLatch listenerLatch = new CountDownLatch (1 );
709
+
710
+ private static UserRecord userPassedIntoListener ;
711
+
712
+ @ Test
713
+ void whenPulsarHeaderObjectMapperIsDefinedThenItIsUsedToDeserializeHeaders () throws Exception {
714
+ var msg = "hello-%s" .formatted (TOPIC );
715
+ // In order to send complex headers (pojo) must manually map and set each
716
+ // header as follows
717
+ var user = new UserRecord ("that" , 100 );
718
+ var headers = new HashMap <String , Object >();
719
+ headers .put ("user" , user );
720
+ var headerMapper = JsonPulsarHeaderMapper .builder ().build ();
721
+ var mappedHeaders = headerMapper .toPulsarHeaders (new MessageHeaders (headers ));
722
+ pulsarTemplate .newMessage (msg )
723
+ .withMessageCustomizer (messageBuilder -> mappedHeaders .forEach (messageBuilder ::property ))
724
+ .withTopic (TOPIC )
725
+ .send ();
726
+ // Custom deser adds suffix to name and bumps age + 5
727
+ var expectedUser = new UserRecord (user .name () + "-deser" , user .age () + 5 );
728
+ assertThat (listenerLatch .await (10 , TimeUnit .SECONDS )).isTrue ();
729
+ assertThat (userPassedIntoListener ).isEqualTo (expectedUser );
730
+ }
731
+
732
+ @ Configuration (proxyBeanMethods = false )
733
+ static class PulsarHeadersCustomObjectMapperTestConfig {
734
+
735
+ @ Bean (name = "pulsarHeaderObjectMapper" )
736
+ ObjectMapper customObjectMapper () {
737
+ var objectMapper = new ObjectMapper ();
738
+ var module = new SimpleModule ();
739
+ module .addDeserializer (UserRecord .class , new UserRecordDeserializer ());
740
+ objectMapper .registerModule (module );
741
+ return objectMapper ;
742
+ }
743
+
744
+ @ ReactivePulsarListener (topics = TOPIC , subscriptionName = TOPIC + "-sub" ,
745
+ consumerCustomizer = "subscriptionInitialPositionEarliest" )
746
+ Mono <Void > listenerWithPojoHeader (String data , @ Header (PulsarHeaders .MESSAGE_ID ) MessageId messageId ,
747
+ @ Header (PulsarHeaders .TOPIC_NAME ) String topicName , @ Header (PulsarHeaders .RAW_DATA ) byte [] rawData ,
748
+ @ Header ("user" ) UserRecord user ) {
749
+ userPassedIntoListener = user ;
750
+ listenerLatch .countDown ();
751
+ return Mono .empty ();
752
+ }
753
+
754
+ }
755
+
756
+ }
757
+
653
758
@ Nested
654
759
@ ContextConfiguration (classes = PulsarListenerConcurrencyTestCases .TestPulsarListenersForConcurrency .class )
655
760
class PulsarListenerConcurrencyTestCases {
0 commit comments