Source code for /engineering/autohit-1998/autohit/utils/Log.javaOriginal file Log.java
   1 /**
   2  * .
   3  * Copyright � 1999 Erich P G.
   4  *
   5  */
   6 
   7 package autohit.utils;
   8 
   9 import java.util.Vector;
  10 import java.util.Date;
  11 import java.io.InputStream;
  12 import java.io.FileInputStream;
  13 import java.io.FileOutputStream;
  14 import java.io.EOFException;
  15 import java.io.File;
  16 import java.io.FileWriter;
  17 import java.io.BufferedWriter;
  18 
  19 import autohit.vm.VM;
  20 
  21 /**
  22  * A logging mechanism.  It will add timing information and is syncronized
  23  * so more than one thread can post.  The data is put in a temp file, so
  24  * memory usage should not get out of hand ( as does a StringBuffer).
  25  * <p>
  26  * All entries will automatically have a LF appended to the end.  Timing
  27  * information shows up to 7 digits--[0000000].  Granularity is passed to the
  28  * constructor; note that the timing will automatically be scaled
  29  * by the VM class.  DO NOT USE the default constructor.
  30  * <p>
  31  * Note that while the log timing does support pause() and resume(), it
  32  * does not account for the timing of client threads, so timing information
  33  * for items posted by threads may or may not be completely accurate.  (For
  34  * instance, pausing all the running VMs AND the Log will not happen
  35  * instantaniously, so there may be some variances in timing when they
  36  * are all resumed.)
  37  * <p>
  38  * To ensure that the temp file is removed, call the close() method when
  39  * done.
  40  * <p>
  41  * Any exceptions that are caught during a put() or putSub() are caught
  42  * and ignored.  I really don't expect any to come out of those
  43  * methods, and if they do, then other REAL bad stuff is probibly
  44  * already underway...
  45  * <p>
  46  * This class actually extends the VM base class, as it provides the
  47  * timing functionality that we want.
  48  * <p>
  49  * NOTE!  The timing is started on construction.  If you want to wait,
  50  * immeadiatly call the pause() method.
  51  *
  52  * @see autohit.vm.VM
  53  *
  54  * @author Erich P. Gatejen
  55  * @version 1.0
  56  * <i>Version History</i>
  57  * <code>EPG - Initial - 15Jan99</code> 
  58  * 
  59  */
  60 public class Log extends VM {
  61    
  62 	// --- FINAL FIELDS ------------------------------------------------------	
  63      
  64     /* 
  65      *  Speed things up a bit...
  66      */ 
  67     String  zeros[] = { "0000000", "000000", "00000", "0000",
  68                         "000", "00", "0", "" }; 
  69                           
  70 
  71 	// --- FIELDS ------------------------------------------------------------
  72 
  73     /**
  74      * The temporary file.
  75      */      	    
  76      private File        tempFile;
  77      
  78     /**
  79      *  An output writer for the tempFile;
  80      */           
  81      private BufferedWriter   out;
  82      
  83     /**
  84      *  An input stream for reading the log temp file.
  85      */ 
  86      private FileInputStream     is =  null;
  87 
  88     /**
  89      *  The object monitor for this log.
  90      */ 
  91      private ObjectMonitor        lock;     
  92      
  93     /**
  94      *  Granulatiry for each tick of the VM's clock.  This will
  95      *  scale the tick value.
  96      *
  97      *  A typical value would be 1000, as this would show timing
  98      *  information in seconds (given that the VM class granularity
  99      *  is set to milliseconds).
 100      */ 
 101      public int     gran;
 102      
 103     /**
 104      *  Constructor.  It throws any exceptions related to creating
 105      *  the temporary file.
 106      *
 107      *  It will start the timing.  See the VM base class to see how
 108      *  timing is implimented.
 109      *
 110      *  @param granulariy granularity for timing information.  This
 111      *                    should be a Positive number.  If it is
 112      *                    0, the constructor will throw an Exception
 113      *                    (since it would cause divide by 0 exceptions
 114      *                    in other methods of this class).
 115      *
 116      *  @throws Exception
 117      *  @see autohit.vm.VM
 118      */
 119      public Log(int   granularity) throws Exception {
 120          super();
 121          
 122          lock = new ObjectMonitor();  
 123          
 124          gran = granularity;
 125          if (gran == 0) { throw new Exception("Zero granularity."); }
 126          
 127          try {
 128               
 129             // Base the tempfile name on the system time;     
 130             Date d = new Date();
 131             tempFile = File.createTempFile
 132                               (Integer.toString((int)d.getTime()),".tmp");
 133             
 134             out = new BufferedWriter(new FileWriter(tempFile));
 135             
 136          } catch (Exception e) { throw e; }
 137          
 138          this.start();
 139      }
 140      
 141     /**
 142      *  Put an entry in the log.  It will append timing information.
 143      *
 144      *  @param text the text to log.
 145      */      
 146      public void put(String   text) {
 147           
 148           lock.lock(true);
 149           
 150           try {
 151                if (state == STATE_PAUSED) return;
 152                
 153                // Build the timing field
 154                String num = Integer.toString(this.ticks()/gran);
 155                int l = num.length();
 156                if (l < 8) {
 157                     out.write("[" + zeros[l] + num  + "] ", 0, 10);     
 158                } else {
 159                     out.write("[" + num + "] ", 0, l + 3);
 160                }
 161                
 162                out.write(text, 0, text.length());
 163                out.write("\n", 0, 1);
 164           
 165           } catch (Exception e) {}
 166           
 167           lock.lock(false);          
 168      }     
 169 
 170     /**
 171      *  Put a sub-entry in the log.  It will not append timing
 172      *  information.  It will be tabbed to match the time text.
 173      *
 174      *  @param text the text to log.
 175      */      
 176      public void putSub(String   text) {
 177 
 178           lock.lock(true);
 179 
 180           if (state == VM.STATE_PAUSED) return;
 181 
 182           try {
 183                out.write("          ", 0, 10);          
 184                out.write(text, 0, text.length());
 185                out.write("\n", 0, 1);
 186           
 187           } catch (Exception e) {}          
 188           
 189           lock.lock(false);                               
 190      }
 191      
 192     /**
 193      *  Done with the log.  It returns an InputStream from which the
 194      *  log can be read.  Once this method is called, no further
 195      *  entries will be logged when calling put() or putSub() (though
 196      *  no error will occur).
 197      *
 198      *  This method  (or its overloaded peer) can only be called once.  After that, a null
 199      *  will be returned, instead of an InputStream.
 200      *
 201      *  @return an InputStream presenting the log data.
 202      */      
 203      public InputStream done() {
 204           
 205           lock.lock(true);
 206 
 207           if (state != VM.STATE_DONE) {
 208                state = VM.STATE_DONE;
 209 
 210                try {              
 211                     out.close();
 212                
 213                     is = new FileInputStream(tempFile);
 214           
 215                } catch (Exception e) {}
 216 
 217           }
 218           
 219           lock.lock(false);
 220           return is;
 221      }
 222 
 223     /**
 224      *  Done with the log.  Returns a File pointing to a file where
 225      *  the log resides.
 226      *
 227      *  This method (or its overloaded peer) can only be called once.  After that, a null
 228      *  will be returned, instead of a File.
 229      *
 230      *  @param pathName pathname to the file where the log should be written.
 231      *
 232      *  @return a File describing the new log file.
 233      */      
 234      public File done(String    pathName) {
 235 
 236             File lf = null;
 237             
 238             lock.lock(true);          
 239             
 240             if (state != VM.STATE_DONE) {
 241                state = VM.STATE_DONE;
 242 
 243                 // If for any reason something goes wrong, abort the whole affair.
 244                 try {  
 245                     out.close();                        
 246                                               
 247                     File fout = new File(pathName);
 248                       
 249                     
 250                     FileInputStream  fis  = new FileInputStream(tempFile);
 251                     FileOutputStream fos  = new FileOutputStream(fout);
 252             
 253                     try {
 254                         
 255                         byte[] buf = new byte[512];
 256                         int a = fis.read(buf);
 257                         while (a > 0) {
 258                             
 259                             fos.write(buf, 0, a);
 260                             a = fis.read(buf);                        
 261                         }                       
 262                         
 263                     } catch (EOFException e) {
 264                         // arggrgr.  The API doc LIES!  sometimes this gets thrown...    
 265                     } catch (Exception e) {
 266                         throw e;    
 267                     }   
 268                     
 269                     fis.close();
 270                     fos.close();
 271                     lf = fout;
 272  
 273                 } catch (Exception e) {                
 274                 }
 275             
 276             }       
 277             
 278             lock.lock(false);              
 279             return lf;
 280      }
 281      
 282     /**
 283      *  Close the log and free/close any resources.
 284      */      
 285      public void close() {
 286           
 287           try {
 288                if (is != null) is.close();
 289           } catch (Exception e) {}
 290           
 291           try {
 292                
 293                tempFile.delete();      
 294           } catch (Exception e) {}
 295           
 296      }         
 297 
 298     /**
 299      *  Doesn't do a thing.  This isn't an actual VM.  However,
 300      *  we have to impliment it since Log extends the abstract VM.
 301      */
 302      public void execute() { }
 303 
 304 }