comparison 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
comparison
equal deleted inserted replaced
29:22b03d5c84c5 30:88972c6daa4f
1 package de.intevation.artifactdatabase;
2
3 import de.intevation.artifacts.Artifact;
4
5 import java.sql.Connection;
6 import java.sql.SQLException;
7 import java.sql.PreparedStatement;
8 import java.sql.ResultSet;
9
10 import javax.sql.DataSource;
11
12 import org.apache.log4j.Logger;
13
14 import java.util.ArrayList;
15
16 /**
17 * @author Sascha L. Teichmann
18 */
19 public class DatabaseCleaner
20 extends Thread
21 {
22 private static Logger logger = Logger.getLogger(DatabaseCleaner.class);
23
24 public static final int MAX_ROWS = 50;
25
26 public static final String SQL_OUTDATED =
27 SQL.get("artifacts.outdated");
28
29 public static final String SQL_DELETE =
30 SQL.get("artifacts.delete");
31
32 public static final String SLEEP_XPATH =
33 "/artifact-database/cleaner/sleep-time/text()";
34
35 public static final long SLEEP_DEFAULT =
36 5 * 60 * 1000L; // 5 minutes
37
38 protected long sleepTime;
39
40 protected Object sleepLock = new Object();
41
42 protected Object context;
43
44 public DatabaseCleaner() {
45 }
46
47 public DatabaseCleaner(Object context) {
48 setDaemon(true);
49 sleepTime = getSleepTime();
50 this.context = context;
51 }
52
53 public void wakeup() {
54 synchronized (sleepLock) {
55 sleepLock.notify();
56 }
57 }
58
59 protected static long getSleepTime() {
60 String sleepTimeString = Config.getStringXPath(SLEEP_XPATH);
61
62 if (sleepTimeString == null) {
63 return SLEEP_DEFAULT;
64 }
65 try {
66 // sleep at least one second
67 return Math.max(Long.parseLong(sleepTimeString), 1000L);
68 }
69 catch (NumberFormatException nfe) {
70 logger.warn("Cleaner sleep time defaults to " + SLEEP_DEFAULT);
71 }
72 return SLEEP_DEFAULT;
73 }
74
75 private static final class IdData {
76
77 int id;
78 byte [] data;
79
80 public IdData(int id, byte [] data) {
81 this.id = id;
82 this.data = data;
83 }
84 } // class IdData
85
86 /**
87 * Cleaning is done in two phases. First we fetch a list of ids
88 * of artifacts. If there are artifacts the cleaning is done.
89 * Second we load the artifacts one by one one and call there
90 * endOfLife() method. In this loop we remove them from database, too.
91 * Each deletion is commited to ensure that a sudden failure
92 * of the artifact database server does delete artifacts twice
93 * or does not delete them at all. After this the first step
94 * is repeated.
95 */
96 protected void cleanup() {
97 logger.info("database cleanup");
98
99 Connection connection = null;
100 PreparedStatement fetchIds = null;
101 PreparedStatement deleteId = null;
102 ResultSet result = null;
103
104 int removedArtifacts = 0;
105
106 DataSource dataSource = DBConnection.getDataSource();
107 try {
108 connection = dataSource.getConnection();
109 connection.setAutoCommit(false);
110 fetchIds = connection.prepareStatement(SQL_OUTDATED);
111 deleteId = connection.prepareStatement(SQL_DELETE);
112
113 // some dbms like derby do not support LIMIT
114 // in SQL statements.
115 fetchIds.setMaxRows(MAX_ROWS);
116
117 for (;;) {
118 ArrayList ids = new ArrayList();
119
120 result = fetchIds.executeQuery();
121
122 while (result.next()) {
123 ids.add(new IdData(
124 result.getInt(1), result.getBytes(2)));
125 }
126
127 result.close(); result = null;
128
129 if (ids.isEmpty()) {
130 break;
131 }
132
133 for (int i = ids.size()-1; i >= 0; --i) {
134 IdData idData = (IdData)ids.get(i);
135 Artifact artifact = Backend.restoreArtifact(
136 idData.data);
137 idData.data = null;
138
139 deleteId.setInt(1, idData.id);
140 deleteId.execute();
141 connection.commit();
142
143 try {
144 if (artifact != null) {
145 artifact.endOfLife(context);
146 }
147 }
148 catch (Exception e) {
149 logger.error(e.getLocalizedMessage(), e);
150 }
151 } // for all fetched data
152
153 removedArtifacts += ids.size();
154 }
155 }
156 catch (SQLException sqle) {
157 logger.error(sqle.getLocalizedMessage(), sqle);
158 }
159 finally {
160 if (result != null) {
161 try { result.close(); }
162 catch (SQLException sqle) {}
163 }
164 if (fetchIds != null) {
165 try { fetchIds.close(); }
166 catch (SQLException sqle) {}
167 }
168 if (deleteId != null) {
169 try { deleteId.close(); }
170 catch (SQLException sqle) {}
171 }
172 if (connection != null) {
173 try { connection.close(); }
174 catch (SQLException sqle) {}
175 }
176 }
177
178 logger.info("artifacts removed: " + removedArtifacts);
179 }
180
181 public void run() {
182 logger.info("sleep time: " + sleepTime + "ms");
183 for (;;) {
184 cleanup();
185 try {
186 synchronized (sleepLock) {
187 sleepLock.wait(sleepTime);
188 }
189 }
190 catch (InterruptedException ie) {
191 }
192 } // for (;;)
193 }
194 }
195 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:

http://dive4elements.wald.intevation.org