Mercurial > dive4elements > framework
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: