KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > vladium > emma > instr > InstrProcessorST


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: InstrProcessorST.java,v 1.1.1.1.2.3 2004/07/16 23:32:28 vlad_r Exp $
8  */

9 package com.vladium.emma.instr;
10
11 import java.io.File JavaDoc;
12 import java.io.FileInputStream JavaDoc;
13 import java.io.FileNotFoundException JavaDoc;
14 import java.io.FileOutputStream JavaDoc;
15 import java.io.IOException JavaDoc;
16 import java.io.InputStream JavaDoc;
17 import java.io.OutputStream JavaDoc;
18 import java.io.RandomAccessFile JavaDoc;
19 import java.util.Date JavaDoc;
20 import java.util.jar.Attributes JavaDoc;
21 import java.util.jar.JarFile JavaDoc;
22 import java.util.jar.JarInputStream JavaDoc;
23 import java.util.jar.JarOutputStream JavaDoc;
24 import java.util.jar.Manifest JavaDoc;
25 import java.util.zip.CRC32 JavaDoc;
26 import java.util.zip.ZipEntry JavaDoc;
27 import java.util.zip.ZipInputStream JavaDoc;
28 import java.util.zip.ZipOutputStream JavaDoc;
29
30 import com.vladium.jcd.cls.ClassDef;
31 import com.vladium.jcd.compiler.ClassWriter;
32 import com.vladium.jcd.parser.ClassDefParser;
33 import com.vladium.logging.Logger;
34 import com.vladium.util.ByteArrayOStream;
35 import com.vladium.util.Descriptors;
36 import com.vladium.util.Files;
37 import com.vladium.util.IPathEnumerator;
38 import com.vladium.util.IProperties;
39 //import com.vladium.util.Profiler;
40
import com.vladium.util.Property;
41 import com.vladium.util.asserts.$assert;
42 import com.vladium.util.exception.Exceptions;
43 //import com.vladium.utils.ObjectSizeProfiler;
44
import com.vladium.emma.IAppConstants;
45 import com.vladium.emma.IAppErrorCodes;
46 import com.vladium.emma.EMMAProperties;
47 import com.vladium.emma.EMMARuntimeException;
48 import com.vladium.emma.data.CoverageOptions;
49 import com.vladium.emma.data.CoverageOptionsFactory;
50 import com.vladium.emma.data.DataFactory;
51 import com.vladium.emma.data.IMetaData;
52
53 // ----------------------------------------------------------------------------
54
/**
55  * @author Vlad Roubtsov, (C) 2003
56  */

57 final class InstrProcessorST extends InstrProcessor
58                              implements IAppErrorCodes
59 {
60     // public: ................................................................
61

62     // TODO: performance of 'copy' mode could be improved by pushing dir filtering
63
// all the way to the path enumerator [although dir listing is reasonably fast]
64

65     
66     // IPathEnumerator.IPathHandler:
67

68     
69     public final void handleArchiveStart (final File JavaDoc parentDir, final File JavaDoc archive, final Manifest JavaDoc manifest)
70     {
71         final Logger log = m_log;
72         if (log.atTRACE2 ()) log.trace2 ("handleArchiveStart", "[" + parentDir + "] [" + archive + "]");
73         
74         // TODO: pass manifest into this callback, if any
75
// TODO: detect if manifest corresonds to a previously intrumented archive already ?
76

77         if (DO_DEPENDS_CHECKING)
78         {
79             final File JavaDoc fullArchiveFile = Files.newFile (parentDir, archive);
80             m_currentArchiveTS = fullArchiveFile.lastModified ();
81             
82             if ($assert.ENABLED) $assert.ASSERT (m_currentArchiveTS > 0, "invalid ts: " + m_currentArchiveTS);
83         }
84         
85         if ((m_outMode == OutMode.OUT_MODE_FULLCOPY) || (m_outMode == OutMode.OUT_MODE_OVERWRITE))
86         {
87             final Manifest JavaDoc outManifest = manifest != null
88                 ? new Manifest JavaDoc (manifest) // shallow copy
89
: new Manifest JavaDoc ();
90             
91             // set some basic main attributes:
92

93             final Attributes JavaDoc mainAttrs = outManifest.getMainAttributes ();
94             if (manifest == null) mainAttrs.put (Attributes.Name.MANIFEST_VERSION, "1.0");
95             mainAttrs.put (new Attributes.Name JavaDoc ("Created-By"), IAppConstants.APP_NAME + " v" + IAppConstants.APP_VERSION_WITH_BUILD_ID_AND_TAG);
96                             
97             // note: Manifest makes these 72-char-safe
98

99             mainAttrs.put (Attributes.Name.IMPLEMENTATION_TITLE, "instrumented version of [" + archive.getAbsolutePath () + "]");
100             mainAttrs.put (Attributes.Name.SPECIFICATION_TITLE, "instrumented on " + new Date JavaDoc (m_timeStamp) + " [" + Property.getSystemFingerprint () + "]");
101             
102             // TODO: remove entries related to signing
103

104             if (m_outMode == OutMode.OUT_MODE_FULLCOPY)
105             {
106                 // create an identically named artive in outdir/lib [the stream is
107
// closed in the archive end event handler]:
108

109                 try
110                 {
111                     final OutputStream JavaDoc out = new FileOutputStream JavaDoc (getFullOutFile (parentDir, archive, IN_LIB));
112                     
113                     m_archiveOut = outManifest != null ? new JarOutputStream JavaDoc (out, outManifest) : new JarOutputStream JavaDoc (out);
114                 }
115                 catch (IOException JavaDoc ioe)
116                 {
117                     // TODO: error code
118
throw new EMMARuntimeException (ioe);
119                 }
120             }
121             else if (m_outMode == OutMode.OUT_MODE_OVERWRITE)
122             {
123                 // create a temp file in the same dir [moved into the original one
124
// in the archive end event handler]:
125

126                 m_origArchiveFile = Files.newFile (parentDir, archive);
127                 
128                 // length > 3:
129
final String JavaDoc archiveName = Files.getFileName (archive) + IAppConstants.APP_NAME_LC;
130                 final String JavaDoc archiveExt = EMMAProperties.PROPERTY_TEMP_FILE_EXT;
131                 
132                 try
133                 {
134                     m_tempArchiveFile = Files.createTempFile (parentDir, archiveName, archiveExt);
135                     if (log.atTRACE2 ()) log.trace2 ("handleArchiveStart", "created temp archive [" + m_tempArchiveFile.getAbsolutePath () + "]");
136                     
137                     final OutputStream JavaDoc out = new FileOutputStream JavaDoc (m_tempArchiveFile);
138                     
139                     m_archiveOut = outManifest != null ? new JarOutputStream JavaDoc (out, outManifest) : new JarOutputStream JavaDoc (out);
140                 }
141                 catch (IOException JavaDoc ioe)
142                 {
143                     // TODO: error code
144
throw new EMMARuntimeException (ioe);
145                 }
146             }
147         }
148     }
149
150     public final void handleArchiveEntry (final JarInputStream JavaDoc in, final ZipEntry JavaDoc entry)
151     {
152         final Logger log = m_log;
153         if (log.atTRACE2 ()) log.trace2 ("handleArchiveEntry", "[" + entry.getName () + "]");
154         
155         final String JavaDoc name = entry.getName ();
156         final String JavaDoc lcName = name.toLowerCase ();
157
158         final boolean notcopymode = (m_outMode == OutMode.OUT_MODE_FULLCOPY) || (m_outMode == OutMode.OUT_MODE_OVERWRITE);
159         
160         boolean copyEntry = false;
161
162         if (lcName.endsWith (".class"))
163         {
164             final String JavaDoc className = name.substring (0, name.length () - 6).replace ('/', '.');
165             
166             // it is possible that a class with this name has already been processed;
167
// however, we can't skip it here because there is no guarantee that
168
// the runtime classpath will be identical to the instrumentation path
169

170             // [the metadata will still contain only a single entry for a class with
171
// this name: it is the responsibility of the user to ensure that both
172
// files represent the same class; in the future I might use a more
173
// robust internal strategy that uses something other than a class name
174
// as a metadata key]
175

176             if ((m_coverageFilter == null) || m_coverageFilter.included (className))
177             {
178                 InputStream JavaDoc clsin = null;
179                 try
180                 {
181                     File JavaDoc outFile = null;
182                     File JavaDoc fullOutFile = null;
183                     
184                     if (DO_DEPENDS_CHECKING)
185                     {
186                         // in 'copy' mode
187

188                         if (m_outMode == OutMode.OUT_MODE_COPY)
189                         {
190                             outFile = new File JavaDoc (className.replace ('.', File.separatorChar).concat (".class"));
191                             fullOutFile = getFullOutFile (null, outFile, IN_CLASSES);
192                             
193                             // if we already processed this class name within this instrumentor
194
// run, skip duplicates in copy mode:
195

196                             if (m_mdata.hasDescriptor (Descriptors.javaNameToVMName (className)))
197                                 return;
198                             
199                             // BUG_SF989071: using outFile here instead resulted in
200
// a zero result regardless of whether the target existed or not
201
final long outTimeStamp = fullOutFile.lastModified (); // 0 if 'fullOutFile' does not exist or if an I/O error occurs
202

203                             if (outTimeStamp > 0)
204                             {
205                                 long inTimeStamp = entry.getTime (); // can return -1
206
if (inTimeStamp < 0) inTimeStamp = m_currentArchiveTS; // default to the archive file timestamp
207

208                                 if ($assert.ENABLED) $assert.ASSERT (inTimeStamp > 0);
209                                 
210                                 if (inTimeStamp <= outTimeStamp)
211                                 {
212                                     if (log.atVERBOSE ()) log.verbose ("destination file [" + outFile + "] skipped: more recent than the source");
213                                     return;
214                                 }
215                             }
216                         }
217                     }
218                     
219                     readZipEntry (in, entry);
220                     
221                     final ClassDef clsDef = ClassDefParser.parseClass (m_readbuf, m_readpos);
222                     
223                     m_visitor.process (clsDef, m_outMode == OutMode.OUT_MODE_OVERWRITE, true, true, m_instrResult);
224                     if (m_instrResult.m_instrumented)
225                     {
226                         if ($assert.ENABLED) $assert.ASSERT (m_instrResult.m_descriptor != null, "no descriptor created for an instrumented class");
227                         
228                         ++ m_classInstrs;
229                         
230                         // update metadata [if this class has not been seen before]:
231

232                         m_mdata.add (m_instrResult.m_descriptor, false);
233                         
234                         // class def modified: write it to an array and submit a write job
235

236                         m_baos.reset ();
237                         ClassWriter.writeClassTable (clsDef, m_baos);
238                         
239                         if (notcopymode)
240                         {
241                             // [destination is a zip entry]
242

243                             entry.setTime (m_timeStamp);
244                             addJob (new EntryWriteJob (m_archiveOut, m_baos.copyByteArray (), entry, false));
245                         }
246                         else // copy mode
247
{
248                             // [destination is a file]
249

250                             if (! DO_DEPENDS_CHECKING) // this block is just a complement to the one above (where fullOutFile is inited)
251
{
252                                 outFile = new File JavaDoc (className.replace ('.', File.separatorChar).concat (".class"));
253                                 fullOutFile = getFullOutFile (null, outFile, IN_CLASSES);
254                             }
255                         
256                             addJob (new FileWriteJob (fullOutFile, m_baos.copyByteArray (), true));
257                         }
258                     }
259                     else if (notcopymode)
260                     {
261                         // original class def already read into m_readbuf:
262
// clone the array and submit an entry write job
263

264                         final byte [] data = new byte [m_readpos];
265                         System.arraycopy (m_readbuf, 0, data, 0, data.length);
266                         ++ m_classCopies;
267                         
268                         entry.setTime (m_timeStamp);
269                         addJob (new EntryWriteJob (m_archiveOut, data, entry, true));
270                     }
271                 }
272                 catch (FileNotFoundException JavaDoc fnfe)
273                 {
274                     // ignore: this should never happen
275
if ($assert.ENABLED)
276                     {
277                         fnfe.printStackTrace (System.out);
278                     }
279                 }
280                 catch (IOException JavaDoc ioe)
281                 {
282                     // TODO: error code
283
throw new EMMARuntimeException (ioe);
284                 }
285                 finally
286                 {
287                     if (clsin != null)
288                         try
289                         {
290                             clsin.close ();
291                         }
292                         catch (Exception JavaDoc e)
293                         {
294                             // TODO: error code
295
throw new EMMARuntimeException (e);
296                         }
297                 }
298             }
299             else
300             {
301                 // copy excluded .class entries in full copy and overwrite modes:
302
copyEntry = notcopymode;
303             }
304         }
305         else
306         {
307             // copy non-.class entries in full copy and overwrite modes:
308
copyEntry = notcopymode;
309             
310             // skipping these entries here is important: this is done as a complement
311
// to Sun jar API workarounds as detailed in PathEnumerator.enumeratePathArchive():
312

313             if (copyEntry && name.equalsIgnoreCase ("META-INF/"))
314                 copyEntry = false;
315             if (copyEntry && name.equalsIgnoreCase (JarFile.MANIFEST_NAME))
316                 copyEntry = false;
317                 
318             // TODO: skip signature-related entries (.SF and .RSA/.DSA/.PGP)
319
}
320         
321         if (copyEntry)
322         {
323             try
324             {
325                 readZipEntry (in, entry);
326                 
327                 final byte [] data = new byte [m_readpos];
328                 System.arraycopy (m_readbuf, 0, data, 0, data.length);
329                 ++ m_classCopies;
330                 
331                 entry.setTime (m_timeStamp);
332                 addJob (new EntryWriteJob (m_archiveOut, data, entry, true));
333             }
334             catch (IOException JavaDoc ioe)
335             {
336                 // TODO: error code
337
throw new EMMARuntimeException (ioe);
338             }
339         }
340     }
341         
342     public final void handleArchiveEnd (final File JavaDoc parentDir, final File JavaDoc archive)
343     {
344         final Logger log = m_log;
345         if (log.atTRACE2 ()) log.trace2 ("handleArchiveEnd", "[" + parentDir + "] [" + archive + "]");
346         
347         m_currentArchiveTS = Long.MAX_VALUE;
348         
349         if ((m_outMode == OutMode.OUT_MODE_FULLCOPY) || (m_outMode == OutMode.OUT_MODE_OVERWRITE))
350         {
351             try
352             {
353                 drainJobQueue (); // drain the queue before closing the archive
354

355                 m_archiveOut.flush ();
356                 m_archiveOut.close ();
357                 m_archiveOut = null;
358             }
359             catch (IOException JavaDoc ioe)
360             {
361                 // TODO: error code
362
throw new EMMARuntimeException (ioe);
363             }
364             
365             // in overwrite mode replace the original archive with the temp archive:
366

367             if (m_outMode == OutMode.OUT_MODE_OVERWRITE)
368             {
369                 if (! Files.renameFile (m_tempArchiveFile, m_origArchiveFile, true)) // overwrite the original archive
370
{
371                     // TODO: disable temp file cleanup in this case so that the user
372
// could do it manually later?
373

374                     // error code
375
throw new EMMARuntimeException ("could not rename temporary file [" + m_tempArchiveFile + "] to [" + m_origArchiveFile + "]: make sure the original file is not locked and can be deleted");
376                 }
377                 else
378                 {
379                     if (log.atTRACE2 ()) log.trace2 ("handleArchiveEnd", "renamed temp archive [" + m_tempArchiveFile.getAbsolutePath () + "] to [" + m_origArchiveFile + "]");
380                     m_origArchiveFile = m_tempArchiveFile = null;
381                 }
382             }
383         }
384     }
385
386
387     public final void handleDirStart (final File JavaDoc pathDir, final File JavaDoc dir)
388     {
389         final Logger log = m_log;
390         if (log.atTRACE2 ()) log.trace2 ("handleDirStart", "[" + pathDir + "] [" + dir + "]");
391         
392         // in full copy mode, create all dirs here; in copy mode, do it as part
393
// of writing each individual file:
394

395         if (m_outMode == OutMode.OUT_MODE_FULLCOPY)
396         {
397             final File JavaDoc saveDir = new File JavaDoc (getFullOutDir (pathDir, IN_CLASSES), dir.getPath ());
398             createDir (saveDir, true);
399         }
400     }
401
402     public final void handleFile (final File JavaDoc pathDir, final File JavaDoc file)
403     {
404         final Logger log = m_log;
405         if (log.atTRACE2 ()) log.trace2 ("handleFile", "[" + pathDir + "] [" + file + "]");
406         
407         final String JavaDoc name = file.getPath ();
408         final String JavaDoc lcName = name.toLowerCase ();
409
410         final boolean fullcopymode = (m_outMode == OutMode.OUT_MODE_FULLCOPY);
411         final boolean mkdir = (m_outMode == OutMode.OUT_MODE_COPY);
412         
413
414         boolean copyFile = false;
415
416         if (lcName.endsWith (".class"))
417         {
418             final String JavaDoc className = name.substring (0, name.length () - 6).replace (File.separatorChar, '.');
419             
420             // it is possible that a class with this name has already been processed;
421
// however, we can't skip it here because there is no guarantee that
422
// the runtime classpath will be identical to the instrumentation path
423

424             // [the metadata will still contain only a single entry for a class with
425
// this name: it is the responsibility of the user to ensure that both
426
// files represent the same class; in the future I might use a more
427
// robust internal strategy that uses something other than a class name
428
// as a metadata key]
429

430             if ((m_coverageFilter == null) || m_coverageFilter.included (className))
431             {
432                 InputStream JavaDoc clsin = null;
433                 try
434                 {
435                     final File JavaDoc inFile = Files.newFile (pathDir, file.getPath ());
436                     final File JavaDoc fullOutFile = getFullOutFile (pathDir, file, IN_CLASSES);
437                     
438                     if (DO_DEPENDS_CHECKING)
439                     {
440                         if (m_outMode == OutMode.OUT_MODE_COPY)
441                         {
442                             // if we already processed this class name within this instrumentor
443
// run, skip duplicates in copy mode:
444

445                             if (m_mdata.hasDescriptor (Descriptors.javaNameToVMName (className)))
446                                 return;
447                             
448                             // otherwise, instrument only if the dest file is out of date
449
// wrt to the source file:
450

451                             final long outTimeStamp = fullOutFile.lastModified (); // 0 if 'fullOutFile' does not exist or if an I/O error occurs
452

453                             if (outTimeStamp > 0)
454                             {
455                                 final long inTimeStamp = inFile.lastModified ();
456                                 
457                                 if (inTimeStamp <= outTimeStamp)
458                                 {
459                                     if (log.atVERBOSE ()) log.verbose ("destination file [" + fullOutFile + "] skipped: more recent that the source file");
460                                     return;
461                                 }
462                             }
463                         }
464                     }
465                     
466                     readFile (inFile);
467                     
468                     ClassDef clsDef = ClassDefParser.parseClass (m_readbuf, m_readpos);
469
470                     // in non-overwrite modes, bail if src file already instrumented:
471
m_visitor.process (clsDef, m_outMode == OutMode.OUT_MODE_OVERWRITE, true, true, m_instrResult);
472                     if (m_instrResult.m_instrumented)
473                     {
474                         if ($assert.ENABLED) $assert.ASSERT (m_instrResult.m_descriptor != null, "no descriptor created for an instrumented class");
475                         
476                         ++ m_classInstrs;
477                         
478                         // update metadata [if this class has not been seen before]:
479

480 // ObjectSizeProfiler.SizeProfile profile = ObjectSizeProfiler.profile (m_instrResult.m_descriptor, true);
481
// System.out.println (clsDef.getName () + " metadata:");
482
// System.out.println (profile.root ().dump (0.2));
483

484                         m_mdata.add (m_instrResult.m_descriptor, false);
485
486                         // class def modified: write it to an array and submit a write job
487

488                         m_baos.reset ();
489                         ClassWriter.writeClassTable (clsDef, m_baos);
490                         clsDef = null;
491                                                 
492                         final byte [] outdata = m_baos.copyByteArray ();
493                         
494                         addJob (new FileWriteJob (fullOutFile, outdata, mkdir));
495                     }
496                     else if (fullcopymode)
497                     {
498                         // original class def already read into m_readbuf:
499
// clone the array and submit a file write job
500

501                         clsDef = null;
502                         
503                         final byte [] outdata = new byte [m_readpos];
504                         System.arraycopy (m_readbuf, 0, outdata, 0, m_readpos);
505                         ++ m_classCopies;
506                         
507                         addJob (new FileWriteJob (fullOutFile, outdata, mkdir));
508                     }
509                 }
510                 catch (FileNotFoundException JavaDoc fnfe)
511                 {
512                     // ignore: this should never happen
513
if ($assert.ENABLED)
514                     {
515                         fnfe.printStackTrace (System.out);
516                     }
517                 }
518                 catch (IOException JavaDoc ioe)
519                 {
520                     // TODO: error code
521
throw new EMMARuntimeException (ioe);
522                 }
523                 finally
524                 {
525                     if (clsin != null)
526                         try
527                         {
528                             clsin.close ();
529                         }
530                         catch (Exception JavaDoc e)
531                         {
532                             // TODO: error code
533
throw new EMMARuntimeException (e);
534                         }
535                 }
536             }
537             else
538             {
539                 // copy excluded .class files in full copy mode:
540
copyFile = fullcopymode;
541             }
542         }
543         else
544         {
545             // copy non-.class files in full copy mode:
546
copyFile = fullcopymode;
547         }
548         
549         if (copyFile)
550         {
551             try
552             {
553                 final File JavaDoc inFile = Files.newFile (pathDir, file.getPath ());
554                 readFile (inFile);
555                 
556                 final byte [] data = new byte [m_readpos];
557                 System.arraycopy (m_readbuf, 0, data, 0, data.length);
558                 ++ m_classCopies;
559                 
560                 final File JavaDoc outFile = getFullOutFile (pathDir, file, IN_CLASSES);
561     
562                 addJob (new FileWriteJob (outFile, data, mkdir));
563             }
564             catch (IOException JavaDoc ioe)
565             {
566                 // TODO: error code
567
throw new EMMARuntimeException (ioe);
568             }
569         }
570     }
571
572     public final void handleDirEnd (final File JavaDoc pathDir, final File JavaDoc dir)
573     {
574         final Logger log = m_log;
575         if (log.atTRACE2 ()) log.trace2 ("handleDirEnd", "[" + pathDir + "] [" + dir + "]");
576         
577         // in overwrite mode, flush the job queue before going to the next directory:
578

579         if (m_outMode == OutMode.OUT_MODE_OVERWRITE)
580         {
581             try
582             {
583                 drainJobQueue ();
584             }
585             catch (IOException JavaDoc ioe)
586             {
587                 // TODO: error code
588
throw new EMMARuntimeException (ioe);
589             }
590         }
591     }
592     
593     // protected: .............................................................
594

595     
596     protected void reset ()
597     {
598         m_visitor = null;
599         m_mdata = null;
600         m_readbuf = null;
601         m_baos = null;
602  
603         for (int j = 0; j < m_jobs.length; ++ j) m_jobs [j] = null;
604                
605         if (CLEANUP_TEMP_ARCHIVE_ON_ERRORS)
606         {
607             if (m_archiveOut != null)
608                 try { m_archiveOut.close (); } catch (Exception JavaDoc ignore) {} // unlock the file descriptor for deletion
609

610             if (m_tempArchiveFile != null)
611                 m_tempArchiveFile.delete ();
612         }
613         
614         m_archiveOut = null;
615         m_origArchiveFile = null;
616         m_tempArchiveFile = null;
617         
618         super.reset ();
619     }
620     
621     protected void _run (final IProperties toolProperties)
622     {
623         final Logger log = m_log;
624
625         final boolean verbose = log.atVERBOSE ();
626         if (verbose)
627         {
628             log.verbose (IAppConstants.APP_VERBOSE_BUILD_ID);
629             
630             // [assertion: m_instrPath != null]
631
log.verbose ("instrumentation path:");
632             log.verbose ("{");
633             for (int p = 0; p < m_instrPath.length; ++ p)
634             {
635                 final File JavaDoc f = m_instrPath [p];
636                 final String JavaDoc nonexistent = f.exists () ? "" : "{nonexistent} ";
637                 
638                 log.verbose (" " + nonexistent + f.getAbsolutePath ());
639             }
640             log.verbose ("}");
641             
642             // [assertion: m_outMode != null]
643
log.verbose ("instrumentation output mode: " + m_outMode);
644         }
645         else
646         {
647             log.info ("processing instrumentation path ...");
648         }
649         
650         RuntimeException JavaDoc failure = null;
651         try
652         {
653             long start = System.currentTimeMillis ();
654             m_timeStamp = start;
655             
656             // construct instr path enumerator [throws on illegal input only]:
657
final IPathEnumerator enumerator = IPathEnumerator.Factory.create (m_instrPath, m_canonical, this);
658             
659             // create out dir(s):
660
{
661                 if (m_outMode != OutMode.OUT_MODE_OVERWRITE) createDir (m_outDir, true);
662                 
663                 if ((m_outMode == OutMode.OUT_MODE_FULLCOPY))
664                 {
665                     final File JavaDoc classesDir = Files.newFile (m_outDir, CLASSES);
666                     createDir (classesDir, false); // note: not using mkdirs() here
667

668                     final File JavaDoc libDir = Files.newFile (m_outDir, LIB);
669                     createDir (libDir, false); // note: not using mkdirs() here
670
}
671             }
672             
673             // get the data out settings [note: this is not conditioned on m_dumpRawData]:
674
File JavaDoc mdataOutFile = m_mdataOutFile;
675             Boolean JavaDoc mdataOutMerge = m_mdataOutMerge;
676             {
677                 if (mdataOutFile == null)
678                     mdataOutFile = new File JavaDoc (toolProperties.getProperty (EMMAProperties.PROPERTY_META_DATA_OUT_FILE,
679                                                                          EMMAProperties.DEFAULT_META_DATA_OUT_FILE));
680                 
681                 if (mdataOutMerge == null)
682                 {
683                     final String JavaDoc _dataOutMerge = toolProperties.getProperty (EMMAProperties.PROPERTY_META_DATA_OUT_MERGE,
684                                                                              EMMAProperties.DEFAULT_META_DATA_OUT_MERGE.toString ());
685                     mdataOutMerge = Property.toBoolean (_dataOutMerge) ? Boolean.TRUE : Boolean.FALSE;
686                 }
687             }
688             
689             if (verbose)
690             {
691                 log.verbose ("metadata output file: " + mdataOutFile.getAbsolutePath ());
692                 log.verbose ("metadata output merge mode: " + mdataOutMerge);
693             }
694                         
695             // TODO: can also register an exit hook to clean up temp files, but this is low value
696

697             // allocate I/O buffers:
698
m_readbuf = new byte [BUF_SIZE]; // don't reuse this across run() calls to reset it to the original size
699
m_readpos = 0;
700             m_baos = new ByteArrayOStream (BUF_SIZE); // don't reuse this across run() calls to reset it to the original size
701

702             // reset job queue position:
703
m_jobPos = 0;
704             
705             m_currentArchiveTS = Long.MAX_VALUE;
706
707             final CoverageOptions options = CoverageOptionsFactory.create (toolProperties);
708             m_visitor = new InstrVisitor (options); // TODO: reuse this?
709

710             m_mdata = DataFactory.newMetaData (options);
711             
712             // actual work is driven by the path enumerator:
713
try
714             {
715                 enumerator.enumerate ();
716                 drainJobQueue ();
717             }
718             catch (IOException JavaDoc ioe)
719             {
720                 throw new EMMARuntimeException (INSTR_IO_FAILURE, ioe);
721             }
722                        
723             if (log.atINFO ())
724             {
725                 final long end = System.currentTimeMillis ();
726                 
727                 log.info ("instrumentation path processed in " + (end - start) + " ms");
728                 log.info ("[" + m_classInstrs + " class(es) instrumented, " + m_classCopies + " resource(s) copied]");
729             }
730             
731             // persist metadata:
732
try
733             {
734                 // TODO: create an empty file earlier to catch any errors sooner? [to avoid scenarios where a user waits throught the entire instr run to find out the file could not be written to]
735

736                 if ($assert.ENABLED) $assert.ASSERT (mdataOutFile != null, "m_metadataOutFile is null");
737                 
738                 if (verbose)
739                 {
740                     if (m_mdata != null)
741                     {
742                         log.verbose ("metadata contains " + m_mdata.size () + " entries");
743                     }
744                 }
745                 
746                 if (m_mdata.isEmpty ())
747                 {
748                     log.info ("no output created: metadata is empty");
749                 }
750                 else
751                 {
752                     start = System.currentTimeMillis ();
753                     DataFactory.persist (m_mdata, mdataOutFile, mdataOutMerge.booleanValue ());
754                     final long end = System.currentTimeMillis ();
755                     
756                     if (log.atINFO ())
757                     {
758                         log.info ("metadata " + (mdataOutMerge.booleanValue () ? "merged into" : "written to") + " [" + mdataOutFile.getAbsolutePath () + "] {in " + (end - start) + " ms}");
759                     }
760                 }
761             }
762             catch (IOException JavaDoc ioe)
763             {
764                 throw new EMMARuntimeException (OUT_IO_FAILURE, new Object JavaDoc [] {mdataOutFile.getAbsolutePath ()}, ioe);
765             }
766         }
767         catch (SecurityException JavaDoc se)
768         {
769             failure = new EMMARuntimeException (SECURITY_RESTRICTION, new String JavaDoc [] {IAppConstants.APP_NAME}, se);
770         }
771         catch (RuntimeException JavaDoc re)
772         {
773             failure = re;
774         }
775         finally
776         {
777             reset ();
778         }
779         
780         if (failure != null)
781         {
782             if (Exceptions.unexpectedFailure (failure, EXPECTED_FAILURES))
783             {
784                 throw new EMMARuntimeException (UNEXPECTED_FAILURE,
785                                                 new Object JavaDoc [] {failure.toString (), IAppConstants.APP_BUG_REPORT_LINK},
786                                                 failure);
787             }
788             else
789                 throw failure;
790         }
791     }
792
793     // package: ...............................................................
794

795     
796     InstrProcessorST ()
797     {
798         m_jobs = new Job [JOB_QUEUE_SIZE];
799         m_instrResult = new InstrVisitor.InstrResult ();
800     }
801     
802     
803     static void writeFile (final byte [] data, final File JavaDoc outFile, final boolean mkdirs)
804         throws IOException JavaDoc
805     {
806         RandomAccessFile JavaDoc raf = null;
807         try
808         {
809             if (mkdirs)
810             {
811                 final File JavaDoc parent = outFile.getParentFile ();
812                 if (parent != null) parent.mkdirs (); // no error checking here [errors will be throw below]
813
}
814             
815             raf = new RandomAccessFile JavaDoc (outFile, "rw");
816             if (DO_RAF_EXTENSION) raf.setLength (data.length);
817             
818             raf.write (data);
819         }
820         finally
821         {
822             if (raf != null) raf.close (); // note: intentionally letting the exception percolate up
823
}
824     }
825     
826     static void writeZipEntry (final byte [] data, final ZipOutputStream JavaDoc out, final ZipEntry JavaDoc entry, final boolean isCopy)
827         throws IOException JavaDoc
828     {
829         if (isCopy)
830         {
831             out.putNextEntry (entry); // reusing ' entry' is ok here because we are not changing the data
832
try
833             {
834                 out.write (data);
835             }
836             finally
837             {
838                 out.closeEntry ();
839             }
840         }
841         else
842         {
843             // need to compute the checksum which slows things down quite a bit:
844

845             final ZipEntry JavaDoc entryCopy = new ZipEntry JavaDoc (entry.getName ());
846             entryCopy.setTime (entry.getTime ()); // avoid repeated calls to System.currentTimeMillis() inside the zip stream
847
entryCopy.setMethod (ZipOutputStream.STORED);
848             // [directory status is implicit in the name]
849
entryCopy.setSize (data.length);
850             entryCopy.setCompressedSize (data.length);
851             
852             final CRC32 JavaDoc crc = new CRC32 JavaDoc ();
853             crc.update (data);
854             entryCopy.setCrc (crc.getValue ());
855             
856             out.putNextEntry (entryCopy);
857             try
858             {
859                 out.write (data);
860             }
861             finally
862             {
863                 out.closeEntry ();
864             }
865         }
866     }
867             
868     // private: ...............................................................
869

870     
871     private static abstract class Job
872     {
873         protected abstract void run () throws IOException JavaDoc;
874         
875     } // end of nested class
876

877     
878     private static final class FileWriteJob extends Job
879     {
880         protected void run () throws IOException JavaDoc
881         {
882             writeFile (m_data, m_outFile, m_mkdirs);
883             m_data = null;
884         }
885         
886         FileWriteJob (final File JavaDoc outFile, final byte [] data, final boolean mkdirs)
887         {
888             m_outFile = outFile;
889             m_data = data;
890             m_mkdirs = mkdirs;
891         }
892         
893
894         final File JavaDoc m_outFile;
895         final boolean m_mkdirs;
896         byte [] m_data;
897         
898     } // end of nested class
899

900
901     private static final class EntryWriteJob extends Job
902     {
903         protected void run () throws IOException JavaDoc
904         {
905             writeZipEntry (m_data, m_out, m_entry, m_isCopy);
906             m_data = null;
907         }
908         
909         EntryWriteJob (final ZipOutputStream JavaDoc out, final byte [] data, final ZipEntry JavaDoc entry, final boolean isCopy)
910         {
911             m_out = out;
912             m_data = data;
913             m_entry = entry;
914             m_isCopy = isCopy;
915         }
916         
917
918         final ZipOutputStream JavaDoc m_out;
919         byte [] m_data;
920         final ZipEntry JavaDoc m_entry;
921         final boolean m_isCopy;
922         
923     } // end of nested class
924

925     
926     private void addJob (final Job job)
927         throws FileNotFoundException JavaDoc, IOException JavaDoc
928     {
929         if (m_jobPos == JOB_QUEUE_SIZE) drainJobQueue ();
930         
931         m_jobs [m_jobPos ++] = job;
932     }
933     
934     private void drainJobQueue ()
935         throws IOException JavaDoc
936     {
937         for (int j = 0; j < m_jobPos; ++ j)
938         {
939             final Job job = m_jobs [j];
940             if (job != null) // a guard just in case
941
{
942                 m_jobs [j] = null;
943                 job.run ();
944             }
945         }
946         
947         m_jobPos = 0;
948     }
949         
950     /*
951      * Reads into m_readbuf (m_readpos is updated correspondingly)
952      */

953     private void readFile (final File JavaDoc file)
954         throws IOException JavaDoc
955     {
956         final int length = (int) file.length ();
957         
958         ensureReadCapacity (length);
959         
960         InputStream JavaDoc in = null;
961         try
962         {
963             in = new FileInputStream JavaDoc (file);
964             
965             int totalread = 0;
966             for (int read;
967                  (totalread < length) && (read = in.read (m_readbuf, totalread, length - totalread)) >= 0;
968                  totalread += read);
969             m_readpos = totalread;
970         }
971         finally
972         {
973             if (in != null) try { in.close (); } catch (Exception JavaDoc ignore) {}
974         }
975     }
976     
977     /*
978      * Reads into m_readbuf (m_readpos is updated correspondingly)
979      */

980     private void readZipEntry (final ZipInputStream JavaDoc in, final ZipEntry JavaDoc entry)
981         throws IOException JavaDoc
982     {
983         final int length = (int) entry.getSize (); // can be -1 if unknown
984

985         if (length >= 0)
986         {
987             ensureReadCapacity (length);
988             
989             int totalread = 0;
990             for (int read;
991                  (totalread < length) && (read = in.read (m_readbuf, totalread, length - totalread)) >= 0;
992                  totalread += read);
993             m_readpos = totalread;
994         }
995         else
996         {
997             ensureReadCapacity (BUF_SIZE);
998             
999             m_baos.reset ();
1000            for (int read; (read = in.read (m_readbuf)) >= 0; m_baos.write (m_readbuf, 0, read));
1001            
1002            m_readbuf = m_baos.copyByteArray ();
1003            m_readpos = m_readbuf.length;
1004        }
1005    }
1006 
1007    private void ensureReadCapacity (final int capacity)
1008    {
1009        if (m_readbuf.length < capacity)
1010        {
1011            final int readbuflen = m_readbuf.length;
1012            m_readbuf = null;
1013            m_readbuf = new byte [Math.max (readbuflen << 1, capacity)];
1014        }
1015    }
1016    
1017    
1018    // internal run()-scoped state:
1019

1020    private final Job [] m_jobs;
1021    private final InstrVisitor.InstrResult m_instrResult;
1022    
1023    private InstrVisitor m_visitor;
1024    private IMetaData m_mdata;
1025    private byte [] m_readbuf;
1026    private int m_readpos;
1027    private ByteArrayOStream m_baos; // TODO: code to guard this from becoming too large
1028
private int m_jobPos;
1029    private long m_currentArchiveTS;
1030    private File JavaDoc m_origArchiveFile, m_tempArchiveFile;
1031    private JarOutputStream JavaDoc m_archiveOut;
1032    private long m_timeStamp;
1033    
1034    
1035    private static final int BUF_SIZE = 32 * 1024;
1036    private static final int JOB_QUEUE_SIZE = 128; // a reasonable size chosen empirically after testing a few SCSI/IDE machines
1037
private static final boolean CLEANUP_TEMP_ARCHIVE_ON_ERRORS = true;
1038    private static final boolean DO_RAF_EXTENSION = true;
1039    
1040    private static final boolean DO_DEPENDS_CHECKING = true;
1041    private static final Class JavaDoc [] EXPECTED_FAILURES; // set in <clinit>
1042

1043    static
1044    {
1045        EXPECTED_FAILURES = new Class JavaDoc []
1046        {
1047            EMMARuntimeException.class,
1048            IllegalArgumentException JavaDoc.class,
1049            IllegalStateException JavaDoc.class,
1050        };
1051    }
1052    
1053} // end of class
1054
// ----------------------------------------------------------------------------
Popular Tags