Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Another API change is replacing existing Builder<T> classes with java.util.function.Supplier<T>. To support backward compatibility, when a static factory method returns an object that implements Builder<T> or Supplier<T>, then that instance is injected and used as a factory for its type T.

The Core plugin type is updated to use this injection API so that in addition to the existing support for injecting @PluginElement, @PluginAttribute, @PluginBuilderAttribute, @PluginValue, @PluginNode, and @PluginConfiguration instances, this is extended to support for injection via fields, methods, and constructors, along with injection of any other instances Injector has bindings for or knows how to create bindings on demand for. Classes that can have on-demand bindings are injectable classes which are classes with either one @Inject constructor or a no-args constructor. Implementations of LogEventFactory are a good example of injectable classes where the choice of class is configurable at runtime, though the dependency chain involved can be made explicit while removing boilerplate dependency injection code to where this class is relevant. An abbreviated example of what a Supplier<LoggerConfig> class may look like:

Code Block
public class Builder implements java.util.function.Supplier<LoggerConfig> {
    // ...

    // note that methods with qualified parameters are implicitly @Inject
    public Builder withLevel(@PluginAttribute Level level) {
        this.level = level;
        return this;
    }

    public Builder withLoggerName(
            @Required(message = "Loggers cannot be configured without a name") @PluginAttribute String name) {
        this.loggerName = name;
        return this;
    }

    public Builder withRefs(@PluginElement AppenderRef[] refs) {
        this.refs = refs;
        return this;
    }

    public Builder withConfig(@PluginConfiguration Configuration config) {
        this.config = config;
        return this;
    }

    public Builder withFilter(@PluginElement Filter filter) {
        this.filter = filter;
        return this;
    }

    // need to specify @Inject here because LogEventFactory is an unqualified bean
    @Inject
    public Builder setLogEventFactory(LogEventFactory logEventFactory) {
        this.logEventFactory = logEventFactory;
        return this;
    }

    @Override
    public LoggerConfig get() {
        // ...
    }
}


Keys and Bindings

The Key<T> class provides a way to identify plugins by type, optional qualifier, and a name. Existing @PluginAttribute and @PluginValue annotations are natural qualifier types, though given that these annotations are duplicated in log4j-plugins and log4j-core, a more generic mechanism is needed to treat these as equivalent. This is where the name aspect of a Key comes in; all named-style qualifier annotations are treated as @Named qualifiers. The ConfigurationInjector and ConfigurationBinder API in log4j-plugins is replaced with a simpler strategy for supplying a (possibly converted) value for a particular Node instance. Nodes can be configured via Injector which is where general dependency injection occurs along with binding of provided configuration attributes in the Node instance. Annotation-handling strategies for configuration injection are replaced with a parsing strategy while strategies for binding are inlined into the general logic of Injector.

...