fr.gael.dhus.database.dao.ActionRecordWritterDao.java Source code

Java tutorial

Introduction

Here is the source code for fr.gael.dhus.database.dao.ActionRecordWritterDao.java

Source

/*
 * Data Hub Service (DHuS) - For Space data distribution.
 * Copyright (C) 2013,2014,2015 GAEL Systems
 *
 * This file is part of DHuS software sources.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package fr.gael.dhus.database.dao;

import fr.gael.dhus.database.dao.interfaces.HibernateDao;
import fr.gael.dhus.database.object.Collection;
import fr.gael.dhus.database.object.User;
import fr.gael.dhus.database.object.statistic.ActionRecord;
import fr.gael.dhus.database.object.statistic.ActionRecordDownload;
import fr.gael.dhus.database.object.statistic.ActionRecordLogon;
import fr.gael.dhus.database.object.statistic.ActionRecordSearch;
import fr.gael.dhus.database.object.statistic.ActionRecordUpload;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.stereotype.Repository;

import java.net.URL;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Data Access Object allows to save and clean user actions, in order to
 * generate statistics tables.
 */
@Repository
public class ActionRecordWritterDao extends HibernateDao<ActionRecord, Long> {
    /**
     * Use the System parameter called "action.record.inactive" to fully 
     * deactivate the insertion of all the action record into the database.
     * Default value it false. 
     */
    private static Boolean inactive = Boolean.parseBoolean(System.getProperty("action.record.inactive", "false"));
    /**
     * Period time in milliseconds
     */
    private static final int LOG_TIMER_PERIOD = 250;

    /**
     * Task which write current waiting logs in database.
     */
    private final TimerTask logWriter = new LogWriterTask();

    /**
     * {@link Timer} allow to execute a task at regular intervals.
     */
    private final Timer logTimer = new Timer("logWriter");

    /**
     * Thread-safe {@link List} allows to save waiting logs to be write in
     * database.
     */
    private final CopyOnWriteArrayList<ActionRecord> logs = new CopyOnWriteArrayList<>();

    /**
     * {@link Timer} scheduled status.
     */
    private final AtomicBoolean isScheduled = new AtomicBoolean(false);

    /**
     * Active {@link Timer} to write logs in database, only if necessary.
     * Calls method {@link Timer#schedule(TimerTask, Date, long)} only once
     * during all program time.
     * Then "action.record.active" is set to false by the user, the timer is 
     * never started.
     */
    private void activeLogTimer() {
        if (inactive)
            return;
        synchronized (isScheduled) {
            if (!isScheduled.get()) {
                logTimer.scheduleAtFixedRate(logWriter, 0, LOG_TIMER_PERIOD);
                isScheduled.set(true);
            }
        }
    }

    /**
     * Called when a user attempts a connection.
     *
     * @param username login of user.
     * @return a {@link ActionRecordLogon} for specific username and
     * with a status STARTED.
     */
    private ActionRecordLogon saveLoginStart(String username) {
        if (inactive)
            return null;
        //      ActionRecordLogon acLogon = new ActionRecordLogon ();
        //      acLogon.setName ("LOGON");
        //      acLogon.setUsername (username);
        //      acLogon.setStatus (ActionRecord.STATUS_STARTED);      
        return null;
    }

    /**
     * Called when a user attempts a connection.<br />
     * Writes asynchronously in database.
     * @param username login of user.
     */
    public void loginStart(final String username) {
        if (inactive)
            return;
        logs.add(saveLoginStart(username));
        activeLogTimer();
    }

    private ActionRecordLogon saveLoginEnd(User user, boolean result) {
        if (inactive)
            return null;

        final String status;
        if (result) {
            status = ActionRecord.STATUS_SUCCEEDED;
            ActionRecordLogon acLogon = new ActionRecordLogon();
            acLogon.setName("LOGON");
            acLogon.setUser(user);
            acLogon.setStatus(status);
            return acLogon;
        } // else status = ActionRecord.STATUS_FAILED ...
        return null;
    }

    public void loginEnd(final User username, final boolean result) {
        if (inactive)
            return;
        logs.add(saveLoginEnd(username, result));
        activeLogTimer();
    }

    private ActionRecordSearch saveSearch(String query, int start_index, int num_element, User user) {
        if (inactive)
            return null;
        if (start_index == 0) {
            ActionRecordSearch acSearch = new ActionRecordSearch();
            acSearch.setName("SEARCH");
            acSearch.setUser(user);
            acSearch.setSearch(query);
            return acSearch;
        }
        return null;
    }

    public void search(final String query, final int start_index, final int num_element, final User user) {
        if (inactive)
            return;
        logs.add(saveSearch(query, start_index, num_element, user));
        activeLogTimer();
    }

    private ActionRecordDownload saveDownloadStart(String identifier, long size, String username) {
        if (inactive)
            return null;
        //      ActionRecordDownload acDownload = new ActionRecordDownload ();
        //      acDownload.setName ("DOWNLOAD");
        //      acDownload.setStatus(ActionRecord.STATUS_STARTED);
        //      acDownload.setUsername (username);
        //      acDownload.setProductIdentifier(identifier);
        //      acDownload.setProductSize(size);
        return null;
    }

    public void downloadStart(final String identifier, final long size, final String user) {
        if (inactive)
            return;
        logs.add(saveDownloadStart(identifier, size, user));
        activeLogTimer();
    }

    private ActionRecordDownload saveDownloadEnd(String identifier, long size, User user) {
        if (inactive)
            return null;
        ActionRecordDownload acDownload = new ActionRecordDownload();
        acDownload.setName("DOWNLOAD");
        acDownload.setStatus(ActionRecord.STATUS_SUCCEEDED);
        acDownload.setUser(user);
        acDownload.setProductIdentifier(identifier);
        acDownload.setProductSize(size);
        //      create (acDownload);
        return acDownload;
    }

    public void downloadEnd(final String identifier, final long size, final User user) {
        if (inactive)
            return;
        logs.add(saveDownloadEnd(identifier, size, user));
        activeLogTimer();
    }

    private ActionRecordDownload saveDownloadFailed(String identifier, long size, String username) {
        if (inactive)
            return null;
        //      ActionRecordDownload acDownload = new ActionRecordDownload ();
        //      acDownload.setName ("DOWNLOAD");
        //      acDownload.setStatus(ActionRecord.STATUS_FAILED);
        //      acDownload.setUsername (username);
        //      acDownload.setProductIdentifier(identifier);
        //      acDownload.setProductSize(size);
        return null;
    }

    public void downloadFailed(final String identifier, final long size, final String user) {
        if (inactive)
            return;
        logs.add(saveDownloadFailed(identifier, size, user));
        activeLogTimer();
    }

    private ActionRecordUpload saveUploadStart(String filename, final String owner) {
        if (inactive)
            return null;
        //      ActionRecordUpload acUpload = new ActionRecordUpload ();
        //      acUpload.setName ("UPLOAD");
        //      acUpload.setStatus(ActionRecord.STATUS_STARTED);
        //      acUpload.setUsername (owner);
        //      acUpload.setProductIdentifier(filename);
        //      // no info on size
        return null;
    }

    public void uploadStart(final String filename, final String owner) {
        if (inactive)
            return;
        logs.add(saveUploadStart(filename, owner));
        activeLogTimer();
    }

    private ActionRecordUpload saveUploadEnd(URL path, final String owner, final List<Collection> collections,
            boolean result) {
        if (inactive)
            return null;
        //      // Create a record into ActionRecordUpload
        //      ActionRecordUpload acUpload = new ActionRecordUpload ();
        //      acUpload.setName ("UPLOAD");
        //      acUpload.setUsername (owner);
        //      if (result == true)
        //      {
        //         acUpload.setStatus(ActionRecord.STATUS_SUCCEEDED);
        //         Product p = productDao.getProductByPath(path);
        //         if ((p != null)                 &&
        //             (p.getIdentifier() != null) &&
        //             (p.getSize() != null))
        //         {
        //            acUpload.setProductIdentifier(p.getIdentifier());
        //            acUpload.setProductSize(p.getSize());
        //         }
        //         else
        //         {
        //            acUpload.setProductIdentifier(path.getFile ());
        //         }
        //      }
        //      else
        //      {
        //         acUpload.setStatus(ActionRecord.STATUS_FAILED);
        //      }
        //
        //      // create zero, one or more records in ActionRecordCollection
        //      if (collections.size() > 0)
        //      {
        //         Set<String> set = new HashSet<String> ();
        //
        //         for (int i = 0; i < collections.size(); i++)
        //         {
        //            set.add(collections.get(i).getName());
        //         }
        //         acUpload.setCollectionNameList(set);
        //      }
        //
        return null;
    }

    public void uploadEnd(final URL path, final String owner, final List<Collection> collections,
            final boolean result) {
        if (inactive)
            return;
        logs.add(saveUploadEnd(path, owner, collections, result));
        activeLogTimer();
    }

    private ActionRecordUpload saveUploadFailed(String path, final String owner) {
        if (inactive)
            return null;
        return null;
    }

    public void uploadFailed(final String path, final String owner) {
        if (inactive)
            return;
        logs.add(saveUploadFailed(path, owner));
        activeLogTimer();
    }

    public void cleanupOlderActionRecords(final int keep_period) {
        if (inactive) {
            logger.warn("Action record access has been deactivated by user via "
                    + "\"action.record.inactive\" parameter: the action record tables " + "will not be purged.");
            return;
        }

        getHibernateTemplate().execute(new HibernateCallback<Void>() {
            @Override
            public Void doInHibernate(Session session) throws HibernateException, SQLException {
                long days = keep_period * 24 * 60 * 60 * 1000L;
                Date date = new Date(System.currentTimeMillis() - days);
                String pattern = "DELETE FROM <table> WHERE created < ?";

                String hql = pattern.replace("<table>", "ActionRecordDownload");
                Query query = session.createQuery(hql);
                query.setDate(0, date);
                query.executeUpdate();

                hql = pattern.replace("<table>", "ActionRecordLogon");
                query = session.createQuery(hql);
                query.setDate(0, date);
                query.executeUpdate();

                hql = pattern.replace("<table>", "ActionRecordSearch");
                query = session.createQuery(hql);
                query.setDate(0, date);
                query.executeUpdate();

                hql = "FROM ActionRecordUpload WHERE created < ?";
                query = session.createQuery(hql);
                query.setDate(0, date);

                @SuppressWarnings("unchecked")
                List<ActionRecordUpload> uploads = query.list();
                for (ActionRecordUpload upload : uploads) {
                    session.evict(upload);
                    session.delete(upload);
                }
                return null;
            }
        });
    }

    private class LogWriterTask extends TimerTask {
        /**
         * Task running status.
         */
        private final AtomicBoolean isRunning = new AtomicBoolean(false);

        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            synchronized (isRunning) {
                if (isRunning.get() || logs.isEmpty()) {
                    return;
                }
                isRunning.set(true);

                List<ActionRecord> logsToWrite;
                synchronized (logs) {
                    logsToWrite = (List<ActionRecord>) logs.clone();
                    logs.clear();
                }
                Session session = getSessionFactory().openSession();
                Transaction transaction = null;
                try {
                    session.setFlushMode(FlushMode.COMMIT);
                    transaction = session.beginTransaction();
                    for (ActionRecord actionRecord : logsToWrite) {
                        if (actionRecord != null) {
                            session.save(actionRecord.getClass().getName(), actionRecord);
                        }
                    }
                    logsToWrite.clear();
                    transaction.commit();
                } catch (Exception e) {
                    logger.error("An error occur during writing logs stats !", e);
                    if (transaction != null) {
                        transaction.rollback();
                    }
                } finally {
                    session.disconnect();
                }

                synchronized (isRunning) {
                    isRunning.set(false);
                }
            }
        }
    }

    // Override inherited methods TYo avoid access to DB
    @Override
    public int count() {
        if (inactive)
            return 0;
        return super.count();
    }

    @Override
    public void update(ActionRecord t) {
        if (inactive)
            return;
        super.update(t);
    }

    @SuppressWarnings("rawtypes")
    @Override
    public List find(String query_string) throws DataAccessException {
        if (inactive)
            return Collections.emptyList();
        return super.find(query_string);
    }

    @Override
    public ActionRecord read(Long id) {
        if (inactive)
            return null;
        return super.read(id);
    }

    @Override
    public List<ActionRecord> readAll() {
        if (inactive)
            return Collections.emptyList();
        return super.readAll();
    }

    @Override
    public ActionRecord create(ActionRecord t) {
        if (inactive)
            return t;
        return super.create(t);
    }

    @Override
    public void delete(ActionRecord t) {
        if (inactive)
            return;
        super.delete(t);
    }

    @Override
    public void deleteAll() {
        if (inactive)
            return;
        super.deleteAll();
    }

    @Override
    public List<ActionRecord> scroll(String clauses, int skip, int n) {
        if (inactive)
            return Collections.emptyList();
        return super.scroll(clauses, skip, n);
    }
}