com.opengamma.security.auditlog.HibernateAuditLogger.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.security.auditlog.HibernateAuditLogger.java

Source

/**
 * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.security.auditlog;

import java.io.Closeable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.SessionFactoryUtils;

import com.opengamma.security.auditlog.AbstractAuditLogger;
import com.opengamma.security.auditlog.AuditLogEntry;
import com.opengamma.util.ArgumentChecker;

/**
 * An audit logger that uses JDBC batch insert (through Hibernate) to insert
 * audit log entries into a database. If there is a server crash, audit log
 * entries stored in memory that have not yet been committed into
 * the database may be lost. 
 *
 */
public class HibernateAuditLogger extends AbstractAuditLogger implements Closeable {

    /** Logger. */
    private static final Logger s_logger = LoggerFactory.getLogger(HibernateAuditLogger.class);

    private HibernateTemplate _hibernateTemplate;

    private final int _batchSize;

    /** Keeps track of audit log entries to flush */
    private List<AuditLogEntry> _auditLogCache;

    private final Timer _timer;

    public HibernateAuditLogger() {
        this(getDefaultOriginatingSystem());
    }

    public HibernateAuditLogger(String originatingSystem) {
        this(originatingSystem, 50, 5);
    }

    public HibernateAuditLogger(int batchSize, int maxSecondsToKeepInMemory) {
        this(getDefaultOriginatingSystem(), batchSize, maxSecondsToKeepInMemory);
    }

    public HibernateAuditLogger(String originatingSystem, int batchSize, int maxSecondsToKeepInMemory) {
        super(originatingSystem);

        if (batchSize <= 0) {
            throw new IllegalArgumentException("Please give positive batch size");
        }
        if (maxSecondsToKeepInMemory <= 0) {
            throw new IllegalArgumentException("Please give positive max seconds to keep in memory");
        }

        _batchSize = batchSize;
        _auditLogCache = new ArrayList<AuditLogEntry>(_batchSize);

        Flusher flusher = new Flusher();
        _timer = new Timer("hibernate-audit-log-flusher");
        _timer.schedule(flusher, 1000 * maxSecondsToKeepInMemory, 1000 * maxSecondsToKeepInMemory);
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        _hibernateTemplate = new HibernateTemplate(sessionFactory);
    }

    private Session getSession() {
        return SessionFactoryUtils.getSession(_hibernateTemplate.getSessionFactory(),
                _hibernateTemplate.getEntityInterceptor(), _hibernateTemplate.getJdbcExceptionTranslator());
    }

    //-------------------------------------------------------------------------
    /**
     * The <code>Flusher</code> background thread ensures that all log entries are written into 
     * the DB in a timely fashion, even if the flow of new log entries from clients stops abruptly.  
     */
    private class Flusher extends TimerTask {
        @Override
        public void run() {
            try {
                flushCache();
            } catch (RuntimeException e) {
                // see http://manikandakumar.blogspot.com/2006/09/drawbacks-of-timertask.html
            }
        }
    }

    @Override
    public void log(String user, String originatingSystem, String object, String operation, String description,
            boolean success) {
        ArgumentChecker.notNull(user, "User ID");
        ArgumentChecker.notNull(user, "Originating system name");
        ArgumentChecker.notNull(object, "Object ID");
        ArgumentChecker.notNull(operation, "Operation name");

        AuditLogEntry auditLogEntry = new AuditLogEntry(user, originatingSystem, object, operation, description,
                success, new Date());
        boolean flushCache = false;
        synchronized (this) {
            _auditLogCache.add(auditLogEntry);
            if (_auditLogCache.size() >= _batchSize) {
                flushCache = true;
            }
        }

        if (flushCache) {
            flushCache();
        }
    }

    @Override
    public void flushCache() {

        List<AuditLogEntry> auditLogCache;
        synchronized (this) {
            auditLogCache = _auditLogCache;
            _auditLogCache = new ArrayList<AuditLogEntry>(_batchSize);
        }

        Session session = getSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            for (int i = 0; i < auditLogCache.size(); i++) {
                AuditLogEntry auditLogEntry = auditLogCache.get(i);
                session.save(auditLogEntry);

                if (i != 0 && i % _batchSize == 0) {
                    session.flush();
                    session.clear();
                }
            }

            tx.commit();
        } catch (RuntimeException e) {
            // If this happens, for now, assume that there was something wrong 
            // with one of the log messages. Therefore do NOT re-insert 
            // the messages into _auditLogCache.
            s_logger.error("Failed to commit batch to Hibernate", e);
            if (tx != null) {
                tx.rollback();
            }
            throw e;
        } finally {
            session.close();
        }
    }

    List<AuditLogEntry> findAll() {
        return _hibernateTemplate.loadAll(AuditLogEntry.class);
    }

    @Override
    public void close() {
        Timer timer = _timer;
        if (timer != null) {
            try {
                _timer.cancel();
            } catch (Throwable ex) {
                s_logger.info("Error during timer cancellation", ex);
            }
            try {
                flushCache();
            } catch (Throwable ex) {
                s_logger.info("Error during flush", ex);
            }
        }
    }

}