Mercurial > dive4elements > framework
comparison artifact-database/src/main/java/org/dive4elements/artifactdatabase/DatabaseCleaner.java @ 541:3b1e48d22ce0
Experimentally let database cleaner and backend share the same sql executor.
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Thu, 03 Sep 2015 15:34:07 +0200 |
parents | 415df0fc4fa1 |
children | 9497f58484a0 |
comparison
equal
deleted
inserted
replaced
540:91b1435fb9ea | 541:3b1e48d22ce0 |
---|---|
12 import org.dive4elements.artifacts.common.utils.StringUtils; | 12 import org.dive4elements.artifacts.common.utils.StringUtils; |
13 | 13 |
14 import org.dive4elements.artifacts.Artifact; | 14 import org.dive4elements.artifacts.Artifact; |
15 | 15 |
16 import org.dive4elements.artifactdatabase.db.SQL; | 16 import org.dive4elements.artifactdatabase.db.SQL; |
17 import org.dive4elements.artifactdatabase.db.SQLExecutor; | |
17 import org.dive4elements.artifactdatabase.db.DBConnection; | 18 import org.dive4elements.artifactdatabase.db.DBConnection; |
18 | 19 |
19 import java.sql.Connection; | 20 import java.sql.Connection; |
20 import java.sql.PreparedStatement; | 21 import java.sql.PreparedStatement; |
21 import java.sql.ResultSet; | 22 import java.sql.ResultSet; |
133 * The reviver used to bring the dead artifact on last | 134 * The reviver used to bring the dead artifact on last |
134 * time back to live to call endOfLife() on them. | 135 * time back to live to call endOfLife() on them. |
135 */ | 136 */ |
136 protected ArtifactReviver reviver; | 137 protected ArtifactReviver reviver; |
137 | 138 |
138 protected DBConnection dbConnection; | 139 protected SQLExecutor sqlExecutor; |
139 | 140 |
140 /** | 141 /** |
141 * Default constructor. | 142 * Default constructor. |
142 */ | 143 */ |
143 public DatabaseCleaner() { | 144 public DatabaseCleaner() { |
147 * Constructor to create a cleaner with a given global context | 148 * Constructor to create a cleaner with a given global context |
148 * and a given reviver. | 149 * and a given reviver. |
149 * @param context The global context of the artifact database | 150 * @param context The global context of the artifact database |
150 * @param reviver The reviver to awake artifact one last time. | 151 * @param reviver The reviver to awake artifact one last time. |
151 */ | 152 */ |
152 public DatabaseCleaner(Object context, ArtifactReviver reviver, DBConfig config) { | 153 public DatabaseCleaner( |
154 Object context, | |
155 ArtifactReviver reviver, | |
156 SQLExecutor sqlExecutor, | |
157 DBConfig config | |
158 ) { | |
153 setDaemon(true); | 159 setDaemon(true); |
154 sleepTime = getSleepTime(); | 160 sleepTime = getSleepTime(); |
155 this.context = context; | 161 this.context = context; |
156 this.reviver = reviver; | 162 this.reviver = reviver; |
157 this.dbConnection = config.getDBConnection(); | 163 this.sqlExecutor = sqlExecutor; |
158 setupSQL(config.getSQL()); | 164 setupSQL(config.getSQL()); |
159 } | 165 } |
160 | 166 |
161 protected void setupSQL(SQL sql) { | 167 protected void setupSQL(SQL sql) { |
162 SQL_OUTDATED = sql.get("artifacts.outdated"); | 168 SQL_OUTDATED = sql.get("artifacts.outdated"); |
247 * is repeated. | 253 * is repeated. |
248 */ | 254 */ |
249 protected void cleanup() { | 255 protected void cleanup() { |
250 logger.info("database cleanup"); | 256 logger.info("database cleanup"); |
251 | 257 |
252 Connection connection = null; | 258 final Set<Integer> lockedIds = lockedIdsProvider != null |
253 PreparedStatement fetchIds = null; | |
254 PreparedStatement stmnt = null; | |
255 ResultSet result = null; | |
256 | |
257 DataSource dataSource = dbConnection.getDataSource(); | |
258 | |
259 Set<Integer> lockedIds = lockedIdsProvider != null | |
260 ? lockedIdsProvider.getLockedIds() | 259 ? lockedIdsProvider.getLockedIds() |
261 : EMPTY_IDS; | 260 : EMPTY_IDS; |
262 | 261 |
263 String questionMarks = lockedIds.isEmpty() | 262 final String questionMarks = lockedIds.isEmpty() |
264 ? "-666" // XXX: A bit hackish. | 263 ? "-666" // XXX: A bit hackish. |
265 : StringUtils.repeat('?', lockedIds.size(), ','); | 264 : StringUtils.repeat('?', lockedIds.size(), ','); |
266 | 265 |
267 List<String> deletedCollections = new ArrayList<String>(); | 266 final List<String> deletedCollections = new ArrayList<String>(); |
268 List<String> deletedArtifacts = new ArrayList<String>(); | 267 final List<String> deletedArtifacts = new ArrayList<String>(); |
269 | 268 |
270 try { | 269 SQLExecutor.Instance exec = sqlExecutor.new Instance() { |
271 connection = dataSource.getConnection(); | 270 |
272 connection.setAutoCommit(false); | 271 @Override |
273 | 272 public boolean doIt() throws SQLException { |
274 fetchIds = connection.prepareStatement( | 273 |
275 SQL_OUTDATED.replace("$LOCKED_IDS$", questionMarks)); | 274 PreparedStatement fetchIds = null; |
276 | 275 PreparedStatement stmnt = null; |
277 // some dbms like derby do not support LIMIT | 276 ResultSet result = null; |
278 // in SQL statements. | 277 |
279 fetchIds.setMaxRows(MAX_ROWS); | 278 try { |
280 | 279 fetchIds = conn.prepareStatement( |
281 // Fetch ids of outdated collections | 280 SQL_OUTDATED.replace("$LOCKED_IDS$", questionMarks)); |
282 stmnt = connection.prepareStatement( | 281 |
283 SQL_OUTDATED_COLLECTIONS.replace( | 282 // some dbms like derby do not support LIMIT |
284 "$LOCKED_IDS$", questionMarks)); | 283 // in SQL statements. |
285 | 284 fetchIds.setMaxRows(MAX_ROWS); |
286 // fill in the locked ids | 285 |
287 int idx = 1; | 286 // Fetch ids of outdated collections |
288 for (Integer id: lockedIds) { | 287 stmnt = conn.prepareStatement( |
289 fetchIds.setInt(idx, id); | 288 SQL_OUTDATED_COLLECTIONS.replace( |
290 stmnt .setInt(idx, id); | 289 "$LOCKED_IDS$", questionMarks)); |
291 ++idx; | 290 |
291 // fill in the locked ids | |
292 int idx = 1; | |
293 for (Integer id: lockedIds) { | |
294 fetchIds.setInt(idx, id); | |
295 stmnt .setInt(idx, id); | |
296 ++idx; | |
297 } | |
298 | |
299 ArrayList<IdIdentifier> cs = new ArrayList<IdIdentifier>(); | |
300 result = stmnt.executeQuery(); | |
301 while (result.next()) { | |
302 cs.add(new IdIdentifier( | |
303 result.getInt(1), | |
304 result.getString(2))); | |
305 } | |
306 | |
307 result.close(); result = null; | |
308 stmnt.close(); stmnt = null; | |
309 | |
310 // delete collection items | |
311 stmnt = conn.prepareStatement(SQL_DELETE_COLLECTION_ITEMS); | |
312 | |
313 for (IdIdentifier id: cs) { | |
314 logger.debug("Mark collection for deletion: " + id.id); | |
315 stmnt.setInt(1, id.id); | |
316 stmnt.execute(); | |
317 } | |
318 | |
319 stmnt.close(); stmnt = null; | |
320 | |
321 // delete collections | |
322 stmnt = conn.prepareStatement(SQL_DELETE_COLLECTION); | |
323 | |
324 for (IdIdentifier id: cs) { | |
325 stmnt.setInt(1, id.id); | |
326 stmnt.execute(); | |
327 deletedCollections.add(id.identifier); | |
328 } | |
329 | |
330 stmnt.close(); stmnt = null; | |
331 conn.commit(); | |
332 | |
333 cs = null; | |
334 | |
335 // remove artifacts | |
336 stmnt = conn.prepareStatement(SQL_DELETE_ARTIFACT); | |
337 | |
338 for (;;) { | |
339 List<IdData> ids = new ArrayList<IdData>(); | |
340 | |
341 result = fetchIds.executeQuery(); | |
342 | |
343 while (result.next()) { | |
344 ids.add(new IdData( | |
345 result.getInt(1), | |
346 result.getString(2), | |
347 result.getBytes(3), | |
348 result.getString(4))); | |
349 } | |
350 | |
351 result.close(); result = null; | |
352 | |
353 if (ids.isEmpty()) { | |
354 break; | |
355 } | |
356 | |
357 for (int i = ids.size()-1; i >= 0; --i) { | |
358 IdData idData = ids.get(i); | |
359 Artifact artifact = reviver.reviveArtifact( | |
360 idData.factoryName, idData.data); | |
361 idData.data = null; | |
362 | |
363 logger.debug("Prepare Artifact (id=" | |
364 + idData.id + ") for deletion."); | |
365 | |
366 stmnt.setInt(1, idData.id); | |
367 stmnt.execute(); | |
368 conn.commit(); | |
369 | |
370 try { | |
371 if (artifact != null) { | |
372 logger.debug("Call endOfLife for Artifact: " | |
373 + artifact.identifier()); | |
374 | |
375 artifact.endOfLife(context); | |
376 } | |
377 } | |
378 catch (Exception e) { | |
379 logger.error(e.getMessage(), e); | |
380 } | |
381 | |
382 deletedArtifacts.add(idData.identifier); | |
383 } // for all fetched data | |
384 } | |
385 } | |
386 finally { | |
387 if (result != null) { | |
388 try { result.close(); } | |
389 catch (SQLException sqle) {} | |
390 } | |
391 if (stmnt != null) { | |
392 try { stmnt.close(); } | |
393 catch (SQLException sqle) {} | |
394 } | |
395 if (fetchIds != null) { | |
396 try { fetchIds.close(); } | |
397 catch (SQLException sqle) {} | |
398 } | |
399 } | |
400 return true; | |
292 } | 401 } |
293 | 402 }; |
294 ArrayList<IdIdentifier> cs = new ArrayList<IdIdentifier>(); | 403 |
295 result = stmnt.executeQuery(); | 404 if (!exec.runWriteNoRollback()) { |
296 while (result.next()) { | 405 logger.error("Deleting artifacts failed."); |
297 cs.add(new IdIdentifier( | |
298 result.getInt(1), | |
299 result.getString(2))); | |
300 } | |
301 | |
302 result.close(); result = null; | |
303 stmnt.close(); stmnt = null; | |
304 | |
305 // delete collection items | |
306 stmnt = connection.prepareStatement(SQL_DELETE_COLLECTION_ITEMS); | |
307 | |
308 for (IdIdentifier id: cs) { | |
309 logger.debug("Mark collection for deletion: " + id.id); | |
310 stmnt.setInt(1, id.id); | |
311 stmnt.execute(); | |
312 } | |
313 | |
314 stmnt.close(); stmnt = null; | |
315 | |
316 // delete collections | |
317 stmnt = connection.prepareStatement(SQL_DELETE_COLLECTION); | |
318 | |
319 for (IdIdentifier id: cs) { | |
320 stmnt.setInt(1, id.id); | |
321 stmnt.execute(); | |
322 deletedCollections.add(id.identifier); | |
323 } | |
324 | |
325 stmnt.close(); stmnt = null; | |
326 connection.commit(); | |
327 | |
328 cs = null; | |
329 | |
330 // remove artifacts | |
331 stmnt = connection.prepareStatement(SQL_DELETE_ARTIFACT); | |
332 | |
333 for (;;) { | |
334 List<IdData> ids = new ArrayList<IdData>(); | |
335 | |
336 result = fetchIds.executeQuery(); | |
337 | |
338 while (result.next()) { | |
339 ids.add(new IdData( | |
340 result.getInt(1), | |
341 result.getString(2), | |
342 result.getBytes(3), | |
343 result.getString(4))); | |
344 } | |
345 | |
346 result.close(); result = null; | |
347 | |
348 if (ids.isEmpty()) { | |
349 break; | |
350 } | |
351 | |
352 for (int i = ids.size()-1; i >= 0; --i) { | |
353 IdData idData = ids.get(i); | |
354 Artifact artifact = reviver.reviveArtifact( | |
355 idData.factoryName, idData.data); | |
356 idData.data = null; | |
357 | |
358 logger.debug("Prepare Artifact (id=" | |
359 + idData.id + ") for deletion."); | |
360 | |
361 stmnt.setInt(1, idData.id); | |
362 stmnt.execute(); | |
363 connection.commit(); | |
364 | |
365 try { | |
366 if (artifact != null) { | |
367 logger.debug("Call endOfLife for Artifact: " | |
368 + artifact.identifier()); | |
369 | |
370 artifact.endOfLife(context); | |
371 } | |
372 } | |
373 catch (Exception e) { | |
374 logger.error(e.getMessage(), e); | |
375 } | |
376 | |
377 deletedArtifacts.add(idData.identifier); | |
378 } // for all fetched data | |
379 } | |
380 } | |
381 catch (SQLException sqle) { | |
382 logger.error(sqle.getLocalizedMessage(), sqle); | |
383 } | |
384 finally { | |
385 if (result != null) { | |
386 try { result.close(); } | |
387 catch (SQLException sqle) {} | |
388 } | |
389 if (stmnt != null) { | |
390 try { stmnt.close(); } | |
391 catch (SQLException sqle) {} | |
392 } | |
393 if (fetchIds != null) { | |
394 try { fetchIds.close(); } | |
395 catch (SQLException sqle) {} | |
396 } | |
397 if (connection != null) { | |
398 try { connection.close(); } | |
399 catch (SQLException sqle) {} | |
400 } | |
401 } | 406 } |
402 | 407 |
403 if (!deletedCollections.isEmpty()) { | 408 if (!deletedCollections.isEmpty()) { |
404 reviver.killedCollections(deletedCollections); | 409 reviver.killedCollections(deletedCollections); |
405 } | 410 } |