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@30: sascha@30: /** sascha@30: * @author Sascha L. Teichmann sascha@30: */ sascha@30: public class DatabaseCleaner sascha@30: extends Thread sascha@30: { 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@30: public DatabaseCleaner() { sascha@30: } sascha@30: sascha@30: public DatabaseCleaner(Object context) { sascha@30: setDaemon(true); sascha@30: sleepTime = getSleepTime(); sascha@30: this.context = context; sascha@30: } sascha@30: 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@30: private static final class IdData { sascha@30: sascha@30: int id; sascha@30: byte [] data; sascha@30: sascha@30: public IdData(int id, byte [] data) { sascha@30: this.id = id; sascha@30: 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@30: ArrayList ids = new ArrayList(); sascha@30: sascha@30: result = fetchIds.executeQuery(); sascha@30: sascha@30: while (result.next()) { sascha@30: ids.add(new IdData( sascha@30: result.getInt(1), result.getBytes(2))); 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@30: for (int i = ids.size()-1; i >= 0; --i) { sascha@30: IdData idData = (IdData)ids.get(i); sascha@30: Artifact artifact = Backend.restoreArtifact( sascha@30: 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@30: try { sascha@30: synchronized (sleepLock) { sascha@30: sleepLock.wait(sleepTime); sascha@30: } sascha@30: } sascha@30: catch (InterruptedException ie) { sascha@30: } sascha@30: } // for (;;) sascha@30: } sascha@30: } sascha@30: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: