view artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java @ 43:b8516aa3d8a1

Time delta calculation in SQL statement to figure out outdated artifacts in H2 database was broken. artifacts/trunk@122 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 24 Sep 2009 09:15:56 +0000
parents 5e4bc24ea438
children 4ae4dc99127d
line wrap: on
line source
package de.intevation.artifactdatabase;

import de.intevation.artifacts.Artifact;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import javax.sql.DataSource;

import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;

/**
 *  @author Sascha L. Teichmann
 */
public class DatabaseCleaner
extends      Thread
{
    public interface ArtifactReviver {

        Artifact reviveArtifact(String factoryName, byte [] bytes);

    } // interface ArtifactReviver

    private static Logger logger = Logger.getLogger(DatabaseCleaner.class);

    public static final int MAX_ROWS = 50;

    public static final String SQL_OUTDATED =
        SQL.get("artifacts.outdated");

    public static final String SQL_DELETE =
        SQL.get("artifacts.delete");

    public static final String SLEEP_XPATH =
        "/artifact-database/cleaner/sleep-time/text()";

    public static final long SLEEP_DEFAULT =
        5 * 60 * 1000L; // 5 minutes

    protected long sleepTime;

    protected Object sleepLock = new Object();

    protected Object context;

    protected Id.Filter filter;

    protected ArtifactReviver reviver;

    public DatabaseCleaner() {
    }

    public DatabaseCleaner(Object context, ArtifactReviver reviver) {
        setDaemon(true);
        sleepTime = getSleepTime();
        this.context = context;
        this.reviver = reviver;
    }

    public void setFilter(Id.Filter filter) {
        this.filter = filter;
    }

    public void wakeup() {
        synchronized (sleepLock) {
            sleepLock.notify();
        }
    }

    protected static long getSleepTime() {
        String sleepTimeString = Config.getStringXPath(SLEEP_XPATH);

        if (sleepTimeString == null) {
            return SLEEP_DEFAULT;
        }
        try {
            // sleep at least one second
            return Math.max(Long.parseLong(sleepTimeString), 1000L);
        }
        catch (NumberFormatException nfe) {
            logger.warn("Cleaner sleep time defaults to " + SLEEP_DEFAULT);
        }
        return SLEEP_DEFAULT;
    }

    private static final class IdData 
    extends                    Id 
    {
        byte [] data;
        String  factoryName;

        public IdData(int id, String factoryName, byte [] data) {
            super(id);
            this.factoryName = factoryName;
            this.data        = data;
        }
    } // class IdData

    /**
     * Cleaning is done in two phases. First we fetch a list of ids
     * of artifacts. If there are artifacts the cleaning is done.
     * Second we load the artifacts one by one one and call there
     * endOfLife() method. In this loop we remove them from database, too.
     * Each deletion is commited to ensure that a sudden failure
     * of the artifact database server does delete artifacts twice
     * or does not delete them at all. After this the first step
     * is repeated.
     */
    protected void cleanup() {
        logger.info("database cleanup");

        Connection connection      = null;
        PreparedStatement fetchIds = null;
        PreparedStatement deleteId = null;
        ResultSet         result   = null;

        int removedArtifacts = 0;

        DataSource dataSource = DBConnection.getDataSource();
        try {
            connection = dataSource.getConnection();
            connection.setAutoCommit(false);
            fetchIds = connection.prepareStatement(SQL_OUTDATED);
            deleteId = connection.prepareStatement(SQL_DELETE);

            // some dbms like derby do not support LIMIT
            // in SQL statements.
            fetchIds.setMaxRows(MAX_ROWS);

            for (;;) {
                List ids = new ArrayList();

                result = fetchIds.executeQuery();

                while (result.next()) {
                    ids.add(new IdData(
                        result.getInt(1), 
                        result.getString(2),
                        result.getBytes(3)));
                }

                result.close(); result = null;

                if (ids.isEmpty()) {
                    break;
                }

                if (filter != null) {
                    ids = filter.filterIds(ids);
                }

                for (int i = ids.size()-1; i >= 0; --i) {
                    IdData idData = (IdData)ids.get(i);
                    Artifact artifact = reviver.reviveArtifact(
                        idData.factoryName, idData.data);
                    idData.data = null;

                    deleteId.setInt(1, idData.id);
                    deleteId.execute();
                    connection.commit();

                    try {
                        if (artifact != null) {
                            artifact.endOfLife(context);
                        }
                    }
                    catch (Exception e) {
                        logger.error(e.getLocalizedMessage(), e);
                    }
                } // for all fetched data

                removedArtifacts += ids.size();
            }
        }
        catch (SQLException sqle) {
            logger.error(sqle.getLocalizedMessage(), sqle);
        }
        finally {
            if (result != null) {
                try { result.close(); }
                catch (SQLException sqle) {}
            }
            if (fetchIds != null) {
                try { fetchIds.close(); }
                catch (SQLException sqle) {}
            }
            if (deleteId != null) {
                try { deleteId.close(); }
                catch (SQLException sqle) {}
            }
            if (connection != null) {
                try { connection.close(); }
                catch (SQLException sqle) {}
            }
        }

        logger.info("artifacts removed: " + removedArtifacts);
    }

    public void run() {
        logger.info("sleep time: " + sleepTime + "ms");
        for (;;) {
            cleanup();
            try {
                synchronized (sleepLock) {
                    sleepLock.wait(sleepTime);
                }
            }
            catch (InterruptedException ie) {
            }
        } // for (;;)
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:

http://dive4elements.wald.intevation.org