This is an automated email from the ASF dual-hosted git repository.

mmarshall pushed a commit to branch branch-2.11
in repository https://gitbox.apache.org/repos/asf/pulsar.git


The following commit(s) were added to refs/heads/branch-2.11 by this push:
     new 5dd729efb39 [fix][bookie] Correctly handle list configuration values 
(#17661)
5dd729efb39 is described below

commit 5dd729efb39109503347c97f0140ccf7227cfb19
Author: Michael Marshall <[email protected]>
AuthorDate: Tue Oct 11 13:19:12 2022 -0700

    [fix][bookie] Correctly handle list configuration values (#17661)
    
    * [fix][bookie] Correctly handle list configuration values
    
    * Remove unused import
    
    ### Motivation
    
    When the `metadataServiceUri` or the `zkServers` configuration for 
`BookieRackAffinityMapping` is provided as a comma delimited list, it is 
automatically parsed into an ArrayList by the configuration class because the 
Bookkeeper configuration class relies on the defaults in the 
`org.apache.commons.configuration.AbstractConfiguration` class. Here is a 
sample error:
    
    ```
    2022-07-29T19:25:43,437+0000 [main] ERROR 
org.apache.bookkeeper.client.RackawareEnsemblePlacementPolicyImpl - Failed to 
initialize DNS Resolver 
org.apache.pulsar.zookeeper.ZkBookieRackAffinityMapping, used default subnet 
resolver
    java.lang.ClassCastException: class java.util.ArrayList cannot be cast to 
class java.lang.String (java.util.ArrayList and java.lang.String are in module 
java.base of loader 'bootstrap')
    ```
    
    Also, see #6349 and https://github.com/apache/pulsar/issues/6343 for 
context.
    
    ### Modifications
    
    * Move the `castToString` method out to a shared class.
    * Use the `castToString` method to safely get the configuration value.
    
    ### Verifying this change
    
    This PR includes a test.
    
    ### Documentation
    
    - [x] `doc-not-needed`
    
    (cherry picked from commit 69deb1f6e440578234c142bf4a962bc2c7ace5e5)
---
 .../rackawareness/BookieRackAffinityMapping.java   |  4 +-
 .../rackawareness/ConfigurationStringUtil.java     | 48 ++++++++++++++++++++++
 .../IsolatedBookieEnsemblePlacementPolicy.java     | 29 ++++---------
 .../BookieRackAffinityMappingTest.java             | 12 ++++++
 4 files changed, 70 insertions(+), 23 deletions(-)

diff --git 
a/pulsar-broker-common/src/main/java/org/apache/pulsar/bookie/rackawareness/BookieRackAffinityMapping.java
 
b/pulsar-broker-common/src/main/java/org/apache/pulsar/bookie/rackawareness/BookieRackAffinityMapping.java
index c0c29637114..53e1a683c55 100644
--- 
a/pulsar-broker-common/src/main/java/org/apache/pulsar/bookie/rackawareness/BookieRackAffinityMapping.java
+++ 
b/pulsar-broker-common/src/main/java/org/apache/pulsar/bookie/rackawareness/BookieRackAffinityMapping.java
@@ -77,7 +77,7 @@ public class BookieRackAffinityMapping extends 
AbstractDNSToSwitchMapping
             store = (MetadataStore) storeProperty;
         } else {
             String url;
-            String metadataServiceUri = (String) 
conf.getProperty("metadataServiceUri");
+            String metadataServiceUri = 
ConfigurationStringUtil.castToString(conf.getProperty("metadataServiceUri"));
             if (StringUtils.isNotBlank(metadataServiceUri)) {
                 try {
                     url = 
metadataServiceUri.replaceFirst(METADATA_STORE_SCHEME + ":", "")
@@ -86,7 +86,7 @@ public class BookieRackAffinityMapping extends 
AbstractDNSToSwitchMapping
                     throw new MetadataException(Code.METADATA_SERVICE_ERROR, 
e);
                 }
             } else {
-                String zkServers = (String) conf.getProperty("zkServers");
+                String zkServers = 
ConfigurationStringUtil.castToString(conf.getProperty("zkServers"));
                 if (StringUtils.isBlank(zkServers)) {
                     String errorMsg = String.format("Neither %s configuration 
set in the BK client configuration nor "
                             + "metadataServiceUri/zkServers set in bk server 
configuration", METADATA_STORE_INSTANCE);
diff --git 
a/pulsar-broker-common/src/main/java/org/apache/pulsar/bookie/rackawareness/ConfigurationStringUtil.java
 
b/pulsar-broker-common/src/main/java/org/apache/pulsar/bookie/rackawareness/ConfigurationStringUtil.java
new file mode 100644
index 00000000000..99736b4f2f4
--- /dev/null
+++ 
b/pulsar-broker-common/src/main/java/org/apache/pulsar/bookie/rackawareness/ConfigurationStringUtil.java
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+package org.apache.pulsar.bookie.rackawareness;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ConfigurationStringUtil {
+
+    /**
+     * The Bookkeeper configuration class converts comma delimited strings to 
ArrayLists, by default. Use
+     * this method to ensure a configuration value is a {@link String}.
+     * @param obj - object to convert to a string
+     * @return The object's conversion to a string where Lists map to a comma 
delimited list.
+     */
+    static String castToString(Object obj) {
+        if (null == obj) {
+            return "";
+        }
+        if (obj instanceof List<?>) {
+            List<String> result = new ArrayList<>();
+            for (Object o : (List<?>) obj) {
+                result.add((String) o);
+            }
+            return String.join(",", result);
+        } else {
+            return obj.toString();
+        }
+    }
+
+}
diff --git 
a/pulsar-broker-common/src/main/java/org/apache/pulsar/bookie/rackawareness/IsolatedBookieEnsemblePlacementPolicy.java
 
b/pulsar-broker-common/src/main/java/org/apache/pulsar/bookie/rackawareness/IsolatedBookieEnsemblePlacementPolicy.java
index b8c288535bc..9e6affbb7ef 100644
--- 
a/pulsar-broker-common/src/main/java/org/apache/pulsar/bookie/rackawareness/IsolatedBookieEnsemblePlacementPolicy.java
+++ 
b/pulsar-broker-common/src/main/java/org/apache/pulsar/bookie/rackawareness/IsolatedBookieEnsemblePlacementPolicy.java
@@ -20,7 +20,6 @@ package org.apache.pulsar.bookie.rackawareness;
 
 import static 
org.apache.pulsar.bookie.rackawareness.BookieRackAffinityMapping.METADATA_STORE_INSTANCE;
 import io.netty.util.HashedWheelTimer;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
@@ -80,7 +79,8 @@ public class IsolatedBookieEnsemblePlacementPolicy extends 
RackawareEnsemblePlac
         Set<String> primaryIsolationGroups = new HashSet<>();
         Set<String> secondaryIsolationGroups = new HashSet<>();
         if (conf.getProperty(ISOLATION_BOOKIE_GROUPS) != null) {
-            String isolationGroupsString = 
castToString(conf.getProperty(ISOLATION_BOOKIE_GROUPS));
+            String isolationGroupsString = ConfigurationStringUtil
+                    .castToString(conf.getProperty(ISOLATION_BOOKIE_GROUPS));
             if (!isolationGroupsString.isEmpty()) {
                 for (String isolationGroup : isolationGroupsString.split(",")) 
{
                     primaryIsolationGroups.add(isolationGroup);
@@ -91,7 +91,8 @@ public class IsolatedBookieEnsemblePlacementPolicy extends 
RackawareEnsemblePlac
             
bookieMappingCache.get(BookieRackAffinityMapping.BOOKIE_INFO_ROOT_PATH).join();
         }
         if (conf.getProperty(SECONDARY_ISOLATION_BOOKIE_GROUPS) != null) {
-            String secondaryIsolationGroupsString = 
castToString(conf.getProperty(SECONDARY_ISOLATION_BOOKIE_GROUPS));
+            String secondaryIsolationGroupsString = ConfigurationStringUtil
+                    
.castToString(conf.getProperty(SECONDARY_ISOLATION_BOOKIE_GROUPS));
             if (!secondaryIsolationGroupsString.isEmpty()) {
                 for (String isolationGroup : 
secondaryIsolationGroupsString.split(",")) {
                     secondaryIsolationGroups.add(isolationGroup);
@@ -102,21 +103,6 @@ public class IsolatedBookieEnsemblePlacementPolicy extends 
RackawareEnsemblePlac
         return super.initialize(conf, optionalDnsResolver, timer, 
featureProvider, statsLogger, bookieAddressResolver);
     }
 
-    private static String castToString(Object obj) {
-        if (null == obj) {
-            return "";
-        }
-        if (obj instanceof List<?>) {
-            List<String> result = new ArrayList<>();
-            for (Object o : (List<?>) obj) {
-                result.add((String) o);
-            }
-            return String.join(",", result);
-        } else {
-            return obj.toString();
-        }
-    }
-
     @Override
     public PlacementResult<List<BookieId>> newEnsemble(int ensembleSize, int 
writeQuorumSize, int ackQuorumSize,
             Map<String, byte[]> customMetadata, Set<BookieId> excludeBookies)
@@ -181,9 +167,10 @@ public class IsolatedBookieEnsemblePlacementPolicy extends 
RackawareEnsemblePlac
         String className = 
IsolatedBookieEnsemblePlacementPolicy.class.getName();
         if 
(ensemblePlacementPolicyConfig.getPolicyClass().getName().equals(className)) {
             Map<String, Object> properties = 
ensemblePlacementPolicyConfig.getProperties();
-            String primaryIsolationGroupString = 
castToString(properties.getOrDefault(ISOLATION_BOOKIE_GROUPS, ""));
-            String secondaryIsolationGroupString =
-                    
castToString(properties.getOrDefault(SECONDARY_ISOLATION_BOOKIE_GROUPS, ""));
+            String primaryIsolationGroupString = ConfigurationStringUtil
+                    
.castToString(properties.getOrDefault(ISOLATION_BOOKIE_GROUPS, ""));
+            String secondaryIsolationGroupString = ConfigurationStringUtil
+                    
.castToString(properties.getOrDefault(SECONDARY_ISOLATION_BOOKIE_GROUPS, ""));
             if (!primaryIsolationGroupString.isEmpty()) {
                 pair.setLeft(new 
HashSet(Arrays.asList(primaryIsolationGroupString.split(","))));
             } else {
diff --git 
a/pulsar-broker-common/src/test/java/org/apache/pulsar/bookie/rackawareness/BookieRackAffinityMappingTest.java
 
b/pulsar-broker-common/src/test/java/org/apache/pulsar/bookie/rackawareness/BookieRackAffinityMappingTest.java
index 4377916ace2..8cbd0ebe9ca 100644
--- 
a/pulsar-broker-common/src/test/java/org/apache/pulsar/bookie/rackawareness/BookieRackAffinityMappingTest.java
+++ 
b/pulsar-broker-common/src/test/java/org/apache/pulsar/bookie/rackawareness/BookieRackAffinityMappingTest.java
@@ -82,6 +82,18 @@ public class BookieRackAffinityMappingTest {
         assertNull(racks.get(2));
     }
 
+    @Test
+    public void testMultipleMetadataServiceUris() {
+        BookieRackAffinityMapping mapping1 = new BookieRackAffinityMapping();
+        ClientConfiguration bkClientConf1 = new ClientConfiguration();
+        bkClientConf1.setProperty("metadataServiceUri", 
"memory:local,memory:local");
+        bkClientConf1.setProperty("zkTimeout", "100000");
+
+        
mapping1.setBookieAddressResolver(BookieSocketAddress.LEGACY_BOOKIEID_RESOLVER);
+        // This previously threw an exception when the metadataServiceUri was 
a comma delimited list.
+        mapping1.setConf(bkClientConf1);
+    }
+
     @Test
     public void testInvalidRackName() {
         String data = "{\"group1\": {\"" + BOOKIE1

Reply via email to