/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE and NOTICE files at the root of the source * tree and available online at * * http://www.dspace.org/license/ */ package org.dspace.curate; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.log4j.Logger; import org.dspace.core.ConfigurationManager; /** * FileTaskQueue provides a TaskQueue implementation based on flat files * for the queues and semaphores. * * @author richardrodgers */ public class FileTaskQueue implements TaskQueue { private static Logger log = Logger.getLogger(TaskQueue.class); // base directory for curation task queues private String tqDir = ConfigurationManager.getProperty("curate", "taskqueue.dir"); // ticket for queue readers private long readTicket = -1L; // list of queues owned by reader private List<Integer> readList = new ArrayList<Integer>(); public FileTaskQueue() { } @Override public String[] queueNames() { return new File(tqDir).list(); } @Override public synchronized void enqueue(String queueName, TaskQueueEntry entry) throws IOException { Set entrySet = new HashSet<TaskQueueEntry>(); entrySet.add(entry); enqueue(queueName, entrySet); } @Override public synchronized void enqueue(String queueName, Set<TaskQueueEntry> entrySet) throws IOException { // don't block or fail - iterate until an unlocked queue found/created int queueIdx = 0; File qDir = ensureQueue(queueName); while (true) { File lock = new File(qDir, "lock" + Integer.toString(queueIdx)); // Check for lock, and create one if it doesn't exist. // If the lock file already exists, this will return false if (lock.createNewFile()) { // append set contents to queue BufferedWriter writer = null; try { File queue = new File(qDir, "queue" + Integer.toString(queueIdx)); writer = new BufferedWriter(new FileWriter(queue, true)); Iterator<TaskQueueEntry> iter = entrySet.iterator(); while (iter.hasNext()) { writer.write(iter.next().toString()); writer.newLine(); } } finally { if (writer != null) { writer.close(); } } // remove lock if (!lock.delete()) { log.error("Unable to remove lock: " + lock.getName()); } break; } queueIdx++; } } @Override public synchronized Set<TaskQueueEntry> dequeue(String queueName, long ticket) throws IOException { Set<TaskQueueEntry> entrySet = new HashSet<TaskQueueEntry>(); if (readTicket == -1L) { // hold the ticket & copy all Ids available, locking queues // stop when no more queues or one found locked File qDir = ensureQueue(queueName); readTicket = ticket; int queueIdx = 0; while (true) { File queue = new File(qDir, "queue" + Integer.toString(queueIdx)); File lock = new File(qDir, "lock" + Integer.toString(queueIdx)); // If the queue file exists, atomically check for a lock file and create one if it doesn't exist // If the lock file exists already, then this simply returns false if (queue.exists() && lock.createNewFile()) { // read contents from file BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(queue)); String entryStr = null; while ((entryStr = reader.readLine()) != null) { entryStr = entryStr.trim(); if (entryStr.length() > 0) { entrySet.add(new TaskQueueEntry(entryStr)); } } } finally { if (reader != null) { reader.close(); } } readList.add(queueIdx); } else { break; } queueIdx++; } } return entrySet; } @Override public synchronized void release(String queueName, long ticket, boolean remove) { if (ticket == readTicket) { readTicket = -1L; File qDir = ensureQueue(queueName); // remove locks & queues (if flag true) for (Integer queueIdx : readList) { File lock = new File(qDir, "lock" + Integer.toString(queueIdx)); if (remove) { File queue = new File(qDir, "queue" + Integer.toString(queueIdx)); if (!queue.delete()) { log.error("Unable to delete queue file: " + queue.getName()); } } if (!lock.delete()) { log.error("Unable to delete lock file: " + lock.getName()); } } readList.clear(); } } private File ensureQueue(String queueName) { // create directory structures as needed File baseDir = new File(tqDir, queueName); if (!baseDir.exists() && !baseDir.mkdirs()) { throw new IllegalStateException("Unable to create directories"); } return baseDir; } }