MethodNotFoundException in ConnectionFactory for log4j2 file - Kotlin

In a spring multi-module gradle based Kotlin project , I am using log4j2 for logging.

Few lines of the log4j2 file is defined below :

<Configuration>
<Appenders>
    <Console name="Console" target="SYSTEM_OUT">
        <PatternLayout
                pattern="%style{%d{ISO8601}}{red} %highlight{%-5level }[%style{%t}{bright,blue}] 
                %style{%C{1.}}{bright,yellow}: %msg%n%throwable" />
    </Console>

    <JDBC name="databaseAppender" tableName="log">
        <ConnectionFactory class="ConnectionFactory" method="databaseConnection" />
    </JDBC>
...
...

The ConnectionFactory class is:

     @Component
     class ConnectionFactory(@Qualifier("oracleDatasource") dataSource: DataSource) {
     
         companion object {
             private var dataSource: DataSource? = null
             val databaseConnection: Connection
                 get() = dataSource!!.connection
         }Preformatted text
    
   init {
     Companion.dataSource = dataSource
    }
    
    }

However at the time of running the application, I get this error:

2021-03-07 16:39:36,777 main ERROR java.lang.NoSuchMethodException: ConnectionFactory.databaseConnection() java.lang.NoSuchMethodException: ConnectionFactory.databaseConnection()
        at java.base/java.lang.Class.getMethod(Class.java:2108)
        at org.apache.logging.log4j.core.appender.db.jdbc.FactoryMethodConnectionSource.createConnectionSource(FactoryMethodConnectionSource.java:83)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.build(PluginBuilder.java:136)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.createPluginObject(AbstractConfiguration.java:1002)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:942)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:934)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:934)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.doConfigure(AbstractConfiguration.java:552)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.initialize(AbstractConfiguration.java:241)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:288)
        at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:618)
        at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:691)
        at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:708)
        at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:263)
        at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:153)
        at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:45)
        at org.apache.logging.log4j.LogManager.getContext(LogManager.java:194)
        at org.apache.commons.logging.LogAdapter$Log4jLog.<clinit>(LogAdapter.java:155)
        at org.apache.commons.logging.LogAdapter$Log4jAdapter.createLog(LogAdapter.java:122)
        at org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java:89)
        at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:67)
        at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:59)
        at org.springframework.boot.SpringApplication.<clinit>(SpringApplication.java:203)
        at MyApplication$Companion.main(Mypplication.kt:26)
        at MyApplication.main(MyApplication.kt)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:107)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)

2021-03-07 16:39:36,797 main ERROR No ConnectionSource provided: connectionSource
2021-03-07 16:39:36,826 main ERROR Could not create plugin of type class org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender for element JDBC org.apache.logging.log4j.core.config.ConfigurationException: Arguments given for element JDBC are invalid: field 'connectionSource' has invalid value 'null'
        at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.injectFields(PluginBuilder.java:208)
        at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.build(PluginBuilder.java:121)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.createPluginObject(AbstractConfiguration.java:1002)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:942)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:934)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.doConfigure(AbstractConfiguration.java:552)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.initialize(AbstractConfiguration.java:241)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:288)
        at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:618)
        at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:691)
        at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:708)
        at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:263)
        at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:153)
        at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:45)
        at org.apache.logging.log4j.LogManager.getContext(LogManager.java:194)
        at org.apache.commons.logging.LogAdapter$Log4jLog.<clinit>(LogAdapter.java:155)
        at org.apache.commons.logging.LogAdapter$Log4jAdapter.createLog(LogAdapter.java:122)
        at org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java:89)
        at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:67)
        at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:59)
        at org.springframework.boot.SpringApplication.<clinit>(SpringApplication.java:203)
        at MyApplication$Companion.main(MyApplication.kt:26)
        at MyApplication.main(MyApplication.kt)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:107)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)

2021-03-07 16:39:36,832 main ERROR Null object returned for JDBC in Appenders.
2021-03-07 16:39:36,840 main ERROR No appender named databaseAppender was configured
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:107)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: java.lang.ExceptionInInitializerError
        at org.apache.commons.logging.LogAdapter$Log4jAdapter.createLog(LogAdapter.java:122)
        at org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java:89)
        at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:67)
        at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:59)
        at org.springframework.boot.SpringApplication.<clinit>(SpringApplication.java:203)
        at MyApplication$Companion.main(MyApplication.kt:26)
        at MyApplication.main(MyApplication.kt)
        ... 8 more
Caused by: org.apache.logging.log4j.core.config.ConfigurationException: No appenders are available for AsyncAppender Async
        at org.apache.logging.log4j.core.appender.AsyncAppender.start(AsyncAppender.java:123)
        at org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:304)
        at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:618)
        at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:691)
        at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:708)
        at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:263)
        at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:153)
        at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:45)
        at org.apache.logging.log4j.LogManager.getContext(LogManager.java:194)
        at org.apache.commons.logging.LogAdapter$Log4jLog.<clinit>(LogAdapter.java:155)
        ... 15 more

I understand that the class can be found by the Log4j2 file however clueless why can’t it detect the method present. How can I solve this issue?

There are two problems with your code.

The first problem is that you defined databaseConnection as a property rather than a method. A property’s getter is by default compiled to a method named getPropertyName. In this case, the generated method is therefore named getDatabaseConnection. To fix this you can either 1) change the method name in the log4j2 config file to getDatabaseConnection, 2) change the property into a regular method, or 3) override the name of the getter with the @JvmName annotation.

The second problem is that a companion object is not just a weird syntax for static members. A companion object is a class by itself. This means that the databaseConnection is not a member of ConnectionFactory but rather a (non-static) member of the companion object class.

It is possible to make the compiler generate static methods from companion object members by annotating them with @JvmStatic. In your case, you could add @JvmStatic to databaseConnection to have the compiler generate a static method on ConnectionFactory. Be aware that the companion object will still exist, and that the static method simply delegates to the companion object.

Another option is to not use a class member. Instead, you can declare the factory method directly as a package-level function. Package-level functions are compiled into static methods. The class containing the static methods will be named after the file; if the file is named fileName.kt then the class will be named FileNameKt. You can override the class name by annotating the file with @JvmName (you annotate a file by writing the annotation like this: @file:JvmName("SomeClassName"))

For more info, read the documentation

2 Likes

Hi @Varia I went ahead with changing the method name by appending “get” to it in the log4j2 file and also added the @JvmStatic annotation to the static function in the companion object.

This solved the issue. It was quite an informative answer with multiple ways. Thanks for helping me out!