You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by pk...@apache.org on 2022/10/29 19:31:22 UTC

[logging-log4j2] 01/02: Add `ServiceLoaderUtil` from `release-2.x`

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

pkarwasz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 04ca410b744d724857f1807432f92337f45f47d3
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Tue Apr 5 22:10:53 2022 +0200

    Add `ServiceLoaderUtil` from `release-2.x`
    
    The Java 8 version of `ServiceLoaderUtil` is much more resilient to
    errors: the spliterator used in`ServiceLoader#stream()` can fail e.g. if
    the service class loading fails, so we can not catch the error and
    ignore it.
---
 .../apache/logging/log4j/util3/PropertiesUtil.java |  3 +-
 .../logging/log4j/util3/ServiceLoaderUtil.java     | 72 ++++++++++++++++++----
 .../logging/log4j/util3/ServiceRegistry.java       |  9 ++-
 3 files changed, 69 insertions(+), 15 deletions(-)

diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util3/PropertiesUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util3/PropertiesUtil.java
index 4000416263..0eea506a84 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util3/PropertiesUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util3/PropertiesUtil.java
@@ -244,7 +244,8 @@ public class PropertiesUtil implements PropertyEnvironment {
             }
             sources.add(propertySource);
             final ServiceRegistry registry = ServiceRegistry.getInstance();
-            sources.addAll(registry.getServices(PropertySource.class, MethodHandles.lookup(), null));
+            // Does not log errors using StatusLogger, which depends on PropertiesUtil being initialized.
+            sources.addAll(registry.getServices(PropertySource.class, MethodHandles.lookup(), null, /*verbose=*/false));
             reload();
         }
 
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util3/ServiceLoaderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util3/ServiceLoaderUtil.java
index 44542b0fd9..d655779778 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util3/ServiceLoaderUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util3/ServiceLoaderUtil.java
@@ -14,6 +14,7 @@
  * See the license for the specific language governing permissions and
  * limitations under the license.
  */
+
 package org.apache.logging.log4j.util3;
 
 import java.lang.invoke.CallSite;
@@ -23,17 +24,22 @@ import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.Collections;
 import java.util.HashSet;
-import java.util.Objects;
+import java.util.Iterator;
 import java.util.ServiceConfigurationError;
 import java.util.ServiceLoader;
 import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.Consumer;
 import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
+import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.status.StatusLogger;
 
 /**
- * Loads all valid instances of a service.
+ * This class should be considered internal.
  */
 public final class ServiceLoaderUtil {
 
@@ -88,6 +94,11 @@ public final class ServiceLoaderUtil {
 
     static <T> Stream<T> loadClassloaderServices(final Class<T> serviceType, final Lookup lookup,
             final ClassLoader classLoader, final boolean verbose) {
+        return StreamSupport.stream(new ServiceLoaderSpliterator<T>(serviceType, lookup, classLoader, verbose), false);
+    }
+
+    static <T> Iterable<T> callServiceLoader(Lookup lookup, Class<T> serviceType, ClassLoader classLoader,
+            boolean verbose) {
         try {
             // Creates a lambda in the caller's domain that calls `ServiceLoader`
             final MethodHandle loadHandle = lookup.findStatic(ServiceLoader.class, "load",
@@ -111,22 +122,57 @@ public final class ServiceLoaderUtil {
                         MethodType.methodType(Object.class, PrivilegedAction.class));
                 serviceLoader = (ServiceLoader<T>) privilegedHandle.invoke(action);
             }
-            return serviceLoader.stream().map(provider -> {
-                try {
-                    return provider.get();
-                } catch (ServiceConfigurationError e) {
-                    if (verbose) {
-                        StatusLogger.getLogger().warn("Unable to load service class for service {}", serviceType, e);
-                    }
-                }
-                return null;
-            }).filter(Objects::nonNull);
+            return serviceLoader;
         } catch (Throwable e) {
             if (verbose) {
                 StatusLogger.getLogger().error("Unable to load services for service {}", serviceType, e);
             }
         }
-        return Stream.empty();
+        return Collections.emptyList();
     }
 
+    private static class ServiceLoaderSpliterator<S> implements Spliterator<S> {
+
+        private final Iterator<S> serviceIterator;
+        private final Logger logger;
+        private final String serviceName;
+
+        public ServiceLoaderSpliterator(final Class<S> serviceType, final Lookup lookup, final ClassLoader classLoader,
+                final boolean verbose) {
+            this.serviceIterator = callServiceLoader(lookup, serviceType, classLoader, verbose).iterator();
+            this.logger = verbose ? StatusLogger.getLogger() : null;
+            this.serviceName = serviceType.toString();
+        }
+
+        @Override
+        public boolean tryAdvance(Consumer<? super S> action) {
+            while (serviceIterator.hasNext()) {
+                try {
+                    action.accept(serviceIterator.next());
+                    return true;
+                } catch (ServiceConfigurationError e) {
+                    if (logger != null) {
+                        logger.warn("Unable to load service class for service {}", serviceName, e);
+                    }
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public Spliterator<S> trySplit() {
+            return null;
+        }
+
+        @Override
+        public long estimateSize() {
+            return Long.MAX_VALUE;
+        }
+
+        @Override
+        public int characteristics() {
+            return NONNULL | IMMUTABLE;
+        }
+
+    }
 }
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util3/ServiceRegistry.java b/log4j-api/src/main/java/org/apache/logging/log4j/util3/ServiceRegistry.java
index cb50982514..75e49e9280 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util3/ServiceRegistry.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util3/ServiceRegistry.java
@@ -70,8 +70,15 @@ public class ServiceRegistry {
      * @return loaded service instances
      */
     public <S> List<S> getServices(final Class<S> serviceType, final Lookup lookup, final Predicate<S> validator) {
+        return getServices(serviceType, lookup, validator, true);
+    }
+
+    /**
+     * Set 'verbose' to false if the `StatusLogger` is not available yet.
+     */
+    <S> List<S> getServices(final Class<S> serviceType, final Lookup lookup, final Predicate<S> validator, boolean verbose) {
         final List<S> services = cast(mainServices.computeIfAbsent(serviceType,
-                ignored -> ServiceLoaderUtil.loadServices(serviceType, lookup)
+                ignored -> ServiceLoaderUtil.loadServices(serviceType, lookup, false, verbose)
                         .filter(validator != null ? validator : unused -> true)
                         .collect(Collectors.toList())));
         return Stream.concat(services.stream(), bundleServices.values().stream().flatMap(map -> {