Clover coverage report - gsbase - 2.0.1
Coverage timestamp: Sat Jan 1 2005 12:30:02 EST
file stats: LOC: 393   Methods: 14
NCLOC: 226   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
TraceItemDispatcher.java 58.3% 60% 78.6% 61.2%
coverage coverage
 1    /*
 2    * Copyright (c) 1998, 2005 Gargoyle Software Inc. All rights reserved.
 3    *
 4    * Redistribution and use in source and binary forms, with or without
 5    * modification, are permitted provided that the following conditions are met:
 6    *
 7    * 1. Redistributions of source code must retain the above copyright notice,
 8    * this list of conditions and the following disclaimer.
 9    * 2. Redistributions in binary form must reproduce the above copyright notice,
 10    * this list of conditions and the following disclaimer in the documentation
 11    * and/or other materials provided with the distribution.
 12    * 3. The end-user documentation included with the redistribution, if any, must
 13    * include the following acknowledgment:
 14    *
 15    * "This product includes software developed by Gargoyle Software Inc.
 16    * (http://www.GargoyleSoftware.com/)."
 17    *
 18    * Alternately, this acknowledgment may appear in the software itself, if
 19    * and wherever such third-party acknowledgments normally appear.
 20    * 4. The name "Gargoyle Software" must not be used to endorse or promote
 21    * products derived from this software without prior written permission.
 22    * For written permission, please contact info@GargoyleSoftware.com.
 23    * 5. Products derived from this software may not be called "GSBase", nor may
 24    * "GSBase" appear in their name, without prior written permission of
 25    * Gargoyle Software Inc.
 26    *
 27    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 28    * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 29    * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
 30    * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 31    * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 32    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 33    * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 34    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 35    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 36    * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 37    */
 38    package com.gargoylesoftware.base.trace;
 39   
 40    import com.gargoylesoftware.base.util.DetailedNullPointerException;
 41    import com.gargoylesoftware.base.util.StringUtil;
 42    import java.io.PrintStream;
 43    import java.lang.reflect.InvocationTargetException;
 44    import java.lang.reflect.Method;
 45    import java.text.Format;
 46    import java.text.SimpleDateFormat;
 47    import java.util.Date;
 48    import java.util.Iterator;
 49    import java.util.Set;
 50   
 51    /**
 52    * <p style="color: orange">Internal use only.</p>.
 53    * <p>A dispatcher for TraceItems</p>
 54    *
 55    * @version $Revision: 1.9 $
 56    * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
 57    */
 58    public class TraceItemDispatcher implements Runnable {
 59   
 60    // Format used for the timestamp in the trace lines
 61    private static final Format TIMESTAMP_FORMAT = new SimpleDateFormat("HH:mm");
 62   
 63    private static final int BUFFER_ENABLED = 1;
 64    private static final int BUFFER_SHUTTING_DOWN = 2;
 65    private static final int BUFFER_DISABLED = 3;
 66   
 67    private static int bufferStatus_ = BUFFER_DISABLED;
 68   
 69    private final TraceItemQueue traceQueue_ = new TraceItemQueue();
 70   
 71    // For caching TraceItems
 72    private TraceItemQueue cacheTraceItemQueue_ = new TraceItemQueue();
 73    private int cacheMaxSize_ = 50;
 74   
 75    /**
 76    *
 77    */
 78  2 public TraceItemDispatcher() {
 79  2 new Thread(this, "TraceItemDispatcher Thread").start();
 80   
 81    // Add a hook so that buffering will be shut down before the JVM
 82    // exits. This will ensure that all information written to Trace
 83    // will be flushed properly before shutdown.
 84    //
 85    // This is done with reflection so that the class will still compile and
 86    // run in a 1.2 JVM.
 87  2 final Runtime runtime = Runtime.getRuntime();
 88  2 try {
 89  2 final Method method = runtime.getClass().getMethod( "addShutdownHook",
 90    new Class[] { Thread.class } );
 91  2 final Thread thread = new Thread() {
 92  2 public void run() {
 93  2 Trace.getController().setBufferingEnabled(false);
 94    }
 95    };
 96  2 method.invoke( runtime, new Object[] { thread } );
 97    }
 98    catch( NoSuchMethodException e ) {
 99    // The method wasn't found - we must be running on a 1.2 JVM. Oh well.
 100    }
 101    catch( final IllegalAccessException e ) {
 102    // Theoretically impossible.
 103  0 e.printStackTrace();
 104    }
 105    catch( final InvocationTargetException e ) {
 106    // Theoretically impossible.
 107  0 e.getTargetException().printStackTrace();
 108    }
 109    }
 110   
 111    /**
 112    *
 113    */
 114  2 public void run() {
 115  2 TraceItem item;
 116   
 117    // Enable the buffering. Note that we can't call
 118    // setBufferingEnabled() at this point because of deadlocks.
 119  2 bufferStatus_ = BUFFER_ENABLED;
 120   
 121    // The reason for the double try blocks is that we want to continue
 122    // looping if we get an exception but if we get an error then
 123    // something is severely wrong so we exit the thread.
 124  2 try {
 125  2 while(true) {
 126  8 try {
 127  8 item = traceQueue_.pop();
 128  8 if( item == null ) {
 129  5 Thread.sleep(500);
 130    }
 131    else {
 132  3 dumpTraceElement(item);
 133    }
 134    }
 135    catch( final Exception e ) {
 136  0 System.out.print("Exception when printing debug information e=");
 137  0 e.printStackTrace();
 138    }
 139    }
 140    }
 141    catch( final Throwable t ) {
 142  0 System.out.print("Exception when printing debug information e=");
 143  0 t.printStackTrace();
 144    }
 145    }
 146   
 147    /**
 148    * Format an item and print it to standard out.
 149    * @param item the item to print.
 150    */
 151  4 private void dumpTraceElement( final TraceItem item ) {
 152  4 assertNotNull("item", item);
 153   
 154  4 final TraceChannel channel = item.getChannel();
 155  4 if( channel != null && channel.isEnabled() && item.containsText() ) {
 156  3 final Set traceWriters = channel.getTraceWriters();
 157  3 synchronized( traceWriters ) {
 158  3 final Iterator iterator = traceWriters.iterator();
 159  3 if( iterator.hasNext() == false ) {
 160    // There aren't any trace writers for this channel
 161  0 defaultTraceWriter(item);
 162    }
 163    else {
 164  3 while( iterator.hasNext() ) {
 165  3 ((TraceWriter)iterator.next()).write(item);
 166    }
 167    }
 168    }
 169    }
 170   
 171    // Is this a synchronous call
 172  4 final Object lock = item.getLock();
 173  4 if( lock != null ) {
 174  1 synchronized(lock) {
 175  1 lock.notify();
 176    }
 177  1 return;
 178    }
 179   
 180  3 disposeTraceItem(item);
 181    }
 182   
 183    /**
 184    * Provide default behaviour
 185    * @param item The item to print
 186    */
 187  0 private static void defaultTraceWriter( final TraceItem item ) {
 188   
 189  0 final PrintStream outStream
 190    = Trace.getController().getRealSystemOut();
 191  0 final StringBuffer prefixBuffer = new StringBuffer();
 192   
 193  0 prefixBuffer.append("[");
 194  0 final Date timestamp = item.getTime();
 195  0 if( timestamp != null ) {
 196  0 prefixBuffer.append( TIMESTAMP_FORMAT.format(timestamp) );
 197  0 prefixBuffer.append(" ");
 198    }
 199   
 200  0 final String threadName = item.getThreadName();
 201  0 if( threadName != null ) {
 202  0 prefixBuffer.append( threadName );
 203    }
 204   
 205  0 prefixBuffer.append("] ");
 206   
 207  0 final String prefix = prefixBuffer.toString();
 208  0 final String message = item.getMessage();
 209   
 210  0 if( message != null ) {
 211  0 outStream.print(prefix);
 212  0 outStream.print( StringUtil.expandTabs(message,3) );
 213    }
 214   
 215  0 final Throwable throwable = item.getThrowable();
 216   
 217  0 if( throwable != null ) {
 218  0 int i;
 219  0 final String strings[] = Trace.throwableToStringArray(throwable);
 220   
 221  0 outStream.print(prefix);
 222  0 outStream.println(strings[0]);
 223   
 224  0 final String blanks = StringUtil.nCopies(prefix.length(), ' ');
 225  0 for( i=1; i<strings.length; i++ ) {
 226  0 outStream.print(blanks);
 227  0 outStream.println( StringUtil.expandTabs(strings[i],3) );
 228    }
 229    }
 230    }
 231   
 232    /**
 233    * Get the queue.
 234    * @return The queue.
 235    */
 236  0 public TraceItemQueue getTraceItemQueue() {
 237  0 return traceQueue_;
 238    }
 239   
 240    /**
 241    * Add an item to the trace queue.
 242    * @param item The item to add.
 243    */
 244  4 public void dispatch( final TraceItem item ) {
 245  4 item.setThread( Thread.currentThread() );
 246  4 item.setTime( new Date() );
 247   
 248  4 switch( bufferStatus_ ) {
 249  3 case BUFFER_ENABLED:
 250  3 traceQueue_.push(item);
 251  3 break;
 252   
 253  0 case BUFFER_SHUTTING_DOWN:
 254  0 flush();
 255  0 dumpTraceElement(item);
 256  0 break;
 257   
 258  1 case BUFFER_DISABLED:
 259  1 dumpTraceElement(item);
 260  1 break;
 261   
 262  0 default:
 263  0 throw new IllegalStateException(
 264    "Unexpected value: bufferStatus_="
 265    + bufferStatus_);
 266    }
 267    }
 268   
 269    /**
 270    *
 271    */
 272    // TODO: Use lock_ in TraceItem
 273  3 private synchronized void waitForQueueToEmpty() {
 274  3 while( traceQueue_.size() != 0 ) {
 275  5 try {
 276  5 Thread.sleep(100);
 277    }
 278    catch( final InterruptedException e ) {
 279  0 return;
 280    }
 281    }
 282    }
 283   
 284    /**
 285    * Set whether or not to buffer the output of the trace calls. Buffering
 286    * will increase perceived performance significantly.
 287    * @param enabled True if buffering should be enabled
 288    */
 289  5 public synchronized void setBufferingEnabled( final boolean enabled ) {
 290   
 291  5 if( enabled ) {
 292  2 if( bufferStatus_ == BUFFER_SHUTTING_DOWN ) {
 293    // Block until the buffer has finished shutting down
 294  0 flush();
 295    }
 296   
 297  2 if( bufferStatus_ == BUFFER_DISABLED ) {
 298  1 bufferStatus_ = BUFFER_ENABLED;
 299    }
 300    }
 301    else {
 302  3 if( bufferStatus_ == BUFFER_ENABLED ) {
 303  3 bufferStatus_ = BUFFER_SHUTTING_DOWN;
 304  3 flush();
 305  3 bufferStatus_ = BUFFER_DISABLED;
 306    }
 307    }
 308    }
 309   
 310    /**
 311    * Return true if buffering is enabled.
 312    * @return true if buffering is enabled.
 313    */
 314  0 public boolean isBufferingEnabled() {
 315  0 return bufferStatus_ == BUFFER_ENABLED;
 316    }
 317   
 318    /**
 319    *
 320    */
 321  4 public void flush() {
 322   
 323  4 switch( bufferStatus_ ) {
 324  1 case BUFFER_ENABLED:
 325  1 final TraceItem item = getNewTraceItem();
 326  1 final Object lock = new Object();
 327  1 item.setLock( lock );
 328  1 try {
 329  1 synchronized(lock) {
 330  1 dispatch( item );
 331  1 lock.wait();
 332    }
 333    }
 334    catch( final InterruptedException e ) {
 335    // Expected path
 336    }
 337  1 break;
 338   
 339  3 case BUFFER_SHUTTING_DOWN:
 340  3 waitForQueueToEmpty();
 341  3 break;
 342   
 343  0 case BUFFER_DISABLED:
 344  0 return;
 345   
 346  0 default:
 347  0 throw new IllegalStateException(
 348    "Unexpected value: bufferStatus_="
 349    + bufferStatus_);
 350    }
 351    }
 352   
 353    /**
 354    * Return a trace item
 355    * @return The new trace item.
 356    */
 357  4 public TraceItem getNewTraceItem() {
 358  4 TraceItem item = cacheTraceItemQueue_.pop();
 359  4 if( item == null ) {
 360  2 item = new TraceItem();
 361    }
 362   
 363  4 return item;
 364    }
 365   
 366    /**
 367    * Dispose of a trace item. Disposing will put the trace item back on a queue for reuse.
 368    * @param item The item to dispose.
 369    */
 370  3 public void disposeTraceItem( final TraceItem item ) {
 371  3 if( cacheTraceItemQueue_.size() < cacheMaxSize_ ) {
 372  3 item.clear();
 373  3 cacheTraceItemQueue_.push(item);
 374    }
 375    }
 376   
 377   
 378    /**
 379    * Verify that the specified value is not null. If it is then throw an exception
 380    *
 381    * @param fieldName The name of the field to check
 382    * @param fieldValue The value of the field to check
 383    * @exception DetailedNullPointerException If fieldValue is null
 384    */
 385  4 protected final void assertNotNull( final String fieldName, final Object fieldValue )
 386    throws DetailedNullPointerException {
 387   
 388  4 if( fieldValue == null ) {
 389  0 throw new DetailedNullPointerException(fieldName);
 390    }
 391    }
 392    }
 393