/* * Copyright (C) Lennart Martens * * Contact: lennart.martens AT UGent.be (' AT ' to be replaced with '@') */ /* * Created by IntelliJ IDEA. * User: Lennart * Date: 25-nov-02 * Time: 15:32:28 */ package com.compomics.util.io; import org.apache.log4j.Logger; import com.compomics.util.interfaces.PickUp; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.util.HashMap; import java.util.Vector; import java.util.Date; import java.text.SimpleDateFormat; /* * CVS information: * * $Revision: 1.3 $ * $Date: 2007/07/06 09:41:53 $ */ /** * This class will monitor a specified folder for activity. * * @author Lennart Martens */ public class FolderMonitor implements Runnable { // Class specific log4j logger for FolderMonitor instances. static Logger logger = Logger.getLogger(FolderMonitor.class); public static final String HOST = "HOST"; public static final String USER = "USER"; public static final String PASSWORD = "PASSWORD"; public static final String TEXTMODE = "TEXTMODE"; public static final String PICKUP = "PICKUP"; public static final String LIMIT = "LIMIT"; public static final int FTP_TO_SPECIFIED_DESTINATION = 0; public static final int GATHER_FILES_FOR_PICKUP = 1; /** * Optional Logger for this class. */ private Logger iLogger = null; /** * Boolean that controls the continuity of Threaded execution. */ private boolean iContinue = true; /** * The HashMap that will store the params for this operation. */ private HashMap iParams = null; /** * The operation that we should perform. */ private int iOperation = 0; /** * The folder we should monitor. */ private File iFolder = null; /** * When specified, the filter to apply to the folder. */ private FilenameFilter iFilter = null; /** * The number of files currently in the folder. */ private int iCurrentLength = 0; /** * The files we should be monitoring. */ private Vector iToMonitor = null; /** * The files we've already processed. */ private Vector iProcessed = null; /** * The delay (in milliseconds) to take into account when monitoring files. */ private long iDelay = 0l; /** * This HashMap holds the properties for the files that are being monitored. */ private HashMap iFileProps = null; /** * The FTPClient we'll be using for transmitting files if we are in FTP_TRANSFER mode. */ private FTPClient iFTP = null; /** * This boolean is 'true' while the FolderMonitor is running. */ private boolean iRunning = false; /** * This Vector is used in the gathering operation, and gathers files. */ private Vector iGatherPlace = null; /** * The class that will be notified of files to pick up when in * 'Gather for pickup' mode. */ private PickUp iPickUp = null; /** * The maximum number of instances to send. * Can remain '-1' to indicate no limits. */ private long iLimit = -1l; /** * This constructor allows the creation of a FolderMonitor that will take care * of performing a specified action, with the specified parameters, whenever a new * file is found. * * @param aFolder File istance with the folder to check. * @param aDelay long with the minimal delay in milliseconds between each folder check. * @param aOperation int with the code for the operation to perform * (use only constants defined on this class). * @param aParams HashMap with the necessary parameters for the operation. */ public FolderMonitor(File aFolder, long aDelay, int aOperation, HashMap aParams) { this(aFolder, aDelay, null, aOperation, aParams, null); } /** * This constructor allows the creation of a FolderMonitor that will take care * of performing a specified action, with the specified parameters, whenever a new * file is found. It also specifies a logger, to which messages detailing the activity can be logged. * * @param aFolder File istance with the folder to check. * @param aDelay long with the minimal delay in milliseconds between each folder check. * @param aOperation int with the code for the operation to perform * (use only constants defined on this class). * @param aParams HashMap with the necessary parameters for the operation. * @param aLogger Logger for messages from this class. */ public FolderMonitor(File aFolder, long aDelay, int aOperation, HashMap aParams, Logger aLogger) { this(aFolder, aDelay, null, aOperation, aParams, aLogger); } /** * This constructor allows the creation of a FolderMonitor for the specified filtered files that will take care * of performing a specified action, with the specified parameters, whenever a new * file is found. * * @param aFolder File instance with the folder to check. * @param aDelay long with the minimal delay in milliseconds between each folder check. * @param aFilter String to filter the files in the folder through. * @param aOperation int with the code for the operation to perform * (use only constants defined on this class). * @param aParams HashMap with the necessary parameters for the operation. */ public FolderMonitor(File aFolder, long aDelay, String aFilter, int aOperation, HashMap aParams) { this(aFolder, aDelay, aFilter, aOperation, aParams, null); } /** * This constructor allows the creation of a FolderMonitor for the specified filtered files that will take care * of performing a specified action, with the specified parameters, whenever a new * file is found. It also specifies a logger, to which messages detailing the activity can be logged. * * @param aFolder File instance with the folder to check. * @param aDelay long with the minimal delay in milliseconds between each folder check. * @param aFilter String to filter the files in the folder through. * @param aOperation int with the code for the operation to perform * (use only constants defined on this class). * @param aParams HashMap with the necessary parameters for the operation. * @param aLogger Logger for messages from this class. */ public FolderMonitor(File aFolder, long aDelay, String aFilter, int aOperation, HashMap aParams, Logger aLogger) { // Check whether the File instance exists and points to a folder, not a file! if(!aFolder.exists() || !aFolder.isDirectory()) { throw new IllegalArgumentException("The File instance you passed does not " + (aFolder.exists()?"point to a directory":"exist") + " (" + aFolder.toString() + ")!"); } else { this.iFolder = aFolder; } // Check whether we know the operation. switch(aOperation) { case FTP_TO_SPECIFIED_DESTINATION: this.iOperation = aOperation; if( (aParams.get(HOST) == null) || (aParams.get(USER) == null) || (aParams.get(PASSWORD) == null) ) { throw new IllegalArgumentException("You did not specify all necessary parameters for the FTP operation (I need host, user AND password)!"); } else { this.iParams = aParams; iFTP = new FTPClient((String)iParams.get(HOST), (String)iParams.get(USER), (String)iParams.get(PASSWORD)); } break; case GATHER_FILES_FOR_PICKUP: this.iOperation = aOperation; if(aParams.get(PICKUP) == null) { throw new IllegalArgumentException("You did not specify the PICKUP class for the gathered files!"); } else { iPickUp = (PickUp)aParams.get(PICKUP); Object tempObject = aParams.get(LIMIT); if(tempObject != null) { this.iLimit = ((Number)tempObject).longValue(); } else { this.iLimit = -1l; } this.iGatherPlace = new Vector(); } break; default: throw new IllegalArgumentException("The operation you specified (code=" + aOperation + ") is not known to me!"); } if(aFilter != null) { this.iFilter = new FilenameExtensionFilter(aFilter); } this.iDelay = aDelay; iToMonitor = new Vector(); iProcessed = new Vector(); iFileProps = new HashMap(); iLogger = aLogger; } /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public void run() { iRunning = true; while(iContinue) { // The list of stuff we'll have to process in this run. Vector toProcess = new Vector(); // The files that will be found in the folder. File[] files; // Get all the files, currently stored in the folder. if(this.iFilter != null) { files = this.iFolder.listFiles(iFilter); } else { files = this.iFolder.listFiles(); } // Find all new files to monitor. if((files != null) && (files.length != 0) && (files.length > iCurrentLength)) { for(int i = 0; i < files.length; i++) { File lFile = files[i]; if(!lFile.isDirectory() && !iToMonitor.contains(lFile) && !iProcessed.contains(lFile)) { iToMonitor.add(lFile); if(iLogger != null) { String time = new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss").format(new Date()); iLogger.info(" - Added file: '" + lFile.getName() + "' to the monitoring list (" + time + ")."); } } } } // Monitor (and process) all files we are monitoring. int liSize = iToMonitor.size(); Vector toRemove = new Vector(); for(int i = 0; i < liSize; i++) { File lFile = (File)iToMonitor.elementAt(i); String lName = lFile.getName(); // If the file no longer exists, just remove it. if(!lFile.exists()) { toRemove.add(lFile); continue; } // If the filesize is not yet stored, store it now. if(iFileProps.get(lName) == null) { try { iFileProps.put(lName, Long.valueOf(lFile.length())); } catch(Exception e) { } } else { long previous = ((Long)iFileProps.get(lName)).longValue(); // We already have a filesize, compare it. try { if(previous == lFile.length()) { // The file has remained stable; // add it to the list to be processed. toProcess.add(lFile); } else { iFileProps.put(lName, Long.valueOf(lFile.length())); } } catch(Exception e){ } } } for(int i = 0; i < toRemove.size(); i++) { File lFile = (File)toRemove.elementAt(i); iToMonitor.remove(lFile); if(iLogger != null) { String time = new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss").format(new Date()); iLogger.info(" # Removed file '" + lFile.getName() + "' from the monitoring list since it was deleted (" + time + ")."); } } toRemove = null; // Now the 'toProcess' Vector holds all files that have remained stable. // Process each. int processSize = toProcess.size(); boolean processSuccessful = true; switch(iOperation) { case FTP_TO_SPECIFIED_DESTINATION: try { this.sendFilesViaFTP(toProcess); } catch(IOException ioe) { if(iLogger != null) { String time = new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss").format(new Date()); iLogger.error(" IOException occurred while attempting to process files, message: " + ioe.getMessage() + "(" + time + ")!"); logger.error(ioe.getMessage(), ioe); } processSuccessful = false; } break; case GATHER_FILES_FOR_PICKUP: // See if we need to add stuff to the gathering place. if(processSize > 0) { for(int i = 0; i < processSize; i++) { iGatherPlace.add(toProcess.elementAt(i)); } } // See if we have anything gathered. if(iGatherPlace.size() > 0) { this.sendGathered(); } break; } // Now move the processed dudes from 'iToMonitor' to 'iProcessed', and // remove them as keys in 'iFileProps'. if(processSuccessful) { for(int i = 0; i < processSize; i++) { File lFile = (File)toProcess.elementAt(i); if(iLogger != null) { String time = new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss").format(new Date()); iLogger.info(" * Processed file '" + lFile.getName() + "' (" + time + ")."); } iProcessed.add(lFile); iToMonitor.remove(lFile); iFileProps.remove(lFile.getName()); } } // Destroy stale references. toProcess = null; files = null; try { Thread.sleep(iDelay); } catch(Exception e) { if(iLogger != null) { iLogger.info("FolderMonitor Thread interrupted."); } } } iRunning = false; } /** * This method can be used to signal the monitor to halt its monitoring. */ public void signalStop() { this.iContinue = false; } /** * This method can be consulted to find out whether the monitor is running. * * @return boolean that indicates whether the monitor is running. */ public boolean isRunning() { return iRunning; } /** * This method will FTP the specified files to the FTP server. * * @param aFiles Vector with the files to send. * @exception IOException when the sending process failed. */ private void sendFilesViaFTP(Vector aFiles) throws IOException { // The mode flag. boolean binary = true; // See if we need to switch to text mode. if(iParams.containsKey(TEXTMODE)) { binary = false; } // Get all the filenames. int liSize = aFiles.size(); String[] allNames = new String[liSize]; for(int i = 0; i < liSize; i++) { File lFile = (File)aFiles.elementAt(i); allNames[i] = lFile.getAbsolutePath(); } // Send them all. if(allNames.length > 0) { iFTP.sendFiles(allNames, binary); } } /** * This method will send gathered files to a gatherer (PickUp). */ private void sendGathered() { // Empty the contents of the gathering Vector. while(this.iGatherPlace.size() > 0) { // Some vars we'll need. int size = this.iGatherPlace.size(); boolean doLimitCheck = iLimit > 0; Vector v = new Vector(size); // Gather all files. for(int i=0;i<size;i++) { // see if we're not exceeding the limit. if((doLimitCheck) && (i >= iLimit)) { break; } else { v.add(iGatherPlace.elementAt(i)); } } // Send the files. File[] result = new File[v.size()]; v.toArray(result); iPickUp.sendIncoming(result); // Now we can safely delete the sent items from the // gatherplace. size = v.size(); for(int i=0;i<size;i++) { iGatherPlace.remove(v.get(i)); } } } /** * Main method starting the foldermonitor that looks for changes to a monitored folder. * * @param args start-up arguments */ public static void main(String[] args) { /* // FTP funtionality. HashMap params = new HashMap(3); params.put(FolderMonitor.HOST, "polaris"); params.put(FolderMonitor.USER, "ftpUser"); params.put(FolderMonitor.PASSWORD, "openFTP"); FolderMonitor fm = new FolderMonitor(new File("f:/temp"), 5000, "txt", FolderMonitor.FTP_TO_SPECIFIED_DESTINATION, params, new DefaultOutputLoggerImplementation()); Thread t = new Thread(fm); t.start(); */ HashMap params2 = new HashMap(2); params2.put(FolderMonitor.PICKUP, new PickUp(){ /** * This method should be called by the notifier when appropriate. * * @param aObject Object with the data that should be sent. */ public void sendIncoming(Object aObject) { try { File[] files = (File[])aObject; for(int i = 0; i < files.length; i++) { File lFile = files[i]; logger.info("File sent: " + lFile.getCanonicalPath()); } } catch(IOException ioe) { logger.error(ioe.getMessage(), ioe); } } }); params2.put(FolderMonitor.LIMIT, Integer.valueOf(5)); FolderMonitor fm = new FolderMonitor(new File("f:/temp"), 1000, FolderMonitor.GATHER_FILES_FOR_PICKUP, params2, null); Thread t = new Thread(fm); t.start(); } }