You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2022/05/15 04:20:45 UTC
[logging-log4j2] branch master updated (ee39fc6a74 -> 5a0eafd109)
This is an automated email from the ASF dual-hosted git repository.
mattsicker pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
from ee39fc6a74 LOG4J2-3493 - ClassArbiter mistakenly used SystemPropertyArbiter.Builder
new c80a381d09 LOG4J2-3496 - Add Optional injection support, plugin interface filtering
new 5a0eafd109 LOG4J2-3496 - Support injection via container types
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../apache/logging/log4j/util/ServiceRegistry.java | 4 +-
.../apache/logging/log4j/core/osgi/Activator.java | 34 +++
.../json/resolver/TemplateResolverFactories.java | 30 +--
.../resolver/TemplateResolverInterceptors.java | 31 ++-
.../log4j/plugin/processor/PluginProcessor.java | 81 ++++--
.../plugins/test/validation/generic/AlphaBean.java | 16 +-
.../plugins/test/validation/generic/BaseBean.java | 10 +-
.../plugins/test/validation/generic/BetaBean.java | 15 +-
.../plugins/test/validation/generic/GammaBean.java | 12 +-
.../logging/log4j/plugins/di/InjectorTest.java | 71 ++++++
.../plugins/{FactoryType.java => Category.java} | 15 +-
.../logging/log4j/plugins/di/DefaultInjector.java | 121 +++++++--
.../org/apache/logging/log4j/plugins/di/Key.java | 7 +
...NameProvider.java => CategoryNameProvider.java} | 6 +-
.../logging/log4j/plugins/osgi/Activator.java | 79 +++---
.../log4j/plugins/processor/PluginEntry.java | 23 +-
.../log4j/plugins/processor/PluginService.java | 25 +-
.../logging/log4j/plugins/util/PluginBundle.java | 88 -------
.../logging/log4j/plugins/util/PluginCategory.java | 54 ++--
.../logging/log4j/plugins/util/PluginRegistry.java | 278 ++++++++++-----------
.../logging/log4j/plugins/util/PluginType.java | 18 +-
src/site/asciidoc/manual/dependencyinjection.adoc | 1 +
src/site/asciidoc/manual/plugins.adoc | 4 +-
23 files changed, 602 insertions(+), 421 deletions(-)
copy log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/PluginNameProvider.java => log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/AlphaBean.java (71%)
copy log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/Recycler.java => log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/BaseBean.java (83%)
copy log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/PluginNameProvider.java => log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/BetaBean.java (74%)
copy log4j-1.2-api/src/main/java/org/apache/log4j/spi/OptionHandler.java => log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/GammaBean.java (75%)
copy log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/{FactoryType.java => Category.java} (70%)
copy log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/{PluginValueNameProvider.java => CategoryNameProvider.java} (81%)
delete mode 100644 log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginBundle.java
[logging-log4j2] 02/02: LOG4J2-3496 - Support injection via container types
Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
mattsicker pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 5a0eafd10956738d8ab4ce00325651a29ec58bce
Author: Matt Sicker <ma...@apache.org>
AuthorDate: Sat May 14 23:20:16 2022 -0500
LOG4J2-3496 - Support injection via container types
- Adds Category qualifier annotation type for injecting plugins from a category
- Adds support for injecting Optional<P>, Collection<P>, Iterable<P>, Set<P>, Stream<P>, List<P>, and Map<String, P>, for a given category
- Moves PluginRegistry into a singleton bean
- Update OSGi activation to work with PluginRegistry
Signed-off-by: Matt Sicker <ma...@apache.org>
---
.../apache/logging/log4j/util/ServiceRegistry.java | 4 +-
.../apache/logging/log4j/core/osgi/Activator.java | 34 +++++++
.../plugins/test/validation/generic/AlphaBean.java | 31 ++++++
.../plugins/test/validation/generic/BaseBean.java | 25 +++++
.../plugins/test/validation/generic/BetaBean.java | 28 +++++
.../plugins/test/validation/generic/GammaBean.java | 26 +++++
.../logging/log4j/plugins/di/InjectorTest.java | 35 +++++++
.../org/apache/logging/log4j/plugins/Category.java | 42 ++++++++
.../logging/log4j/plugins/di/DefaultInjector.java | 113 +++++++++++++++++----
.../log4j/plugins/name/CategoryNameProvider.java | 30 ++++++
.../logging/log4j/plugins/osgi/Activator.java | 77 ++++++++------
.../logging/log4j/plugins/util/PluginRegistry.java | 18 +---
src/site/asciidoc/manual/dependencyinjection.adoc | 1 +
src/site/asciidoc/manual/plugins.adoc | 4 +-
14 files changed, 395 insertions(+), 73 deletions(-)
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 2268ffaddd..83a40fdb73 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
@@ -105,7 +105,9 @@ public class ServiceRegistry {
* @param <S> type of service
*/
public <S> void registerBundleServices(final Class<S> serviceType, final long bundleId, final List<S> services) {
- bundleServices.computeIfAbsent(bundleId, ignored -> new ConcurrentHashMap<>()).put(serviceType, services);
+ bundleServices.computeIfAbsent(bundleId, ignored -> new ConcurrentHashMap<>())
+ .computeIfAbsent(serviceType, ignored -> new ArrayList<>())
+ .addAll(cast(services));
}
/**
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java
index 332275d488..4549e63ca6 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java
@@ -20,16 +20,22 @@ package org.apache.logging.log4j.core.osgi;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.util.Constants;
import org.apache.logging.log4j.core.util.ContextDataProvider;
+import org.apache.logging.log4j.plugins.di.Injector;
import org.apache.logging.log4j.plugins.di.InjectorCallback;
+import org.apache.logging.log4j.plugins.di.Key;
import org.apache.logging.log4j.plugins.processor.PluginService;
+import org.apache.logging.log4j.plugins.util.PluginRegistry;
import org.apache.logging.log4j.spi.Provider;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.apache.logging.log4j.util.ServiceRegistry;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.wiring.BundleWiring;
+import java.util.Hashtable;
+import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -37,13 +43,33 @@ import java.util.concurrent.atomic.AtomicReference;
*/
public final class Activator implements BundleActivator {
private final AtomicReference<BundleContext> contextRef = new AtomicReference<>();
+ private ServiceRegistration<PluginRegistry> pluginRegistryServiceRegistration;
+ private PluginRegistry pluginRegistry;
+ private ServiceRegistration<InjectorCallback> injectorCallbackServiceRegistration;
+ private InjectorCallback injectorCallback;
@Override
public void start(final BundleContext context) throws Exception {
+ pluginRegistryServiceRegistration = context.registerService(PluginRegistry.class, new PluginRegistry(), new Hashtable<>());
+ pluginRegistry = context.getService(pluginRegistryServiceRegistration.getReference());
+ injectorCallbackServiceRegistration = context.registerService(InjectorCallback.class, new InjectorCallback() {
+ @Override
+ public void configure(final Injector injector) {
+ injector.registerBinding(Key.forClass(PluginRegistry.class),
+ () -> context.getService(pluginRegistryServiceRegistration.getReference()));
+ }
+
+ @Override
+ public int getOrder() {
+ return -50;
+ }
+ }, new Hashtable<>());
+ injectorCallback = context.getService(injectorCallbackServiceRegistration.getReference());
final ServiceRegistry registry = ServiceRegistry.getInstance();
final Bundle bundle = context.getBundle();
final long bundleId = bundle.getBundleId();
final ClassLoader classLoader = bundle.adapt(BundleWiring.class).getClassLoader();
+ registry.registerBundleServices(InjectorCallback.class, bundleId, List.of(injectorCallback));
registry.loadServicesFromBundle(PluginService.class, bundleId, classLoader);
registry.loadServicesFromBundle(Provider.class, bundleId, classLoader);
registry.loadServicesFromBundle(ContextDataProvider.class, bundleId, classLoader);
@@ -58,6 +84,14 @@ public final class Activator implements BundleActivator {
@Override
public void stop(final BundleContext context) throws Exception {
ServiceRegistry.getInstance().unregisterBundleServices(context.getBundle().getBundleId());
+ if (injectorCallback != null) {
+ injectorCallback = null;
+ injectorCallbackServiceRegistration.unregister();
+ }
+ if (pluginRegistry != null) {
+ pluginRegistry = null;
+ pluginRegistryServiceRegistration.unregister();
+ }
this.contextRef.compareAndSet(context, null);
LogManager.shutdown(false, true);
}
diff --git a/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/AlphaBean.java b/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/AlphaBean.java
new file mode 100644
index 0000000000..40ce717911
--- /dev/null
+++ b/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/AlphaBean.java
@@ -0,0 +1,31 @@
+/*
+ * 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.plugins.test.validation.generic;
+
+import org.apache.logging.log4j.plugins.Factory;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.Singleton;
+
+@Plugin(category = "Bean", name = "Alpha")
+@Singleton
+public class AlphaBean implements BaseBean {
+ @Factory
+ static AlphaBean newAlphaBean() {
+ return new AlphaBean();
+ }
+}
diff --git a/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/BaseBean.java b/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/BaseBean.java
new file mode 100644
index 0000000000..e481a085d9
--- /dev/null
+++ b/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/BaseBean.java
@@ -0,0 +1,25 @@
+/*
+ * 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.plugins.test.validation.generic;
+
+import org.apache.logging.log4j.plugins.Category;
+
+@Category("Bean")
+public
+interface BaseBean {
+}
diff --git a/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/BetaBean.java b/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/BetaBean.java
new file mode 100644
index 0000000000..f872c47348
--- /dev/null
+++ b/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/BetaBean.java
@@ -0,0 +1,28 @@
+/*
+ * 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.plugins.test.validation.generic;
+
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.PluginOrder;
+import org.apache.logging.log4j.plugins.Singleton;
+
+@Plugin(category = "Bean", name = "Beta")
+@PluginOrder(PluginOrder.FIRST)
+@Singleton
+public class BetaBean implements BaseBean {
+}
diff --git a/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/GammaBean.java b/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/GammaBean.java
new file mode 100644
index 0000000000..73519484d5
--- /dev/null
+++ b/log4j-plugins-test/src/main/java/org/apache/logging/log4j/plugins/test/validation/generic/GammaBean.java
@@ -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.
+ */
+
+package org.apache.logging.log4j.plugins.test.validation.generic;
+
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.plugins.Singleton;
+
+@Plugin(category = "Bean", name = "Gamma")
+@Singleton
+public class GammaBean implements BaseBean {
+}
diff --git a/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/di/InjectorTest.java b/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/di/InjectorTest.java
index 5fb1ba3f5d..7ce4484220 100644
--- a/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/di/InjectorTest.java
+++ b/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/di/InjectorTest.java
@@ -17,6 +17,7 @@
package org.apache.logging.log4j.plugins.di;
+import org.apache.logging.log4j.plugins.Category;
import org.apache.logging.log4j.plugins.Factory;
import org.apache.logging.log4j.plugins.Inject;
import org.apache.logging.log4j.plugins.Named;
@@ -31,6 +32,9 @@ import org.apache.logging.log4j.plugins.ScopeType;
import org.apache.logging.log4j.plugins.Singleton;
import org.apache.logging.log4j.plugins.processor.PluginEntry;
import org.apache.logging.log4j.plugins.test.validation.ValidatingPluginWithGenericBuilder;
+import org.apache.logging.log4j.plugins.test.validation.generic.BaseBean;
+import org.apache.logging.log4j.plugins.test.validation.generic.BetaBean;
+import org.apache.logging.log4j.plugins.test.validation.generic.GammaBean;
import org.apache.logging.log4j.plugins.util.PluginType;
import org.apache.logging.log4j.plugins.util.TypeUtil;
import org.apache.logging.log4j.plugins.validation.constraints.Required;
@@ -41,14 +45,17 @@ import org.junit.jupiter.api.parallel.Resources;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -789,4 +796,32 @@ class InjectorTest {
assertThat(first.c).isNotNull().isNotSameAs(second.c);
assertThat(second.c).isNotNull();
}
+
+ /**
+ * @see <a href="https://issues.apache.org/jira/browse/LOG4J2-3496">LOG4J2-3496</a>
+ */
+ static class ContainerPluginBeanInjection {
+ @Category("Bean") Optional<BaseBean> optional;
+ @Category("Bean") Collection<BaseBean> collection;
+ @Category("Bean") Iterable<BaseBean> iterable;
+ @Category("Bean") Set<BaseBean> set;
+ @Category("Bean") Stream<BaseBean> stream;
+ @Category("Bean") List<BaseBean> list;
+ @Category("Bean") Map<String, BaseBean> map;
+ }
+
+ @Test
+ void categoryQualifierInjection() {
+ final ContainerPluginBeanInjection instance = DI.createInjector()
+ .registerBinding(Keys.PLUGIN_PACKAGES_KEY, () -> List.of(BaseBean.class.getPackageName()))
+ .getInstance(ContainerPluginBeanInjection.class);
+ assertThat(instance.list).hasSize(3).first().isInstanceOf(BetaBean.class);
+ assertThat(instance.collection).containsExactlyElementsOf(instance.list);
+ assertThat(instance.iterable).containsExactlyElementsOf(instance.list);
+ assertThat(instance.set).containsExactlyElementsOf(instance.list);
+ assertThat(instance.stream).containsExactlyElementsOf(instance.list);
+ assertThat(instance.map).hasSize(3);
+ assertThat(instance.map.get("gamma")).isInstanceOf(GammaBean.class);
+ assertThat(instance.optional).get().isInstanceOf(BetaBean.class);
+ }
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Category.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Category.java
new file mode 100644
index 0000000000..7f7e47a5e8
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/Category.java
@@ -0,0 +1,42 @@
+/*
+ * 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.plugins;
+
+import org.apache.logging.log4j.plugins.name.CategoryNameProvider;
+import org.apache.logging.log4j.plugins.name.NameProvider;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Qualifier used for matching against injectable plugins in a given category.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE, ElementType.TYPE_USE })
+@Documented
+@QualifierType
+@NameProvider(CategoryNameProvider.class)
+public @interface Category {
+ /**
+ * The category to use plugins from.
+ */
+ String value();
+}
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 fe2c0097d8..590ba47bd9 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
@@ -18,11 +18,13 @@
package org.apache.logging.log4j.plugins.di;
import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.plugins.Category;
import org.apache.logging.log4j.plugins.FactoryType;
import org.apache.logging.log4j.plugins.Inject;
import org.apache.logging.log4j.plugins.Named;
import org.apache.logging.log4j.plugins.Node;
import org.apache.logging.log4j.plugins.PluginException;
+import org.apache.logging.log4j.plugins.PluginOrder;
import org.apache.logging.log4j.plugins.QualifierType;
import org.apache.logging.log4j.plugins.ScopeType;
import org.apache.logging.log4j.plugins.Singleton;
@@ -250,21 +252,57 @@ class DefaultInjector implements Injector {
if (existing != null) {
return existing.getSupplier();
}
+
final Class<T> rawType = key.getRawType();
final Scope scope = getScopeForType(rawType);
+
+ // @Named PluginCategory injection
if (rawType == PluginCategory.class && key.getQualifierType() == Named.class) {
final Key<PluginCategory> pluginCategoryKey = TypeUtil.cast(key);
final Supplier<PluginCategory> pluginCategoryFactory = createPluginCategoryFactory(pluginCategoryKey);
- bindingMap.put(pluginCategoryKey, pluginCategoryFactory);
+ bindingMap.putIfAbsent(pluginCategoryKey, pluginCategoryFactory);
+ return bindingMap.get(key, aliases).getSupplier();
+ }
+
+ // @Category Collection<T>/Map<String, T>/Stream<T> injection
+ if (key.getQualifierType() == Category.class) {
+ if (Stream.class.isAssignableFrom(rawType)) {
+ final Key<Stream<T>> streamKey = TypeUtil.cast(key);
+ final Supplier<Stream<T>> streamFactory =
+ () -> streamPluginInstancesFromCategory(key.getParameterizedTypeArgument(0));
+ bindingMap.putIfAbsent(streamKey, streamFactory);
+ } else if (Set.class.isAssignableFrom(rawType)) {
+ final Key<Set<T>> setKey = TypeUtil.cast(key);
+ final Supplier<Set<T>> setFactory = () -> getPluginSet(key.getParameterizedTypeArgument(0));
+ bindingMap.putIfAbsent(setKey, setFactory);
+ } else if (Map.class.isAssignableFrom(rawType)) {
+ final Key<Map<String, T>> mapKey = TypeUtil.cast(key);
+ final Supplier<Map<String, T>> mapFactory = () -> getPluginMap(key.getParameterizedTypeArgument(1));
+ bindingMap.putIfAbsent(mapKey, mapFactory);
+ } else if (Iterable.class.isAssignableFrom(rawType)) {
+ final Key<Iterable<T>> iterableKey = TypeUtil.cast(key);
+ final Supplier<Iterable<T>> iterableFactory = () -> getPluginList(key.getParameterizedTypeArgument(0));
+ bindingMap.putIfAbsent(iterableKey, iterableFactory);
+ } else if (Optional.class.isAssignableFrom(rawType)) {
+ final Key<Optional<T>> optionalKey = TypeUtil.cast(key);
+ final Supplier<Optional<T>> optionalFactory = () -> getOptionalPlugin(key.getParameterizedTypeArgument(0));
+ bindingMap.putIfAbsent(optionalKey, optionalFactory);
+ } else {
+ throw new InjectException("Cannot inject plugins into " + key);
+ }
return bindingMap.get(key, aliases).getSupplier();
}
+
+ // Optional<T> injection
if (rawType == Optional.class) {
final Key<Optional<T>> optionalKey = TypeUtil.cast(key);
- final Supplier<Optional<T>> optionalFactory =
- createOptionalFactory(key.getParameterizedTypeArgument(0), aliases, node, chain);
+ final Supplier<Optional<T>> optionalFactory = () ->
+ getOptionalInstance(key.getParameterizedTypeArgument(0), aliases, node, chain);
bindingMap.put(optionalKey, optionalFactory);
return bindingMap.get(key, aliases).getSupplier();
}
+
+ // generic T injection
final Supplier<T> instanceSupplier = () -> {
final StringBuilder debugLog = new StringBuilder();
final T instance = TypeUtil.cast(getInjectableInstance(key, node, chain, debugLog));
@@ -275,24 +313,57 @@ class DefaultInjector implements Injector {
}
private Supplier<PluginCategory> createPluginCategoryFactory(final Key<PluginCategory> key) {
- return LazyValue.from(() -> {
- final String categoryName = key.getName();
- final Binding<List<String>> pluginPackagesBinding = bindingMap.get(Keys.PLUGIN_PACKAGES_KEY, List.of());
- final List<String> pluginPackages = pluginPackagesBinding != null ? pluginPackagesBinding.getSupplier().get() : List.of();
- // TODO: can this be a bean, too? might be tricky in OSGi
- return PluginRegistry.getInstance().getCategory(categoryName, pluginPackages);
- });
+ return LazyValue.from(() -> getInstance(PluginRegistry.class).getCategory(key.getName(), getPluginPackages()));
+ }
+
+ private List<String> getPluginPackages() {
+ final Binding<List<String>> pluginPackagesBinding = bindingMap.get(Keys.PLUGIN_PACKAGES_KEY, List.of());
+ return pluginPackagesBinding != null ? pluginPackagesBinding.getSupplier().get() : List.of();
+ }
+
+ private <T> Stream<PluginType<? extends T>> streamPluginsFromCategory(final Key<T> itemKey) {
+ if (itemKey == null) {
+ return Stream.empty();
+ }
+ final PluginCategory category = getInstance(PluginRegistry.class).getCategory(itemKey.getName(), getPluginPackages());
+ final Type type = itemKey.getType();
+ final Class<T> rawType = itemKey.getRawType();
+ return category.stream()
+ .filter(pluginType -> rawType.isInterface() && TypeUtil.isAssignable(type, pluginType.getPluginClass()) ||
+ TypeUtil.isAssignable(type, pluginType.getPluginClass()))
+ .sorted(Comparator.comparing(PluginType::getPluginClass, PluginOrder.COMPARATOR))
+ .map(TypeUtil::cast);
}
- private <T> Supplier<Optional<T>> createOptionalFactory(
+ private <T> Stream<T> streamPluginInstancesFromCategory(final Key<T> key) {
+ return streamPluginsFromCategory(key).map(pluginType -> getInstance(pluginType.getPluginClass()));
+ }
+
+ private <T> Set<T> getPluginSet(final Key<T> key) {
+ return streamPluginInstancesFromCategory(key).collect(Collectors.toCollection(LinkedHashSet::new));
+ }
+
+ private <T> Map<String, T> getPluginMap(final Key<T> key) {
+ return streamPluginsFromCategory(key).collect(
+ Collectors.toMap(PluginType::getKey, pluginType -> getInstance(pluginType.getPluginClass()), (lhs, rhs) -> lhs,
+ LinkedHashMap::new));
+ }
+
+ private <T> List<T> getPluginList(final Key<T> key) {
+ return streamPluginInstancesFromCategory(key).collect(Collectors.toList());
+ }
+
+ public <T> Optional<T> getOptionalPlugin(final Key<T> key) {
+ return streamPluginInstancesFromCategory(key).findFirst();
+ }
+
+ private <T> Optional<T> getOptionalInstance(
final Key<T> key, final Collection<String> aliases, final Node node, final Set<Key<?>> chain) {
- return () -> {
- try {
- return Optional.ofNullable(getFactory(key, aliases, node, chain).get());
- } catch (final PluginException e) {
- return Optional.empty();
- }
- };
+ try {
+ return Optional.ofNullable(getFactory(key, aliases, node, chain).get());
+ } catch (final PluginException e) {
+ return Optional.empty();
+ }
}
private Object getInjectableInstance(
@@ -361,13 +432,11 @@ class DefaultInjector implements Injector {
if (conflictingConverter != null) {
final boolean overridable;
if (converter instanceof Comparable) {
- @SuppressWarnings("unchecked")
- final Comparable<TypeConverter<?>> comparableConverter =
+ @SuppressWarnings("unchecked") final Comparable<TypeConverter<?>> comparableConverter =
(Comparable<TypeConverter<?>>) converter;
overridable = comparableConverter.compareTo(conflictingConverter) < 0;
} else if (conflictingConverter instanceof Comparable) {
- @SuppressWarnings("unchecked")
- final Comparable<TypeConverter<?>> comparableConflictingConverter =
+ @SuppressWarnings("unchecked") final Comparable<TypeConverter<?>> comparableConflictingConverter =
(Comparable<TypeConverter<?>>) conflictingConverter;
overridable = comparableConflictingConverter.compareTo(converter) > 0;
} else {
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/CategoryNameProvider.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/CategoryNameProvider.java
new file mode 100644
index 0000000000..9772498072
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/name/CategoryNameProvider.java
@@ -0,0 +1,30 @@
+/*
+ * 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.plugins.name;
+
+import org.apache.logging.log4j.plugins.Category;
+import org.apache.logging.log4j.util.Strings;
+
+import java.util.Optional;
+
+public class CategoryNameProvider implements AnnotatedElementNameProvider<Category> {
+ @Override
+ public Optional<String> getSpecifiedName(final Category annotation) {
+ return Strings.trimToOptional(annotation.value());
+ }
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/Activator.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/Activator.java
index 6ff109df5d..f17cd67d7c 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/Activator.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/Activator.java
@@ -44,34 +44,22 @@ public final class Activator implements BundleActivator, SynchronousBundleListen
private static final Logger LOGGER = StatusLogger.getLogger();
private static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
+ public static final String CORE_MODULE_NAME = "org.apache.logging.log4j.core";
private final AtomicReference<BundleContext> contextRef = new AtomicReference<>();
+ private int state = Bundle.UNINSTALLED;
+
+ private ServiceReference<PluginRegistry> pluginRegistryServiceReference;
+ private PluginRegistry pluginRegistry;
+
@Override
public void start(final BundleContext bundleContext) throws Exception {
- loadPlugins(bundleContext);
+ state = Bundle.STARTING;
bundleContext.addBundleListener(this);
- final Bundle[] bundles = bundleContext.getBundles();
- for (final Bundle bundle : bundles) {
- loadPlugins(bundle);
- }
- scanInstalledBundlesForPlugins(bundleContext);
this.contextRef.compareAndSet(null, bundleContext);
}
- private void loadPlugins(final BundleContext bundleContext) {
- final PluginRegistry pluginRegistry = PluginRegistry.getInstance();
- try {
- final Collection<ServiceReference<PluginService>> serviceReferences = bundleContext.getServiceReferences(PluginService.class, null);
- for (final ServiceReference<PluginService> serviceReference : serviceReferences) {
- final PluginService pluginService = bundleContext.getService(serviceReference);
- pluginRegistry.loadFromBundle(bundleContext.getBundle().getBundleId(), pluginService.getCategories());
- }
- } catch (final InvalidSyntaxException ex) {
- LOGGER.error("Error accessing Plugins", ex);
- }
- }
-
private void loadPlugins(final Bundle bundle) {
if (bundle.getState() == Bundle.UNINSTALLED) {
return;
@@ -83,10 +71,17 @@ public final class Activator implements BundleActivator, SynchronousBundleListen
if (bundleContext == null) {
LOGGER.debug("Bundle {} has no context (state={}), skipping loading plugins", bundle.getSymbolicName(), toStateString(bundle.getState()));
} else {
- loadPlugins(bundleContext);
+ final Collection<ServiceReference<PluginService>> serviceReferences =
+ bundleContext.getServiceReferences(PluginService.class, null);
+ for (final ServiceReference<PluginService> serviceReference : serviceReferences) {
+ final PluginService pluginService = bundleContext.getService(serviceReference);
+ pluginRegistry.loadFromBundle(bundleContext.getBundle().getBundleId(), pluginService.getCategories());
+ }
}
} catch (final SecurityException e) {
LOGGER.debug("Cannot access bundle [{}] contents. Ignoring.", bundle.getSymbolicName(), e);
+ } catch (final InvalidSyntaxException ex) {
+ LOGGER.error("Error accessing Plugins", ex);
} catch (final Exception e) {
LOGGER.warn("Problem checking bundle {} for Log4j 2 provider.", bundle.getSymbolicName(), e);
}
@@ -117,28 +112,28 @@ public final class Activator implements BundleActivator, SynchronousBundleListen
}
}
- private static void scanInstalledBundlesForPlugins(final BundleContext context) {
+ private void scanInstalledBundlesForPlugins(final BundleContext context) {
final Bundle[] bundles = context.getBundles();
for (final Bundle bundle : bundles) {
- // TODO: bundle state can change during this
scanBundleForPlugins(bundle);
}
}
- private static void scanBundleForPlugins(final Bundle bundle) {
+ private void scanBundleForPlugins(final Bundle bundle) {
final long bundleId = bundle.getBundleId();
// LOG4J2-920: don't scan system bundle for plugins
if (bundle.getState() == Bundle.ACTIVE && bundleId != 0) {
LOGGER.trace("Scanning bundle [{}, id={}] for plugins.", bundle.getSymbolicName(), bundleId);
final ClassLoader classLoader = bundle.adapt(BundleWiring.class).getClassLoader();
- PluginRegistry.getInstance().loadFromBundle(bundleId, classLoader);
+ pluginRegistry.loadFromBundle(bundleId, classLoader);
}
}
- private static void stopBundlePlugins(final Bundle bundle) {
+ private void stopBundlePlugins(final Bundle bundle) {
LOGGER.trace("Stopping bundle [{}] plugins.", bundle.getSymbolicName());
- // TODO: plugin lifecycle code
- PluginRegistry.getInstance().clearBundlePlugins(bundle.getBundleId());
+ if (pluginRegistry != null) {
+ pluginRegistry.clearBundlePlugins(bundle.getBundleId());
+ }
}
@Override
@@ -153,15 +148,33 @@ public final class Activator implements BundleActivator, SynchronousBundleListen
@Override
public void bundleChanged(final BundleEvent event) {
+ final Bundle bundle = event.getBundle();
switch (event.getType()) {
- // FIXME: STARTING instead of STARTED?
- case BundleEvent.STARTED:
- loadPlugins(event.getBundle());
- scanBundleForPlugins(event.getBundle());
+ case BundleEvent.STARTING:
+ if (CORE_MODULE_NAME.equals(bundle.getSymbolicName()) && state != Bundle.ACTIVE) {
+ break;
+ }
break;
+ case BundleEvent.STARTED:
+ if (CORE_MODULE_NAME.equals(bundle.getSymbolicName()) && state != Bundle.ACTIVE) {
+ final BundleContext bundleContext = contextRef.get();
+ pluginRegistryServiceReference =
+ bundleContext.getServiceReference(PluginRegistry.class);
+ pluginRegistry = bundleContext.getService(pluginRegistryServiceReference);
+ scanInstalledBundlesForPlugins(bundleContext);
+ state = Bundle.ACTIVE;
+ } else if (state == Bundle.ACTIVE) {
+ loadPlugins(bundle);
+ scanBundleForPlugins(bundle);
+ }
+
case BundleEvent.STOPPING:
- stopBundlePlugins(event.getBundle());
+ if (CORE_MODULE_NAME.equals(bundle.getSymbolicName()) && pluginRegistry != null) {
+ pluginRegistry = null;
+ contextRef.get().ungetService(pluginRegistryServiceReference);
+ }
+ stopBundlePlugins(bundle);
break;
default:
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
index d665fac555..867e7d2edf 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
@@ -20,6 +20,7 @@ package org.apache.logging.log4j.plugins.util;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.plugins.Plugin;
import org.apache.logging.log4j.plugins.PluginAliases;
+import org.apache.logging.log4j.plugins.Singleton;
import org.apache.logging.log4j.plugins.processor.PluginCache;
import org.apache.logging.log4j.plugins.processor.PluginEntry;
import org.apache.logging.log4j.plugins.processor.PluginService;
@@ -41,13 +42,13 @@ import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Supplier;
import static org.apache.logging.log4j.util.Unbox.box;
/**
* Registry singleton for PluginType maps partitioned by source type and then by category names.
*/
+@Singleton
public class PluginRegistry {
/**
@@ -57,8 +58,6 @@ public class PluginRegistry {
"META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat";
private static final Logger LOGGER = StatusLogger.getLogger();
- private static final Supplier<PluginRegistry> INSTANCE = new LazyValue<>(PluginRegistry::new);
-
/**
* Contains plugins found from {@link PluginService} services and legacy Log4j2Plugins.dat cache files in the main CLASSPATH.
*/
@@ -99,19 +98,6 @@ public class PluginRegistry {
*/
private final Map<String, Categories> pluginCategoriesByPackage = new ConcurrentHashMap<>();
- private PluginRegistry() {
- }
-
- /**
- * Returns the global PluginRegistry instance.
- *
- * @return the global PluginRegistry instance.
- * @since 2.1
- */
- public static PluginRegistry getInstance() {
- return INSTANCE.get();
- }
-
/**
* Resets the registry to an empty state.
*/
diff --git a/src/site/asciidoc/manual/dependencyinjection.adoc b/src/site/asciidoc/manual/dependencyinjection.adoc
index 272ba4b461..2f292c53c1 100644
--- a/src/site/asciidoc/manual/dependencyinjection.adoc
+++ b/src/site/asciidoc/manual/dependencyinjection.adoc
@@ -141,6 +141,7 @@ Some of these bindings were previously configured through various system propert
* `org.apache.logging.log4j.core.ContextDataInjector`
* `org.apache.logging.log4j.core.config.ConfigurationFactory`
+* `org.apache.logging.log4j.core.config.composite.MergeStrategy`
* `org.apache.logging.log4j.core.impl.LogEventFactory`
* `org.apache.logging.log4j.core.lookup.InterpolatorFactory`
* `org.apache.logging.log4j.core.lookup.StrSubstitutor`
diff --git a/src/site/asciidoc/manual/plugins.adoc b/src/site/asciidoc/manual/plugins.adoc
index f198c86934..af3271bd0e 100644
--- a/src/site/asciidoc/manual/plugins.adoc
+++ b/src/site/asciidoc/manual/plugins.adoc
@@ -24,11 +24,11 @@ extend the PatternLayout class and add them via code. One goal of Log4j
2 is to make extending it extremely easy through the use of plugins.
In Log4j 2 a plugin is declared by adding a
-link:../log4j-core/apidocs/org/apache/logging/log4j/core/config/plugins/Plugin.html[`@Plugin`]
+link:../log4j-plugins/apidocs/org/apache/logging/log4j/plugins/Plugin.html[`@Plugin`]
annotation to the class declaration. During initialization the
link:../log4j-core/apidocs/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`]
will invoke the
-link:../log4j-core/apidocs/org/apache/logging/log4j/core/config/plugins/util/PluginManager.html[`PluginManager`]
+link:../log4j-plugins/apidocs/org/apache/logging/log4j/plugins/util/PluginRegistry.html[`PluginRegistry`]
to load the built-in Log4j plugins as well as any custom plugins. The
`Injector` locates plugins by looking in five places:
[logging-log4j2] 01/02: LOG4J2-3496 - Add Optional injection support, plugin interface filtering
Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
mattsicker pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit c80a381d09d8475b2c5ea7f30f3f279606bf6615
Author: Matt Sicker <ma...@apache.org>
AuthorDate: Sat May 14 19:41:27 2022 -0500
LOG4J2-3496 - Add Optional injection support, plugin interface filtering
- Adds support for using Optional<T> as an injection point where any errors involved in looking up an instance are collapsed into an empty value.
- Adds additional plugin metadata caching for implemented interfaces to help with filtering plugins by type.
- Moves PluginBundle into PluginRegistry.Categories
Signed-off-by: Matt Sicker <ma...@apache.org>
---
.../json/resolver/TemplateResolverFactories.java | 30 ++-
.../resolver/TemplateResolverInterceptors.java | 31 ++-
.../log4j/plugin/processor/PluginProcessor.java | 81 +++++--
.../logging/log4j/plugins/di/InjectorTest.java | 36 +++
.../logging/log4j/plugins/di/DefaultInjector.java | 44 +++-
.../org/apache/logging/log4j/plugins/di/Key.java | 7 +
.../logging/log4j/plugins/osgi/Activator.java | 6 +-
.../log4j/plugins/processor/PluginEntry.java | 23 +-
.../log4j/plugins/processor/PluginService.java | 25 +-
.../logging/log4j/plugins/util/PluginBundle.java | 88 -------
.../logging/log4j/plugins/util/PluginCategory.java | 54 ++---
.../logging/log4j/plugins/util/PluginRegistry.java | 260 ++++++++++-----------
.../logging/log4j/plugins/util/PluginType.java | 18 +-
13 files changed, 367 insertions(+), 336 deletions(-)
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolverFactories.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolverFactories.java
index 57c0187143..8bd503b358 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolverFactories.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolverFactories.java
@@ -20,7 +20,6 @@ import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.plugins.di.Key;
import org.apache.logging.log4j.plugins.util.PluginCategory;
-import org.apache.logging.log4j.plugins.util.PluginType;
import org.apache.logging.log4j.plugins.util.TypeUtil;
import org.apache.logging.log4j.status.StatusLogger;
@@ -80,22 +79,19 @@ public final class TemplateResolverFactories {
final Class<V> valueClass,
final Class<C> contextClass) {
final Map<String, F> factoryByName = new LinkedHashMap<>();
- for (final PluginType<?> pluginType : factoryPlugins) {
- final Class<?> pluginClass = pluginType.getPluginClass();
- final boolean pluginClassMatched =
- TemplateResolverFactory.class.isAssignableFrom(pluginClass);
- if (pluginClassMatched) {
- @SuppressWarnings("rawtypes")
- final Class<? extends TemplateResolverFactory> factoryClass =
- pluginClass.asSubclass(TemplateResolverFactory.class);
- final TemplateResolverFactory<?, ?> rawFactory =
- configuration.getComponent(Key.forClass(factoryClass));
- final F factory = castFactory(valueClass, contextClass, rawFactory);
- if (factory != null) {
- addFactory(factoryByName, factory);
- }
- }
- }
+ factoryPlugins.forEachMatching(
+ pluginType -> pluginType.getImplementedInterfaces().contains(TemplateResolverFactory.class),
+ pluginType -> {
+ @SuppressWarnings("rawtypes")
+ final Class<? extends TemplateResolverFactory> factoryClass =
+ pluginType.getPluginClass().asSubclass(TemplateResolverFactory.class);
+ final TemplateResolverFactory<?, ?> rawFactory =
+ configuration.getComponent(Key.forClass(factoryClass));
+ final F factory = castFactory(valueClass, contextClass, rawFactory);
+ if (factory != null) {
+ addFactory(factoryByName, factory);
+ }
+ });
return factoryByName;
}
diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolverInterceptors.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolverInterceptors.java
index d793c8f139..99dc926647 100644
--- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolverInterceptors.java
+++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolverInterceptors.java
@@ -20,7 +20,6 @@ import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.plugins.di.Key;
import org.apache.logging.log4j.plugins.util.PluginCategory;
-import org.apache.logging.log4j.plugins.util.PluginType;
import org.apache.logging.log4j.plugins.util.TypeUtil;
import org.apache.logging.log4j.status.StatusLogger;
@@ -77,23 +76,21 @@ public class TemplateResolverInterceptors {
final Class<V> valueClass,
final Class<C> contextClass) {
final List<I> interceptors = new LinkedList<>();
- for (final PluginType<?> pluginType : interceptorPlugins) {
- final Class<?> pluginClass = pluginType.getPluginClass();
- final boolean pluginClassMatched =
- TemplateResolverInterceptor.class.isAssignableFrom(pluginClass);
- if (pluginClassMatched) {
- @SuppressWarnings("rawtypes")
- final Class<? extends TemplateResolverInterceptor> interceptorClass =
- pluginClass.asSubclass(TemplateResolverInterceptor.class);
- final TemplateResolverInterceptor<?, ?> rawInterceptor =
- configuration.getComponent(Key.forClass(interceptorClass));
- final I interceptor =
- castInterceptor(valueClass, contextClass, rawInterceptor);
- if (interceptor != null) {
- interceptors.add(interceptor);
+ interceptorPlugins.forEachMatching(
+ pluginType -> pluginType.getImplementedInterfaces().contains(TemplateResolverInterceptor.class),
+ pluginType -> {
+ @SuppressWarnings("rawtypes")
+ final Class<? extends TemplateResolverInterceptor> interceptorClass =
+ pluginType.getPluginClass().asSubclass(TemplateResolverInterceptor.class);
+ final TemplateResolverInterceptor<?, ?> rawInterceptor =
+ configuration.getComponent(Key.forClass(interceptorClass));
+ final I interceptor =
+ castInterceptor(valueClass, contextClass, rawInterceptor);
+ if (interceptor != null) {
+ interceptors.add(interceptor);
+ }
}
- }
- }
+ );
return interceptors;
}
diff --git a/log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/PluginProcessor.java b/log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/PluginProcessor.java
index cf6a7a6ecd..9dca19fa3f 100644
--- a/log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/PluginProcessor.java
+++ b/log4j-plugin-processor/src/main/java/org/apache/logging/log4j/plugin/processor/PluginProcessor.java
@@ -30,10 +30,16 @@ import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementKindVisitor9;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleElementVisitor8;
+import javax.lang.model.util.SimpleTypeVisitor9;
+import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject;
import javax.tools.JavaFileObject;
@@ -45,6 +51,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -85,7 +92,7 @@ public class PluginProcessor extends AbstractProcessor {
return false;
}
messager.printMessage(Kind.NOTE, "Retrieved " + elements.size() + " Plugin elements");
- List<PluginEntry> list = new ArrayList<>();
+ List<PluginEntryMirror> list = new ArrayList<>();
packageName = collectPlugins(packageName, elements, list);
writeClassFile(packageName, list);
writeServiceFile(packageName);
@@ -100,26 +107,23 @@ public class PluginProcessor extends AbstractProcessor {
processingEnv.getMessager().printMessage(Kind.ERROR, message);
}
- private String collectPlugins(String packageName, final Iterable<? extends Element> elements, List<PluginEntry> list) {
+ private String collectPlugins(String packageName, final Iterable<? extends Element> elements, List<PluginEntryMirror> list) {
boolean calculatePackage = packageName == null;
final Elements elementUtils = processingEnv.getElementUtils();
- final ElementVisitor<PluginEntry, Plugin> pluginVisitor = new PluginElementVisitor(elementUtils);
- final ElementVisitor<Collection<PluginEntry>, Plugin> pluginAliasesVisitor = new PluginAliasesElementVisitor(
+ final ElementVisitor<PluginEntryMirror, Plugin> pluginVisitor = new PluginElementVisitor(elementUtils);
+ final ElementVisitor<Collection<PluginEntryMirror>, Plugin> pluginAliasesVisitor = new PluginAliasesElementVisitor(
elementUtils);
for (final Element element : elements) {
final Plugin plugin = element.getAnnotation(Plugin.class);
if (plugin == null) {
continue;
}
- final PluginEntry entry = element.accept(pluginVisitor, plugin);
+ final PluginEntryMirror entry = element.accept(pluginVisitor, plugin);
list.add(entry);
if (calculatePackage) {
packageName = calculatePackage(elementUtils, element, packageName);
}
- final Collection<PluginEntry> entries = element.accept(pluginAliasesVisitor, plugin);
- for (final PluginEntry pluginEntry : entries) {
- list.add(pluginEntry);
- }
+ list.addAll(element.accept(pluginAliasesVisitor, plugin));
}
return packageName;
}
@@ -151,7 +155,7 @@ public class PluginProcessor extends AbstractProcessor {
}
}
- private void writeClassFile(String pkg, List<PluginEntry> list) {
+ private void writeClassFile(String pkg, List<PluginEntryMirror> list) {
String fqcn = createFqcn(pkg);
try (final PrintWriter writer = createSourceFile(fqcn)) {
writer.println("package " + pkg + ".plugins;");
@@ -165,14 +169,19 @@ public class PluginProcessor extends AbstractProcessor {
StringBuilder sb = new StringBuilder();
int max = list.size() - 1;
for (int i = 0; i < list.size(); ++i) {
- PluginEntry entry = list.get(i);
+ PluginEntryMirror mirror = list.get(i);
+ final PluginEntry entry = mirror.entry;
sb.append(" ").append("new PluginEntry(\"");
sb.append(entry.getKey()).append("\", \"");
sb.append(entry.getClassName()).append("\", \"");
sb.append(entry.getName()).append("\", ");
sb.append(entry.isPrintable()).append(", ");
sb.append(entry.isDefer()).append(", \"");
- sb.append(entry.getCategory()).append("\")");
+ sb.append(entry.getCategory()).append("\"");
+ for (final Name implementedInterface : getImplementedInterfaces(mirror.element.asType())) {
+ sb.append(", ").append(implementedInterface).append(".class");
+ }
+ sb.append(')');
if (i < max) {
sb.append(",");
}
@@ -199,10 +208,20 @@ public class PluginProcessor extends AbstractProcessor {
return packageName + ".plugins.Log4jPlugins";
}
+ private static class PluginEntryMirror {
+ private final TypeElement element;
+ private final PluginEntry entry;
+
+ private PluginEntryMirror(final TypeElement element, final PluginEntry entry) {
+ this.element = element;
+ this.entry = entry;
+ }
+ }
+
/**
* ElementVisitor to scan the Plugin annotation.
*/
- private static class PluginElementVisitor extends SimpleElementVisitor8<PluginEntry, Plugin> {
+ private static class PluginElementVisitor extends SimpleElementVisitor8<PluginEntryMirror, Plugin> {
private final Elements elements;
@@ -211,7 +230,7 @@ public class PluginProcessor extends AbstractProcessor {
}
@Override
- public PluginEntry visitType(final TypeElement e, final Plugin plugin) {
+ public PluginEntryMirror visitType(final TypeElement e, final Plugin plugin) {
Objects.requireNonNull(plugin, "Plugin annotation is null.");
final PluginEntry entry = new PluginEntry();
entry.setKey(plugin.name().toLowerCase(Locale.US));
@@ -220,10 +239,34 @@ public class PluginProcessor extends AbstractProcessor {
entry.setPrintable(plugin.printObject());
entry.setDefer(plugin.deferChildren());
entry.setCategory(plugin.category());
- return entry;
+ return new PluginEntryMirror(e, entry);
}
}
+ private Set<Name> getImplementedInterfaces(final TypeMirror base) {
+ final Set<Name> implementedInterfaces = new LinkedHashSet<>();
+ final Types types = processingEnv.getTypeUtils();
+ base.accept(new SimpleTypeVisitor9<Void, Void>() {
+ @Override
+ public Void visitDeclared(final DeclaredType t, final Void unused) {
+ for (final TypeMirror directSupertype : types.directSupertypes(t)) {
+ directSupertype.accept(this, null);
+ }
+ t.asElement().accept(new ElementKindVisitor9<Void, Void>() {
+ @Override
+ public Void visitTypeAsInterface(final TypeElement e, final Void unused) {
+ if (e.getModifiers().contains(Modifier.PUBLIC)) {
+ implementedInterfaces.add(e.getQualifiedName());
+ }
+ return null;
+ }
+ }, null);
+ return null;
+ }
+ }, null);
+ return implementedInterfaces;
+ }
+
private String commonPrefix(String str1, String str2) {
int minLength = Math.min(str1.length(), str2.length());
for (int i = 0; i < minLength; i++) {
@@ -241,7 +284,7 @@ public class PluginProcessor extends AbstractProcessor {
/**
* ElementVisitor to scan the PluginAliases annotation.
*/
- private static class PluginAliasesElementVisitor extends SimpleElementVisitor8<Collection<PluginEntry>, Plugin> {
+ private static class PluginAliasesElementVisitor extends SimpleElementVisitor8<Collection<PluginEntryMirror>, Plugin> {
private final Elements elements;
@@ -251,12 +294,12 @@ public class PluginProcessor extends AbstractProcessor {
}
@Override
- public Collection<PluginEntry> visitType(final TypeElement e, final Plugin plugin) {
+ public Collection<PluginEntryMirror> visitType(final TypeElement e, final Plugin plugin) {
final PluginAliases aliases = e.getAnnotation(PluginAliases.class);
if (aliases == null) {
return DEFAULT_VALUE;
}
- final Collection<PluginEntry> entries = new ArrayList<>(aliases.value().length);
+ final Collection<PluginEntryMirror> entries = new ArrayList<>(aliases.value().length);
for (final String alias : aliases.value()) {
final PluginEntry entry = new PluginEntry();
entry.setKey(alias.toLowerCase(Locale.US));
@@ -265,7 +308,7 @@ public class PluginProcessor extends AbstractProcessor {
entry.setPrintable(plugin.printObject());
entry.setDefer(plugin.deferChildren());
entry.setCategory(plugin.category());
- entries.add(entry);
+ entries.add(new PluginEntryMirror(e, entry));
}
return entries;
}
diff --git a/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/di/InjectorTest.java b/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/di/InjectorTest.java
index 8f93480a0b..5fb1ba3f5d 100644
--- a/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/di/InjectorTest.java
+++ b/log4j-plugins-test/src/test/java/org/apache/logging/log4j/plugins/di/InjectorTest.java
@@ -44,6 +44,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
@@ -277,6 +278,12 @@ class InjectorTest {
.hasMessage("No @Inject constructors or no-arg constructor found for " + key);
}
+ @Test
+ void optionalUnknownInstance() {
+ final Key<Optional<UnknownInstance>> key = new Key<>() {};
+ assertThat(DI.createInjector().getInstance(key)).isEmpty();
+ }
+
@Singleton
static class DeferredSingleton {
private final int id;
@@ -753,4 +760,33 @@ class InjectorTest {
final MultipleElements instance = DI.createInjector(NoOpStringSubstitution.class).configure(root);
assertThat(instance.objects).hasSize(2);
}
+
+ static class OptionalInjection {
+ @Inject
+ Optional<BeanA> a;
+
+ final BeanB b;
+ BeanC c;
+
+ @Inject
+ OptionalInjection(final Optional<BeanB> b) {
+ this.b = b.orElse(null);
+ }
+
+ @Inject
+ void setC(final Optional<BeanC> c) {
+ this.c = c.orElse(null);
+ }
+ }
+
+ @Test
+ void optionalInjection() {
+ final Injector injector = DI.createInjector();
+ final OptionalInjection first = injector.getInstance(OptionalInjection.class);
+ final OptionalInjection second = injector.getInstance(OptionalInjection.class);
+ assertThat(first.a).isPresent().isEqualTo(second.a);
+ assertThat(first.b).isNotNull().isEqualTo(second.b);
+ assertThat(first.c).isNotNull().isNotSameAs(second.c);
+ assertThat(second.c).isNotNull();
+ }
}
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 2ae9342a63..fe2c0097d8 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
@@ -22,6 +22,7 @@ import org.apache.logging.log4j.plugins.FactoryType;
import org.apache.logging.log4j.plugins.Inject;
import org.apache.logging.log4j.plugins.Named;
import org.apache.logging.log4j.plugins.Node;
+import org.apache.logging.log4j.plugins.PluginException;
import org.apache.logging.log4j.plugins.QualifierType;
import org.apache.logging.log4j.plugins.ScopeType;
import org.apache.logging.log4j.plugins.Singleton;
@@ -64,6 +65,7 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.UnknownFormatConversionException;
@@ -251,17 +253,16 @@ class DefaultInjector implements Injector {
final Class<T> rawType = key.getRawType();
final Scope scope = getScopeForType(rawType);
if (rawType == PluginCategory.class && key.getQualifierType() == Named.class) {
- final Supplier<T> factory = () -> {
- final String categoryName = key.getName();
- final Binding<List<String>> pluginPackagesBinding = bindingMap.get(Keys.PLUGIN_PACKAGES_KEY, List.of());
- final List<String> pluginPackages = pluginPackagesBinding != null ? pluginPackagesBinding.getSupplier().get() : List.of();
- // TODO: can this be a bean, too? might be tricky in OSGi
- final var registry = PluginRegistry.getInstance();
- // Then, iterate over packages registered in PluginManager
- final var category = registry.getCategory(categoryName, pluginPackages);
- return TypeUtil.cast(category);
- };
- bindingMap.put(key, scope.get(key, factory));
+ final Key<PluginCategory> pluginCategoryKey = TypeUtil.cast(key);
+ final Supplier<PluginCategory> pluginCategoryFactory = createPluginCategoryFactory(pluginCategoryKey);
+ bindingMap.put(pluginCategoryKey, pluginCategoryFactory);
+ return bindingMap.get(key, aliases).getSupplier();
+ }
+ if (rawType == Optional.class) {
+ final Key<Optional<T>> optionalKey = TypeUtil.cast(key);
+ final Supplier<Optional<T>> optionalFactory =
+ createOptionalFactory(key.getParameterizedTypeArgument(0), aliases, node, chain);
+ bindingMap.put(optionalKey, optionalFactory);
return bindingMap.get(key, aliases).getSupplier();
}
final Supplier<T> instanceSupplier = () -> {
@@ -273,6 +274,27 @@ class DefaultInjector implements Injector {
return bindingMap.bindIfAbsent(key, scope.get(key, instanceSupplier));
}
+ private Supplier<PluginCategory> createPluginCategoryFactory(final Key<PluginCategory> key) {
+ return LazyValue.from(() -> {
+ final String categoryName = key.getName();
+ final Binding<List<String>> pluginPackagesBinding = bindingMap.get(Keys.PLUGIN_PACKAGES_KEY, List.of());
+ final List<String> pluginPackages = pluginPackagesBinding != null ? pluginPackagesBinding.getSupplier().get() : List.of();
+ // TODO: can this be a bean, too? might be tricky in OSGi
+ return PluginRegistry.getInstance().getCategory(categoryName, pluginPackages);
+ });
+ }
+
+ private <T> Supplier<Optional<T>> createOptionalFactory(
+ final Key<T> key, final Collection<String> aliases, final Node node, final Set<Key<?>> chain) {
+ return () -> {
+ try {
+ return Optional.ofNullable(getFactory(key, aliases, node, chain).get());
+ } catch (final PluginException e) {
+ return Optional.empty();
+ }
+ };
+ }
+
private Object getInjectableInstance(
final Key<?> key, final Node node, final Set<Key<?>> chain, final StringBuilder debugLog) {
final Class<?> rawType = key.getRawType();
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Key.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Key.java
index 83686604c5..62e33e6afa 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Key.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/di/Key.java
@@ -118,6 +118,13 @@ public class Key<T> {
return null;
}
+ public final <P> Key<P> getParameterizedTypeArgument(final int arg) {
+ if (type instanceof ParameterizedType) {
+ return forQualifiedNamedType(qualifierType, name, ((ParameterizedType) type).getActualTypeArguments()[arg]);
+ }
+ return null;
+ }
+
@Override
public final boolean equals(final Object o) {
if (o == this) {
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/Activator.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/Activator.java
index ea7b76be3f..6ff109df5d 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/Activator.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/osgi/Activator.java
@@ -65,7 +65,7 @@ public final class Activator implements BundleActivator, SynchronousBundleListen
final Collection<ServiceReference<PluginService>> serviceReferences = bundleContext.getServiceReferences(PluginService.class, null);
for (final ServiceReference<PluginService> serviceReference : serviceReferences) {
final PluginService pluginService = bundleContext.getService(serviceReference);
- pluginRegistry.loadFromBundle(pluginService.getBundle(), bundleContext.getBundle().getBundleId());
+ pluginRegistry.loadFromBundle(bundleContext.getBundle().getBundleId(), pluginService.getCategories());
}
} catch (final InvalidSyntaxException ex) {
LOGGER.error("Error accessing Plugins", ex);
@@ -130,8 +130,8 @@ public final class Activator implements BundleActivator, SynchronousBundleListen
// LOG4J2-920: don't scan system bundle for plugins
if (bundle.getState() == Bundle.ACTIVE && bundleId != 0) {
LOGGER.trace("Scanning bundle [{}, id={}] for plugins.", bundle.getSymbolicName(), bundleId);
- PluginRegistry.getInstance().loadFromBundle(bundleId,
- bundle.adapt(BundleWiring.class).getClassLoader());
+ final ClassLoader classLoader = bundle.adapt(BundleWiring.class).getClassLoader();
+ PluginRegistry.getInstance().loadFromBundle(bundleId, classLoader);
}
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginEntry.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginEntry.java
index 92b19ce9f5..c35f9aa192 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginEntry.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginEntry.java
@@ -26,7 +26,8 @@ public class PluginEntry {
private String name;
private boolean printable;
private boolean defer;
- private transient String category;
+ private String category;
+ private Class<?>[] interfaces;
public PluginEntry() {
}
@@ -40,6 +41,18 @@ public class PluginEntry {
this.category = category;
}
+ public PluginEntry(
+ final String key, final String className, final String name, final boolean printable, final boolean defer,
+ final String category, final Class<?>... interfaces) {
+ this.key = key;
+ this.className = className;
+ this.name = name;
+ this.printable = printable;
+ this.defer = defer;
+ this.category = category;
+ this.interfaces = interfaces;
+ }
+
public String getKey() {
return key;
}
@@ -88,6 +101,14 @@ public class PluginEntry {
this.category = category;
}
+ public Class<?>[] getInterfaces() {
+ return interfaces;
+ }
+
+ public void setInterfaces(final Class<?>... interfaces) {
+ this.interfaces = interfaces;
+ }
+
@Override
public String toString() {
return "PluginEntry [key=" + key + ", className=" + className + ", name=" + name + ", printable=" + printable
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java
index 69233c64d4..ae2f0e3ef3 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/processor/PluginService.java
@@ -16,10 +16,13 @@
*/
package org.apache.logging.log4j.plugins.processor;
-import org.apache.logging.log4j.plugins.util.PluginBundle;
import org.apache.logging.log4j.plugins.util.PluginCategory;
import org.apache.logging.log4j.plugins.util.PluginType;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+
/**
* Provides {@linkplain PluginEntry plugin metadata} for a module. Implementation classes are typically generated by
* {@code PluginProcessor} in log4j-plugin-processor.
@@ -28,29 +31,33 @@ import org.apache.logging.log4j.plugins.util.PluginType;
*/
public abstract class PluginService {
- private final PluginBundle bundle = new PluginBundle();
+ private final Map<String, PluginCategory> categories = new LinkedHashMap<>();
public PluginService() {
PluginEntry[] entries = getEntries();
ClassLoader classLoader = getClass().getClassLoader();
for (PluginEntry entry : entries) {
- final PluginType<?> pluginType = new PluginType<>(entry, classLoader);
- bundle.add(pluginType);
+ final String category = entry.getCategory();
+ categories.computeIfAbsent(category.toLowerCase(Locale.ROOT), key -> new PluginCategory(key, category))
+ .merge(entry.getKey(), new PluginType<>(entry, classLoader));
}
}
public abstract PluginEntry[] getEntries();
- public PluginBundle getBundle() {
- return bundle;
+ public Map<String, PluginCategory> getCategories() {
+ return categories;
}
public PluginCategory getCategory(String category) {
- return bundle.get(category);
+ return categories.get(category.toLowerCase(Locale.ROOT));
}
- public long size() {
- return bundle.size();
+ public int size() {
+ return categories.values()
+ .stream()
+ .mapToInt(PluginCategory::size)
+ .sum();
}
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginBundle.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginBundle.java
deleted file mode 100644
index a1a3271cae..0000000000
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginBundle.java
+++ /dev/null
@@ -1,88 +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.logging.log4j.plugins.util;
-
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-
-/**
- * Bundles plugins by category from a plugin source.
- */
-public class PluginBundle {
- private final Map<String, PluginCategory> categories = new LinkedHashMap<>();
-
- public int size() {
- return categories.size();
- }
-
- public boolean isEmpty() {
- return categories.isEmpty();
- }
-
- public void put(final String category, final List<PluginType<?>> pluginTypes) {
- final var pluginCategory = new PluginCategory(category);
- pluginCategory.putAll(pluginTypes);
- categories.put(pluginCategory.getKey(), pluginCategory);
- }
-
- public void put(final PluginCategory category) {
- categories.put(category.getKey(), category);
- }
-
- public int merge(final PluginCategory category) {
- final PluginCategory existingCategory = getOrCreate(category.getKey());
- final AtomicInteger addedCount = new AtomicInteger();
- category.forEach((key, plugin) -> {
- final var merged = existingCategory.merge(key, plugin);
- if (merged == plugin) {
- addedCount.incrementAndGet();
- }
- });
- return addedCount.get();
- }
-
- public void add(final PluginType<?> pluginType) {
- getOrCreate(pluginType.getCategory()).put(pluginType);
- }
-
- public void addAll(final String category, final Collection<PluginType<?>> pluginTypes) {
- getOrCreate(category).putAll(pluginTypes);
- }
-
- public PluginCategory get(final String category) {
- return categories.get(category.toLowerCase(Locale.ROOT));
- }
-
- public PluginCategory getOrCreate(final String category) {
- return categories.computeIfAbsent(category.toLowerCase(Locale.ROOT), key -> new PluginCategory(key, category));
- }
-
- public void forEach(final Consumer<? super PluginCategory> consumer) {
- categories.values().forEach(consumer);
- }
-
- public void forEach(final BiConsumer<? super String, ? super PluginCategory> biConsumer) {
- categories.forEach(biConsumer);
- }
-}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginCategory.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginCategory.java
index 63a7dae8e2..0d39d4681e 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginCategory.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginCategory.java
@@ -22,22 +22,23 @@ import org.apache.logging.log4j.plugins.PluginOrder;
import org.apache.logging.log4j.plugins.Singleton;
import org.apache.logging.log4j.status.StatusLogger;
-import java.util.Collection;
+import java.util.AbstractCollection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
* Plugin categories are mappings of plugin keys to plugin classes where plugin keys are lower-cased
* versions of plugin names.
*/
@Singleton
-public class PluginCategory implements Iterable<PluginType<?>> {
+public class PluginCategory extends AbstractCollection<PluginType<?>> {
private static final Logger LOGGER = StatusLogger.getLogger();
private final String key;
@@ -48,7 +49,7 @@ public class PluginCategory implements Iterable<PluginType<?>> {
this(name.toLowerCase(Locale.ROOT), name);
}
- PluginCategory(final String key, final String name) {
+ public PluginCategory(final String key, final String name) {
this.key = key;
this.name = name;
}
@@ -88,20 +89,6 @@ public class PluginCategory implements Iterable<PluginType<?>> {
return Collections.unmodifiableSet(plugins.keySet());
}
- /**
- * Returns an unmodifiable collection of plugin types in this category.
- */
- public Collection<PluginType<?>> getPluginTypes() {
- return Collections.unmodifiableCollection(plugins.values());
- }
-
- /**
- * Returns an unmodifiable map of plugin keys to plugin types in this category.
- */
- public Map<String, PluginType<?>> asMap() {
- return Collections.unmodifiableMap(plugins);
- }
-
/**
* Gets the plugin type for the provided plugin name (case-insensitive) if available or {@code null}.
*/
@@ -109,13 +96,6 @@ public class PluginCategory implements Iterable<PluginType<?>> {
return plugins.get(name.toLowerCase(Locale.ROOT));
}
- /**
- * Puts all the provided plugin types into this category.
- */
- public void putAll(final Collection<PluginType<?>> pluginTypes) {
- pluginTypes.forEach(this::put);
- }
-
/**
* Puts the provided plugin type into this category.
*/
@@ -131,6 +111,11 @@ public class PluginCategory implements Iterable<PluginType<?>> {
LOGGER.trace("Put PluginCategory[{}][{}] = {}", name, key, pluginType);
}
+ @Override
+ public boolean add(final PluginType<?> pluginType) {
+ return pluginType == merge(pluginType.getKey(), pluginType);
+ }
+
/**
* Merges the provided plugin type into this category using the given key and returns the merged result.
* Merging is done by preferring plugins according to {@link PluginOrder} where a conflict occurs with the
@@ -146,17 +131,10 @@ public class PluginCategory implements Iterable<PluginType<?>> {
return result;
}
- public int mergeAll(final PluginCategory category) {
+ public void mergeAll(final PluginCategory category) {
if (category != null) {
- final AtomicInteger addedCount = new AtomicInteger();
- category.forEach((pluginKey, pluginType) -> {
- if (pluginType == merge(pluginKey, pluginType)) {
- addedCount.incrementAndGet();
- }
- });
- return addedCount.get();
+ category.forEach(this::merge);
}
- return 0;
}
@Override
@@ -170,4 +148,12 @@ public class PluginCategory implements Iterable<PluginType<?>> {
public void forEach(final BiConsumer<? super String, ? super PluginType<?>> biConsumer) {
plugins.forEach(biConsumer);
}
+
+ public void forEachMatching(
+ final Predicate<? super PluginType<?>> predicate, final Consumer<? super PluginType<?>> consumer) {
+ plugins.values()
+ .stream()
+ .filter(predicate)
+ .forEach(consumer);
+ }
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
index 09762c4a0e..d665fac555 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginRegistry.java
@@ -33,15 +33,18 @@ import java.net.URI;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Consumer;
import java.util.function.Supplier;
+import static org.apache.logging.log4j.util.Unbox.box;
+
/**
* Registry singleton for PluginType maps partitioned by source type and then by category names.
*/
@@ -57,19 +60,44 @@ public class PluginRegistry {
private static final Supplier<PluginRegistry> INSTANCE = new LazyValue<>(PluginRegistry::new);
/**
- * Contains plugins found in Log4j2Plugins.dat cache files in the main CLASSPATH.
+ * Contains plugins found from {@link PluginService} services and legacy Log4j2Plugins.dat cache files in the main CLASSPATH.
*/
- private final AtomicReference<PluginBundle> pluginsByCategoryRef = new AtomicReference<>();
+ private final LazyValue<Categories> mainPluginCategories = LazyValue.from(() -> {
+ final Categories bundle = decodeCacheFiles(LoaderUtil.getClassLoader());
+ Throwable throwable = null;
+ ClassLoader errorClassLoader = null;
+ boolean allFail = true;
+ for (ClassLoader classLoader : LoaderUtil.getClassLoaders()) {
+ try {
+ loadPlugins(classLoader, bundle);
+ allFail = false;
+ } catch (Throwable ex) {
+ if (throwable == null) {
+ throwable = ex;
+ errorClassLoader = classLoader;
+ }
+ }
+ }
+ if (allFail && throwable != null) {
+ LOGGER.debug("Unable to retrieve provider from ClassLoader {}", errorClassLoader, throwable);
+ }
+ if (bundle.isEmpty()) {
+ // If we didn't find any plugins above, someone must have messed with the log4j-core.jar.
+ // Search the standard package in the hopes we can find our core plugins.
+ loadFromPackage(bundle, "org.apache.logging.log4j.core");
+ }
+ return bundle;
+ });
/**
- * Contains plugins found in Log4j2Plugins.dat cache files in OSGi Bundles.
+ * Contains plugins found in PluginService services and legacy Log4j2Plugins.dat cache files in OSGi Bundles.
*/
- private final ConcurrentMap<Long, PluginBundle> pluginsByCategoryByBundleId = new ConcurrentHashMap<>();
+ private final Map<Long, Categories> pluginCategoriesByBundleId = new ConcurrentHashMap<>();
/**
* Contains plugins found by searching for annotated classes at runtime.
*/
- private final ConcurrentMap<String, PluginBundle> pluginsByCategoryByPackage = new ConcurrentHashMap<>();
+ private final Map<String, Categories> pluginCategoriesByPackage = new ConcurrentHashMap<>();
private PluginRegistry() {
}
@@ -88,36 +116,9 @@ public class PluginRegistry {
* Resets the registry to an empty state.
*/
public void clear() {
- pluginsByCategoryRef.set(null);
- pluginsByCategoryByPackage.clear();
- pluginsByCategoryByBundleId.clear();
- }
-
- public void forEachOsgiPluginBundle(final Consumer<? super PluginBundle> consumer) {
- pluginsByCategoryByBundleId.values().forEach(consumer);
- }
-
- /**
- * Retrieve plugins from the main classloader.
- * @return Map of the List of PluginTypes by category.
- * @since 2.1
- */
- public PluginBundle loadFromMainClassLoader() {
- final var existing = pluginsByCategoryRef.get();
- if (existing != null) {
- // already loaded
- return existing;
- }
- final PluginBundle newPluginsByCategory = decodeCacheFiles(LoaderUtil.getClassLoader());
- loadPlugins(newPluginsByCategory);
-
- // Note multiple threads could be calling this method concurrently. Both will do the work,
- // but only one will be allowed to store the result in the AtomicReference.
- // Return the map produced by whichever thread won the race, so all callers will get the same result.
- if (pluginsByCategoryRef.compareAndSet(null, newPluginsByCategory)) {
- return newPluginsByCategory;
- }
- return pluginsByCategoryRef.get();
+ mainPluginCategories.set(null);
+ pluginCategoriesByPackage.clear();
+ pluginCategoriesByBundleId.clear();
}
/**
@@ -126,82 +127,45 @@ public class PluginRegistry {
* @since 2.1
*/
public void clearBundlePlugins(final long bundleId) {
- pluginsByCategoryByBundleId.remove(bundleId);
+ pluginCategoriesByBundleId.remove(bundleId);
}
/**
* Load plugins from a bundle.
* @param bundleId The bundle id.
* @param loader The ClassLoader.
- * @return the Map of Lists of plugins organized by category.
- * @since 2.1
+ * @since 3.0.0
*/
- public PluginBundle loadFromBundle(final long bundleId, final ClassLoader loader) {
- PluginBundle existing = pluginsByCategoryByBundleId.get(bundleId);
- if (existing != null) {
- // already loaded from this classloader
- return existing;
- }
- final PluginBundle newPluginsByCategory = decodeCacheFiles(loader);
- loadPlugins(loader, newPluginsByCategory);
-
- // Note multiple threads could be calling this method concurrently. Both will do the work,
- // but only one will be allowed to store the result in the outer map.
- // Return the inner map produced by whichever thread won the race, so all callers will get the same result.
- existing = pluginsByCategoryByBundleId.putIfAbsent(bundleId, newPluginsByCategory);
- if (existing != null) {
- return existing;
- }
- return newPluginsByCategory;
+ public void loadFromBundle(final long bundleId, final ClassLoader loader) {
+ pluginCategoriesByBundleId.computeIfAbsent(bundleId, ignored -> {
+ final Categories bundle = decodeCacheFiles(loader);
+ loadPlugins(loader, bundle);
+ return bundle;
+ });
}
/**
* Loads all the plugins in a Bundle.
- * @param categories All the categories in the bundle.
- * @param bundleId The bundle Id.
- * @since 3.0
+ * @param bundleId The bundle id.
+ * @param pluginsByCategory the plugins organized by category
+ * @since 3.0.0
*/
- public void loadFromBundle(PluginBundle categories, Long bundleId) {
- pluginsByCategoryByBundleId.put(bundleId, categories);
- }
-
- /**
- * Load plugins across all ClassLoaders.
- * @param map The Map of the lists of plugins organized by category.
- * @since 3.0
- */
- public void loadPlugins(PluginBundle map) {
- Throwable throwable = null;
- ClassLoader errorClassLoader = null;
- boolean allFail = true;
- for (ClassLoader classLoader : LoaderUtil.getClassLoaders()) {
- try {
- loadPlugins(classLoader, map);
- allFail = false;
- } catch (Throwable ex) {
- if (throwable == null) {
- throwable = ex;
- errorClassLoader = classLoader;
- }
- }
- }
- if (allFail && throwable != null) {
- LOGGER.debug("Unable to retrieve provider from ClassLoader {}", errorClassLoader, throwable);
- }
+ public void loadFromBundle(final long bundleId, final Map<String, PluginCategory> pluginsByCategory) {
+ pluginCategoriesByBundleId.put(bundleId, new Categories(pluginsByCategory));
}
/**
* Load plugins from a specific ClassLoader.
* @param classLoader The ClassLoader.
- * @param bundle The PluginBundle to merge discovered plugins to
+ * @param categories The Categories to merge discovered plugins to
* @since 3.0
*/
- public void loadPlugins(ClassLoader classLoader, PluginBundle bundle) {
+ private void loadPlugins(ClassLoader classLoader, Categories categories) {
final long startTime = System.nanoTime();
final ServiceLoader<PluginService> serviceLoader = ServiceLoader.load(PluginService.class, classLoader);
final AtomicInteger pluginCount = new AtomicInteger();
for (final PluginService pluginService : serviceLoader) {
- pluginService.getBundle().forEach((category, plugins) -> pluginCount.addAndGet(bundle.merge(plugins)));
+ pluginService.getCategories().values().forEach(category -> pluginCount.addAndGet(categories.merge(category)));
}
final int numPlugins = pluginCount.get();
LOGGER.debug(() -> {
@@ -212,7 +176,7 @@ public class PluginRegistry {
});
}
- private PluginBundle decodeCacheFiles(final ClassLoader classLoader) {
+ private Categories decodeCacheFiles(final ClassLoader classLoader) {
final long startTime = System.nanoTime();
final PluginCache cache = new PluginCache();
try {
@@ -225,12 +189,12 @@ public class PluginRegistry {
} catch (final IOException ioe) {
LOGGER.warn("Unable to preload plugins", ioe);
}
- final PluginBundle newPluginsByCategory = new PluginBundle();
+ final Categories categories = new Categories();
final AtomicInteger pluginCount = new AtomicInteger();
cache.getAllCategories().forEach((key, outer) ->
outer.values().forEach(entry -> {
final PluginType<?> type = new PluginType<>(entry, classLoader);
- newPluginsByCategory.add(type);
+ categories.add(type);
pluginCount.incrementAndGet();
}));
final int numPlugins = pluginCount.get();
@@ -240,26 +204,14 @@ public class PluginRegistry {
return "Took " + numFormat.format((endTime - startTime) * 1e-9) +
" seconds to load " + numPlugins + " plugins from " + classLoader;
});
- return newPluginsByCategory;
+ return categories;
}
- /**
- * Load plugin types from a package.
- * @param pkg The package name.
- * @return A Map of the lists of plugin types organized by category.
- * @since 2.1
- */
- public PluginBundle loadFromPackage(final String pkg) {
+ private void loadFromPackage(final Categories bundle, final String pkg) {
if (Strings.isBlank(pkg)) {
// happens when splitting an empty string
- return new PluginBundle();
+ return;
}
- PluginBundle existing = pluginsByCategoryByPackage.get(pkg);
- if (existing != null) {
- // already loaded this package
- return existing;
- }
-
final long startTime = System.nanoTime();
final ResolverUtil resolver = new ResolverUtil();
final ClassLoader classLoader = LoaderUtil.getClassLoader(getClass(), LoaderUtil.class);
@@ -268,34 +220,31 @@ public class PluginRegistry {
}
resolver.findInPackage(new PluginTest(), pkg);
- final PluginBundle newPluginsByCategory = new PluginBundle();
for (final Class<?> clazz : resolver.getClasses()) {
final Plugin plugin = clazz.getAnnotation(Plugin.class);
final PluginEntry mainEntry = new PluginEntry();
final String mainElementName = plugin.elementType().equals(
- Plugin.EMPTY) ? plugin.name() : plugin.elementType();
+ Plugin.EMPTY) ? plugin.name() : plugin.elementType();
mainEntry.setKey(plugin.name().toLowerCase());
mainEntry.setName(plugin.name());
mainEntry.setCategory(plugin.category());
mainEntry.setClassName(clazz.getName());
mainEntry.setPrintable(plugin.printObject());
mainEntry.setDefer(plugin.deferChildren());
- final PluginType<?> mainType = new PluginType<>(mainEntry, clazz, mainElementName);
- newPluginsByCategory.add(mainType);
+ bundle.add(new PluginType<>(mainEntry, clazz, mainElementName));
final PluginAliases pluginAliases = clazz.getAnnotation(PluginAliases.class);
if (pluginAliases != null) {
for (final String alias : pluginAliases.value()) {
final PluginEntry aliasEntry = new PluginEntry();
final String aliasElementName = plugin.elementType().equals(
- Plugin.EMPTY) ? alias.trim() : plugin.elementType();
+ Plugin.EMPTY) ? alias.trim() : plugin.elementType();
aliasEntry.setKey(alias.trim().toLowerCase());
aliasEntry.setName(plugin.name());
aliasEntry.setCategory(plugin.category());
aliasEntry.setClassName(clazz.getName());
aliasEntry.setPrintable(plugin.printObject());
aliasEntry.setDefer(plugin.deferChildren());
- final PluginType<?> aliasType = new PluginType<>(aliasEntry, clazz, aliasElementName);
- newPluginsByCategory.add(aliasType);
+ bundle.add(new PluginType<>(aliasEntry, clazz, aliasElementName));
}
}
}
@@ -305,44 +254,36 @@ public class PluginRegistry {
return "Took " + numFormat.format((endTime - startTime) * 1e-9) +
" seconds to load " + resolver.getClasses().size() + " plugins from package " + pkg;
});
-
- // Note multiple threads could be calling this method concurrently. Both will do the work,
- // but only one will be allowed to store the result in the outer map.
- // Return the inner map produced by whichever thread won the race, so all callers will get the same result.
- existing = pluginsByCategoryByPackage.putIfAbsent(pkg, newPluginsByCategory);
- if (existing != null) {
- return existing;
- }
- return newPluginsByCategory;
}
/**
* Gets the registered plugins for the given category. If additional scan packages are provided, then plugins
* are scanned and loaded from there as well.
*/
- public PluginCategory getCategory(final String categoryName, List<String> additionalScanPackages) {
+ public PluginCategory getCategory(final String categoryName, final List<String> additionalScanPackages) {
final var category = new PluginCategory(categoryName);
+
// First, iterate the PluginService services and legacy Log4j2Plugin.dat files found in the main CLASSPATH
- PluginBundle builtInPlugins = loadFromMainClassLoader();
- if (builtInPlugins.isEmpty()) {
- // If we didn't find any plugins above, someone must have messed with the log4j-core.jar.
- // Search the standard package in the hopes we can find our core plugins.
- builtInPlugins = loadFromPackage("org.apache.logging.log4j.core");
+ final Categories builtInPlugins = mainPluginCategories.get();
+ if (builtInPlugins != null) {
+ category.mergeAll(builtInPlugins.get(categoryName));
}
- final AtomicInteger addedCount = new AtomicInteger(category.mergeAll(builtInPlugins.get(categoryName)));
// Next, iterate OSGi modules that provide plugins as OSGi services
- forEachOsgiPluginBundle(bundle ->
- addedCount.addAndGet(category.mergeAll(bundle.get(categoryName))));
+ pluginCategoriesByBundleId.values().forEach(bundle -> category.mergeAll(bundle.get(categoryName)));
// Finally, iterate over additional packages from configuration
if (additionalScanPackages != null) {
for (final String pkg : additionalScanPackages) {
- addedCount.addAndGet(category.mergeAll(loadFromPackage(pkg).get(categoryName)));
+ category.mergeAll(pluginCategoriesByPackage.computeIfAbsent(pkg, ignored -> {
+ final var bundle = new Categories();
+ loadFromPackage(bundle, pkg);
+ return bundle;
+ }).get(categoryName));
}
}
- LOGGER.debug("Discovered {} new plugins in category '{}'", addedCount.get(), categoryName);
+ LOGGER.debug("Discovered {} plugins in category '{}'", box(category.size()), categoryName);
return category;
}
@@ -379,4 +320,51 @@ public class PluginRegistry {
return false;
}
}
+
+ /**
+ * Bundles plugins by category from a plugin source.
+ */
+ private static class Categories implements Iterable<PluginCategory> {
+ private final Map<String, PluginCategory> categories;
+
+ private Categories() {
+ categories = new LinkedHashMap<>();
+ }
+
+ private Categories(final Map<String, PluginCategory> categories) {
+ this.categories = categories;
+ }
+
+ public boolean isEmpty() {
+ return categories.isEmpty();
+ }
+
+ public int merge(final PluginCategory category) {
+ final PluginCategory existingCategory = getOrCreate(category.getKey());
+ int added = 0;
+ for (final PluginType<?> pluginType : category) {
+ if (existingCategory.add(pluginType)) {
+ added++;
+ }
+ }
+ return added;
+ }
+
+ public void add(final PluginType<?> pluginType) {
+ getOrCreate(pluginType.getCategory()).put(pluginType);
+ }
+
+ public PluginCategory get(final String category) {
+ return categories.get(category.toLowerCase(Locale.ROOT));
+ }
+
+ public PluginCategory getOrCreate(final String category) {
+ return categories.computeIfAbsent(category.toLowerCase(Locale.ROOT), key -> new PluginCategory(key, category));
+ }
+
+ @Override
+ public Iterator<PluginCategory> iterator() {
+ return categories.values().iterator();
+ }
+ }
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginType.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginType.java
index f934936356..dca0daaa94 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginType.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/util/PluginType.java
@@ -19,6 +19,7 @@ package org.apache.logging.log4j.plugins.util;
import org.apache.logging.log4j.plugins.processor.PluginEntry;
import org.apache.logging.log4j.util.LazyValue;
+import java.util.Set;
import java.util.function.Supplier;
/**
@@ -32,6 +33,7 @@ public class PluginType<T> {
private final PluginEntry pluginEntry;
private final Supplier<Class<T>> pluginClass;
private final String elementName;
+ private final Supplier<Set<Class<?>>> implementedInterfaces;
/**
* Constructor.
@@ -45,6 +47,8 @@ public class PluginType<T> {
this.pluginEntry = pluginEntry;
this.pluginClass = () -> pluginClass;
this.elementName = elementName;
+ final var interfaces = Set.of(pluginClass.getInterfaces());
+ this.implementedInterfaces = () -> interfaces;
}
/**
@@ -54,7 +58,7 @@ public class PluginType<T> {
*/
public PluginType(final PluginEntry pluginEntry, final ClassLoader classLoader) {
this.pluginEntry = pluginEntry;
- this.pluginClass = new LazyValue<>(() -> {
+ final LazyValue<Class<T>> classProvider = LazyValue.from(() -> {
try {
return TypeUtil.cast(classLoader.loadClass(pluginEntry.getClassName()));
} catch (final ClassNotFoundException e) {
@@ -62,6 +66,14 @@ public class PluginType<T> {
" located for element " + pluginEntry.getName(), e);
}
});
+ this.pluginClass = classProvider;
+ final Class<?>[] interfaces = pluginEntry.getInterfaces();
+ if (interfaces != null) {
+ final var implementedInterfaces = Set.of(interfaces);
+ this.implementedInterfaces = () -> implementedInterfaces;
+ } else {
+ this.implementedInterfaces = classProvider.map(clazz -> Set.of(clazz.getInterfaces()));
+ }
this.elementName = pluginEntry.getName();
}
@@ -73,6 +85,10 @@ public class PluginType<T> {
return pluginClass.get();
}
+ public Set<Class<?>> getImplementedInterfaces() {
+ return implementedInterfaces.get();
+ }
+
public String getElementName() {
return this.elementName;
}