KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cobertura > instrument > Main


1 /*
2  * Cobertura - http://cobertura.sourceforge.net/
3  *
4  * Copyright (C) 2003 jcoverage ltd.
5  * Copyright (C) 2005 Mark Doliner
6  * Copyright (C) 2005 Joakim Erdfelt
7  * Copyright (C) 2005 Grzegorz Lukasik
8  * Copyright (C) 2006 John Lewis
9  * Contact information for the above is given in the COPYRIGHT file.
10  *
11  * Cobertura is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published
13  * by the Free Software Foundation; either version 2 of the License,
14  * or (at your option) any later version.
15  *
16  * Cobertura is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Cobertura; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
24  * USA
25  */

26
27 package net.sourceforge.cobertura.instrument;
28
29 import java.io.ByteArrayOutputStream JavaDoc;
30 import java.io.File JavaDoc;
31 import java.io.FileInputStream JavaDoc;
32 import java.io.FileNotFoundException JavaDoc;
33 import java.io.FileOutputStream JavaDoc;
34 import java.io.IOException JavaDoc;
35 import java.io.InputStream JavaDoc;
36 import java.io.OutputStream JavaDoc;
37 import java.util.ArrayList JavaDoc;
38 import java.util.Collection JavaDoc;
39 import java.util.Iterator JavaDoc;
40 import java.util.List JavaDoc;
41 import java.util.Vector JavaDoc;
42 import java.util.zip.ZipEntry JavaDoc;
43 import java.util.zip.ZipInputStream JavaDoc;
44 import java.util.zip.ZipOutputStream JavaDoc;
45
46 import net.sourceforge.cobertura.coveragedata.CoverageDataFileHandler;
47 import net.sourceforge.cobertura.coveragedata.ProjectData;
48 import net.sourceforge.cobertura.util.ArchiveUtil;
49 import net.sourceforge.cobertura.util.CommandLineBuilder;
50 import net.sourceforge.cobertura.util.Header;
51 import net.sourceforge.cobertura.util.IOUtil;
52 import net.sourceforge.cobertura.util.RegexUtil;
53
54 import org.apache.log4j.Logger;
55 import org.objectweb.asm.ClassReader;
56 import org.objectweb.asm.ClassWriter;
57
58 /**
59  * <p>
60  * Add coverage instrumentation to existing classes.
61  * </p>
62  *
63  * <h3>What does that mean, exactly?</h3>
64  * <p>
65  * It means Cobertura will look at each class you give it. It
66  * loads the bytecode into memory. For each line of source,
67  * Cobertura adds a few extra instructions. These instructions
68  * do the following:
69  * </p>
70  *
71  * <ol>
72  * <li>Get an instance of the ProjectData class.</li>
73  * <li>Call a method in this ProjectData class that increments
74  * a counter for this line of code.
75  * </ol>
76  *
77  * <p>
78  * After every line in a class has been "instrumented," Cobertura
79  * edits the bytecode for the class one more time and adds
80  * "implements net.sourceforge.cobertura.coveragedata.HasBeenInstrumented"
81  * This is basically just a flag used internally by Cobertura to
82  * determine whether a class has been instrumented or not, so
83  * as not to instrument the same class twice.
84  * </p>
85  */

86 public class Main
87 {
88
89     private static final Logger logger = Logger.getLogger(Main.class);
90
91     private File JavaDoc destinationDirectory = null;
92
93     private Collection JavaDoc ignoreRegexes = new Vector JavaDoc();
94
95     private ClassPattern classPattern = new ClassPattern();
96
97     private ProjectData projectData = null;
98
99     /**
100      * @param entry A zip entry.
101      * @return True if the specified entry has "class" as its extension,
102      * false otherwise.
103      */

104     private static boolean isClass(ZipEntry JavaDoc entry)
105     {
106         return entry.getName().endsWith(".class");
107     }
108
109     private boolean addInstrumentationToArchive(CoberturaFile file, InputStream JavaDoc archive,
110             OutputStream JavaDoc output) throws Exception JavaDoc
111     {
112         ZipInputStream JavaDoc zis = null;
113         ZipOutputStream JavaDoc zos = null;
114
115         try
116         {
117             zis = new ZipInputStream JavaDoc(archive);
118             zos = new ZipOutputStream JavaDoc(output);
119             return addInstrumentationToArchive(file, zis, zos);
120         }
121         finally
122         {
123             zis = (ZipInputStream JavaDoc)IOUtil.closeInputStream(zis);
124             zos = (ZipOutputStream JavaDoc)IOUtil.closeOutputStream(zos);
125         }
126     }
127
128     private boolean addInstrumentationToArchive(CoberturaFile file, ZipInputStream JavaDoc archive,
129             ZipOutputStream JavaDoc output) throws Exception JavaDoc
130     {
131         /*
132          * "modified" is returned and indicates that something was instrumented.
133          * If nothing is instrumented, the original entry will be used by the
134          * caller of this method.
135          */

136         boolean modified = false;
137         ZipEntry JavaDoc entry;
138         while ((entry = archive.getNextEntry()) != null)
139         {
140             try
141             {
142                 String JavaDoc entryName = entry.getName();
143
144                 /*
145                  * If this is a signature file then don't copy it,
146                  * but don't set modified to true. If the only
147                  * thing we do is strip the signature, just use
148                  * the original entry.
149                  */

150                 if (ArchiveUtil.isSignatureFile(entry.getName()))
151                 {
152                     continue;
153                 }
154                 ZipEntry JavaDoc outputEntry = new ZipEntry JavaDoc(entry.getName());
155                 outputEntry.setComment(entry.getComment());
156                 outputEntry.setExtra(entry.getExtra());
157                 outputEntry.setTime(entry.getTime());
158                 output.putNextEntry(outputEntry);
159
160                 // Read current entry
161
byte[] entryBytes = IOUtil
162                         .createByteArrayFromInputStream(archive);
163
164                 // Instrument embedded archives if a classPattern has been specified
165
if ((classPattern.isSpecified()) && ArchiveUtil.isArchive(entryName))
166                 {
167                     Archive archiveObj = new Archive(file, entryBytes);
168                     addInstrumentationToArchive(archiveObj);
169                     if (archiveObj.isModified())
170                     {
171                         modified = true;
172                         entryBytes = archiveObj.getBytes();
173                         outputEntry.setTime(System.currentTimeMillis());
174                     }
175                 }
176                 else if (isClass(entry) && classPattern.matches(entryName))
177                 {
178                     // Instrument class
179
ClassReader cr = new ClassReader(entryBytes);
180                     ClassWriter cw = new ClassWriter(true);
181                     ClassInstrumenter cv = new ClassInstrumenter(projectData,
182                             cw, ignoreRegexes);
183                     cr.accept(cv, false);
184
185                     // If class was instrumented, get bytes that define the
186
// class
187
if (cv.isInstrumented())
188                     {
189                         logger.debug("Putting instrumented entry: "
190                                 + entry.getName());
191                         entryBytes = cw.toByteArray();
192                         modified = true;
193                         outputEntry.setTime(System.currentTimeMillis());
194                     }
195                 }
196
197                 // Add entry to the output
198
output.write(entryBytes);
199                 output.closeEntry();
200                 archive.closeEntry();
201             }
202             catch (Exception JavaDoc e)
203             {
204                 logger.warn("Problems with archive entry: " + entry);
205                 throw e;
206             }
207             output.flush();
208         }
209         return modified;
210     }
211
212     private void addInstrumentationToArchive(Archive archive) throws Exception JavaDoc
213     {
214         InputStream JavaDoc in = null;
215         ByteArrayOutputStream JavaDoc out = null;
216         try
217         {
218             in = archive.getInputStream();
219             out = new ByteArrayOutputStream JavaDoc();
220             boolean modified = addInstrumentationToArchive(archive.getCoberturaFile(), in, out);
221
222             if (modified)
223             {
224                 out.flush();
225                 byte[] bytes = out.toByteArray();
226                 archive.setModifiedBytes(bytes);
227             }
228         }
229         finally
230         {
231             in = IOUtil.closeInputStream(in);
232             out = (ByteArrayOutputStream JavaDoc)IOUtil.closeOutputStream(out);
233         }
234     }
235
236     private void addInstrumentationToArchive(CoberturaFile archive)
237     {
238         logger.debug("Instrumenting archive " + archive.getAbsolutePath());
239
240         File JavaDoc outputFile = null;
241         ZipInputStream JavaDoc input = null;
242         ZipOutputStream JavaDoc output = null;
243         boolean modified = false;
244         try
245         {
246             // Open archive
247
try
248             {
249                 input = new ZipInputStream JavaDoc(new FileInputStream JavaDoc(archive));
250             }
251             catch (FileNotFoundException JavaDoc e)
252             {
253                 logger.warn("Cannot open archive file: "
254                         + archive.getAbsolutePath(), e);
255                 return;
256             }
257
258             // Open output archive
259
try
260             {
261                 // check if destination folder is set
262
if (destinationDirectory != null)
263                 {
264                     // if so, create output file in it
265
outputFile = new File JavaDoc(destinationDirectory, archive.getPathname());
266                 }
267                 else
268                 {
269                     // otherwise create output file in temporary location
270
outputFile = File.createTempFile(
271                             "CoberturaInstrumentedArchive", "jar");
272                     outputFile.deleteOnExit();
273                 }
274                 output = new ZipOutputStream JavaDoc(new FileOutputStream JavaDoc(outputFile));
275             }
276             catch (IOException JavaDoc e)
277             {
278                 logger.warn("Cannot open file for instrumented archive: "
279                         + archive.getAbsolutePath(), e);
280                 return;
281             }
282
283             // Instrument classes in archive
284
try
285             {
286                 modified = addInstrumentationToArchive(archive, input, output);
287             }
288             catch (Exception JavaDoc e)
289             {
290                 logger.warn("Cannot instrument archive: "
291                         + archive.getAbsolutePath(), e);
292                 return;
293             }
294         }
295         finally
296         {
297             input = (ZipInputStream JavaDoc)IOUtil.closeInputStream(input);
298             output = (ZipOutputStream JavaDoc)IOUtil.closeOutputStream(output);
299         }
300
301         // If destination folder was not set, overwrite orginal archive with
302
// instrumented one
303
if (modified && (destinationDirectory == null))
304         {
305             try
306             {
307                 logger.debug("Moving " + outputFile.getAbsolutePath() + " to "
308                         + archive.getAbsolutePath());
309                 IOUtil.moveFile(outputFile, archive);
310             }
311             catch (IOException JavaDoc e)
312             {
313                 logger.warn("Cannot instrument archive: "
314                         + archive.getAbsolutePath(), e);
315                 return;
316             }
317         }
318         if ((destinationDirectory != null) && (!modified))
319         {
320             outputFile.delete();
321         }
322     }
323
324     private void addInstrumentationToSingleClass(File JavaDoc file)
325     {
326         logger.debug("Instrumenting class " + file.getAbsolutePath());
327
328         InputStream JavaDoc inputStream = null;
329         ClassWriter cw;
330         ClassInstrumenter cv;
331         try
332         {
333             inputStream = new FileInputStream JavaDoc(file);
334             ClassReader cr = new ClassReader(inputStream);
335             cw = new ClassWriter(true);
336             cv = new ClassInstrumenter(projectData, cw, ignoreRegexes);
337             cr.accept(cv, false);
338         }
339         catch (Throwable JavaDoc t)
340         {
341             logger.warn("Unable to instrument file " + file.getAbsolutePath(),
342                     t);
343             return;
344         }
345         finally
346         {
347             inputStream = IOUtil.closeInputStream(inputStream);
348         }
349
350         OutputStream JavaDoc outputStream = null;
351         try
352         {
353             if (cv.isInstrumented())
354             {
355                 // If destinationDirectory is null, then overwrite
356
// the original, uninstrumented file.
357
File JavaDoc outputFile;
358                 if (destinationDirectory == null)
359                     outputFile = file;
360                 else
361                     outputFile = new File JavaDoc(destinationDirectory, cv
362                             .getClassName().replace('.', File.separatorChar)
363                             + ".class");
364
365                 File JavaDoc parentFile = outputFile.getParentFile();
366                 if (parentFile != null)
367                 {
368                     parentFile.mkdirs();
369                 }
370
371                 byte[] instrumentedClass = cw.toByteArray();
372                 outputStream = new FileOutputStream JavaDoc(outputFile);
373                 outputStream.write(instrumentedClass);
374             }
375         }
376         catch (Throwable JavaDoc t)
377         {
378             logger.warn("Unable to instrument file " + file.getAbsolutePath(),
379                     t);
380             return;
381         }
382         finally
383         {
384             outputStream = IOUtil.closeOutputStream(outputStream);
385         }
386     }
387
388     // TODO: Don't attempt to instrument a file if the outputFile already
389
// exists and is newer than the input file, and the output and
390
// input file are in different locations?
391
private void addInstrumentation(CoberturaFile coberturaFile)
392     {
393         if (coberturaFile.isClass() && classPattern.matches(coberturaFile.getPathname()))
394         {
395             addInstrumentationToSingleClass(coberturaFile);
396         }
397         else if (coberturaFile.isDirectory())
398         {
399             String JavaDoc[] contents = coberturaFile.list();
400             for (int i = 0; i < contents.length; i++)
401             {
402                 File JavaDoc relativeFile = new File JavaDoc(coberturaFile.getPathname(), contents[i]);
403                 CoberturaFile relativeCoberturaFile = new CoberturaFile(coberturaFile.getBaseDir(),
404                         relativeFile.toString());
405                 //recursion!
406
addInstrumentation(relativeCoberturaFile);
407             }
408         }
409     }
410
411     private void parseArguments(String JavaDoc[] args)
412     {
413         File JavaDoc dataFile = CoverageDataFileHandler.getDefaultDataFile();
414
415         // Parse our parameters
416
List JavaDoc filePaths = new ArrayList JavaDoc();
417         String JavaDoc baseDir = null;
418         for (int i = 0; i < args.length; i++)
419         {
420             if (args[i].equals("--basedir"))
421                 baseDir = args[++i];
422             else if (args[i].equals("--datafile"))
423                 dataFile = new File JavaDoc(args[++i]);
424             else if (args[i].equals("--destination"))
425                 destinationDirectory = new File JavaDoc(args[++i]);
426             else if (args[i].equals("--ignore"))
427             {
428                 RegexUtil.addRegex(ignoreRegexes, args[++i]);
429             }
430             else if (args[i].equals("--includeClasses"))
431             {
432                 classPattern.addIncludeClassesRegex(args[++i]);
433             }
434             else if (args[i].equals("--excludeClasses"))
435             {
436                 classPattern.addExcludeClassesRegex(args[++i]);
437             }
438             else
439             {
440                 CoberturaFile coberturaFile = new CoberturaFile(baseDir, args[i]);
441                 filePaths.add(coberturaFile);
442             }
443         }
444
445         // Load coverage data
446
if (dataFile.isFile())
447             projectData = CoverageDataFileHandler.loadCoverageData(dataFile);
448         if (projectData == null)
449             projectData = new ProjectData();
450         
451         // Instrument classes
452
System.out.println("Instrumenting " + filePaths.size() + " "
453                 + (filePaths.size() == 1 ? "file" : "files")
454                 + (destinationDirectory != null ? " to "
455                         + destinationDirectory.getAbsoluteFile() : ""));
456
457         Iterator JavaDoc iter = filePaths.iterator();
458         while (iter.hasNext())
459         {
460             CoberturaFile coberturaFile = (CoberturaFile)iter.next();
461             if (coberturaFile.isArchive())
462             {
463                 addInstrumentationToArchive(coberturaFile);
464             }
465             else
466             {
467                 addInstrumentation(coberturaFile);
468             }
469         }
470
471         // Save coverage data
472
CoverageDataFileHandler.saveCoverageData(projectData, dataFile);
473     }
474
475     public static void main(String JavaDoc[] args)
476     {
477         Header.print(System.out);
478
479         long startTime = System.currentTimeMillis();
480
481         Main main = new Main();
482
483         try {
484             args = CommandLineBuilder.preprocessCommandLineArguments( args);
485         } catch( Exception JavaDoc ex) {
486             System.err.println( "Error: Cannot process arguments: " + ex.getMessage());
487             System.exit(1);
488         }
489         main.parseArguments(args);
490
491         long stopTime = System.currentTimeMillis();
492         System.out.println("Instrument time: " + (stopTime - startTime) + "ms");
493     }
494
495 }
496
Popular Tags