/* * $Id$ * * Copyright 2006, The jCoderZ.org Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the jCoderZ.org Project nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jcoderz.phoenix.jcoverage; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import net.sourceforge.cobertura.instrument.Main; import org.jcoderz.commons.util.FileUtils; import org.jcoderz.commons.util.JarUtils; /** * * @author Michael Griffel */ public final class Instrumenter { private static final String CLASSNAME = Instrumenter.class.getName(); private static final Logger logger = Logger.getLogger(CLASSNAME); private final ConfigurationParameters mConfig; private final File mBaseTempDir = new File(System.getProperty("java.io.tmpdir")); /** * Constructor. * @param config the configuration parameters. */ public Instrumenter (ConfigurationParameters config) { mConfig = config; } /** * Main method. * @param args command line arguments. */ public static void main (String[] args) throws Exception { Main.main(args); logger.info("Instrumentation of '" + args[0] + "' done."); } private static void parseArguments (String[] args) { // final List arguments = new ArrayList(); try { for (int i = 0; i < args.length; ) { logger.info("Parsing argument '" + args[i] + "' = '" + args[i + 1] + "'"); if (args[i].equals("-jcoverage")) { // FIXME: result.setJcoverage(new File(args[i + 1])); } else if (args[i].equals("-log4j")) { // FIXME: result.setLog4j(new File(args[i + 1])); } else if (args[i].equals("-archive")) { // FIXME: result.setArchive(new File(args[i + 1])); } else if (args[i].equals("-loglevel")) { Logger.getLogger("").setLevel(Level.parse(args[i + 1])); } else { throw new IllegalArgumentException("Invalid argument '" + args[i] + "'"); } ++i; ++i; } } catch (IndexOutOfBoundsException e) { final IllegalArgumentException ex = new IllegalArgumentException( "Missing value for " + args[args.length - 1]); ex.initCause(e); throw ex; } // FIXME: checkFileExists(result.getArchive()); // FIXME: checkFileExists(result.getLog4j()); // FIXME: checkFileExists(result.getJcoverage()); } /** * Checks if the given file exists. * @param f the file to check * @throws IllegalArgumentException if the file does not exists. */ private static void checkFileExists (File f) { if (!f.exists()) { throw new IllegalArgumentException("The file '" + f.getAbsolutePath() + "' does not exists."); } } /** * Run JCoverage instrumentation on the archive file. * @throws IOException in case of an I/O error. */ public void instrument () throws IOException { instrument(mConfig.getArchive()); } /** * Run JCoverage instrumentation on file <code>f</code>. * @param f file to instrument. * @throws IOException in case of an I/O error. */ public void instrument (File f) throws IOException { if (isArchive(f)) { final File tempDir = FileUtils.createTempDir(mBaseTempDir, f.getName()); JarUtils.extractJarArchive(tempDir, f); instrument(tempDir); if (!f.delete()) { throw new IOException("Cannot remove file " + f); } JarUtils.createJarArchive(tempDir, f); FileUtils.rmdir(tempDir); logger.info("Creating new jar " + f); } else if (f.isDirectory()) { instrumentClasses(f); findNestedArchives(f); } } /** * @param f */ private void instrumentClasses (File f) throws IOException { final List classPath = new ArrayList(); findClassPath(f, classPath); final List classFiles = new ArrayList(); for (final Iterator pathEntrys = classPath.iterator(); pathEntrys.hasNext();) { final File originDir = (File) pathEntrys.next(); classFiles.clear(); findClassFiles(originDir, originDir, classFiles); final File tmpDir = FileUtils.createTempDir( mBaseTempDir, "instrumented-classes"); instrument(classFiles, originDir, tmpDir); // move back to origin basedir FileUtils.copySlashStar(tmpDir, originDir); // copy coverage file to jar location // the jcoverage*ser files are written to the current working directory // of the JVM! /* final List jcoverageFiles = FileUtils.findFile( new File("."), "jcoverage.*\\.ser"); for (final Iterator iterator = jcoverageFiles.iterator(); iterator.hasNext();) { final File file = (File) iterator.next(); FileUtils.copy(file, originDir); } */ // add jcoverage.jar/log4j classes JarUtils.extractJarArchive(originDir, mConfig.getJcoverage()); JarUtils.extractJarArchive(originDir, mConfig.getLog4j()); FileUtils.rmdir(tmpDir); } } /** * Calls the Instrument class from the jcoverage package. * * @param classFiles * @param classpath * @param destinationDir * @throws IOException */ private void instrument ( List classFiles, File classpath, File destinationDir) throws IOException { // Instrumentation final List args = new ArrayList(); args.add("-d"); args.add(destinationDir.getCanonicalPath()); args.add("-ignore"); args.add("org.apache.log4j.*"); args.add("-basedir"); args.add(classpath.getCanonicalPath()); args.addAll(classFiles); logger.finest("Instrument with the following args: " + args); Main.main((String[]) args.toArray(new String[0])); } /** * @param pathEntry * @param pathEntry2 * @param classFiles */ private void findClassFiles (File basePath, File file, List classFiles) throws IOException { if (file.isFile()) { final String className = FileUtils.getRelativePath(basePath, file); if (className.endsWith(".class") && className.indexOf('$') == -1 && className.matches(".*org.jcoderz.*")) { //className = className.substring( // 0, className.length() - ".class".length()); /* if (File.separator.equals("\\")) { className = className.replaceAll("\\\\", "."); } else { className = className.replaceAll(File.separator, "."); } */ classFiles.add(className); } } else if (file.isDirectory()) { final File [] files = file.listFiles(); for (int i = 0; files != null && i < files.length; i++) { findClassFiles(basePath, files[i], classFiles); } } } /** * @param f * @param classPath */ private void findClassPath (File f, List classPath) { final File [] files = f.listFiles(new FileFilter() { public boolean accept (File pathname) { return pathname.isDirectory(); } }); boolean found = false; for (int i = 0; !found && i < files.length; i++) { if (files[i].getName().equals("com") || files[i].getName().equals("junit")) { classPath.add(files[i].getParentFile()); found = true; } } if (!found) { for (int i = 0; i < files.length; i++) { findClassPath(files[i], classPath); } } } /** * @param f */ private void findNestedArchives (File file) throws IOException { if (file == null) { // done... } else if (file.isDirectory()) { final File [] files = file.listFiles(); for (int i = 0; i < files.length; i++) { findNestedArchives(files[i]); } } else { if (isArchive(file)) { instrument(file); } } } private boolean isArchive (File file) throws IOException { return file.getCanonicalPath().matches(".*\\.[jerw]ar"); } private static class ConfigurationParameters { private File mArchive = null; private File mLog4j = null; private File mJcoverage = null; /** * Returns the archive. * @return the archive. */ public final File getArchive () { return mArchive; } /** * Sets the archive to given <code>archive</code>. * @param archive The archive to set. */ public final void setArchive (File archive) { mArchive = archive; } /** * Returns the jcoverage. * @return the jcoverage. */ public final File getJcoverage () { return mJcoverage; } /** * Sets the jcoverage to given <code>jcoverage</code>. * @param jcoverage The jcoverage to set. */ public final void setJcoverage (File jcoverage) { mJcoverage = jcoverage; } /** * Returns the log4j. * @return the log4j. */ public final File getLog4j () { return mLog4j; } /** * Sets the log4j to given <code>log4j</code>. * @param log4j The log4j to set. */ public final void setLog4j (File log4j) { mLog4j = log4j; } } }