blob: 92733488b6831d1f5b19e5724e180718884b2c92 [file] [log] [blame]
/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
*
* This program and the accompanying materials are made available under
* the terms of the Common Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/cpl-v10.html
*
* $Id: RT.java,v 1.2.2.3 2004/07/16 23:32:03 vlad_r Exp $
*/
package com.vladium.emma.rt;
import java.io.File;
import com.vladium.logging.Logger;
import com.vladium.util.IProperties;
import com.vladium.util.Property;
import com.vladium.util.exit.ExitHookManager;
import com.vladium.emma.IAppConstants;
import com.vladium.emma.EMMAProperties;
import com.vladium.emma.data.ICoverageData;
import com.vladium.emma.data.DataFactory;
// ----------------------------------------------------------------------------
/**
* @author Vlad Roubtsov, (C) 2003
*/
public
abstract class RT implements IAppConstants
{
// public: ................................................................
public static synchronized ICoverageData reset (final boolean createCoverageData, final boolean createExitHook)
{
// reload the app properties [needs to be done to accomodate classloader rearrangements]:
// avoid the call context tricks at runtime in case security causes problems,
// use an explicit caller parameter for getAppProperties():
ClassLoader loader = RT.class.getClassLoader ();
if (loader == null) loader = ClassLoader.getSystemClassLoader ();
IProperties appProperties = null;
try
{
appProperties = EMMAProperties.getAppProperties (loader);
}
catch (Throwable t)
{
// TODO: handle better
t.printStackTrace (System.out);
}
s_appProperties = appProperties;
if (EXIT_HOOK_MANAGER != null)
{
// disable/remove the current hook, if any:
if (s_exitHook != null)
{
// note: no attempt is made to execute the existing hook, so its coverage
// data may be simply discarded
EXIT_HOOK_MANAGER.removeExitHook (s_exitHook);
s_exitHook = null;
}
}
ICoverageData cdata = s_cdata; // no sync accessor needed
if (createCoverageData)
{
cdata = DataFactory.newCoverageData ();
s_cdata = cdata;
}
else
{
s_cdata = null;
}
if (EXIT_HOOK_MANAGER != null)
{
if (createExitHook && (cdata != null))
{
final Runnable exitHook = new RTExitHook (RT.class, cdata, getCoverageOutFile (), getCoverageOutMerge ());
// FR SF978671: fault all classes that we might need to do coverage
// data dumping (this forces classdefs to be loaded into classloader
// class cache and allows output file writing to succeed even if
// the RT classloader is some component loader (e.g, in a J2EE container)
// that gets invalidated by the time the exit hook thread is run:
RTExitHook.createClassLoaderClosure ();
if (EXIT_HOOK_MANAGER.addExitHook (exitHook))
{
s_exitHook = exitHook;
}
// else TODO: log/warn
}
}
return cdata;
}
public static void r (final boolean [][] coverage, final String classVMName, final long stamp)
{
// note that we use class names, not the actual Class objects, as the keys here. This
// is not the best possible solution because it is not capable of supporting
// multiply (re)loaded classes within the same app, but the rest of the toolkit
// isn't designed to support this anyway. Furthermore, this does not interfere
// with class unloading.
final ICoverageData cdata = getCoverageData (); // need to use accessor for JMM reasons
// ['cdata' can be null if a previous call to dumpCoverageData() disabled data collection]
if (cdata != null)
{
synchronized (cdata.lock ())
{
// TODO: could something useful be communicated back to the class
// by returning something here [e.g., unique class ID (solves the
// issues of class name collisions and class reloading) or RT.class
// (to prevent RT reloading)]
cdata.addClass (coverage, classVMName, stamp);
}
}
}
public static synchronized ICoverageData getCoverageData ()
{
return s_cdata;
}
public static synchronized IProperties getAppProperties ()
{
return s_appProperties;
}
/**
* Public API for forcing coverage data dump.
*
* @param outFile
* @param merge
* @param stopDataCollection
*/
public static synchronized void dumpCoverageData (File outFile, final boolean merge, final boolean stopDataCollection)
{
if (DEBUG) System.out.println ("RT::dumpCoverageData() DUMPING " + RT.class.getClassLoader ());
outFile = outFile != null ? outFile : getCoverageOutFile ();
ICoverageData cdata = s_cdata; // no need to use accessor
if (stopDataCollection) s_cdata = null; // TODO: log this NOTE: this does not really stop data collection, merely prevents new class registration
RTCoverageDataPersister.dumpCoverageData (cdata, ! stopDataCollection, outFile, merge);
}
public static synchronized void dumpCoverageData (File outFile, final boolean stopDataCollection)
{
outFile = outFile != null ? outFile : getCoverageOutFile ();
ICoverageData cdata = s_cdata; // no need to use accessor
if (stopDataCollection) s_cdata = null; // TODO: log this NOTE: this does not really stop data collection, merely prevents new class registration
RTCoverageDataPersister.dumpCoverageData (cdata, ! stopDataCollection, outFile, getCoverageOutMerge ());
}
// protected: .............................................................
// package: ...............................................................
// private: ...............................................................
private RT () {} // prevent subclassing
private static File getCoverageOutFile ()
{
final IProperties appProperties = getAppProperties (); // sync accessor
if (appProperties != null)
{
final String property = appProperties.getProperty (EMMAProperties.PROPERTY_COVERAGE_DATA_OUT_FILE,
EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_FILE);
return new File (property);
}
return new File (EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_FILE);
}
private static boolean getCoverageOutMerge ()
{
final IProperties appProperties = getAppProperties (); // sync accessor
if (appProperties != null)
{
// [Boolean.toString (boolean) is J2SDK 1.4+]
final String property = appProperties.getProperty (EMMAProperties.PROPERTY_COVERAGE_DATA_OUT_MERGE,
EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_MERGE.toString ());
return Property.toBoolean (property);
}
return EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_MERGE.booleanValue ();
}
private static ICoverageData s_cdata;
private static Runnable s_exitHook;
private static IProperties s_appProperties; // TODO: this is better of as java.util.Properties
private static final ExitHookManager EXIT_HOOK_MANAGER; // set in <clinit>
private static final boolean DEBUG = false;
static
{
if (DEBUG) System.out.println ("RT[" + System.identityHashCode (RT.class) + "]::<clinit>: loaded by " + RT.class.getClassLoader ());
ExitHookManager temp = null;
try
{
temp = ExitHookManager.getSingleton ();
}
catch (Throwable t)
{
// TODO: handle better
t.printStackTrace (System.out);
}
EXIT_HOOK_MANAGER = temp;
if (RTSettings.isStandaloneMode ())
{
if (DEBUG) System.out.println ("RT::<clinit>: STANDALONE MODE");
// load app props, create coverage data, and register an exit hook for it:
reset (true, true);
// use method-scoped loggers in RT:
final Logger log = Logger.getLogger ();
if (log.atINFO ())
{
log.info ("collecting runtime coverage data ...");
}
}
else
{
// load app props only:
reset (false, false);
}
}
} // end of class
// ----------------------------------------------------------------------------