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

szetszwo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ratis.git


The following commit(s) were added to refs/heads/master by this push:
     new 862684066 RATIS-2561. Refactor MetricRegistriesLoader. (#1483)
862684066 is described below

commit 862684066a5b6ac5ba49e3a10c0b0f01e488b775
Author: Tsz-Wo Nicholas Sze <[email protected]>
AuthorDate: Mon Jun 15 13:31:59 2026 -0700

    RATIS-2561. Refactor MetricRegistriesLoader. (#1483)
---
 .../java/org/apache/ratis/util/ServiceUtils.java   | 74 ++++++++++++++++++
 .../org/apache/ratis/metrics/MetricRegistries.java |  4 +-
 .../ratis/metrics/MetricRegistriesLoader.java      | 90 ----------------------
 .../dropwizard3/TestLoadDm3MetricRegistries.java   |  5 +-
 .../apache/ratis/metrics/TestMetricRegistries.java | 36 +++++----
 5 files changed, 101 insertions(+), 108 deletions(-)

diff --git a/ratis-common/src/main/java/org/apache/ratis/util/ServiceUtils.java 
b/ratis-common/src/main/java/org/apache/ratis/util/ServiceUtils.java
new file mode 100644
index 000000000..bb772b516
--- /dev/null
+++ b/ratis-common/src/main/java/org/apache/ratis/util/ServiceUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.ratis.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ServiceLoader;
+import java.util.function.Supplier;
+
+/** Utility methods for {@link ServiceLoader}. */
+public final class ServiceUtils {
+  private static final Logger LOG = 
LoggerFactory.getLogger(ServiceUtils.class);
+
+  public static <T> T load(Class<T> serviceInterface, String defaultClass) {
+    final Supplier<T> defaultInstance = () -> {
+      try {
+        return 
ReflectionUtils.newInstance(Class.forName(defaultClass).asSubclass(serviceInterface));
+      } catch (ClassNotFoundException e) {
+        throw new IllegalStateException("Failed to load " + defaultClass, e);
+      }
+    };
+    final List<T> providers = loadServiceProviders(serviceInterface);
+    return load(serviceInterface, defaultInstance, providers);
+  }
+
+  public static <T> T load(Class<T> serviceInterface, Supplier<T> 
defaultInstance, List<T> loaded) {
+    if (loaded.isEmpty()) {
+      return defaultInstance.get();
+    }
+
+    final T first = loaded.get(0);
+    if (loaded.size() == 1) {
+      LOG.debug("Loaded {}", first.getClass());
+    } else {
+      // Warn that there are more than one services configured.
+      final String classes = loaded.stream()
+          .map(Object::getClass)
+          .map(Class::getName)
+          .reduce((a, b) -> a + ", " + b).orElse("");
+      LOG.warn("Loaded {} duplicated services of {}: {}", loaded.size(), 
serviceInterface.getSimpleName(), classes);
+      LOG.warn("Using the first: {}", first.getClass());
+    }
+    return first;
+  }
+
+  private static <T> List<T> loadServiceProviders(Class<T> serviceInterface) {
+    final ServiceLoader<T> loader = ServiceLoader.load(serviceInterface, 
serviceInterface.getClassLoader());
+    final List<T> loaded = new ArrayList<>();
+    for (T impl : loader) {
+      loaded.add(impl);
+    }
+    return loaded;
+  }
+
+  private ServiceUtils() {}
+}
diff --git 
a/ratis-metrics-api/src/main/java/org/apache/ratis/metrics/MetricRegistries.java
 
b/ratis-metrics-api/src/main/java/org/apache/ratis/metrics/MetricRegistries.java
index 3a11213bb..b05afe7cc 100644
--- 
a/ratis-metrics-api/src/main/java/org/apache/ratis/metrics/MetricRegistries.java
+++ 
b/ratis-metrics-api/src/main/java/org/apache/ratis/metrics/MetricRegistries.java
@@ -18,6 +18,7 @@
 
 package org.apache.ratis.metrics;
 
+import org.apache.ratis.util.ServiceUtils;
 import org.apache.ratis.util.TimeDuration;
 
 import java.util.Collection;
@@ -30,9 +31,10 @@ import java.util.function.Consumer;
  * ref-counting of MetricRegistry's via create() and remove() methods.
  */
 public abstract class MetricRegistries {
+  static final String DEFAULT_CLASS = 
"org.apache.ratis.metrics.impl.MetricRegistriesImpl";
 
   private static final class LazyHolder {
-    private static final MetricRegistries GLOBAL = 
MetricRegistriesLoader.load();
+    private static final MetricRegistries GLOBAL = 
ServiceUtils.load(MetricRegistries.class, DEFAULT_CLASS);
   }
 
   /**
diff --git 
a/ratis-metrics-api/src/main/java/org/apache/ratis/metrics/MetricRegistriesLoader.java
 
b/ratis-metrics-api/src/main/java/org/apache/ratis/metrics/MetricRegistriesLoader.java
deleted file mode 100644
index 8baac7a46..000000000
--- 
a/ratis-metrics-api/src/main/java/org/apache/ratis/metrics/MetricRegistriesLoader.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- *
- * 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.ratis.metrics;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.ServiceLoader;
-import java.util.stream.Collectors;
-
-import 
org.apache.ratis.thirdparty.com.google.common.annotations.VisibleForTesting;
-import org.apache.ratis.util.ReflectionUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-
-public final class MetricRegistriesLoader {
-  private static final Logger LOG = 
LoggerFactory.getLogger(MetricRegistriesLoader.class);
-
-  static final String DEFAULT_CLASS = 
"org.apache.ratis.metrics.impl.MetricRegistriesImpl";
-
-  private MetricRegistriesLoader() {
-  }
-
-  /**
-   * Creates a {@link MetricRegistries} instance using the corresponding 
{@link MetricRegistries}
-   * available to {@link ServiceLoader} on the classpath. If no instance is 
found, then default
-   * implementation will be loaded.
-   * @return A {@link MetricRegistries} implementation.
-   */
-  public static MetricRegistries load() {
-    List<MetricRegistries> availableImplementations = 
getDefinedImplementations();
-    return load(availableImplementations);
-  }
-
-  /**
-   * Creates a {@link MetricRegistries} instance using the corresponding 
{@link MetricRegistries}
-   * available to {@link ServiceLoader} on the classpath. If no instance is 
found, then default
-   * implementation will be loaded.
-   * @return A {@link MetricRegistries} implementation.
-   */
-  @VisibleForTesting
-  static MetricRegistries load(List<MetricRegistries> registries) {
-    if (registries.isEmpty()) {
-      try {
-        return 
ReflectionUtils.newInstance(Class.forName(DEFAULT_CLASS).asSubclass(MetricRegistries.class));
-      } catch (ClassNotFoundException e) {
-        throw new IllegalStateException("Failed to load default 
MetricRegistries " + DEFAULT_CLASS, e);
-      }
-    }
-
-    final MetricRegistries first = registries.get(0);
-    if (registries.size() == 1) {
-      // One and only one instance -- what we want/expect
-      LOG.debug("Loaded {}", first.getClass());
-    } else {
-      // Tell the user they're doing something wrong, and choose the first 
impl.
-      final List<? extends Class<?>> classes = 
registries.stream().map(Object::getClass).collect(Collectors.toList());
-      LOG.warn("Found multiple MetricRegistries: {}. Using the first: {}", 
classes, first.getClass());
-    }
-    return first;
-  }
-
-  private static List<MetricRegistries> getDefinedImplementations() {
-    ServiceLoader<MetricRegistries> loader = ServiceLoader.load(
-        MetricRegistries.class,
-        MetricRegistries.class.getClassLoader());
-    List<MetricRegistries> availableFactories = new ArrayList<>();
-    for (MetricRegistries impl : loader) {
-      availableFactories.add(impl);
-    }
-    return availableFactories;
-  }
-}
diff --git 
a/ratis-metrics-dropwizard3/src/test/java/org/apache/ratis/metrics/dropwizard3/TestLoadDm3MetricRegistries.java
 
b/ratis-metrics-dropwizard3/src/test/java/org/apache/ratis/metrics/dropwizard3/TestLoadDm3MetricRegistries.java
index 5b3897ffd..4bae01738 100644
--- 
a/ratis-metrics-dropwizard3/src/test/java/org/apache/ratis/metrics/dropwizard3/TestLoadDm3MetricRegistries.java
+++ 
b/ratis-metrics-dropwizard3/src/test/java/org/apache/ratis/metrics/dropwizard3/TestLoadDm3MetricRegistries.java
@@ -20,19 +20,18 @@ package org.apache.ratis.metrics.dropwizard3;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 import org.apache.ratis.metrics.MetricRegistries;
-import org.apache.ratis.metrics.MetricRegistriesLoader;
 import org.apache.ratis.metrics.MetricRegistryInfo;
 import org.apache.ratis.metrics.RatisMetricRegistry;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 /**
- * Test class for {@link MetricRegistriesLoader}.
+ * Test class for loading {@link Dm3MetricRegistriesImpl}.
  */
 public class TestLoadDm3MetricRegistries {
   @Test
   public void testLoadDm3() {
-    final MetricRegistries r = MetricRegistriesLoader.load();
+    final MetricRegistries r = MetricRegistries.global();
     Assertions.assertSame(Dm3MetricRegistriesImpl.class, r.getClass());
   }
 
diff --git 
a/ratis-metrics-default/src/test/java/org/apache/ratis/metrics/TestMetricRegistriesLoader.java
 b/ratis-test/src/test/java/org/apache/ratis/metrics/TestMetricRegistries.java
similarity index 67%
rename from 
ratis-metrics-default/src/test/java/org/apache/ratis/metrics/TestMetricRegistriesLoader.java
rename to 
ratis-test/src/test/java/org/apache/ratis/metrics/TestMetricRegistries.java
index 9816cc99c..f58480bc9 100644
--- 
a/ratis-metrics-default/src/test/java/org/apache/ratis/metrics/TestMetricRegistriesLoader.java
+++ 
b/ratis-test/src/test/java/org/apache/ratis/metrics/TestMetricRegistries.java
@@ -17,13 +17,17 @@
  */
 package org.apache.ratis.metrics;
 
+import static org.apache.ratis.metrics.MetricRegistries.DEFAULT_CLASS;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.mockito.Mockito.mock;
 
+import java.util.List;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 import org.apache.ratis.metrics.impl.MetricRegistriesImpl;
+import org.apache.ratis.util.ServiceUtils;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -31,39 +35,43 @@ import java.util.Arrays;
 import java.util.Collections;
 
 /**
- * Test class for {@link MetricRegistriesLoader}.
+ * Test class for loading {@link MetricRegistries} using {@link ServiceUtils}.
  */
-public class TestMetricRegistriesLoader {
+public class TestMetricRegistries {
+  static MetricRegistries load(List<MetricRegistries> loaded) {
+    return ServiceUtils.load(MetricRegistries.class, MetricRegistries::global, 
loaded);
+  }
+
   @Test
   public void testLoadEmptyInstance() {
-    MetricRegistries instance = 
MetricRegistriesLoader.load(Collections.emptyList());
-    assertEquals(MetricRegistriesLoader.DEFAULT_CLASS, 
instance.getClass().getName());
+    MetricRegistries instance = load(Collections.emptyList());
+    assertEquals(DEFAULT_CLASS, instance.getClass().getName());
+    assertSame(MetricRegistries.global(), instance);
   }
 
   @Test
   public void testLoadSingleInstance() {
     MetricRegistries loader = mock(MetricRegistries.class);
-    MetricRegistries instance = 
MetricRegistriesLoader.load(Collections.singletonList(loader));
+    MetricRegistries instance = load(Collections.singletonList(loader));
     assertEquals(loader, instance);
   }
 
   @Test
   public void testLoadMultipleInstances() {
-    MetricRegistries loader1 = mock(MetricRegistries.class);
-    MetricRegistries loader2 = mock(MetricRegistries.class);
-    MetricRegistries loader3 = mock(MetricRegistries.class);
-    MetricRegistries instance = 
MetricRegistriesLoader.load(Arrays.asList(loader1, loader2, loader3));
+    final MetricRegistries first = mock(MetricRegistries.class);
+    final MetricRegistries second = MetricRegistries.global();
+    final MetricRegistries instance = load(Arrays.asList(first, second));
 
     // the load() returns the first instance
-    assertEquals(loader1, instance);
-    assertNotEquals(loader2, instance);
-    assertNotEquals(loader3, instance);
+    assertEquals(first, instance);
+    assertNotEquals(second, instance);
   }
 
   @Test
   public void testLoadDefault() {
-    final MetricRegistries r = MetricRegistriesLoader.load();
-    Assertions.assertSame(MetricRegistriesImpl.class, r.getClass());
+    final MetricRegistries loaded = MetricRegistries.global();
+    Assertions.assertSame(MetricRegistriesImpl.class, loaded.getClass());
+    Assertions.assertSame(MetricRegistries.global(), loaded);
   }
 
   @Test

Reply via email to