/* * Copyright 2012, CMM, University of Queensland. * * This file is part of Paul. * * Paul 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. * * Paul 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 Paul. If not, see <http://www.gnu.org/licenses/>. */ package au.edu.uq.cmm.paul.queue; import java.util.Date; import java.util.Objects; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import au.edu.uq.cmm.aclslib.service.MonitoredThreadServiceBase; import au.edu.uq.cmm.paul.Paul; import au.edu.uq.cmm.paul.PaulConfiguration; import au.edu.uq.cmm.paul.queue.QueueManager.Removal; /** * The expirer is a single thread service that periodically expires * old entries from the ingestion queue. * <p> * This class also provides a command-line entry-point method. * * @author scrawley */ public class QueueExpirer extends MonitoredThreadServiceBase { private static final Logger LOG = LoggerFactory.getLogger(QueueExpirer.class); private PaulConfiguration config; private QueueManager queueManager; public QueueExpirer(Paul services) { this(services.getConfiguration(), services.getQueueManager()); } public QueueExpirer(PaulConfiguration config, QueueManager queueManager) { this.config = Objects.requireNonNull(config); this.queueManager = Objects.requireNonNull(queueManager); } @Override public void run() { long expiryTime = config.getQueueExpiryTime(); long expiryInterval = config.getQueueExpiryInterval(); Removal removal = config.isExpireByDeleting() ? Removal.DELETE : Removal.ARCHIVE; try { if (expiryInterval <= 0 || expiryTime <= 0) { LOG.info("Automatic queue expiration is disabled"); Object lock = new Object(); synchronized (lock) { lock.wait(); } } else { while (true) { LOG.info("Running automatic queue expiration"); doExpiry(expiryTime * 60, removal); LOG.info("Completed automatic queue expiration"); Thread.sleep(expiryInterval * 60 * 1000); } } } catch (InterruptedException ex) { LOG.info("Interrupted - we're done"); } } /** * Run the expiration. * * @param expiryTime the expiry age (in seconds). * @param removal the method of removal (or not) * @throws InterruptedException */ private int doExpiry(long expiryTime, Removal removal) throws InterruptedException { if (expiryTime <= 0) { throw new IllegalArgumentException("Non-positive expiry time"); } long millis = System.currentTimeMillis() - expiryTime * 1000; Date cutoff = new Date(millis); LOG.info("Expiry cutoff date/time is " + cutoff); int nosExpired = queueManager.expireAll( removal, null, QueueManager.Slice.ALL, cutoff); LOG.info("Expired " + nosExpired + " queue entries"); return nosExpired; } /** * Entry point for running the Paul queue expirer from the command line. This is * intended to be run out of the installed webapp (with appropriate classpath) * so that it will pick up the persistence configuration of the deployed service. * * @param args command line arguments. */ public static void main(String[] args) { int pos = 0; boolean delete = true; boolean dryRun = false; String logLevel = "error"; while (pos < args.length) { if (args[pos].equals("--help")) { bail("", 0); } else if (args[pos].equals("--delete")) { pos++; delete = true; } else if (args[pos].equals("--archive")) { pos++; delete = false; } else if (args[pos].equals("--dryrun")) { pos++; dryRun = true; } else if (args[pos].equals("--logging")) { pos++; if (pos >= args.length) { bail("Missing value for --logging", 1); } logLevel = args[pos++]; } else { break; } } setLogLevel(logLevel); if (pos >= args.length) { bail("Missing expiry time", 1); } else if (args[pos].startsWith("-")) { bail("Unrecognised option: " + args[pos], 1); } else if (pos < args.length - 1) { bail("Too many command arguments", 1); } String timespec = args[pos]; char unit = 'h'; if (timespec.length() > 0 && !Character.isDigit(timespec.charAt(timespec.length() - 1))) { unit = timespec.charAt(timespec.length() - 1); timespec = timespec.substring(0, timespec.length() - 1); } long expiryTime; try { expiryTime = Long.parseLong(timespec); if (expiryTime <= 0) { bail("Invalid timespec " + args[pos] + ": the numeric part must be > 0", 1); } } catch (NumberFormatException ex) { bail("Invalid timespec " + args[pos] + ": the numeric part is not numeric", 1); expiryTime = -1; // shouldn't be reachable ... } switch (unit) { case 'd': expiryTime *= 60 * 60 * 24; break; case 'h': expiryTime *= 60 * 60; break; case 'm': expiryTime *= 60; break; case 's': break; default: bail("Invalid timespec " + args[pos] + ": unknown time unit", 1); } EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("au.edu.uq.cmm.paul"); PaulConfiguration config = PaulConfiguration.load(entityManagerFactory); QueueManager queueManager = new QueueManager(config, entityManagerFactory); QueueExpirer expirer = new QueueExpirer(config, queueManager); try { Removal removal = dryRun ? Removal.DRY_RUN : delete ? Removal.DELETE : Removal.ARCHIVE; int expired = expirer.doExpiry(expiryTime, removal); String actionDescription = (dryRun ? "would have " : "") + (delete ? "deleted " : "archived "); System.out.println( "Expiration " + actionDescription + (expired == 1 ? "1 queue entry" : (expired + " queue entries"))); } catch (InterruptedException ex) { // We can ignore this ... } } private static void setLogLevel(String logLevel) { // FIXME don't hard-wire the logger implementation. org.apache.log4j.Logger logger = org.apache.log4j.Logger.getRootLogger(); org.apache.log4j.Level level = org.apache.log4j.Level.toLevel(logLevel); logger.setLevel(level); } private static void bail(String message, int rc) { if (message.length() > 0) { System.err.println(message); } System.err.println("Usage: paulExpire [ <options> ] <timeSpec> "); System.err.println(" <options> are:"); System.err.println(" --help - prints this text"); System.err.println(" --delete - delete expired entries (default)"); System.err.println(" --archive - archive expired entries"); System.err.println(" --logging <level> - enable logging"); System.err.println(" <timeSpec> is analogous to the timespec in 'tmpReaper(8)'"); System.exit(rc); } }