Coverage Report - net.sf.jabref.collab.FileUpdateMonitor
 
Classes in this File Line Coverage Branch Coverage Complexity
FileUpdateMonitor
61%
42/68
45%
9/20
2.588
FileUpdateMonitor$Entry
81%
26/32
62%
5/8
2.588
 
 1  
 package net.sf.jabref.collab;
 2  
 
 3  
 import net.sf.jabref.Globals;
 4  
 import net.sf.jabref.Util;
 5  
 import java.util.HashMap;
 6  
 import java.io.File;
 7  
 import java.io.IOException;
 8  
 import java.util.Iterator;
 9  
 
 10  
 /**
 11  
  * This thread monitors a set of files, each associated with a FileUpdateListener, for changes
 12  
 * in the file's last modification time stamp. The
 13  
  */
 14  
 public class FileUpdateMonitor extends Thread {
 15  
 
 16  50266856725
   final int WAIT = 4000;
 17  50266856725
   static int tmpNum = 0;
 18  50266856725
   int no = 0;
 19  50266856725
   HashMap<String, Entry> entries = new HashMap<String, Entry>();
 20  
   boolean running;
 21  
 
 22  50266856725
   public FileUpdateMonitor() {
 23  50266856725
     setPriority(MIN_PRIORITY);
 24  50266856725
   }
 25  
 
 26  
   public void run() {
 27  50266856725
     running = true;
 28  
 
 29  
     // The running variable is used to make the thread stop when needed.
 30  635581533509
     while (running) {
 31  
       //System.out.println("Polling...");
 32  635581533509
       Iterator<String> i = entries.keySet().iterator();
 33  667970336615
       for (;i.hasNext();) {
 34  32388864947
         Entry e = entries.get(i.next());
 35  
         try {
 36  32388864947
           if (e.hasBeenUpdated())
 37  1581461
             e.notifyListener();
 38  
 
 39  
           //else
 40  
           //System.out.println("File '"+e.file.getPath()+"' not modified.");
 41  0
         } catch (IOException ex) {
 42  0
           e.notifyFileRemoved();
 43  32388803106
         }
 44  32388803106
       }
 45  
 
 46  
       // Sleep for a while before starting a new polling round.
 47  
       try {
 48  635581471668
         sleep(WAIT);
 49  0
       } catch (InterruptedException ex) {
 50  585314676784
       }
 51  585314676784
     }
 52  0
   }
 53  
 
 54  
   /**
 55  
    * Cause the thread to stop monitoring. It will finish the current round before stopping.
 56  
    */
 57  
   public void stopMonitoring() {
 58  0
     running = false;
 59  0
   }
 60  
 
 61  
   /**
 62  
    * Add a new file to monitor. Returns a handle for accessing the entry.
 63  
    * @param ul FileUpdateListener The listener to notify when the file changes.
 64  
    * @param file File The file to monitor.
 65  
    * @throws IOException if the file does not exist.
 66  
    */
 67  
   public String addUpdateListener(FileUpdateListener ul, File file) throws IOException {
 68  
      // System.out.println(file.getPath());
 69  3305884131
     if (!file.exists())
 70  0
       throw new IOException("File not found");
 71  3305884131
     no++;
 72  3305884131
     String key = ""+no;
 73  3305884131
     entries.put(key, new Entry(ul, file));
 74  3305884131
     return key;
 75  
   }
 76  
 
 77  
     /**
 78  
      * Forces a check on the file, and returns the result. Does not
 79  
      * force a report to all listeners before the next routine check.
 80  
      */
 81  
     public boolean hasBeenModified(String handle) throws IllegalArgumentException {
 82  145407574
         Object o = entries.get(handle);
 83  145407574
         if (o == null)
 84  0
             return false;
 85  
         //            throw new IllegalArgumentException("Entry not found");
 86  
         try {
 87  145407574
             return ((Entry)o).hasBeenUpdated();
 88  0
         } catch (IOException ex) {
 89  
             // Thrown if file has been removed. We return false.
 90  0
             return false;
 91  
         }
 92  
     }
 93  
 
 94  
     /**
 95  
      * Change the stored timestamp for the given file. If the timestamp equals
 96  
      * the file's timestamp on disk, after this call the file will appear to
 97  
      * have been modified. Used if a file has been modified, and the change
 98  
      * scan fails, in order to ensure successive checks.
 99  
      * @param handle the handle to the correct file.
 100  
      */
 101  
     public void perturbTimestamp(String handle) {
 102  0
         Object o = entries.get(handle);
 103  0
         if (o == null)
 104  0
             return;
 105  0
         ((Entry)o).timeStamp--;
 106  0
     }
 107  
 
 108  
   /**
 109  
    * Removes a listener from the monitor.
 110  
    * @param handle String The handle for the listener to remove.
 111  
    */
 112  
   public void removeUpdateListener(String handle) {
 113  53932610
     entries.remove(handle);
 114  53932610
   }
 115  
 
 116  
   public void updateTimeStamp(String key) throws IllegalArgumentException {
 117  145373041
     Object o = entries.get(key);
 118  145373041
     if (o == null)
 119  0
       throw new IllegalArgumentException("Entry not found");
 120  145373041
     Entry entry = (Entry)o;
 121  145373041
     entry.updateTimeStamp();
 122  
 
 123  145373041
   }
 124  
 
 125  
   public void changeFile(String key, File file) throws IOException, IllegalArgumentException {
 126  0
     if (!file.exists())
 127  0
       throw new IOException("File not found");
 128  0
     Object o = entries.get(key);
 129  0
     if (o == null)
 130  0
       throw new IllegalArgumentException("Entry not found");
 131  0
     ((Entry)o).file = file;
 132  0
   }
 133  
 
 134  
   /**
 135  
    * Method for getting the temporary file used for this database. The tempfile
 136  
    * is used for comparison with the changed on-disk version.
 137  
    * @param key String The handle for this monitor.
 138  
    * @throws IllegalArgumentException If the handle doesn't correspond to an entry.
 139  
    * @return File The temporary file.
 140  
    */
 141  
   public File getTempFile(String key) throws IllegalArgumentException {
 142  1580800
     Object o = entries.get(key);
 143  1580800
     if (o == null)
 144  0
       throw new IllegalArgumentException("Entry not found");
 145  1580800
     return ((Entry)o).tmpFile;
 146  
   }
 147  
 
 148  
   /**
 149  
    * A class containing the File, the FileUpdateListener and the current time stamp for one file.
 150  
    */
 151  
   class Entry {
 152  
     FileUpdateListener listener;
 153  
     File file;
 154  
     File tmpFile;
 155  
     long timeStamp, fileSize;
 156  
 
 157  3305884131
     public Entry(FileUpdateListener ul, File f) {
 158  3305884131
       listener = ul;
 159  3305884131
       file = f;
 160  3305884131
       timeStamp = file.lastModified();
 161  3305884131
       fileSize = file.length();
 162  3305884131
       tmpFile = getTempFile();
 163  3305884131
       tmpFile.deleteOnExit();
 164  3305884131
       copy();
 165  3305884131
     }
 166  
 
 167  
     /**
 168  
      * Check if time stamp or the file size has changed.
 169  
      * @throws IOException if the file does no longer exist.
 170  
      * @return boolean true if the file has changed.
 171  
      */
 172  
     public boolean hasBeenUpdated() throws IOException {
 173  32534272521
       long modified = file.lastModified();
 174  32534272521
       long fileSizeNow = file.length();
 175  32534272385
       if (modified == 0L)
 176  0
         throw new IOException("File deleted");
 177  32534210680
       return timeStamp != modified || fileSize != fileSizeNow;
 178  
     }
 179  
 
 180  
     public void updateTimeStamp() {
 181  145373041
       timeStamp = file.lastModified();
 182  145373041
       if (timeStamp == 0L)
 183  0
         notifyFileRemoved();
 184  145373041
       fileSize = file.length();
 185  
 
 186  145373041
       copy();
 187  145373041
     }
 188  
 
 189  
     public boolean copy() {
 190  
         
 191  
         //Util.pr("<copy file=\""+tmpFile.getPath()+"\">");
 192  3451257172
       boolean res = false;
 193  
       try {
 194  3451257172
         res = Util.copyFile(file, tmpFile, true);
 195  0
       } catch (IOException ex) {
 196  0
         Globals.logger("Cannot copy to temporary file '"+tmpFile.getPath()+"'");
 197  3451257172
       }
 198  
       //Util.pr("</copy>");
 199  3451257172
       return res;
 200  
         
 201  
       //return true;
 202  
     }
 203  
 
 204  
     /**
 205  
      * Call the listener method to signal that the file has changed.
 206  
      */
 207  
     public void notifyListener() {
 208  
       // Update time stamp.
 209  1581461
       timeStamp = file.lastModified();
 210  1581461
       fileSize = file.length();
 211  1581461
       listener.fileUpdated();
 212  1581461
     }
 213  
 
 214  
     /**
 215  
      * Call the listener method to signal that the file has been removed.
 216  
      */
 217  
     public void notifyFileRemoved() {
 218  0
       listener.fileRemoved();
 219  0
     }
 220  
 
 221  
     /*public void finalize() {
 222  
       try {
 223  
         tmpFile.delete();
 224  
       } catch (Throwable e) {
 225  
         Globals.logger("Cannot delete temporary file '"+tmpFile.getPath()+"'");
 226  
       }
 227  
     }*/
 228  
   }
 229  
 
 230  
   static synchronized File getTempFile() {
 231  3305884131
     File f = null;
 232  
     // Globals.prefs.get("tempDir")
 233  
     //while ((f = File.createTempFile("jabref"+(tmpNum++), null)).exists());
 234  
     try {
 235  3305884131
             f = File.createTempFile("jabref", null);
 236  3305884131
         f.deleteOnExit();
 237  
         //System.out.println(f.getPath());
 238  0
     } catch (IOException ex) {
 239  0
         ex.printStackTrace();
 240  3305884131
     }
 241  3305884131
     return f;
 242  
   }
 243  
 }