--- Daniel Blumenthal <[EMAIL PROTECTED]> wrote:

> A related question to this is, how can you set up a
> process within the
> servlet that fires off at X time, or every Y
> seconds?  The equivalent of a
> cron job inside the servlet.
> 
> If you have high enough traffic, there's an easy
> hack - just store a Date
> object as a servlet-level attribute, and check on
> each request if the
> current time is greater than or equal.  But is there
> a cleaner solution?
> Also, it would be nice to have a little more precise
> control than hoping
> that a user will happen to come to the site at the
> right time.
> 
> I suppose you could spin off a thread that sleeps
> for X amount of time,
> wakes to check on the situation, and goes back to
> sleep when done.
> 
> To the specific question - I would guess
> (incorrectly?) that there's no Java
> file system listener.  But I could certainly be
> wrong... ?
> 
> Daniel
> 
> > -----Original Message-----
> > From: Khawaja Shams [mailto:[EMAIL PROTECTED] 
> > Sent: Monday, December 26, 2005 8:20 PM
> > To: Tomcat Users List
> > Subject: detecting a new file
> > 
> > Hello all,
> >     I am curious if anyone has ever had to write a
> servlet 
> > that listens for new files on the server and
> processes them 
> > upon appearence.  Basically, I need to write a
> server side 
> > program that detects when (our automated
> > process) delivers a file to the server filesystem
> and then 
> > process the file.  I would sincerely appreciate
> any pointers 
> > on how this can be accomplished from within
> tomcat.
> > 
> > 
> > Happy Holidays.
> > 
> > 
> > Sincerely,
> > Khawaja Shams
> > 
There is no standard file system watcher...at least
not in standard API's which I know of.  Yes, you have
to use a thread or java.util.Timer (uses a thread
behind the abstraction and might make it easier for
you) to do this unless you use SharedMemory or TCP/IP
and spawn off a separate process so you don't break
the rule about not creating threads in the container
(but I don't want to complicate this discussion), but
I prefer to go by what I need when I need it vs the
spec ( ;-) ) utility threads like this are needed at
times depending on what you need/have to accomplish. 
I would use a thread for this as it's pretty simple
stuff.  Just make sure you protect the tread with good
try-catch so you don't bust out of the watching thread
on some goofy error...you'll have to weigh this
obviously.  Setup a simple event mechanism like:
public interface FileChangeListener extends
java.util.EventListener {
    public void fileChanged(FileChangeEvent evt);
}

public class FileChangeEvent extends
java.util.EventObject {
    public static final int TYPE_NEW = 1;
    public static final int TYPE_MODIFIED = 2;
    private int eventType = TYPE_NEW;
    private MonitoredFile source = null;
    private MonitoredFile watched = null;

    public FileChangeEvent(){

    }

    public void setSource(MonitoredFile source){
        this.source = source;
    }

    public MonitoredFile getSource(){
        return this.source;
    }
    //.......
    //put other setters here for watched and type

}

public class MonitoredFile extends File {
    public void
addFileChangeListener(FileChangeListener listener){
     //if using a JDK/JRE with
javax.swing.event.EventListenerList
 then use that for holding the events...just makes it
a little easier
}

    public void
removeFileChangeListener(FileChangeListener listener){
   //use your javax.swing.event.EventListenerList

}

   public FileChangeListener[]
getFileChangeListeners(){
    //use your javax.swing.event.EventListenerList

}

   public void fireFileChangeEvent(FileChangeEvent
evt) throws Throwable {
    //get all the listeners set for FileChangeEvent
and call fileChanged(evt)
}

}

....

now all you would have to do is to create a class for
watching your directory which you can do different
things...either watch a directory and all sub
directories....if you don't watch sub directories you
can simply call File.listFiles or File.list depending
on whether you want File objects or String objects. 
If you want to watch all the directories then you can
use this code I'll give you ... what the heck.....
    public static void enumerateFiles(Vector out,
String dir, int howDeep) throws Throwable {
        enumerateFiles(out, dir, 0, howDeep);
    }
    
    /**
     *File used to recursively run over and sort a
directory hierarchy to flatten out the directory
     *structure of a file system into an array or in
this case a Vector.
     [EMAIL PROTECTED] out the Vector the enumerated file names
will be written to.  These will be sorted with each
     *       directory listing with
java.util.Arrays.sort(File[]) so see that method for
the format of the
     *       sort
     [EMAIL PROTECTED] dir the directory we want the listing for
     [EMAIL PROTECTED] deepCounter the counter for tracking how
deep we can go.  This should be 0 in normal calls
     [EMAIL PROTECTED] howDeep the number of directory levels
deep we want to return
     */
    protected static void
enumerateFiles(java.util.Vector out, String dir, int
deepCounter, int howDeep) throws Throwable {
        deepCounter++;
        if(howDeep>=0&&deepCounter>howDeep){
            return;
        }
        java.io.File f = new java.io.File(dir);
        if(!f.isDirectory()){
            throw new IllegalArgumentException("The
given directory "+dir+" is not a valid directory.");
        }
        java.io.File[] files = f.listFiles();
        java.util.Arrays.sort(files);
        for(int i = 0;
files!=null&&i<files.length;i++){
           
out.add(files[i].getAbsoluteFile().getCanonicalPath());
            if(files[i].isDirectory()){
                enumerateFiles(out,
files[i].getAbsoluteFile().getCanonicalPath(),
(deepCounter), howDeep);
            }
        }
    }

Run that code and you'll see how it organizes the
returned files.  Works really well, and you can even
control how deep you want to expose the directory
structure....moving on

So, all you have to do now is write your thread and
decide on how you want to fire events on your new
MonitoredFile objects...fire events on new files and
fire events on files that change and possibly
directories that change....what ever you want.  You
can easily tell if something changes or is added by
storing off a set of info in a RandomAccessFile say
first column is 1000 (just to be safe) null terminate
it and is the file name and full path, second a
creation date and time (as long), third a modification
date and time (as long), and file size (as long) You
could cram the long dates into a long and actually
make the date and time show up within the long and
only take up 8 bytes instead of more i.e.
20050101010101111 which is YYYYMMDDHHMMSSsss where
s=millisecond space holder. and then 3 bytes for the
time zone if needed, but for this it's probably
not...get the dates to and from longs with:
   public static final long
convertLongDateToReadableLongDate(long date)
   {
      long ret = 0;
      GregorianCalendar cal = new GregorianCalendar();
      cal.setTimeInMillis(date);
      long year = cal.get(cal.YEAR);
      long month = cal.get(cal.MONTH) + 1;
      long day = cal.get(cal.DAY_OF_MONTH);
      long hour = cal.get(cal.HOUR_OF_DAY);
      long minute = cal.get(cal.MINUTE);
      long sec = cal.get(cal.SECOND);
      long millis = cal.get(cal.MILLISECOND);
      ret = (year * 10000000000000L);
      ret += (month * 100000000000L);
      ret += (day * 1000000000L);
      ret += (hour * 10000000L);
      ret += (minute * 100000L);
      ret += (sec * 1000L);
      ret += millis;
      return ret;
   }
   
   public static final long
convertPackedLongDateToLongDate(long date)
   {
      if( date == PDBase.NULL_DATE )
      {
         return date;
      }
      long ret = 0;
      String sdate = ""+date;
      if( sdate.length() != 17 )
      {
         throw new IllegalArgumentException("The date
is not a real packed date.");
      }
      
      char[] cyear = new char[4];
      char[] cmonth = new char[2];
      char[] cday = new char[2];
      char[] chh = new char[2];
      char[] cmm = new char[2];
      char[] css = new char[2];
      char[] cmmm = new char[3];
      
      cyear[0] = sdate.charAt(0);
      cyear[1] = sdate.charAt(1);
      cyear[2] = sdate.charAt(2);
      cyear[3] = sdate.charAt(3);
      cmonth[0] = sdate.charAt(4);
      cmonth[1] = sdate.charAt(5);
      cday[0] = sdate.charAt(6);
      cday[1] = sdate.charAt(7);
      chh[0] = sdate.charAt(8);
      chh[1] = sdate.charAt(9);
      cmm[0] = sdate.charAt(10);
      cmm[1] = sdate.charAt(11);
      css[0] = sdate.charAt(12);
      css[1] = sdate.charAt(13);
      cmmm[0] = sdate.charAt(14);
      cmmm[1] = sdate.charAt(15);
      cmmm[2] = sdate.charAt(16);
      
      int year = Integer.parseInt(new String(cyear));
      int month = Integer.parseInt(new String(cmonth))
- 1;
      int day = Integer.parseInt(new String(cday));
      int hh = Integer.parseInt(new String(chh));
      int mm = Integer.parseInt(new String(cmm));
      int ss = Integer.parseInt(new String(css));
      int mmm = Integer.parseInt(new String(cmmm));
      
      GregorianCalendar cal = new GregorianCalendar();
      cal.set(cal.YEAR, year);
      cal.set(cal.MONTH, month);
      cal.set(cal.DAY_OF_MONTH, day);
      cal.set(cal.HOUR_OF_DAY, hh);
      cal.set(cal.MINUTE, mm);
      cal.set(cal.SECOND, ss);
      cal.set(cal.MILLISECOND, mmm);
      ret = cal.getTimeInMillis();
      return ret;
   }

that'll make it readable if you store this stuff in a
RandomAccessFile or DB and need to look at it later.

Now all you need to do is wake up your thread every so
often and do some checks.  I'll leave the part of
figuring out how to use the information you'll have
available, writing it and reading it to and from the
RandomAccessFile to you...shouldn't be that difficult
though.  There are a couple of different schemes you
could use.  You might not even store off this stuff
and instead keep MonitoredFile objects for every
single file name you get back from enumerating the
files.  Then what you need to do is check all of the
MonitoredFile Objects you have every time to see if
they are deleted or still exist or if any of their
other information has changed or if you have any new
ones.  You could store them off in map by file name so
then you're able to see if they existed before or not.
 You could even store them all out in .ser files
(meaning serialize the objects) and then just store
the serialized file names in a simple text file...line
by line and read them back in this way when you wake
up to perform some checks the scheme used should be
dictated by how often you think you should wake up. 
Obviously loading that many files could take some
time, so you'll have to figure out what suits your
needs.

Some of this may be jumbled up and some pieces
missing, but hey it's on the fly and should get you
started.  You might even rip some TC code or Netbeans
or Eclipse code which does this type of stuff.  For a
starter I might use java.util.Timer and create start
and stop and destroy this guy in my
ServletContextListener webapp comes up start goes down
off.

Hope it helps,

Wade

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to