diff artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java @ 30:88972c6daa4f

Added a cleanup thread which periodically removes outdated artifacts from database and calls there endOfLife() method. artifacts/trunk@70 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 10 Sep 2009 23:16:18 +0000
parents
children c2d53bd30ab8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java	Thu Sep 10 23:16:18 2009 +0000
@@ -0,0 +1,195 @@
+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;
+
+/**
+ *  @author Sascha L. Teichmann
+ */
+public class DatabaseCleaner
+extends      Thread
+{
+    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;
+
+    public DatabaseCleaner() {
+    }
+
+    public DatabaseCleaner(Object context) {
+        setDaemon(true);
+        sleepTime = getSleepTime();
+        this.context = context;
+    }
+
+    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 {
+
+        int     id;
+        byte [] data;
+
+        public IdData(int id, byte [] data) {
+            this.id   = id;
+            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 (;;) {
+                ArrayList ids = new ArrayList();
+
+                result = fetchIds.executeQuery();
+
+                while (result.next()) {
+                    ids.add(new IdData(
+                        result.getInt(1), result.getBytes(2)));
+                }
+
+                result.close(); result = null;
+
+                if (ids.isEmpty()) {
+                    break;
+                }
+
+                for (int i = ids.size()-1; i >= 0; --i) {
+                    IdData idData = (IdData)ids.get(i);
+                    Artifact artifact = Backend.restoreArtifact(
+                        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