?? directorypoller.java
字號:
package org.sadun.util.polling;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.sadun.util.BidirectionalComparator;
import org.sadun.util.PathNormalizer;
import org.sadun.util.Terminable;
import com.deltax.util.listener.BaseSignalSourceThread;
import com.deltax.util.listener.ExceptionSignal;
/**
* A Thread class to periodically poll the contents of one or more directories.
* External asynchronous processes may put some files in one of the controlled directories,
* and the poller can be used to detect their arrival.
* <p>
* Once started, this thread polls one or more directories every <i>x</i> milliseconds,
* (see {@link DirectoryPoller#setPollInterval(long) setPollInterval()} and
* {@link DirectoryPoller#getPollInterval() getPollInterval()}), looking for new files
* satisfying a given filter.
* <br>Optionally, the thread may start by going to sleep (if
* {@link DirectoryPoller#setStartBySleeping(boolean) setStartBySleeping()} is invoked).
* <p>
* To communicate results, the thread notifies events to any registered listener (see
* below). Notably, if any files are "found", a {@link FileSetFoundEvent FileSetFoundEvent}
* event is notified.
* <p>
* A {@link java.io.FilenameFilter java.io.FilenameFilter} can be provided at construction to identify the files to
* look for. Besides, the poller can be set to run in two different time-based filtering modes (disabled by
* default), which select only files satisfying the user-given filter and
* <p>
* <ul>
* <li> whose <i>last modification time</i><a href="#note"><sup>*</sup></a> (<b>LMF</b>) is greater
* than the time of the last polling <i>(default)</i>
* <p><font size=-1>(Only files added to the directory after the last polling pass are selected)</font>
* <p><i>or</i><p>
* <li> whose <b>LMF</b> is greater than the higher <b>LMF</b> rilevated in
* the file set selected in the last polling pass (or 0 for the first pass).
* <p><font size=-1>(Only files which are newer than the newer file already polled are selected)</font>
* </ul>
* <p>
* Use {@link DirectoryPoller#setTimeBased(boolean) setTimeBased()} and {@link
* DirectoryPoller#isTimeBased() isTimeBased()} to enable the time-based mode and
* {@link DirectoryPoller#setPollingTimeBased(boolean) setPollingTimeBased()}
* and {@link DirectoryPoller#isPollingTimeBased() isPollingTimeBased()} to
* select the specific subtype of time-based mode.
* <p>
* Without time-based mode, on every pass the poller will select <i>all</i> the files
* in a controlled directory that match the filter, so it's client code responsability
* to phisically move the files already processed somewhere else (see also the
* <a href="#automove">automove</a> mode below).
* <p>
* The poller always notifies an event for a set of file discovered in a particular
* directory. It may optionally notify a separate event for each file (see {@link
* DirectoryPoller#setSendSingleFileEvent(boolean) setSendSingleFileEvent()} and
* {@link DirectoryPoller#isSendSingleFileEvent() isSendSingleFileEvent()}).
* <p>
* The following events (all time-stamped) are produced:
* <ul>
* <li> {@link CycleStartEvent CycleStartEvent}
* <br> The poller has awaken, and is starting to look in the directory set
* <li> {@link DirectoryLookupStartEvent DirectoryLookupStartEvent}
* <br> The poller is starting to look into a specific directory
* <li> {@link FileSetFoundEvent FileSetFoundEvent}
* <br> The poller has found some files in a directory, which match the polling criteria
* <li> {@link FileFoundEvent FileFoundEvent} (<i>optional</i>)
* <br> The poller has found one file in a directory, which match the polling criteria
* <li> {@link DirectoryLookupEndEvent DirectoryLookupStartEvent}
* <br> The poller has finished to look a specific directory
* <li> {@link CycleEndEvent CycleEndEvent}
* <br> The poller has finished to look in the directory set, and is going to sleep
* </ul>
* <p>
* Any object implementing the {@link com.deltax.util.lf.Listener com.deltax.Listener} interface
* can be registered and be notified of these events. However, a predefined listener,
* {@link DefaultListener DefaultListener} is provided, which dispatches events to an object
* implementing the {@link PollManager PollManager} interface, and a base
* implementation of {@link PollManager PollManager}, {@link BasePollManager
* BasePollManager} is provided as well.
* <p>
* The simplest way to receive events from the poller is therefore to create a class
* extending {@link BasePollManager BasePollManager} overriding the proper
* method(s) and register it to the directory poller by using
* {@link DirectoryPoller#addPollManager(PollManager) addPollManager()}.
* <p>
* The order in which file events are received can be controlled by providing a
* {@link java.util.Comparator} object which assigns an order to the file sets
* read on each cycle and assigning it by {@link #setFilesSortComparator(Comparator)}.
* By default, no order is imposed, i.e. the file events will be fired in an arbitrary
* order, depending on the operating system. (Two pre-packaged comparators
* {@link DirectoryPoller.ModificationTimeComparator}
* and {@link DirectoryPoller.FileSizeComparator} are provided by this class).
* <p>
* <i>Note</i>: listeners will receive events <b><i>asynchronously</i></b> with respect
* to the poller thread.
* Therefore, if file processing is performed without moving the file out of the polled
* directory, the polling interval should be big enough to allow such processing to
* complete, or the same file may be notified more than once (expecially if time-based
* polling is disabled).
* <a name="automove"><p>
* The version 1.2 of the class adds a new runtime mode - the <b>autoMove</b> mode.
* When running in this mode, the poller automatically moves all the files found
* in a controlled directory to an associated directory (which is <i>not</i> polled)
* <i>before</i> notifying the listeners and/or the pollmanager. The events notified will
* refer to the <i>new</i> location of the file.
* <p>
* This allows the user to set very short polling periods while ensuring that even slow
* file processing will not cause the same file to be polled more than once (even if the
* time-based polling mode is disabled).
* <p>
* If any exception occurs during the move operation, the relevant methods
* of {@link PollManager PollManager} are invoked (as a consequence of the notification
* of an underlying {@link com.deltax.util.listener.ExceptionSignal
* com.deltax.util.listener.ExceptionSignal}).
* <p>
* If autoMove mode is enabled (see {@link DirectoryPoller#setAutoMove(boolean) setAutoMove()})
* an arbitary directory may be explicitly associated to one of the controlled directories
* by using {@link DirectoryPoller#setAutoMoveDirectory(java.io.File, java.io.File)
* setAutoMoveDirectory()}.
* <p>
* If autoMove mode is enabled and the byPassLockedFiles property is true (defaults to false),
* locked files will be ignored. Otherwise, an exception signal will be raised if any file found in
* the controlled directory is locked.
* <p>
* If not, the poller will automatically associate to each
* controlled directory a subdirectory whose name is defined by the public constant
* {@link DirectoryPoller#DEFAULT_AUTOMOVE_DIRECTORY
* DirectoryPoller.DEFAULT_AUTOMOVE_DIRECTORY}. The poller will attempt to create
* the associated directory, if necessary - and on failure, an ExceptionSignal is
* sent to the listeners, and the poller shuts down.
* <p>
* Note that ExceptionSignals are sent only when the auto-move mode is enabled.
* </a>
* <p>
* <a name="note">(*) as returned by <tt>File.lastModified()</tt>.</a>
*
* @author C. Sadun (patched by Doug.Liao@fnf.com)
* @version 1.4.4
*/
public class DirectoryPoller extends BaseSignalSourceThread implements Terminable {
/**
* An exception raised by the poller when auto-move mode is enabled,
* but the move operation failed.
*/
public class AutomoveException extends Exception {
private File origin;
private File dest;
AutomoveException(File origin, File dest, String msg) {
super(msg);
this.origin=origin;
this.dest=dest;
if (verbose)
System.out.println("[Automove] Exception: "+msg);
}
/**
* Return the poller associated to this exception.
* @return the poller associated to this exception.
*/
public DirectoryPoller getPoller() { return DirectoryPoller.this; }
/**
* Return the file to be moved
* @return the file to be moved
*/
public File getOrigin() { return origin; }
/**
* Return the destination file
* @return the destination file
*/
public File getDestination() { return dest; }
}
/**
* An exception raised by the poller when auto-move mode is enabled,
* but the target file of the move operation exists and cannot be
* deleted.
*/
public class AutomoveDeleteException extends AutomoveException {
AutomoveDeleteException(File origin, File dest, String msg) {
super(origin, dest, msg);
}
}
/**
* The name of subdirectory automatically associated by the poller to any controlled
* directory for the autoMode mode, unless {@link DirectoryPoller#setAutoMoveDirectory(java.io.File,
* java.io.File) setAutoMoveDirectory()} is explicitly called before starting
* the poller.
* <p>
* The current value is "<b><tt>received</tt></b>"
*/
public static final String DEFAULT_AUTOMOVE_DIRECTORY = "received";
private static int counter=0;
private volatile boolean shutdownRequested = false;
private FilenameFilter filter;
private File[]dirs;
private long[] baseTime;
private boolean verbose=( System.getProperty("org.sadun.verbose") != null);
private boolean timeBasedOnLastLookup=true;
protected List pollManagersList = new ArrayList();
private boolean autoMove=false;
private Map autoMoveDirs = new HashMap();
private FilenameFilter originalFilter;
private long pollInterval = 10000;
private boolean startBySleeping=false;
private boolean sendSingleFileEvent=false;
private int currentDir=-1;
private Comparator filesSortComparator = null;
private boolean bypassLockedFiles = false;
private volatile boolean sleeping=false;
private volatile boolean debugExceptions;
private static class DirectoryFilter implements FilenameFilter {
FilenameFilter additionalFilter;
DirectoryFilter(FilenameFilter additionalFilter) {
this.additionalFilter=additionalFilter;
}
public boolean accept(File dir, String name) {
if (new File(dir, name).isDirectory()) return false;
return additionalFilter.accept(dir, name);
}
public String toString() {
return "Directory filter over a "+additionalFilter;
}
FilenameFilter getAdditionalFilter() { return additionalFilter; }
}
private class TimeFilter implements FilenameFilter {
private FilenameFilter additionalFilter;
public TimeFilter(FilenameFilter additionalFilter) {
this.additionalFilter=additionalFilter;
}
public boolean accept(File dir, String name) {
File f = new File(dir,name);
if (f.isDirectory()) return false;
if (f.lastModified() <= baseTime[currentDir]) {
if (verbose)
System.out.println(name+"("+f.lastModified()+"): out of base time ("+baseTime[currentDir]+"), ignoring");
return false;
} else {
if (verbose)
System.out.println(name+"("+f.lastModified()+"): older than base time ("+baseTime[currentDir]+"), accepted");
}
return additionalFilter.accept(dir, name);
}
}
public static final class NullFilenameFilter implements FilenameFilter {
public boolean accept(File dir, String name) { return true; }
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -