log4j custom jdbc appender , datasource

2019-06-08 22:42发布

问题:

TO use a datasource in my log4j appender, I have written a custom appender. The appender tries to get the datasource as a spring bean. However, the appender is not able to get the bean. The technology stack I am using is: mybatis, tomcat, and spring.

My log4j files looks like so:

#configuring the requestinterceptor  
log4j.category.com.db.wscis.core.web.interceptor=INFO, sql
log4j.appender.sql=com.db.wscis.core.util.appender.CustomJDBCAppender
log4j.appender.sql.sql=INSERT INTO LOGS VALUES ('%x', current_timestamp ,'%C','%p','%m')
log4j.appender.sql.layout=org.apache.log4j.PatternLayout

The code for the custom appender is as follows:

public class CustomJDBCAppender extends org.apache.log4j.jdbc.JDBCAppender {

 protected java.sql.Connection getConnection() throws java.sql.SQLException {

         if(connection == null) {
             org.springframework.jdbc.datasource.DataSourceTransactionManager dstm = (org.springframework.jdbc.datasource.DataSourceTransactionManager) ExternalBeanFactory.getBean("txManager");
             if(dstm==null) throw new java.sql.SQLException("dstm is null");
             System.out.println("here");
             connection = DataSourceUtils.getConnection(dstm.getDataSource());
             return connection;
         } else{
             return connection;
         }
     }

    /* protected void execute(String sql) throws java.sql.SQLException {

         Connection con = null;
         Statement stmt = null;

         try {
             con = getConnection();
             stmt = con.createStatement();
             stmt.executeUpdate(sql);
             con.commit();
         } catch (SQLException e) {
            if (stmt != null)
                 stmt.close();
            closeConnection(con);
            throw e;
         } 
       }
     */
     protected void closeConnection() throws java.sql.SQLException {
             if (connection != null && !connection.isClosed())
                   connection.close();
     }
}

The External Bean factory class looks like so:

/* 
 */    
package com.db.wscis.core.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

public class ExternalBeanFactory implements BeanFactoryAware {

    /**
     * Bean factory instance.
     */
    private static BeanFactory beanFactory;

    /**
     * Sets beanfactory.
     * 
     * @param beanFactory
     *            BeanFactory object
     * 
     * @see org.springframework.beans.factory.BeanFactoryAware#
     *      setBeanFactory(org.springframework.beans.factory.BeanFactory)
     * @exception BeansException
     *                if failed to set factory
     */
    public final void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    /**
     * DO NOT use this method in the application classes. This is meant to be
     * used by the framework level classes ONLY!!!!
     * 
     * Finds from bean from the factory.
     * 
     * @param beanName
     *            Name of the bean
     * @return bean object
     */
    @Deprecated
    public static final Object getBean(final String beanName) {
    //  if(beanFactory==null) System.out.println("Bean factory is null");
        return beanFactory.getBean(beanName);
    }

    public final Object getBeanInstance(final String beanName) {
        return beanFactory.getBean(beanName);
    }
}

Now, when the tomcat server starts,my guess is that it tries to initialize log4j. Thus, it calls getConnection method in the custom appender. This inturn calls the ExernalFactory.getBean(), however, the beanfactory property in External BeanFactory has not been initialized yet.
The error stack trace is as follows:

SEVERE: Error configuring application listener of class com.db.wscis.core.web.context.FWContextLoaderLis
java.lang.NullPointerException
        at com.db.wscis.core.util.ExternalBeanFactory.getBean(ExternalBeanFactory.java:64)
        at com.db.wscis.core.util.appender.CustomJDBCAppender.getConnection(CustomJDBCAppender.java:27)
        at org.apache.log4j.jdbc.JDBCAppender.execute(JDBCAppender.java:215)
        at org.apache.log4j.jdbc.JDBCAppender.flushBuffer(JDBCAppender.java:289)
        at org.apache.log4j.jdbc.JDBCAppender.append(JDBCAppender.java:186)
        at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)
        at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.
        at org.apache.log4j.Category.callAppenders(Category.java:206)
        at org.apache.log4j.Category.forcedLog(Category.java:391)
        at org.apache.log4j.Category.log(Category.java:856)
        at org.apache.commons.logging.impl.Log4JLogger.info(Log4JLogger.java:176)
        at com.db.wscis.core.web.context.FWContextLoaderListener.<init>(FWContextLoaderListener.java:34)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.j
        at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
        at java.lang.Class.newInstance(Class.java:374)
        at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:140)
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4888)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:632)
        at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1073)
        at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1857)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
        at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
        at java.util.concurrent.FutureTask.run(FutureTask.java:166)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:724)

Please tell me what I am doing wrong and how I can get the datasource bean instance in my appender class.

回答1:

I managed to solve this one:

So, my datasource bean was declared in an application context. My external bean factory on the other hand was declared in the root context. Now, beans in the root context cannot see the beans in the application Context. So, the beanfactory property in my External Bean Factory never got initialized. It took me some time to figure it out, but thankfully, it worked!