1 26 27 package net.sourceforge.groboutils.codecoverage.v2.logger; 28 29 import java.io.Writer ; 30 import java.io.IOException ; 31 32 import java.util.Properties ; 33 import java.util.HashSet ; 34 import java.util.Set ; 35 36 import net.sourceforge.groboutils.codecoverage.v2.IChannelLogger; 37 import net.sourceforge.groboutils.codecoverage.v2.IChannelLoggerFactory; 38 39 40 47 public abstract class AbstractSingleSourceLoggerFactory 48 implements ISingleSource, IChannelLoggerFactory, Runnable 49 { 50 public static final String WRITES_PER_FLUSH_PROP = "writes-per-flush"; 51 private static final int DEFAULT_WRITES_PER_FLUSH = 2; 52 private static final int INIT_BUFFER_SIZE = 4096; 53 54 public static final String MILLIS_PER_FLUSH_PROP = "millis-per-flush"; 55 private static final long DEFAULT_MILLIS_PER_FLUSH = 0; 56 57 public static final String USE_CACHE_PROP = "use-cache"; 58 59 private StringBuffer buffer = new StringBuffer ( INIT_BUFFER_SIZE ); 61 private Writer source; 62 private int writesPerFlush = DEFAULT_WRITES_PER_FLUSH; 63 private long millisPerFlush = DEFAULT_MILLIS_PER_FLUSH; 64 private boolean reloadSourceAfterError = false; 65 66 private volatile int writeCount = 0; 69 70 private boolean isSetup = false; 71 private boolean useCache = true; 72 private Thread flushThread = null; 73 74 private Set covered = new HashSet ( 20000, 0.75f ); 75 76 private Object sync = buffer; 82 83 84 87 private class FlushRunner implements Runnable 88 { 89 public void run() 90 { 91 Thread t = Thread.currentThread(); 92 try 93 { 94 while (true) 95 { 96 Thread.sleep( millisPerFlush ); 97 flushBuffer(); 98 if (t.interrupted()) 99 { 100 break; 101 } 102 } 103 } 104 catch (InterruptedException e) 105 { 106 } 109 } 110 } 111 112 113 114 public IChannelLogger createChannelLogger( 115 String propertyPrefix, Properties props, short channelIndex ) 116 { 117 synchronized( this.sync ) 119 { 120 if (!this.isSetup) 121 { 122 setupProps( propertyPrefix, props ); 123 124 this.source = setupSource(); 125 126 addShutdownHook(); 127 this.isSetup = true; 128 } 129 } 130 131 return new SingleSourceLogger( channelIndex, this ); 132 } 133 134 135 139 public void cover( short channelIndex, String classSig, 140 short methodIndex, short markIndex ) 141 { 142 synchronized (this.sync) 143 { 144 if (this.source == null) 145 { 146 if (this.reloadSourceAfterError) 147 { 148 this.source = setupSource(); 150 if (this.source == null) 151 { 152 return; 156 } 157 } 158 else 159 { 160 return; 166 } 167 168 this.buffer.insert( 0, '\n' ); 174 this.writeCount = this.writesPerFlush; 175 } 176 } 177 178 StringBuffer sb = new StringBuffer (); 181 appendToLog( sb, channelIndex, classSig, methodIndex, markIndex ); 182 String s = sb.toString(); 183 if (this.useCache) 184 { 185 synchronized (this.sync) 186 { 187 if (this.covered.contains( s )) 188 { 189 return; 192 } 193 this.covered.add( s ); 196 } 197 } 198 this.buffer.append( s ); 202 203 int wpf = this.writesPerFlush; 209 if (wpf != 0 && ++this.writeCount >= wpf) 210 { 211 flushBuffer(); 212 } 213 } 214 215 216 220 public void run() 221 { 222 synchronized( this.buffer ) 224 { 225 if (this.flushThread != null) 226 { 227 this.flushThread.interrupt(); 228 this.flushThread = null; 229 } 230 231 234 if (this.source == null) 235 { 236 if (this.reloadSourceAfterError) 237 { 238 this.source = setupSource(); 239 } 240 if (this.source == null) 241 { 242 244 this.reloadSourceAfterError = false; 247 return; 248 } 249 this.buffer.insert( 0, '\n' ); 250 } 251 252 flushBuffer(); 253 254 try 255 { 256 this.source.close(); 257 } 258 catch (Exception e) 259 { 260 } 263 264 this.source = null; 267 this.reloadSourceAfterError = false; 268 } 270 } 271 272 273 277 protected abstract Writer setupSource(); 278 279 280 285 protected void setupProps( String prefix, Properties props ) 286 { 287 String wpf = props.getProperty( prefix + WRITES_PER_FLUSH_PROP ); 288 if (wpf != null) 289 { 290 try 291 { 292 this.writesPerFlush = Integer.parseInt( wpf ); 293 } 294 catch (NumberFormatException e) 295 { 296 this.writesPerFlush = DEFAULT_WRITES_PER_FLUSH; 298 } 300 } 301 303 String uc = props.getProperty( prefix + USE_CACHE_PROP ); 304 if (uc != null) 305 { 306 this.useCache = Boolean.valueOf( uc ).booleanValue(); 307 } 308 309 310 String mpf = props.getProperty( prefix + MILLIS_PER_FLUSH_PROP ); 311 if (mpf != null) 312 { 313 try 314 { 315 this.millisPerFlush = Long.parseLong( mpf ); 316 } 317 catch (NumberFormatException e) 318 { 319 this.millisPerFlush = DEFAULT_MILLIS_PER_FLUSH; 320 } 322 } 323 if (this.millisPerFlush > 0) 325 { 326 if (this.flushThread == null) 327 { 328 Thread t = new Thread ( new FlushRunner() ); 330 t.setDaemon( true ); 331 t.setPriority( Thread.MIN_PRIORITY ); 332 t.setName( "Code Coverage Flusher" ); 333 t.start(); 334 this.flushThread = t; 335 } 336 } 337 } 338 339 340 345 protected void setReloadSourceAfterError( boolean yes ) 346 { 347 this.reloadSourceAfterError = yes; 348 } 349 350 351 355 private final void flushBuffer() 356 { 357 synchronized (this.sync) 358 { 359 if (this.writeCount <= 0 || this.source == null) 360 { 361 return; 364 } 365 try 366 { 367 this.source.write( this.buffer.toString() ); 369 370 this.source.flush(); 372 373 this.buffer.setLength( 0 ); 379 this.writeCount = 0; 380 } 381 catch (IOException e) 382 { 383 e.printStackTrace(); 384 this.source = null; 386 } 387 } 388 } 389 390 391 392 protected void addShutdownHook() 393 { 394 Class c = Runtime .class; 395 try 396 { 397 java.lang.reflect.Method m = c.getMethod( 398 "addShutdownHook", new Class [] { Thread .class } ); 399 Thread t = new Thread ( this, 400 this.getClass().getName()+" shutdown hook" ); 401 m.invoke( Runtime.getRuntime(), new Object [] { t } ); 402 } 403 catch (Exception ex) 404 { 405 } 409 } 410 411 412 private static final void appendToLog( StringBuffer log, 413 short channelIndex, String classSig, short methodIndex, 414 short markIndex ) 415 { 416 log.append( channelIndex ). 417 append( ',' ).append( classSig ).append( ',' ). 418 append( DirectoryChannelLogger.createCoverString( 419 methodIndex, markIndex ) ); 420 } 421 } 422 423 | Popular Tags |