/* The contents of this file are subject to the license and copyright terms * detailed in the license directory at the root of the source tree (also * available online at http://fedora-commons.org/license/). */ package fedora.server.journal.readerwriter.multifile; import java.io.File; import java.io.IOException; import java.util.Map; import fedora.server.journal.JournalException; import fedora.server.journal.ServerInterface; import fedora.server.journal.helpers.ParameterHelper; import fedora.server.journal.recoverylog.JournalRecoveryLog; /** * A Following Journal Reader that can be made quiescent by the presence of a * lock file. * <p> * This works like the MultiFileFollowingJournalReader, except that when looking * for the next Journal file to be processed, it will take a moment to check the * locking protocol. If a lock has been requested, it will accept the lock, and * ignore any additional Journal files if present. * <p> * Polling continues, however. At each polling interval, the reader will check * for the lock request. If the request has been removed, the lock acceptance * will be removed also, and the reader will process the next Journal file, if * one is found. * * @author Jim Blake */ public class LockingFollowingJournalReader extends MultiFileJournalReader { /** How many milliseconds between polls? */ private final long pollingIntervalMillis; /** * The name of the file that signals a request to go quiescent after the * current journal file. */ private final File lockRequestedFile; /** * The name of the file that acknowledges a request to go quiescent after * the current journal file. */ private final File lockAcceptedFile; /** Poll immediately for the next file, or pause first? */ private final boolean pauseBeforePolling; /** Currently quiescent? */ private boolean wasLocked = false; /** * Require parameters for polling interval, lock request filename and lock * acceptance filename. */ public LockingFollowingJournalReader(Map<String, String> parameters, String role, JournalRecoveryLog recoveryLog, ServerInterface server) throws JournalException { super(parameters, role, recoveryLog, server); pollingIntervalMillis = MultiFileJournalHelper .parseParametersForPollingInterval(parameters); lockRequestedFile = new File(MultiFileJournalHelper .getRequiredParameter(parameters, PARAMETER_LOCK_REQUESTED_FILENAME)); lockAcceptedFile = new File(MultiFileJournalHelper .getRequiredParameter(parameters, PARAMETER_LOCK_ACCEPTED_FILENAME)); pauseBeforePolling = ParameterHelper .getOptionalBooleanParameter(parameters, PARAMETER_PAUSE_BEFORE_POLLING, false); } /** * Process the locking mechanism. If we are not locked, we should look for * another journal file to process. Ask for a new file, using the superclass * method, but if none is found, wait for a while and repeat. This will * continue until we get a server shutdown signal. */ @Override protected synchronized JournalInputFile openNextFile() throws JournalException { while (open) { boolean locked = processLockingMechanism(); if (pauseBeforePolling) { try { wait(pollingIntervalMillis); } catch (InterruptedException e) { // no special action on interrupt. } } if (!locked) { JournalInputFile nextFile = super.openNextFile(); if (nextFile != null) { return nextFile; } } try { wait(pollingIntervalMillis); } catch (InterruptedException e) { // no special action on interrupt. } } return null; } /** * If the server requests a shutdown, stop waiting the next file to come in. */ @Override public synchronized void shutdown() throws JournalException { super.shutdown(); notifyAll(); } /** * If we see a lock request, issue an acceptance. If we do not, withdraw the * acceptance. * * @return true if locked, false if not locked. * @throws JournalException * if we fail to create the 'lock accepted' file. */ private boolean processLockingMechanism() throws JournalException { boolean locked; if (lockRequestedFile.exists()) { try { if (!lockAcceptedFile.exists()) { lockAcceptedFile.createNewFile(); } } catch (IOException e) { throw new JournalException("Unable to create 'Lock Accepted' file at '" + lockAcceptedFile.getPath() + "'"); } locked = true; } else { if (lockAcceptedFile.exists()) { lockAcceptedFile.delete(); } locked = false; } if (locked && !wasLocked) { recoveryLog.log("Lock request detected: " + lockRequestedFile.getPath() + ", Lock accepted: " + lockAcceptedFile.getPath()); } else if (wasLocked && !locked) { recoveryLog.log("Lock request removed: " + lockRequestedFile.getPath()); } wasLocked = locked; return locked; } }