diff options
author | Simon Rettberg | 2015-08-22 17:29:15 +0200 |
---|---|---|
committer | Simon Rettberg | 2015-08-22 17:29:15 +0200 |
commit | 5b94675225077ed8c88f39d15279e920f267d954 (patch) | |
tree | 8b55ee6d7a8a881fe49695f23bc5234832a85031 /dozentenmodulserver | |
parent | [client] synced image details fetching (diff) | |
download | tutor-module-5b94675225077ed8c88f39d15279e920f267d954.tar.gz tutor-module-5b94675225077ed8c88f39d15279e920f267d954.tar.xz tutor-module-5b94675225077ed8c88f39d15279e920f267d954.zip |
[server] Foundations for the maintenance module
Diffstat (limited to 'dozentenmodulserver')
5 files changed, 190 insertions, 19 deletions
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java index 5a002e76..23a71228 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java @@ -110,14 +110,15 @@ public class DbImage { public static LocalImageVersion getLocalImageData(String imageVersionId) throws TNotFoundException, SQLException { try (MysqlConnection connection = Database.getConnection()) { - MysqlStatement stmt = connection.prepareStatement("SELECT imageversionid, imagebaseid, filepath, filesize, createtime, isvalid" + MysqlStatement stmt = connection.prepareStatement("SELECT" + + " imageversionid, imagebaseid, filepath, filesize, createtime, expiretime, isvalid" + " FROM imageversion WHERE imageversionid = :imageversionid"); stmt.setString("imageversionid", imageVersionId); ResultSet rs = stmt.executeQuery(); if (!rs.next()) throw new TNotFoundException(); return new LocalImageVersion(rs.getString("imageversionid"), rs.getString("imagebaseid"), - rs.getString("filepath"), rs.getLong("filesize"), rs.getLong("createtime"), + rs.getString("filepath"), rs.getLong("filesize"), rs.getLong("createtime"), rs.getLong("expiretime"), rs.getBoolean("isvalid")); } catch (SQLException e) { LOGGER.error("Query failed in DbImage.getLocalImageData()", e); @@ -127,19 +128,40 @@ public class DbImage { protected static List<LocalImageVersion> getLocalImageVersions(MysqlConnection connection, String imageBaseId) throws SQLException { - MysqlStatement stmt = connection.prepareStatement("SELECT imageversionid, imagebaseid, filepath, filesize, createtime, isvalid" + MysqlStatement stmt = connection.prepareStatement("SELECT" + + " imageversionid, imagebaseid, filepath, filesize, createtime, expiretime, isvalid" + " FROM imageversion WHERE imagebaseid = :imagebaseid"); stmt.setString("imagebaseid", imageBaseId); ResultSet rs = stmt.executeQuery(); List<LocalImageVersion> list = new ArrayList<>(); while (rs.next()) { list.add(new LocalImageVersion(rs.getString("imageversionid"), rs.getString("imagebaseid"), - rs.getString("filepath"), rs.getLong("filesize"), rs.getLong("createtime"), + rs.getString("filepath"), rs.getLong("filesize"), rs.getLong("createtime"), rs.getLong("expiretime"), rs.getBoolean("isvalid"))); } return list; } + public static List<LocalImageVersion> getExpiringLocalImageVersions(int maxRemainingDays) throws SQLException { + try (MysqlConnection connection = Database.getConnection()) { + MysqlStatement stmt = connection.prepareStatement("SELECT" + + " imageversionid, imagebaseid, filepath, filesize, createtime, expiretime, isvalid" + + " FROM imageversion WHERE expiretime < :deadline"); + stmt.setLong("deadline", (System.currentTimeMillis() / 1000) + (maxRemainingDays * 86400)); + ResultSet rs = stmt.executeQuery(); + List<LocalImageVersion> list = new ArrayList<>(); + while (rs.next()) { + list.add(new LocalImageVersion(rs.getString("imageversionid"), rs.getString("imagebaseid"), + rs.getString("filepath"), rs.getLong("filesize"), rs.getLong("createtime"), rs.getLong("expiretime"), + rs.getBoolean("isvalid"))); + } + return list; + } catch (SQLException e) { + LOGGER.error("Query failed in DbImage.getAllLocalImages()", e); + throw e; + } + } + /** * Private helper to create an {@link ImageSummaryRead} instance from a * {@link ResultSet} @@ -418,7 +440,7 @@ public class DbImage { LOGGER.error("Query failed in DbImage.markForDeletion()", e); throw e; } - updateLatestVersionAsync(imageVersionId, null); + updateLatestVersionAsync(imageVersionId); } public static void setShareMode(String imageBaseId, ImageBaseWrite newData) throws SQLException { @@ -474,39 +496,81 @@ public class DbImage { } } - public static void markValid(MysqlConnection connection, LocalImageVersion imageVersion, boolean valid) + public static void markValid(MysqlConnection connection, boolean valid, LocalImageVersion... imageVersion) throws SQLException { + if (imageVersion == null || imageVersion.length == 0) + return; MysqlStatement stmt = connection.prepareStatement("UPDATE imageversion SET isvalid = :valid" + " WHERE imageversionid = :imageversionid"); - stmt.setString("imageversionid", imageVersion.imageVersionId); - stmt.setBoolean("valid", valid); - stmt.executeUpdate(); + for (LocalImageVersion version : imageVersion) { + stmt.setString("imageversionid", version.imageVersionId); + stmt.setBoolean("valid", valid); + stmt.executeUpdate(); + } } - public static void markValid(LocalImageVersion imageVersion, boolean valid) throws SQLException { + public static void markValid(boolean valid, boolean async, LocalImageVersion... imageVersion) throws SQLException { + if (imageVersion == null || imageVersion.length == 0) + return; try (MysqlConnection connection = Database.getConnection()) { - markValid(connection, imageVersion, valid); + markValid(connection, valid, imageVersion); + if (!async) { + updateLatestVersion(connection, imageVersion); + } connection.commit(); } catch (SQLException e) { LOGGER.error("Query failed in DbImage.markInvalid()", e); throw e; } - updateLatestVersionAsync(imageVersion.imageVersionId, imageVersion.imageBaseId); + if (async) { + updateLatestVersionAsync(imageVersion); + } + } + + private static void updateLatestVersionAsync(final LocalImageVersion... changingVersion) { + if (changingVersion == null || changingVersion.length == 0) + return; + QuickTimer.scheduleOnce(new Task() { + @Override + public void fire() { + try (MysqlConnection connection = Database.getConnection()) { + updateLatestVersion(connection, changingVersion); + connection.commit(); + } catch (SQLException e) { + LOGGER.error("Query failed in DbImage.updateLatestVersionAsync()", e); + } + } + }); } - private static void updateLatestVersionAsync(final String changingImageVersionId, final String imageBaseId) { + private static void updateLatestVersionAsync(final String changingImageVersionId) { QuickTimer.scheduleOnce(new Task() { @Override public void fire() { try (MysqlConnection connection = Database.getConnection()) { - updateLatestVersion(connection, changingImageVersionId, imageBaseId); + updateLatestVersion(connection, changingImageVersionId, null); connection.commit(); } catch (SQLException | TNotFoundException e) { - LOGGER.error("Query failed in DbImage.updateLatestVersion()", e); + LOGGER.error("Query failed in DbImage.updateLatestVersionAsync()", e); } } }); } + + private static void updateLatestVersion(MysqlConnection connection, + LocalImageVersion... versions) throws + SQLException { + if (versions == null || versions.length == 0) + return; + for (LocalImageVersion version : versions) { + try { + updateLatestVersion(connection, version.imageVersionId, + version.imageBaseId); + } catch (TNotFoundException e) { + // Swallow - logging happens in called method + } + } + } /** * Makes sure the latestVersionId-field of the given base image is @@ -540,7 +604,7 @@ public class DbImage { if (versionFile.canRead() && versionFile.length() == version.fileSize) { newVersion = version; } else { - markValid(connection, version, false); + markValid(connection, false, version); } } } diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/models/LocalImageVersion.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/models/LocalImageVersion.java index 875c46f3..238633fd 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/models/LocalImageVersion.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/models/LocalImageVersion.java @@ -13,14 +13,17 @@ public class LocalImageVersion { public final boolean isValid; public final long createTime; + + public final long expireTime; public LocalImageVersion(String imageVersionId, String imageBaseId, String filePath, long fileSize, - long createTime, boolean isValid) { + long createTime, long expireTime, boolean isValid) { this.imageVersionId = imageVersionId; this.imageBaseId = imageBaseId; this.filePath = filePath; this.fileSize = fileSize; this.createTime = createTime; + this.expireTime = expireTime; this.isValid = isValid; } diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/FileServer.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/FileServer.java index bfa5699f..7d158459 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/FileServer.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/fileserv/FileServer.java @@ -195,7 +195,7 @@ public class FileServer implements IncomingEvent { LOGGER.warn("Rejecting download of VID " + localImageData.imageVersionId + ": Missing " + srcFile.getPath()); try { - DbImage.markValid(localImageData, false); + DbImage.markValid(false, true, localImageData); } catch (SQLException e) { } throw new TTransferRejectedException("File missing on server"); @@ -205,7 +205,7 @@ public class FileServer implements IncomingEvent { + srcFile.getPath() + " (expected " + localImageData.fileSize + ", is " + srcFile.length() + ")"); try { - DbImage.markValid(localImageData, false); + DbImage.markValid(false, true, localImageData); } catch (SQLException e) { } throw new TTransferRejectedException("File corrupted on server"); diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/DeleteOldImages.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/DeleteOldImages.java new file mode 100644 index 00000000..90e7e80d --- /dev/null +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/DeleteOldImages.java @@ -0,0 +1,46 @@ +package org.openslx.bwlp.sat.maintenance; + +import java.sql.SQLException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.log4j.Logger; +import org.openslx.bwlp.sat.database.mappers.DbImage; +import org.openslx.bwlp.sat.database.models.LocalImageVersion; +import org.openslx.bwlp.sat.fileserv.FileServer; + +/** + * Delete old image versions (images that reached their expire time). + */ +public class DeleteOldImages implements Runnable { + + private static final Logger LOGGER = Logger.getLogger(DeleteOldImages.class); + + @Override + public void run() { + List<LocalImageVersion> versions; + try { + versions = DbImage.getExpiringLocalImageVersions(0); + } catch (SQLException e) { + LOGGER.error("Will not be able to clean up old image versions"); + return; + } + // Mark all as invalid. This will also trigger emails. + try { + DbImage.markValid(false, false, versions.toArray(new LocalImageVersion[versions.size()])); + } catch (SQLException e) { + LOGGER.error("Could not mark images to be deleted as invalid. Cleanup of old images failed."); + } + // Delete them permanently only if they expired (at least) one day ago + Set<String> deleteVersionIds = new HashSet<>(); + final long hardDelete = (System.currentTimeMillis() / 1000) - 86400; + for (LocalImageVersion version : versions) { + if (version.expireTime < hardDelete) { + deleteVersionIds.add(version.imageVersionId); + FileServer.composeAbsolutePath(version).delete(); + } + } + } + +} diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/Maintenance.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/Maintenance.java new file mode 100644 index 00000000..98f7f09b --- /dev/null +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/Maintenance.java @@ -0,0 +1,58 @@ +package org.openslx.bwlp.sat.maintenance; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.apache.log4j.Logger; + +public class Maintenance extends Thread { + + private static final Logger LOGGER = Logger.getLogger(Maintenance.class); + + private static Maintenance worker = null; + + private static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(50); + + private Maintenance() { + } + + private synchronized static void ensureRunning() { + if (worker == null || !worker.isAlive()) { + worker = new Maintenance(); + worker.start(); + } + } + + public static void submit(Runnable job) throws InterruptedException { + ensureRunning(); + queue.put(job); + } + + public static boolean trySubmit(Runnable job) { + ensureRunning(); + return queue.offer(job); + } + + @Override + public void run() { + LOGGER.info("Maintenance Thread started"); + try { + Runnable job = queue.take(); + runJob(job); + } catch (InterruptedException e) { + LOGGER.warn("Maintenance Thread was interrupted!", e); + if (!queue.isEmpty()) { + ensureRunning(); + } + } + } + + private void runJob(Runnable job) { + try { + job.run(); + } catch (Throwable t) { + LOGGER.warn("Uncaught exception in job '" + job.getClass().getSimpleName() + "'", t); + } + } + +} |