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/06/18 08:14:50 UTC
[logging-log4j2] branch master updated: Cherry-pick the `ServiceLoaderUtil` changes 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
The following commit(s) were added to refs/heads/master by this push:
new c02b699bba Cherry-pick the `ServiceLoaderUtil` changes from `release-2.x`
c02b699bba is described below
commit c02b699bbaed4c889ff38cf9a21f4fd43a87391f
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Sat Jun 18 10:14:41 2022 +0200
Cherry-pick the `ServiceLoaderUtil` changes from `release-2.x`
---
.../logging/log4j/ServiceLoaderUtilTest.java | 69 +++++++++++++
.../apache/logging/log4j/test/BetterService.java | 21 ++++
.../org/apache/logging/log4j/test/Service.java | 21 ++++
.../org/apache/logging/log4j/test/Service1.java | 21 ++++
.../org/apache/logging/log4j/test/Service2.java | 21 ++++
.../apache/logging/log4j/util/LoaderUtilTest.java | 4 +-
...rg.apache.logging.log4j.util.test.BetterService | 16 +++
.../org.apache.logging.log4j.util.test.Service | 26 +++++
.../logging/log4j/message/ThreadDumpMessage.java | 14 +--
.../org/apache/logging/log4j/util/LoaderUtil.java | 8 +-
.../apache/logging/log4j/util/PropertiesUtil.java | 13 ++-
.../log4j/util/PropertyFilePropertySource.java | 10 +-
.../apache/logging/log4j/util/ProviderUtil.java | 15 ++-
.../logging/log4j/util/ServiceLoaderUtil.java | 113 ++++++++++++++-------
.../apache/logging/log4j/util/ServiceRegistry.java | 32 +++---
.../log4j/core/config/AbstractConfiguration.java | 43 ++++----
.../log4j/core/impl/ThreadContextDataInjector.java | 20 ++--
.../logging/log4j/core/util/WatchManager.java | 22 ++--
.../logging/log4j/plugins/di/DefaultInjector.java | 3 +-
19 files changed, 371 insertions(+), 121 deletions(-)
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ServiceLoaderUtilTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ServiceLoaderUtilTest.java
new file mode 100644
index 0000000000..a40121a6dd
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/ServiceLoaderUtilTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.logging.log4j;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Collections;
+import java.util.List;
+import java.util.ServiceConfigurationError;
+import java.util.stream.Collectors;
+
+import org.apache.logging.log4j.test.BetterService;
+import org.apache.logging.log4j.test.Service;
+import org.apache.logging.log4j.util.PropertySource;
+import org.apache.logging.log4j.util.ServiceLoaderUtil;
+import org.junit.jupiter.api.Test;
+
+public class ServiceLoaderUtilTest {
+
+ @Test
+ public void testServiceResolution() {
+ // Run only if we are a module
+ if (ServiceLoaderUtil.class.getModule().isNamed()) {
+ List<Object> services = Collections.emptyList();
+ // Service from test module
+ try {
+ services = ServiceLoaderUtil.loadServices(Service.class, MethodHandles.lookup())
+ .collect(Collectors.toList());
+ } catch (ServiceConfigurationError e) {
+ fail(e);
+ }
+ assertEquals(2, services.size(), "Service services");
+ // BetterService from test module
+ services.clear();
+ try {
+ services = ServiceLoaderUtil.loadServices(BetterService.class, MethodHandles.lookup())
+ .collect(Collectors.toList());
+ } catch (ServiceConfigurationError e) {
+ fail(e);
+ }
+ assertEquals(1, services.size(), "BetterService services");
+ // PropertySource from org.apache.logging.log4j module from this module
+ services.clear();
+ try {
+ services = ServiceLoaderUtil.loadServices(PropertySource.class, MethodHandles.lookup())
+ .collect(Collectors.toList());
+ } catch (ServiceConfigurationError e) {
+ fail(e);
+ }
+ assertEquals(0, services.size(), "PropertySource services");
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/test/BetterService.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/BetterService.java
new file mode 100644
index 0000000000..ca8b893e0e
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/BetterService.java
@@ -0,0 +1,21 @@
+/*
+ * 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.logging.log4j.test;
+
+public interface BetterService extends Service {
+}
\ No newline at end of file
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service.java
new file mode 100644
index 0000000000..25b1b33681
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service.java
@@ -0,0 +1,21 @@
+/*
+ * 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.logging.log4j.test;
+
+public interface Service {
+}
\ No newline at end of file
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service1.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service1.java
new file mode 100644
index 0000000000..82c233b852
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service1.java
@@ -0,0 +1,21 @@
+/*
+ * 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.logging.log4j.test;
+
+public class Service1 implements Service {
+}
\ No newline at end of file
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service2.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service2.java
new file mode 100644
index 0000000000..c4a8c39b97
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/test/Service2.java
@@ -0,0 +1,21 @@
+/*
+ * 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.logging.log4j.test;
+
+public class Service2 implements BetterService {
+}
\ No newline at end of file
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java
index fb4dd7c256..675c54f33e 100644
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/util/LoaderUtilTest.java
@@ -49,10 +49,10 @@ public class LoaderUtilTest {
};
thread.setContextClassLoader(loader);
try {
- assertEquals(0, LoaderUtil.findUrlResources("Log4j-charsets.properties").size());
+ assertEquals(0, LoaderUtil.findUrlResources("Log4j-charsets.properties", false).size());
LoaderUtil.forceTcclOnly = false;
- assertEquals(1, LoaderUtil.findUrlResources("Log4j-charsets.properties").size());
+ assertEquals(1, LoaderUtil.findUrlResources("Log4j-charsets.properties", false).size());
} finally {
thread.setContextClassLoader(tccl);
}
diff --git a/log4j-api-test/src/test/resources/META-INF/services/org.apache.logging.log4j.util.test.BetterService b/log4j-api-test/src/test/resources/META-INF/services/org.apache.logging.log4j.util.test.BetterService
new file mode 100644
index 0000000000..69b68983a6
--- /dev/null
+++ b/log4j-api-test/src/test/resources/META-INF/services/org.apache.logging.log4j.util.test.BetterService
@@ -0,0 +1,16 @@
+# 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.
+
+org.apache.logging.log4j.util.test.Service2
diff --git a/log4j-api-test/src/test/resources/META-INF/services/org.apache.logging.log4j.util.test.Service b/log4j-api-test/src/test/resources/META-INF/services/org.apache.logging.log4j.util.test.Service
new file mode 100644
index 0000000000..5fde1d5351
--- /dev/null
+++ b/log4j-api-test/src/test/resources/META-INF/services/org.apache.logging.log4j.util.test.Service
@@ -0,0 +1,26 @@
+# 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.
+
+# A correct entry
+org.apache.logging.log4j.util.test.Service1
+
+# Simulates a service retrieved from the server's classloader in a servlet environment.
+java.lang.String
+
+# Simulates a broken service.
+invalid.Service
+
+# Another correct service
+org.apache.logging.log4j.util.test.Service2
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java b/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
index 6b198f2688..07c35b2dbd 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
@@ -16,19 +16,19 @@
*/
package org.apache.logging.log4j.message;
-import org.apache.logging.log4j.util.LazyValue;
-import org.apache.logging.log4j.util.ServiceRegistry;
-import org.apache.logging.log4j.util.StringBuilderFormattable;
-import org.apache.logging.log4j.util.Strings;
-
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
+import java.lang.invoke.MethodHandles;
import java.util.HashMap;
import java.util.Map;
-import java.util.ServiceLoader;
import java.util.function.Supplier;
+import org.apache.logging.log4j.util.LazyValue;
+import org.apache.logging.log4j.util.ServiceRegistry;
+import org.apache.logging.log4j.util.StringBuilderFormattable;
+import org.apache.logging.log4j.util.Strings;
+
/**
* Captures information about all running Threads.
*/
@@ -37,7 +37,7 @@ public class ThreadDumpMessage implements Message, StringBuilderFormattable {
private static final long serialVersionUID = -1103400781608841088L;
private static final Supplier<ThreadInfoFactory> FACTORY = new LazyValue<>(() -> {
final var services = ServiceRegistry.getInstance()
- .getServices(ThreadInfoFactory.class, layer -> ServiceLoader.load(layer, ThreadInfoFactory.class), null);
+ .getServices(ThreadInfoFactory.class, MethodHandles.lookup(), null);
return services.isEmpty() ? new BasicThreadInfoFactory() : services.get(0);
});
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
index 9974a8e2f1..3c5e646c10 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
@@ -353,7 +353,11 @@ public final class LoaderUtil {
* @since 2.1
*/
public static Collection<URL> findResources(final String resource) {
- final Collection<UrlResource> urlResources = findUrlResources(resource);
+ return findResources(resource, true);
+ }
+
+ static Collection<URL> findResources(final String resource, final boolean useTccl) {
+ final Collection<UrlResource> urlResources = findUrlResources(resource, useTccl);
final Collection<URL> resources = new LinkedHashSet<>(urlResources.size());
for (final UrlResource urlResource : urlResources) {
resources.add(urlResource.getUrl());
@@ -368,7 +372,7 @@ public final class LoaderUtil {
* @param resource The resource to locate.
* @return The located resources.
*/
- static Collection<UrlResource> findUrlResources(final String resource) {
+ static Collection<UrlResource> findUrlResources(final String resource, boolean useTccl) {
// @formatter:off
final ClassLoader[] candidates = {
getThreadContextClassLoader(),
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
index 453f72354c..27f4486a92 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java
@@ -18,12 +18,13 @@ package org.apache.logging.log4j.util;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
import java.nio.charset.Charset;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -73,7 +74,11 @@ public final class PropertiesUtil {
* @param propertiesFileName the location of properties file to load
*/
public PropertiesUtil(final String propertiesFileName) {
- this.environment = new Environment(new PropertyFilePropertySource(propertiesFileName));
+ this(propertiesFileName, true);
+ }
+
+ private PropertiesUtil(final String propertiesFileName, final boolean useTccl) {
+ this.environment = new Environment(new PropertyFilePropertySource(propertiesFileName, useTccl));
}
/**
@@ -455,9 +460,7 @@ public final class PropertiesUtil {
}
sources.add(propertySource);
final ServiceRegistry registry = ServiceRegistry.getInstance();
- sources.addAll(registry.getServices(PropertySource.class,
- layer -> ServiceLoader.load(layer, PropertySource.class),
- null));
+ sources.addAll(registry.getServices(PropertySource.class, MethodHandles.lookup(), null));
reload();
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java
index bb8ccd7319..318011524a 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertyFilePropertySource.java
@@ -29,12 +29,16 @@ import java.util.Properties;
public class PropertyFilePropertySource extends PropertiesPropertySource {
public PropertyFilePropertySource(final String fileName) {
- super(loadPropertiesFile(fileName));
+ this(fileName, true);
}
- private static Properties loadPropertiesFile(final String fileName) {
+ public PropertyFilePropertySource(final String fileName, final boolean useTccl) {
+ super(loadPropertiesFile(fileName, useTccl));
+ }
+
+ private static Properties loadPropertiesFile(final String fileName, final boolean useTccl) {
final Properties props = new Properties();
- for (final URL url : LoaderUtil.findResources(fileName)) {
+ for (final URL url : LoaderUtil.findResources(fileName, useTccl)) {
try (final InputStream in = url.openStream()) {
props.load(in);
} catch (final IOException e) {
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderUtil.java
index 08eec62db6..3c103c7c57 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderUtil.java
@@ -16,19 +16,19 @@
*/
package org.apache.logging.log4j.util;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.spi.Provider;
-import org.apache.logging.log4j.status.StatusLogger;
-
import java.io.IOException;
+import java.lang.invoke.MethodHandles;
import java.net.URL;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
-import java.util.ServiceLoader;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.spi.Provider;
+import org.apache.logging.log4j.status.StatusLogger;
+
/**
* <em>Consider this class private.</em> Utility class for Log4j {@link Provider}s. When integrating with an application
* container framework, any Log4j Providers not accessible through standard classpath scanning should
@@ -63,9 +63,8 @@ public final class ProviderUtil {
private ProviderUtil() {
PROVIDERS.addAll(ServiceRegistry.getInstance()
- .getServices(Provider.class, layer -> ServiceLoader.load(layer, Provider.class),
- provider -> validVersion(provider.getVersions())));
- for (final LoaderUtil.UrlResource resource : LoaderUtil.findUrlResources(PROVIDER_RESOURCE)) {
+ .getServices(Provider.class, MethodHandles.lookup(), provider -> validVersion(provider.getVersions())));
+ for (final LoaderUtil.UrlResource resource : LoaderUtil.findUrlResources(PROVIDER_RESOURCE, false)) {
loadProvider(resource.getUrl(), resource.getClassLoader());
}
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java
index 33ac8762a1..32a2790b62 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java
@@ -16,51 +16,96 @@
*/
package org.apache.logging.log4j.util;
-import java.util.ArrayList;
-import java.util.List;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
-import java.util.function.Function;
-import java.util.function.Predicate;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.apache.logging.log4j.status.StatusLogger;
/**
* Loads all valid instances of a service.
*/
public class ServiceLoaderUtil {
- public static <S> List<S> loadServices(final Class<S> clazz, final Function<ModuleLayer, ServiceLoader<S>> loader,
- final Predicate<S> validator) {
- final List<S> services = new ArrayList<>();
- final ModuleLayer moduleLayer = ServiceLoaderUtil.class.getModule().getLayer();
- if (moduleLayer == null) {
- final ClassLoader[] classLoaders = LoaderUtil.getClassLoaders();
- Throwable throwable = null;
- ClassLoader errorClassLoader = null;
- for (ClassLoader classLoader : classLoaders) {
+ private static final MethodType LOAD_CLASS_CLASSLOADER = MethodType.methodType(ServiceLoader.class, Class.class,
+ ClassLoader.class);
+
+ private ServiceLoaderUtil() {
+ }
+
+ /**
+ * Retrieves the available services from the caller's classloader.
+ *
+ * Broken services will be ignored.
+ *
+ * @param <T> The service type.
+ * @param serviceType The class of the service.
+ * @param lookup The calling class data.
+ * @return A stream of service instances.
+ */
+ public static <T> Stream<T> loadServices(final Class<T> serviceType, Lookup lookup) {
+ return loadServices(serviceType, lookup, false);
+ }
+
+ /**
+ * Retrieves the available services from the caller's classloader and possibly
+ * the thread context classloader.
+ *
+ * Broken services will be ignored.
+ *
+ * @param <T> The service type.
+ * @param serviceType The class of the service.
+ * @param lookup The calling class data.
+ * @param useTccl If true the thread context classloader will also be used.
+ * @return A stream of service instances.
+ */
+ public static <T> Stream<T> loadServices(final Class<T> serviceType, Lookup lookup, boolean useTccl) {
+ return loadServices(serviceType, lookup, useTccl, true);
+ }
+
+ static <T> Stream<T> loadServices(final Class<T> serviceType, final Lookup lookup, final boolean useTccl,
+ final boolean verbose) {
+ final ClassLoader classLoader = lookup.lookupClass().getClassLoader();
+ Stream<T> services = loadClassloaderServices(serviceType, lookup, classLoader, verbose);
+ if (useTccl) {
+ final ClassLoader contextClassLoader = LoaderUtil.getThreadContextClassLoader();
+ if (contextClassLoader != classLoader) {
+ services = Stream.concat(services,
+ loadClassloaderServices(serviceType, lookup, contextClassLoader, verbose));
+ }
+ }
+ final Set<Class<?>> classes = new HashSet<>();
+ // only the first occurrence of a class
+ return services.filter(service -> classes.add(service.getClass()));
+ }
+
+ static <T> Stream<T> loadClassloaderServices(final Class<T> serviceType, final Lookup lookup,
+ final ClassLoader classLoader, final boolean verbose) {
+ try {
+ final MethodHandle handle = lookup.findStatic(ServiceLoader.class, "load", LOAD_CLASS_CLASSLOADER);
+ final ServiceLoader<T> serviceLoader = (ServiceLoader<T>) handle.invokeExact(serviceType, classLoader);
+ return serviceLoader.stream().map(provider -> {
try {
- final ServiceLoader<S> serviceLoader = ServiceLoader.load(clazz, classLoader);
- for (final S service : serviceLoader) {
- if (!services.contains(service) && (validator == null || validator.test(service))) {
- services.add(service);
- }
- }
- } catch (final Throwable ex) {
- if (throwable == null) {
- throwable = ex;
- errorClassLoader = classLoader;
+ return provider.get();
+ } catch (ServiceConfigurationError e) {
+ if (verbose) {
+ StatusLogger.getLogger().warn("Unable to load service class for service {}", serviceType, e);
}
}
- }
- if (services.size() == 0 && throwable != null) {
- LowLevelLogUtil.logException("Unable to retrieve provider from ClassLoader " + errorClassLoader, throwable);
- }
- } else {
- final ServiceLoader<S> serviceLoader = loader.apply(moduleLayer);
- for (final S service : serviceLoader) {
- if (!services.contains(service) && (validator == null || validator.test(service))) {
- services.add(service);
- }
+ return null;
+ }).filter(Objects::nonNull);
+ } catch (Throwable e) {
+ if (verbose) {
+ StatusLogger.getLogger().error("Unable to load services for service {}", serviceType, e);
}
}
- return services;
+ return Stream.empty();
}
+
}
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceRegistry.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceRegistry.java
index 83a40fdb73..23f55b6b16 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceRegistry.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceRegistry.java
@@ -17,12 +17,12 @@
package org.apache.logging.log4j.util;
+import java.lang.invoke.MethodHandles.Lookup;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -56,27 +56,27 @@ public class ServiceRegistry {
}
/**
- * Gets service instances loaded from the calling context and any previously registered bundle services.
- * The {@code loaderCallerContext} parameter is provided to ensure the caller can specify which calling
- * function context to load services.
+ * Gets service instances loaded from the calling context and any previously registered bundle services. The {@code loaderCallerContext} parameter is
+ * provided to ensure the caller can specify which calling function context to
+ * load services.
*
* @param serviceType service class
- * @param loaderCallerContext function located in same module as caller context to use for loading services
- * @param validator if non-null, used to validate service instances, removing invalid instances from the returned list
+ * @param loaderCallerContext function located in same module as caller context
+ * to use for loading services
+ * @param validator if non-null, used to validate service instances,
+ * removing invalid instances from the returned list
* @param <S> type of service
* @return loaded service instances
*/
- public <S> List<S> getServices(
- final Class<S> serviceType, final Function<ModuleLayer, ServiceLoader<S>> loaderCallerContext,
- final Predicate<S> validator) {
+ public <S> List<S> getServices(final Class<S> serviceType, final Lookup lookup, final Predicate<S> validator) {
final List<S> services = cast(mainServices.computeIfAbsent(serviceType,
- ignored -> ServiceLoaderUtil.loadServices(serviceType, loaderCallerContext, validator)));
- return Stream.concat(services.stream(), bundleServices.values().stream()
- .flatMap(map -> {
- final Stream<S> stream = map.getOrDefault(serviceType, List.of()).stream().map(serviceType::cast);
- return validator != null ? stream.filter(validator) : stream;
- }))
- .distinct().collect(Collectors.toCollection(ArrayList::new));
+ ignored -> ServiceLoaderUtil.loadServices(serviceType, lookup)
+ .filter(validator != null ? validator : unused -> true)
+ .collect(Collectors.toList())));
+ return Stream.concat(services.stream(), bundleServices.values().stream().flatMap(map -> {
+ final Stream<S> stream = map.getOrDefault(serviceType, List.of()).stream().map(serviceType::cast);
+ return validator != null ? stream.filter(validator) : stream;
+ })).distinct().collect(Collectors.toCollection(ArrayList::new));
}
/**
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
index 692fa0e288..8c3adced80 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java
@@ -16,6 +16,26 @@
*/
package org.apache.logging.log4j.core.config;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.invoke.MethodHandles;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Core;
@@ -65,26 +85,6 @@ import org.apache.logging.log4j.util.NameUtil;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.apache.logging.log4j.util.ServiceRegistry;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Serializable;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.ServiceLoader;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
-import java.util.function.Supplier;
-
/**
* The base Configuration. Many configuration implementations will extend this class.
*/
@@ -271,8 +271,7 @@ public abstract class AbstractConfiguration extends AbstractFilterable implement
private void initializeScriptManager() {
try {
ServiceRegistry.getInstance()
- .getServices(ScriptManagerFactory.class, layer -> ServiceLoader.load(layer, ScriptManagerFactory.class),
- null)
+ .getServices(ScriptManagerFactory.class, MethodHandles.lookup(), null)
.stream()
.findFirst()
.ifPresent(factory -> setScriptManager(factory.createScriptManager(this, getWatchManager())));
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
index 43712fd281..9fc5dbcee8 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ThreadContextDataInjector.java
@@ -16,6 +16,15 @@
*/
package org.apache.logging.log4j.core.impl;
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.ContextDataInjector;
import org.apache.logging.log4j.core.config.Property;
@@ -25,15 +34,6 @@ import org.apache.logging.log4j.util.ReadOnlyStringMap;
import org.apache.logging.log4j.util.ServiceRegistry;
import org.apache.logging.log4j.util.StringMap;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.ServiceLoader;
-import java.util.concurrent.ConcurrentLinkedDeque;
-
/**
* {@code ThreadContextDataInjector} contains a number of strategies for copying key-value pairs from the various
* {@code ThreadContext} map implementations into a {@code StringMap}. In the case of duplicate keys,
@@ -71,7 +71,7 @@ public class ThreadContextDataInjector {
private static List<ContextDataProvider> getServiceProviders() {
final List<ContextDataProvider> providers = new ArrayList<>();
final List<ContextDataProvider> services = ServiceRegistry.getInstance()
- .getServices(ContextDataProvider.class, layer -> ServiceLoader.load(layer, ContextDataProvider.class), null);
+ .getServices(ContextDataProvider.class, MethodHandles.lookup(), null);
for (final ContextDataProvider provider : services) {
if (providers.stream().noneMatch((p) -> p.getClass().isAssignableFrom(provider.getClass()))) {
providers.add(provider);
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
index 960f246a43..457da9181d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/WatchManager.java
@@ -16,22 +16,13 @@
*/
package org.apache.logging.log4j.core.util;
-import org.apache.logging.log4j.Logger;
-import org.apache.logging.log4j.core.AbstractLifeCycle;
-import org.apache.logging.log4j.core.config.ConfigurationFileWatcher;
-import org.apache.logging.log4j.core.config.ConfigurationScheduler;
-import org.apache.logging.log4j.plugins.Inject;
-import org.apache.logging.log4j.plugins.Singleton;
-import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.logging.log4j.util.ServiceRegistry;
-
import java.io.File;
+import java.lang.invoke.MethodHandles;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.ServiceLoader;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -39,6 +30,15 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.AbstractLifeCycle;
+import org.apache.logging.log4j.core.config.ConfigurationFileWatcher;
+import org.apache.logging.log4j.core.config.ConfigurationScheduler;
+import org.apache.logging.log4j.plugins.Inject;
+import org.apache.logging.log4j.plugins.Singleton;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.ServiceRegistry;
+
/**
* Manages {@link FileWatcher}s.
*
@@ -138,7 +138,7 @@ public class WatchManager extends AbstractLifeCycle {
public WatchManager(final ConfigurationScheduler scheduler) {
this.scheduler = Objects.requireNonNull(scheduler, "scheduler");
eventServiceList = ServiceRegistry.getInstance()
- .getServices(WatchEventService.class, layer -> ServiceLoader.load(layer, WatchEventService.class), null);
+ .getServices(WatchEventService.class, MethodHandles.lookup(), null);
}
public void checkFiles() {
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInjector.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInjector.java
index 13a0c72e01..aee5cf183c 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInjector.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/DefaultInjector.java
@@ -45,6 +45,7 @@ import org.apache.logging.log4j.util.ServiceRegistry;
import org.apache.logging.log4j.util.StringBuilders;
import java.lang.annotation.Annotation;
+import java.lang.invoke.MethodHandles;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
@@ -100,7 +101,7 @@ class DefaultInjector implements Injector {
@Override
public void init() {
final List<InjectorCallback> callbacks = ServiceRegistry.getInstance()
- .getServices(InjectorCallback.class, layer -> ServiceLoader.load(layer, InjectorCallback.class), null);
+ .getServices(InjectorCallback.class, MethodHandles.lookup(), null);
callbacks.sort(InjectorCallback.COMPARATOR);
for (final InjectorCallback callback : callbacks) {
try {