1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2 *
3 * This program and the accompanying materials are made available under
4 * the terms of the Common Public License v1.0 which accompanies this distribution,
5 * and is available at http://www.eclipse.org/legal/cpl-v10.html
6 *
7 * $Id: RT.java,v 1.2.2.3 2004/07/16 23:32:03 vlad_r Exp $
8 */
9package com.vladium.emma.rt;
10
11import java.io.File;
12
13import com.vladium.logging.Logger;
14import com.vladium.util.IProperties;
15import com.vladium.util.Property;
16import com.vladium.util.exit.ExitHookManager;
17import com.vladium.emma.IAppConstants;
18import com.vladium.emma.EMMAProperties;
19import com.vladium.emma.data.ICoverageData;
20import com.vladium.emma.data.DataFactory;
21
22// ----------------------------------------------------------------------------
23/**
24 * @author Vlad Roubtsov, (C) 2003
25 */
26public
27abstract class RT implements IAppConstants
28{
29    // public: ................................................................
30
31
32    public static synchronized ICoverageData reset (final boolean createCoverageData, final boolean createExitHook)
33    {
34        // reload the app properties [needs to be done to accomodate classloader rearrangements]:
35
36        // avoid the call context tricks at runtime in case security causes problems,
37        // use an explicit caller parameter for getAppProperties():
38
39        ClassLoader loader = RT.class.getClassLoader ();
40        if (loader == null) loader = ClassLoader.getSystemClassLoader ();
41
42        IProperties appProperties = null;
43        try
44        {
45            appProperties = EMMAProperties.getAppProperties (loader);
46        }
47        catch (Throwable t)
48        {
49            // TODO: handle better
50            t.printStackTrace (System.out);
51        }
52        s_appProperties = appProperties;
53
54
55        if (EXIT_HOOK_MANAGER != null)
56        {
57            // disable/remove the current hook, if any:
58
59            if (s_exitHook != null)
60            {
61                 // note: no attempt is made to execute the existing hook, so its coverage
62                 // data may be simply discarded
63
64                EXIT_HOOK_MANAGER.removeExitHook (s_exitHook);
65                s_exitHook = null;
66            }
67        }
68
69        ICoverageData cdata = s_cdata; // no sync accessor needed
70        if (createCoverageData)
71        {
72            cdata = DataFactory.newCoverageData ();
73            s_cdata = cdata;
74        }
75        else
76        {
77            s_cdata = null;
78        }
79
80        if (EXIT_HOOK_MANAGER != null)
81        {
82            if (createExitHook && (cdata != null))
83            {
84                final Runnable exitHook = new RTExitHook (RT.class, cdata, getCoverageOutFile (), getCoverageOutMerge ());
85
86                // FR SF978671: fault all classes that we might need to do coverage
87                // data dumping (this forces classdefs to be loaded into classloader
88                // class cache and allows output file writing to succeed even if
89                // the RT classloader is some component loader (e.g, in a J2EE container)
90                // that gets invalidated by the time the exit hook thread is run:
91
92                RTExitHook.createClassLoaderClosure ();
93
94                if (EXIT_HOOK_MANAGER.addExitHook (exitHook))
95                {
96                    s_exitHook = exitHook;
97                }
98                // else TODO: log/warn
99            }
100        }
101
102        return cdata;
103    }
104
105    public static void r (final boolean [][] coverage, final String classVMName, final long stamp)
106    {
107        // note that we use class names, not the actual Class objects, as the keys here. This
108        // is not the best possible solution because it is not capable of supporting
109        // multiply (re)loaded classes within the same app, but the rest of the toolkit
110        // isn't designed to support this anyway. Furthermore, this does not interfere
111        // with class unloading.
112
113        final ICoverageData cdata = getCoverageData (); // need to use accessor for JMM reasons
114
115        // ['cdata' can be null if a previous call to dumpCoverageData() disabled data collection]
116
117        if (cdata != null)
118        {
119            synchronized (cdata.lock ())
120            {
121                // TODO: could something useful be communicated back to the class
122                // by returning something here [e.g., unique class ID (solves the
123                // issues of class name collisions and class reloading) or RT.class
124                // (to prevent RT reloading)]
125
126                cdata.addClass (coverage, classVMName, stamp);
127            }
128        }
129    }
130
131    public static synchronized ICoverageData getCoverageData ()
132    {
133        return s_cdata;
134    }
135
136    public static synchronized IProperties getAppProperties ()
137    {
138        return s_appProperties;
139    }
140
141    /**
142     * Public API for forcing coverage data dump.
143     *
144     * @param outFile
145     * @param merge
146     * @param stopDataCollection
147     */
148    public static synchronized void dumpCoverageData (File outFile, final boolean merge, final boolean stopDataCollection)
149    {
150        if (DEBUG) System.out.println ("RT::dumpCoverageData() DUMPING " + RT.class.getClassLoader ());
151        outFile = outFile != null ? outFile : getCoverageOutFile ();
152
153        ICoverageData cdata = s_cdata; // no need to use accessor
154        if (stopDataCollection) s_cdata = null; // TODO: log this NOTE: this does not really stop data collection, merely prevents new class registration
155
156        RTCoverageDataPersister.dumpCoverageData (cdata, ! stopDataCollection, outFile, merge);
157    }
158
159    public static synchronized void dumpCoverageData (File outFile, final boolean stopDataCollection)
160    {
161        outFile = outFile != null ? outFile : getCoverageOutFile ();
162
163        ICoverageData cdata = s_cdata; // no need to use accessor
164        if (stopDataCollection) s_cdata = null; // TODO: log this NOTE: this does not really stop data collection, merely prevents new class registration
165
166        RTCoverageDataPersister.dumpCoverageData (cdata, ! stopDataCollection, outFile, getCoverageOutMerge ());
167    }
168
169    // protected: .............................................................
170
171    // package: ...............................................................
172
173    // private: ...............................................................
174
175
176    private RT () {} // prevent subclassing
177
178
179    private static File getCoverageOutFile ()
180    {
181        final IProperties appProperties = getAppProperties (); // sync accessor
182        if (appProperties != null)
183        {
184            final String property = appProperties.getProperty (EMMAProperties.PROPERTY_COVERAGE_DATA_OUT_FILE,
185                                                               EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_FILE);
186            return new File (property);
187        }
188
189        return new File (EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_FILE);
190    }
191
192    private static boolean getCoverageOutMerge ()
193    {
194        final IProperties appProperties = getAppProperties (); // sync accessor
195        if (appProperties != null)
196        {
197            // [Boolean.toString (boolean) is J2SDK 1.4+]
198
199            final String property = appProperties.getProperty (EMMAProperties.PROPERTY_COVERAGE_DATA_OUT_MERGE,
200                                                               EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_MERGE.toString ());
201            return Property.toBoolean (property);
202        }
203
204        return EMMAProperties.DEFAULT_COVERAGE_DATA_OUT_MERGE.booleanValue ();
205    }
206
207
208    private static ICoverageData s_cdata;
209    private static Runnable s_exitHook;
210    private static IProperties s_appProperties; // TODO: this is better of as java.util.Properties
211
212    private static final ExitHookManager EXIT_HOOK_MANAGER; // set in <clinit>
213
214    private static final boolean DEBUG = false;
215
216    static
217    {
218        if (DEBUG) System.out.println ("RT[" + System.identityHashCode (RT.class) + "]::<clinit>: loaded by " + RT.class.getClassLoader ());
219
220        ExitHookManager temp = null;
221        try
222        {
223            temp = ExitHookManager.getSingleton ();
224        }
225        catch (Throwable t)
226        {
227            // TODO: handle better
228            t.printStackTrace (System.out);
229        }
230        EXIT_HOOK_MANAGER = temp;
231
232
233        if (RTSettings.isStandaloneMode ())
234        {
235            if (DEBUG) System.out.println ("RT::<clinit>: STANDALONE MODE");
236
237            // load app props, create coverage data, and register an exit hook for it:
238            reset (true, true);
239
240            // use method-scoped loggers in RT:
241            final Logger log = Logger.getLogger ();
242            if (log.atINFO ())
243            {
244                log.info ("collecting runtime coverage data ...");
245            }
246        }
247        else
248        {
249            // load app props only:
250            reset (false, false);
251        }
252    }
253
254} // end of class
255// ----------------------------------------------------------------------------