sascha@30: package de.intevation.artifactdatabase; sascha@30: sascha@30: import de.intevation.artifacts.Artifact; sascha@30: sascha@30: import java.sql.Connection; sascha@30: import java.sql.SQLException; sascha@30: import java.sql.PreparedStatement; sascha@30: import java.sql.ResultSet; sascha@30: sascha@30: import javax.sql.DataSource; sascha@30: sascha@30: import org.apache.log4j.Logger; sascha@30: sascha@30: import java.util.ArrayList; sascha@32: import java.util.List; sascha@30: sascha@30: /** sascha@30: * @author Sascha L. Teichmann sascha@30: */ sascha@30: public class DatabaseCleaner sascha@30: extends Thread sascha@30: { sascha@41: public interface ArtifactReviver { sascha@41: sascha@41: Artifact reviveArtifact(String factoryName, byte [] bytes); sascha@41: sascha@41: } // interface ArtifactReviver sascha@41: sascha@30: private static Logger logger = Logger.getLogger(DatabaseCleaner.class); sascha@30: sascha@30: public static final int MAX_ROWS = 50; sascha@30: sascha@30: public static final String SQL_OUTDATED = sascha@30: SQL.get("artifacts.outdated"); sascha@30: sascha@30: public static final String SQL_DELETE = sascha@30: SQL.get("artifacts.delete"); sascha@30: sascha@30: public static final String SLEEP_XPATH = sascha@30: "/artifact-database/cleaner/sleep-time/text()"; sascha@30: sascha@30: public static final long SLEEP_DEFAULT = sascha@30: 5 * 60 * 1000L; // 5 minutes sascha@30: sascha@30: protected long sleepTime; sascha@30: sascha@30: protected Object sleepLock = new Object(); sascha@30: sascha@30: protected Object context; sascha@30: sascha@32: protected Id.Filter filter; sascha@32: sascha@41: protected ArtifactReviver reviver; sascha@41: sascha@30: public DatabaseCleaner() { sascha@30: } sascha@30: sascha@41: public DatabaseCleaner(Object context, ArtifactReviver reviver) { sascha@30: setDaemon(true); sascha@30: sleepTime = getSleepTime(); sascha@30: this.context = context; sascha@41: this.reviver = reviver; sascha@30: } sascha@30: sascha@32: public void setFilter(Id.Filter filter) { sascha@32: this.filter = filter; sascha@32: } sascha@32: sascha@30: public void wakeup() { sascha@30: synchronized (sleepLock) { sascha@30: sleepLock.notify(); sascha@30: } sascha@30: } sascha@30: sascha@30: protected static long getSleepTime() { sascha@30: String sleepTimeString = Config.getStringXPath(SLEEP_XPATH); sascha@30: sascha@30: if (sleepTimeString == null) { sascha@30: return SLEEP_DEFAULT; sascha@30: } sascha@30: try { sascha@30: // sleep at least one second sascha@30: return Math.max(Long.parseLong(sleepTimeString), 1000L); sascha@30: } sascha@30: catch (NumberFormatException nfe) { sascha@30: logger.warn("Cleaner sleep time defaults to " + SLEEP_DEFAULT); sascha@30: } sascha@30: return SLEEP_DEFAULT; sascha@30: } sascha@30: sascha@47: private static final class IdData sascha@47: extends Id sascha@32: { sascha@30: byte [] data; sascha@41: String factoryName; sascha@30: sascha@41: public IdData(int id, String factoryName, byte [] data) { sascha@32: super(id); sascha@41: this.factoryName = factoryName; sascha@41: this.data = data; sascha@30: } sascha@30: } // class IdData sascha@30: sascha@30: /** sascha@30: * Cleaning is done in two phases. First we fetch a list of ids sascha@30: * of artifacts. If there are artifacts the cleaning is done. sascha@30: * Second we load the artifacts one by one one and call there sascha@30: * endOfLife() method. In this loop we remove them from database, too. sascha@30: * Each deletion is commited to ensure that a sudden failure sascha@30: * of the artifact database server does delete artifacts twice sascha@30: * or does not delete them at all. After this the first step sascha@30: * is repeated. sascha@30: */ sascha@30: protected void cleanup() { sascha@30: logger.info("database cleanup"); sascha@30: sascha@30: Connection connection = null; sascha@30: PreparedStatement fetchIds = null; sascha@30: PreparedStatement deleteId = null; sascha@30: ResultSet result = null; sascha@30: sascha@30: int removedArtifacts = 0; sascha@30: sascha@30: DataSource dataSource = DBConnection.getDataSource(); sascha@30: try { sascha@30: connection = dataSource.getConnection(); sascha@30: connection.setAutoCommit(false); sascha@30: fetchIds = connection.prepareStatement(SQL_OUTDATED); sascha@30: deleteId = connection.prepareStatement(SQL_DELETE); sascha@30: sascha@30: // some dbms like derby do not support LIMIT sascha@30: // in SQL statements. sascha@30: fetchIds.setMaxRows(MAX_ROWS); sascha@30: sascha@30: for (;;) { sascha@32: List ids = new ArrayList(); sascha@30: sascha@30: result = fetchIds.executeQuery(); sascha@30: sascha@30: while (result.next()) { sascha@30: ids.add(new IdData( sascha@47: result.getInt(1), sascha@41: result.getString(2), sascha@41: result.getBytes(3))); sascha@30: } sascha@30: sascha@30: result.close(); result = null; sascha@30: sascha@30: if (ids.isEmpty()) { sascha@30: break; sascha@30: } sascha@30: sascha@32: if (filter != null) { sascha@32: ids = filter.filterIds(ids); sascha@32: } sascha@32: sascha@30: for (int i = ids.size()-1; i >= 0; --i) { sascha@30: IdData idData = (IdData)ids.get(i); sascha@41: Artifact artifact = reviver.reviveArtifact( sascha@41: idData.factoryName, idData.data); sascha@30: idData.data = null; sascha@30: sascha@30: deleteId.setInt(1, idData.id); sascha@30: deleteId.execute(); sascha@30: connection.commit(); sascha@30: sascha@30: try { sascha@30: if (artifact != null) { sascha@30: artifact.endOfLife(context); sascha@30: } sascha@30: } sascha@30: catch (Exception e) { sascha@30: logger.error(e.getLocalizedMessage(), e); sascha@30: } sascha@30: } // for all fetched data sascha@30: sascha@30: removedArtifacts += ids.size(); sascha@30: } sascha@30: } sascha@30: catch (SQLException sqle) { sascha@30: logger.error(sqle.getLocalizedMessage(), sqle); sascha@30: } sascha@30: finally { sascha@30: if (result != null) { sascha@30: try { result.close(); } sascha@30: catch (SQLException sqle) {} sascha@30: } sascha@30: if (fetchIds != null) { sascha@30: try { fetchIds.close(); } sascha@30: catch (SQLException sqle) {} sascha@30: } sascha@30: if (deleteId != null) { sascha@30: try { deleteId.close(); } sascha@30: catch (SQLException sqle) {} sascha@30: } sascha@30: if (connection != null) { sascha@30: try { connection.close(); } sascha@30: catch (SQLException sqle) {} sascha@30: } sascha@30: } sascha@30: sascha@30: logger.info("artifacts removed: " + removedArtifacts); sascha@30: } sascha@30: sascha@30: public void run() { sascha@30: logger.info("sleep time: " + sleepTime + "ms"); sascha@30: for (;;) { sascha@30: cleanup(); sascha@48: long startTime = System.currentTimeMillis(); sascha@48: sascha@30: try { sascha@30: synchronized (sleepLock) { sascha@30: sleepLock.wait(sleepTime); sascha@30: } sascha@30: } sascha@30: catch (InterruptedException ie) { sascha@30: } sascha@48: sascha@48: long stopTime = System.currentTimeMillis(); sascha@48: sascha@48: if (logger.isDebugEnabled()) { sascha@48: logger.debug("Cleaner slept " + (stopTime - startTime) + "ms"); sascha@48: } sascha@30: } // for (;;) sascha@30: } sascha@30: } sascha@30: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: