Re: Problems creating custom appender using JPMS service in 3.0.0-beta3

"Piotr P. Karwasz" <[email protected]> Fri, 29 Nov 2024 13:01:21 +0100
Newsgroups gmane.comp.jakarta.log4j.user,gmane.comp.apache.incubator.bigtop.devel
Message-ID <[email protected]>
Hi Pavel,

[NOTE: I am cross-posting this to `dev@logging`, since I believe it is 
more appropriate there]

On 29.11.2024 10:26, PavelTurk wrote:
> In the world of JPMS, there’s a service, we implement it, and we add 
> it. I haven’t heard of a service + PROCESSOR that needs to be used. 
> Can you point out any other projects that involve a service + 
> processor? The reasoning is that we should only need to know the 
> INTERFACE, and we’ll handle the IMPLEMENTATION ourselves. After all, 
> it’s possible to do without it—for example, by writing a PluginService 
> manually, which would scan the module itself and return the necessary 
> classes or just data(entries) about them. That’s why I said that using 
> a processor here seems odd to me. In general, I think code generation 
> should only be used as a last resort—for example, for JPA 
> metamodels—but that’s just my opinion.

The processor doesn't need to be used, but it is quite useful, when you 
have hundreds of plugins (which is the case for Log4j Core). Feel free 
to update the Plugins documentation in `3.x`[1] to make that explicit. 
We do have a draft PR to update that page, but it is a little bit stale[2].

Note that currently `PluginService` fulfills multiple functions, that 
could be delegated to separate classes/files:

1. It provides a mapping between (namespace, pluginName) and a class 
name, so we don't need to scan the classpath/modulepath. In this role it 
replaces the binary `Log4j2Plugins.dat` file, with something that IMHO 
is even harder to manipulate. We could probably replace it with a Java 
properties file or a JSON file like the one in [3]. The metadata could 
be generated by an annotation processor.

2. In a JPMS environment, it forces HotSpot to load all the available 
modules containing Log4j Core Plugins. For this to work, `PluginService` 
could just be an empty interface (easy to implement ;-) ).

3. In a non-JPMS environment, it simplifies the common practice of 
shading. `Log4j2Plugins.dat` files are hard to merge and many shading 
tools do not handle them. On the other hand most tools know how to merge 
`META-INF/services` files.

It is a little bit late in the lifecycle of 3.x to make drastic changes 
to how `PluginService` works, but we could:

* Either make `PluginService` an interface. Those that prefer to 
implement the interface can do it themselves, but we will provide an 
annotation processor that generates a JSON file and creates a minimal 
implementation that reads the JSON file.

* Or merge the functionality of 
`ConfigurableInstanceFactoryPostProcessor`[4] and `PluginService`. 
Strongly simplifying, what the `PluginService` does is registering a 
plugin builder `Supplier<T>` for each Log4j plugin `T` using a specific 
namespace and name. CIFPP does the same thing, but for "global" services 
like `MessageFactory`.

The latter proposition needs to be confirmed by Matt, who wrote the 
dependency injection system from scratch. Since the DI in Log4j Core is 
only used in Log4j Core, personally I find that the less features it 
provides, the better.

[1] https://logging.apache.org/log4j/3.x/manual/plugins.html

[2] https://github.com/apache/logging-log4j2/pull/2716

[3] 
https://github.com/apache/logging-log4j-transform/blob/main/log4j-converter-plugin-descriptor/src/main/resources/Log4j2Plugins.schema.json

[4] 
https://logging.apache.org/log4j/3.x/javadoc/log4j-plugins/org/apache/logging/log4j/plugins/di/spi/ConfigurableInstanceFactoryPostProcessor.html

[5] 
https://logging.apache.org/log4j/3.x/javadoc/log4j-plugins/org/apache/logging/log4j/plugins/di/Key.html

>
> I assume module-info was also generated automatically. The problem is 
> that it’s not present in log4j-core-3.0.0-beta3-sources.jar see [1], 
> but it does exist in log4j-core-3.0.0-beta3.jar see [2]. Additionally, 
> it’s not in the repository see [3]. So, it is not possible to see what 
> is in this file. Or was I looking in the wrong place?
>
You assume correctly. We use the JPMS Libraries plugins for BND[5] to 
generate the module descriptor. There are several reasons behind this 
choice:

* Last time I checked, JPMS support in IDEs was terrible, especially for 
unit tests. Right now it seems better (in IntelliJ), but for a Maven 
project I need to choose if `src/test` contains whitebox tests that will 
run on the classpath or blackbox tests in a different package, that will 
run on the module path.

* It is hard to maintain a module descriptor manually, especially to 
keep track of the `transitive` attribute or unused dependencies. BND 
computes that perfectly.

* We want to have OSGi descriptors perfectly aligned with the JPMS 
descriptor.

Piotr

[5] https://bnd.bndtools.org/chapters/330-jpms.html