This is an automated email from the ASF dual-hosted git repository. lhotari pushed a commit to branch lh-support-webservice-bindaddresses-and-advertised-listeners in repository https://gitbox.apache.org/repos/asf/pulsar.git
commit 10c370b57f410ca40b8673b27cb1b7829e740b3e Author: Lari Hotari <[email protected]> AuthorDate: Thu May 21 09:35:11 2026 +0300 [improve][broker] Fall back to first advertised listener when internalListenerName is blank When the user explicitly sets `internalListenerName=` (blank) and configures `advertisedListeners` with custom listener names, the validator now uses the first parsed listener as the internal listener instead of forcing the constant default `internal`. This preserves the pre-PR behavior for users who relied on "first listener becomes the internal one". Resolution order in `MultipleListenerValidator`: 1. Parse `advertisedListeners`. 2. If `internalListenerName` is blank, fall back to the first listener parsed from `advertisedListeners`; if that map is empty too, fall back to the constant default `internal`. 3. Synthesize / merge the legacy-port URLs as before. Cleanup: - Remove the duplicate fallback that lived inside `parseAdvertisedListeners` (and the now-unused `Optional` import) so the outer method is the single source of truth for the resolution rule. Tests: - `MultipleListenerValidatorTest`: two new cases — `testBlankInternalListenerNameFallsBackToFirstAdvertisedListener` and `testBlankInternalListenerNameFallsBackToDefaultWhenNoAdvertisedListeners`. - `AdvertisedListenersTest`: update to exercise the new fallback by setting `internalListenerName=null` and collapsing three differently-named entries into one listener (`public`) with three schemes. --- .../validator/MultipleListenerValidator.java | 26 +++++++---------- .../validator/MultipleListenerValidatorTest.java | 34 ++++++++++++++++++++++ .../loadbalance/AdvertisedListenersTest.java | 7 +++-- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/validator/MultipleListenerValidator.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/validator/MultipleListenerValidator.java index c300da9682f..73fbef9305d 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/validator/MultipleListenerValidator.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/validator/MultipleListenerValidator.java @@ -23,7 +23,6 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.TreeSet; import org.apache.commons.lang3.StringUtils; @@ -56,13 +55,19 @@ public final class MultipleListenerValidator { * @return the parsed and validated advertised listeners, keyed by listener name. */ public static Map<String, AdvertisedListener> validateAndAnalysisAdvertisedListener(ServiceConfiguration config) { - String internalListenerName = StringUtils.defaultIfBlank(config.getInternalListenerName(), - ServiceConfiguration.DEFAULT_INTERNAL_LISTENER_NAME); + Map<String, AdvertisedListener> result = parseAdvertisedListeners(config); + + // Resolve `internalListenerName` if the user left it blank. For backward compatibility, + // prefer the first entry parsed from `advertisedListeners`; otherwise fall back to the + // constant default ("internal"). The field's own default is also "internal", so this + // branch is only taken when the user explicitly set the property to an empty string. if (StringUtils.isBlank(config.getInternalListenerName())) { - config.setInternalListenerName(internalListenerName); + String fallback = result.isEmpty() + ? ServiceConfiguration.DEFAULT_INTERNAL_LISTENER_NAME + : result.keySet().iterator().next(); + config.setInternalListenerName(fallback); } - - Map<String, AdvertisedListener> result = parseAdvertisedListeners(config); + String internalListenerName = config.getInternalListenerName(); // Merge the legacy-port-derived internal listener URLs into any explicit configuration. // Explicit URLs in `advertisedListeners` take precedence; the legacy-port URLs fill in the @@ -111,7 +116,6 @@ public final class MultipleListenerValidator { if (StringUtils.isBlank(config.getAdvertisedListeners())) { return new LinkedHashMap<>(); } - Optional<String> firstListenerName = Optional.empty(); Map<String, List<String>> listeners = new LinkedHashMap<>(); for (final String str : StringUtils.split(config.getAdvertisedListeners(), ",")) { int index = str.indexOf(":"); @@ -120,18 +124,10 @@ public final class MultipleListenerValidator { + str + " do not contain listener name"); } String listenerName = StringUtils.trim(str.substring(0, index)); - if (firstListenerName.isEmpty()) { - firstListenerName = Optional.of(listenerName); - } String value = StringUtils.trim(str.substring(index + 1)); listeners.computeIfAbsent(listenerName, k -> new ArrayList<>(2)); listeners.get(listenerName).add(value); } - // For backward compatibility, if `internalListenerName` was left blank, default it to the first - // listener parsed from `advertisedListeners`. - if (StringUtils.isBlank(config.getInternalListenerName())) { - config.setInternalListenerName(firstListenerName.get()); - } final Map<String, AdvertisedListener> result = new LinkedHashMap<>(); final Map<String, Set<String>> reverseMappings = new LinkedHashMap<>(); for (final Map.Entry<String, List<String>> entry : listeners.entrySet()) { diff --git a/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/validator/MultipleListenerValidatorTest.java b/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/validator/MultipleListenerValidatorTest.java index 438490d70c1..98fbe6cc1e8 100644 --- a/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/validator/MultipleListenerValidatorTest.java +++ b/pulsar-broker-common/src/test/java/org/apache/pulsar/broker/validator/MultipleListenerValidatorTest.java @@ -75,6 +75,40 @@ public class MultipleListenerValidatorTest { assertEquals("internal", config.getInternalListenerName()); } + @Test + public void testBlankInternalListenerNameFallsBackToFirstAdvertisedListener() { + // Backwards compatibility: if the user explicitly leaves `internalListenerName` blank but + // configures `advertisedListeners` with custom names, the first listener parsed from + // `advertisedListeners` is used as the internal listener — the previous Pulsar behavior. + ServiceConfiguration config = newConfigWithNoPorts(); + config.setInternalListenerName(""); + config.setAdvertisedListeners( + "region1:pulsar://region1.example.com:6650,region2:pulsar://region2.example.com:6650"); + Map<String, AdvertisedListener> listeners = + MultipleListenerValidator.validateAndAnalysisAdvertisedListener(config); + // `region1` is the first declared, so it becomes the internal listener. + assertEquals(config.getInternalListenerName(), "region1"); + // Both region entries are preserved in the result map. + assertEquals(listeners.size(), 2); + assertNotNull(listeners.get("region1")); + assertNotNull(listeners.get("region2")); + } + + @Test + public void testBlankInternalListenerNameFallsBackToDefaultWhenNoAdvertisedListeners() { + // When both `internalListenerName` and `advertisedListeners` are blank, the validator falls + // back to the constant default `"internal"`. + ServiceConfiguration config = newConfigWithNoPorts(); + config.setInternalListenerName(""); + config.setBrokerServicePort(Optional.of(6650)); + config.setWebServicePort(Optional.of(8080)); + Map<String, AdvertisedListener> listeners = + MultipleListenerValidator.validateAndAnalysisAdvertisedListener(config); + assertEquals(config.getInternalListenerName(), ServiceConfiguration.DEFAULT_INTERNAL_LISTENER_NAME); + assertNotNull(listeners.get(ServiceConfiguration.DEFAULT_INTERNAL_LISTENER_NAME), + "the legacy-port-synthesized internal listener should be present"); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testMalformedListener() { ServiceConfiguration config = newConfigWithNoPorts(); diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/loadbalance/AdvertisedListenersTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/loadbalance/AdvertisedListenersTest.java index 64c0ee3f47a..2b6137ce221 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/loadbalance/AdvertisedListenersTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/loadbalance/AdvertisedListenersTest.java @@ -73,10 +73,13 @@ public class AdvertisedListenersTest extends MultiBrokerBaseTest { // Use invalid domain name as identifier and instead make sure the advertised listeners work as intended conf.setAdvertisedAddress(advertisedAddress); + // let it be detected from advertised listeners + conf.setInternalListenerName(null); + // set advertised listeners conf.setAdvertisedListeners( "public:pulsar://localhost:" + pulsarPort - + ",public_http:http://localhost:" + httpPort - + ",public_https:https://localhost:" + httpsPort); + + ",public:http://localhost:" + httpPort + + ",public:https://localhost:" + httpsPort); conf.setBrokerServicePort(Optional.of(pulsarPort)); conf.setWebServicePort(Optional.of(httpPort)); }
