/* * SONEWS News Server * Copyright (C) 2009-2015 Christian Lins <christian@lins.me> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.sonews.util; import java.text.DateFormat; import java.text.ParseException; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.logging.Level; import org.sonews.config.Config; import org.sonews.daemon.DaemonRunnable; import org.sonews.daemon.DaemonRunner; import org.sonews.daemon.DaemonThread; import org.sonews.storage.Article; import org.sonews.storage.Group; import org.sonews.storage.Headers; import org.sonews.storage.StorageBackendException; import org.sonews.storage.StorageManager; /** * The purger is started in configurable intervals to search for messages that * can be purged. A message must be deleted if its lifetime has exceeded, if it * was marked as deleted or if the maximum number of articles in the database is * reached. * * @author Christian Lins * @since sonews/0.5.0 */ public class Purger extends DaemonRunner implements DaemonRunnable { private DaemonThread daemon; /** * Loops through all messages and deletes them if their time has come. */ @Override public void run() { try { while (daemon.isRunning()) { purgeDeleted(); purgeOutdated(); Thread.sleep(120000); // Sleep for two minutes } } catch (StorageBackendException ex) { ex.printStackTrace(); } catch (InterruptedException ex) { Log.get().log(Level.WARNING, "Purger interrupted: {0}", ex); } } /** * Purge messages from storage backend that have been marked as deleted. * * @throws StorageBackendException */ private void purgeDeleted() throws StorageBackendException { List<Group> groups = Group.getAll(); if (groups == null) { Log.get().warning("No groups?"); return; } for (Group group : groups) { // Look for groups that are marked as deleted if (group.isDeleted()) { List<Long> ids = StorageManager.current().getArticleNumbers( group.getInternalID()); if (ids.isEmpty()) { StorageManager.current().purgeGroup(group); Log.get().log(Level.INFO, "Group {0} purged.", group.getName()); } for (int n = 0; n < ids.size() && n < 10; n++) { Article art = StorageManager.current().getArticle( ids.get(n), group.getInternalID()); if(art != null) { StorageManager.current().delete(art.getMessageID()); Log.get().log(Level.INFO, "Article {0} purged.", art.getMessageID()); } } } } } /** * Purge messages that are older then the given treshold. * * @throws InterruptedException * @throws StorageBackendException */ private void purgeOutdated() throws InterruptedException, StorageBackendException { long articleMaximum = Config.inst().get("sonews.article.maxnum", Long.MAX_VALUE); long lifetime = Config.inst().get("sonews.article.lifetime", -1); if (lifetime > 0 || articleMaximum < StorageManager.current().countArticles()) { Log.get().info("Purging old messages..."); String mid = StorageManager.current().getOldestArticle(); if (mid == null) { // No articles in the database return; } Article art = StorageManager.current().getArticle(mid); if (art == null) { Log.get().log(Level.WARNING, "Could not retrieve or delete article: {0}", mid); return; } long artDate = 0; String dateStr = art.getHeader(Headers.DATE)[0]; // FIXME Refactor using java.time classes try { DateFormat dateFormat = DateFormat.getDateInstance( DateFormat.LONG, Locale.US); artDate = dateFormat.parse(dateStr).getTime() / 1000 / 3600 / 24; } catch (ParseException ex) { Log.get().log( Level.WARNING, "Could not parse date string: {0} {1}", new Object[]{dateStr, ex}); } // Should we delete the message because of its age or because the // article maximum was reached? if (lifetime < 0 || artDate < (new Date().getTime() + lifetime)) { StorageManager.current().delete(mid); System.out.println("Deleted: " + mid); } else { Thread.sleep(1000 * 60); // Wait 60 seconds } } else { Log.get().info("Lifetime purger is disabled"); Thread.sleep(1000 * 60 * 30); // Wait 30 minutes } } }