@@ -554,4 +554,74 @@ public void testSendHeartbeatToAllBrokerConcurrently() {
554554 fail ("failed: " + e .getMessage ());
555555 }
556556 }
557+
558+ @ Test
559+ public void testFindBrokerVersionWhenBrokerNameNotExist () throws Exception {
560+ ConcurrentMap <String , ConcurrentHashMap <String , Integer >> brokerVersionTable =
561+ (ConcurrentMap <String , ConcurrentHashMap <String , Integer >>) FieldUtils .readDeclaredField (
562+ mqClientInstance , "brokerVersionTable" , true );
563+ brokerVersionTable .clear ();
564+
565+ FindBrokerResult result = mqClientInstance .findBrokerAddressInSubscribe ("nonExistentBroker" , 0 , false );
566+ assertNull (result );
567+ }
568+
569+ @ Test
570+ public void testFindBrokerVersionWhenBrokerAddrNotExist () throws Exception {
571+ ConcurrentMap <String , ConcurrentHashMap <String , Integer >> brokerVersionTable =
572+ (ConcurrentMap <String , ConcurrentHashMap <String , Integer >>) FieldUtils .readDeclaredField (
573+ mqClientInstance , "brokerVersionTable" , true );
574+ brokerVersionTable .clear ();
575+ brokerVersionTable .put ("broker-a" , new ConcurrentHashMap <>());
576+
577+ FindBrokerResult result = mqClientInstance .findBrokerAddressInSubscribe ("broker-a" , 0 , false );
578+ assertNull (result );
579+ }
580+
581+ @ Test
582+ public void testFindBrokerVersionConcurrentRemoval () throws Exception {
583+ ConcurrentMap <String , ConcurrentHashMap <String , Integer >> brokerVersionTable =
584+ (ConcurrentMap <String , ConcurrentHashMap <String , Integer >>) FieldUtils .readDeclaredField (
585+ mqClientInstance , "brokerVersionTable" , true );
586+ brokerVersionTable .clear ();
587+
588+ String brokerName = "broker-a" ;
589+ ConcurrentHashMap <String , Integer > versionMap = new ConcurrentHashMap <>(4 );
590+ versionMap .put ("127.0.0.1:10911" , 401 );
591+ brokerVersionTable .put (brokerName , versionMap );
592+
593+ // Simulate concurrent removal between containsKey and get
594+ // With the fix using local variable caching, this should not cause NPE
595+ brokerVersionTable .remove (brokerName );
596+
597+ // findBrokerVersion is private, but it's called via findBrokerAddressInSubscribe
598+ // The key test is that no NPE is thrown when brokerName is absent
599+ FindBrokerResult result = mqClientInstance .findBrokerAddressInSubscribe (brokerName , 0 , false );
600+ assertNull (result );
601+ }
602+
603+ @ Test
604+ public void testBrokerVersionTableComputeIfAbsent () throws Exception {
605+ ConcurrentMap <String , ConcurrentHashMap <String , Integer >> brokerVersionTable =
606+ (ConcurrentMap <String , ConcurrentHashMap <String , Integer >>) FieldUtils .readDeclaredField (
607+ mqClientInstance , "brokerVersionTable" , true );
608+ brokerVersionTable .clear ();
609+
610+ String brokerName = "broker-a" ;
611+
612+ // Use computeIfAbsent as the fix does
613+ ConcurrentHashMap <String , Integer > inner = brokerVersionTable .computeIfAbsent (
614+ brokerName , k -> new ConcurrentHashMap <>(4 ));
615+ inner .put ("127.0.0.1:10911" , 401 );
616+
617+ // Verify the entry was created atomically
618+ assertNotNull (brokerVersionTable .get (brokerName ));
619+ assertEquals (Integer .valueOf (401 ), brokerVersionTable .get (brokerName ).get ("127.0.0.1:10911" ));
620+
621+ // Calling computeIfAbsent again should return the same map, not create a new one
622+ ConcurrentHashMap <String , Integer > inner2 = brokerVersionTable .computeIfAbsent (
623+ brokerName , k -> new ConcurrentHashMap <>(4 ));
624+ assertEquals (inner , inner2 );
625+ assertEquals (Integer .valueOf (401 ), inner2 .get ("127.0.0.1:10911" ));
626+ }
557627}
0 commit comments