org.apache.tools.ant.taskdefs.optional.IContract Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of testatoo-container-jetty-full Show documentation
Show all versions of testatoo-container-jetty-full Show documentation
Testatoo Jetty Container with JSP support
/*
* Copyright 2001-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.tools.ant.taskdefs.optional;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Date;
import java.util.Properties;
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.taskdefs.Javac;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.taskdefs.Mkdir;
import org.apache.tools.ant.taskdefs.compilers.DefaultCompilerAdapter;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
/**
* Instruments Java classes with iContract DBC preprocessor.
*
* The task can generate a properties file for
* iControl,
* a graphical user interface that lets you turn on/off assertions.
* iControl generates a control file that you can refer to
* from this task using the controlfile attribute.
* iContract is at
*
* http://www.reliable-systems.com/tools/
*
* Thanks to Rainer Schmitz for enhancements and comments.
*
*
*
*
*
* Attribute
* Description
* Required
*
*
* srcdir
* Location of the java files.
* Yes
*
*
* instrumentdir
* Indicates where the instrumented source
* files should go.
* Yes
*
*
* repositorydir
* Indicates where the repository source
* files should go.
* Yes
*
*
* builddir
* Indicates where the compiled instrumented
* classes should go. Defaults to the value of
* instrumentdir.
*
* NOTE: Don't use the same directory for compiled
* instrumented classes and uninstrumented classes. It will break the
* dependency checking. (Classes will not be reinstrumented if you
* change them).
* No
*
*
* repbuilddir
* Indicates where the compiled repository classes
* should go. Defaults to the value of repositorydir.
* No
*
*
* pre
* Indicates whether or not to instrument for
* preconditions. Defaults to true
unless
* controlfile is specified, in which case it defaults
* to false
.
* No
*
*
* post
* Indicates whether or not to instrument for
* postconditions. Defaults to true
unless
* controlfile is specified, in which case it defaults
* to false
.
* No
*
*
* invariant
* Indicates whether or not to instrument for invariants.
* Defaults to true
unless controlfile is
* specified, in which case it defaults to
* false
.
* No
*
*
* failthrowable
* The full name of the Throwable (Exception) that
* should be thrown when an assertion is violated.
* Defaults to java.lang.Error
* No
*
*
* verbosity
* Indicates the verbosity level of iContract.
* Any combination of
* error*,warning*,note*,info*,progress*,debug*
* (comma separated) can be used. Defaults to error*
* No
*
*
* quiet
* Indicates if iContract should be quiet. Turn it off
* if many your classes extend uninstrumented classes and you don't
* want warnings about this. Defaults to false
* No
*
*
* updateicontrol
* If set to true, it indicates that the properties
* file for iControl in the current directory should be updated
* (or created if it doesn't exist). Defaults to false
.
*
* No
*
*
* controlfile
* The name of the control file to pass to iContract.
* Consider using iControl to generate the file.
* Default is not to pass a file.
*
* Only if updateicontrol=true
*
*
* classdir
* Indicates where compiled (unistrumented) classes are
* located. This is required in order to properly update
* the icontrol.properties file, not for instrumentation.
*
* Only if
* updateicontrol=true
*
*
* targets
* Name of the file that will be generated by this task,
* which lists all the classes that iContract will
* instrument. If specified, the file will not be deleted
* after execution. If not specified, a file will still
* be created, but it will be deleted after execution.
* No
*
*
*
*
* Note: iContract will use the java compiler indicated by the project's
* build.compiler
property. See documentation of the Javac task for
* more information.
*
* Nested includes and excludes are also supported.
*
* Example:
*
* <icontract
* srcdir="${build.src}"
* instrumentdir="${build.instrument}"
* repositorydir="${build.repository}"
* builddir="${build.instrclasses}"
* updateicontrol="true"
* classdir="${build.classes}"
* controlfile="control"
* targets="targets"
* verbosity="error*,warning*"
* quiet="true"
* >
* <classpath refid="compile-classpath"/>
* </icontract>
*
*
*/
public class IContract extends MatchingTask {
private static final String ICONTROL_PROPERTIES_HEADER
= "You might want to set classRoot to point to your normal "
+ "compilation class root directory.";
/** compiler to use for instrumenation */
private String icCompiler = "javac";
/** temporary file with file names of all java files to be instrumented */
private File targets = null;
/**
* will be set to true if any of the source files are newer than the
* instrumented files
*/
private boolean dirty = false;
/** set to true if the iContract jar is missing */
private boolean iContractMissing = false;
/** source file root */
private File srcDir = null;
/** instrumentation src root */
private File instrumentDir = null;
/** instrumentation build root */
private File buildDir = null;
/** repository src root */
private File repositoryDir = null;
/** repository build root */
private File repBuildDir = null;
/** classpath */
private Path classpath = null;
/** The class of the Throwable to be thrown on failed assertions */
private String failThrowable = "java.lang.Error";
/** The -v option */
private String verbosity = "error*";
/** The -q option */
private boolean quiet = false;
/** The -m option */
private File controlFile = null;
/** Indicates whether or not to instrument for preconditions */
private boolean pre = true;
private boolean preModified = false;
/** Indicates whether or not to instrument for postconditions */
private boolean post = true;
private boolean postModified = false;
/** Indicates whether or not to instrument for invariants */
private boolean invariant = true;
private boolean invariantModified = false;
/**
* Indicates whether or not to instrument all files regardless of timestamp
*
* Can't be explicitly set, is set if control file exists and is newer
* than any source file
*/
private boolean instrumentall = false;
/**
* Indicates the name of a properties file (intentionally for iControl)
* where the classpath property should be updated.
*/
private boolean updateIcontrol = false;
/** Regular compilation class root */
private File classDir = null;
/**
* Sets the source directory.
*
* @param srcDir the source directory
*/
public void setSrcdir(File srcDir) {
this.srcDir = srcDir;
}
/**
* Sets the class directory (uninstrumented classes).
*
* @param classDir the source directory
*/
public void setClassdir(File classDir) {
this.classDir = classDir;
}
/**
* Sets the instrumentation directory.
*
* @param instrumentDir the source directory
*/
public void setInstrumentdir(File instrumentDir) {
this.instrumentDir = instrumentDir;
if (this.buildDir == null) {
setBuilddir(instrumentDir);
}
}
/**
* Sets the build directory for instrumented classes.
*
* @param buildDir the build directory
*/
public void setBuilddir(File buildDir) {
this.buildDir = buildDir;
}
/**
* Sets the build directory for repository classes.
*
* @param repositoryDir the source directory
*/
public void setRepositorydir(File repositoryDir) {
this.repositoryDir = repositoryDir;
if (this.repBuildDir == null) {
setRepbuilddir(repositoryDir);
}
}
/**
* Sets the build directory for instrumented classes.
*
* @param repBuildDir the build directory
*/
public void setRepbuilddir(File repBuildDir) {
this.repBuildDir = repBuildDir;
}
/**
* Turns on/off precondition instrumentation.
*
* @param pre true turns it on
*/
public void setPre(boolean pre) {
this.pre = pre;
preModified = true;
}
/**
* Turns on/off postcondition instrumentation.
*
* @param post true turns it on
*/
public void setPost(boolean post) {
this.post = post;
postModified = true;
}
/**
* Turns on/off invariant instrumentation.
*
* @param invariant true turns it on
*/
public void setInvariant(boolean invariant) {
this.invariant = invariant;
invariantModified = true;
}
/**
* Sets the Throwable (Exception) to be thrown on assertion violation.
*
* @param clazz the fully qualified Throwable class name
*/
public void setFailthrowable(String clazz) {
this.failThrowable = clazz;
}
/**
* Sets the verbosity level of iContract. Any combination of
* error*,warning*,note*,info*,progress*,debug* (comma separated) can be
* used. Defaults to error*,warning*
*
* @param verbosity verbosity level
*/
public void setVerbosity(String verbosity) {
this.verbosity = verbosity;
}
/**
* Tells iContract to be quiet.
*
* @param quiet true if iContract should be quiet.
*/
public void setQuiet(boolean quiet) {
this.quiet = quiet;
}
/**
* Sets the name of the file where targets will be written. That is the
* file that tells iContract what files to process.
*
* @param targets the targets file name
*/
public void setTargets(File targets) {
this.targets = targets;
}
/**
* Sets the control file to pass to iContract.
*
* @param controlFile the control file
*/
public void setControlfile(File controlFile) {
if (!controlFile.exists()) {
log("WARNING: Control file " + controlFile.getAbsolutePath()
+ " doesn't exist. iContract will be run "
+ "without control file.");
}
this.controlFile = controlFile;
}
/**
* Sets the classpath to be used for invocation of iContract.
*
* @param path the classpath
*/
public void setClasspath(Path path) {
createClasspath().append(path);
}
/**
* Sets the classpath.
*
* @return the nested classpath element
* @todo this overwrites the classpath so only one
* effective classpath element would work. This
* is not how we do this elsewhere.
*/
public Path createClasspath() {
if (classpath == null) {
classpath = new Path(getProject());
}
return classpath;
}
/**
* Adds a reference to a classpath defined elsewhere.
*
* @param reference referenced classpath
*/
public void setClasspathRef(Reference reference) {
createClasspath().setRefid(reference);
}
/**
* If true, updates iControl properties file
*
* @param updateIcontrol true if iControl properties file should be
* updated
*/
public void setUpdateicontrol(boolean updateIcontrol) {
this.updateIcontrol = updateIcontrol;
}
/**
* Executes the task
*
* @exception BuildException if the instrumentation fails
*/
public void execute() throws BuildException {
preconditions();
scan();
if (dirty) {
// turn off assertions if we're using controlfile, unless they are not explicitly set.
boolean useControlFile = (controlFile != null) && controlFile.exists();
if (useControlFile && !preModified) {
pre = false;
}
if (useControlFile && !postModified) {
post = false;
}
if (useControlFile && !invariantModified) {
invariant = false;
}
// issue warning if pre,post or invariant is used together with controlfile
if ((pre || post || invariant) && controlFile != null) {
log("WARNING: specifying pre,post or invariant will "
+ "override control file settings");
}
// We want to be notified if iContract jar is missing.
// This makes life easier for the user who didn't understand
// that iContract is a separate library (duh!)
getProject().addBuildListener(new IContractPresenceDetector());
// Prepare the directories for iContract. iContract will make
// them if they don't exist, but for some reason I don't know,
// it will complain about the REP files afterwards
Mkdir mkdir = (Mkdir) getProject().createTask("mkdir");
mkdir.setDir(instrumentDir);
mkdir.execute();
mkdir.setDir(buildDir);
mkdir.execute();
mkdir.setDir(repositoryDir);
mkdir.execute();
// Set the classpath that is needed for regular Javac compilation
Path baseClasspath = createClasspath();
// Might need to add the core classes if we're not using
// Sun's Javac (like Jikes)
String compiler = getProject().getProperty("build.compiler");
ClasspathHelper classpathHelper = new ClasspathHelper(compiler);
classpathHelper.modify(baseClasspath);
// Create the classpath required to compile the sourcefiles
// BEFORE instrumentation
Path beforeInstrumentationClasspath = ((Path) baseClasspath.clone());
beforeInstrumentationClasspath.append(new Path(getProject(),
srcDir.getAbsolutePath()));
// Create the classpath required to compile the sourcefiles
// AFTER instrumentation
Path afterInstrumentationClasspath = ((Path) baseClasspath.clone());
afterInstrumentationClasspath.append(new Path(getProject(),
instrumentDir.getAbsolutePath()));
afterInstrumentationClasspath.append(new Path(getProject(),
repositoryDir.getAbsolutePath()));
afterInstrumentationClasspath.append(new Path(getProject(),
srcDir.getAbsolutePath()));
afterInstrumentationClasspath.append(new Path(getProject(),
buildDir.getAbsolutePath()));
// Create the classpath required to automatically compile the
// repository files
Path repositoryClasspath = ((Path) baseClasspath.clone());
repositoryClasspath.append(new Path(getProject(),
instrumentDir.getAbsolutePath()));
repositoryClasspath.append(new Path(getProject(),
srcDir.getAbsolutePath()));
repositoryClasspath.append(new Path(getProject(),
repositoryDir.getAbsolutePath()));
repositoryClasspath.append(new Path(getProject(),
buildDir.getAbsolutePath()));
// Create the classpath required for iContract itself
Path iContractClasspath = ((Path) baseClasspath.clone());
iContractClasspath.append(new Path(getProject(),
System.getProperty("java.home") + File.separator + ".."
+ File.separator + "lib" + File.separator + "tools.jar"));
iContractClasspath.append(new Path(getProject(),
srcDir.getAbsolutePath()));
iContractClasspath.append(new Path(getProject(),
repositoryDir.getAbsolutePath()));
iContractClasspath.append(new Path(getProject(),
instrumentDir.getAbsolutePath()));
iContractClasspath.append(new Path(getProject(),
buildDir.getAbsolutePath()));
// Create a forked java process
Java iContract = (Java) getProject().createTask("java");
iContract.setTaskName(getTaskName());
iContract.setFork(true);
iContract.setClassname("com.reliablesystems.iContract.Tool");
iContract.setClasspath(iContractClasspath);
// Build the arguments to iContract
StringBuffer args = new StringBuffer();
args.append(directiveString());
args.append("-v").append(verbosity).append(" ");
args.append("-b").append("\"").append(icCompiler);
args.append(" -classpath ").append(beforeInstrumentationClasspath);
args.append("\" ");
args.append("-c").append("\"").append(icCompiler);
args.append(" -classpath ").append(afterInstrumentationClasspath);
args.append(" -d ").append(buildDir).append("\" ");
args.append("-n").append("\"").append(icCompiler);
args.append(" -classpath ").append(repositoryClasspath);
args.append("\" ");
args.append("-d").append(failThrowable).append(" ");
args.append("-o").append(instrumentDir).append(File.separator);
args.append("@p").append(File.separator).append("@f.@e ");
args.append("-k").append(repositoryDir).append(File.separator);
args.append("@p ");
args.append(quiet ? "-q " : "");
// reinstrument everything if controlFile exists and is newer
// than any class
args.append(instrumentall ? "-a " : "");
args.append("@").append(targets.getAbsolutePath());
iContract.createArg().setLine(args.toString());
//System.out.println( "JAVA -classpath " + iContractClasspath
// + " com.reliablesystems.iContract.Tool " + args.toString() );
// update iControlProperties if it's set.
if (updateIcontrol) {
Properties iControlProps = new Properties();
try {
// to read existing propertiesfile
iControlProps.load(new FileInputStream("icontrol.properties"));
} catch (IOException e) {
log("File icontrol.properties not found. That's ok. "
+ "Writing a default one.");
}
iControlProps.setProperty("sourceRoot",
srcDir.getAbsolutePath());
iControlProps.setProperty("classRoot",
classDir.getAbsolutePath());
iControlProps.setProperty("classpath",
afterInstrumentationClasspath.toString());
iControlProps.setProperty("controlFile",
controlFile.getAbsolutePath());
iControlProps.setProperty("targetsFile",
targets.getAbsolutePath());
try {
// to read existing propertiesfile
iControlProps.store(new FileOutputStream("icontrol.properties"),
ICONTROL_PROPERTIES_HEADER);
log("Updated icontrol.properties");
} catch (IOException e) {
log("Couldn't write icontrol.properties.");
}
}
// do it!
int result = iContract.executeJava();
if (result != 0) {
if (iContractMissing) {
log("iContract can't be found on your classpath. "
+ "Your classpath is:");
log(classpath.toString());
log("If you don't have the iContract jar, go get it at "
+ "http://www.reliable-systems.com/tools/");
}
throw new BuildException("iContract instrumentation failed. "
+ "Code = " + result);
}
} else {
// not dirty
//log( "Nothing to do. Everything up to date." );
}
}
/** Checks that the required attributes are set. */
private void preconditions() throws BuildException {
if (srcDir == null) {
throw new BuildException("srcdir attribute must be set!",
getLocation());
}
if (!srcDir.exists()) {
throw new BuildException("srcdir \"" + srcDir.getPath()
+ "\" does not exist!", getLocation());
}
if (instrumentDir == null) {
throw new BuildException("instrumentdir attribute must be set!",
getLocation());
}
if (repositoryDir == null) {
throw new BuildException("repositorydir attribute must be set!",
getLocation());
}
if (updateIcontrol && classDir == null) {
throw new BuildException("classdir attribute must be specified "
+ "when updateicontrol=true!", getLocation());
}
if (updateIcontrol && controlFile == null) {
throw new BuildException("controlfile attribute must be specified "
+ "when updateicontrol=true!", getLocation());
}
}
/**
* Verifies whether any of the source files have changed. Done by
* comparing date of source/class files. The whole lot is "dirty" if at
* least one source file or the control file is newer than the
* instrumented files. If not dirty, iContract will not be executed.
* Also creates a temporary file with a list of the source files, that
* will be deleted upon exit.
*/
private void scan() throws BuildException {
long now = (new Date()).getTime();
DirectoryScanner ds = null;
ds = getDirectoryScanner(srcDir);
String[] files = ds.getIncludedFiles();
FileOutputStream targetOutputStream = null;
PrintStream targetPrinter = null;
boolean writeTargets = false;
try {
if (targets == null) {
targets = new File("targets");
log("Warning: targets file not specified. generating file: "
+ targets.getName());
writeTargets = true;
} else if (!targets.exists()) {
log("Specified targets file doesn't exist. generating file: "
+ targets.getName());
writeTargets = true;
}
if (writeTargets) {
log("You should consider using iControl to create a target file.");
targetOutputStream = new FileOutputStream(targets);
targetPrinter = new PrintStream(targetOutputStream);
}
for (int i = 0; i < files.length; i++) {
File srcFile = new File(srcDir, files[i]);
if (files[i].endsWith(".java")) {
// print the target, while we're at here. (Only if generatetarget=true).
if (targetPrinter != null) {
targetPrinter.println(srcFile.getAbsolutePath());
}
File classFile
= new File(buildDir, files[i].substring(0, files[i].indexOf(".java")) + ".class");
if (srcFile.lastModified() > now) {
log("Warning: file modified in the future: "
+ files[i], Project.MSG_WARN);
}
if (!classFile.exists() || srcFile.lastModified() > classFile.lastModified()) {
//log( "Found a file newer than the instrumentDir class file: "
// + srcFile.getPath() + " newer than " + classFile.getPath()
// + ". Running iContract again..." );
dirty = true;
}
}
}
if (targetPrinter != null) {
targetPrinter.flush();
targetPrinter.close();
}
} catch (IOException e) {
throw new BuildException("Could not create target file:" + e.getMessage());
}
// also, check controlFile timestamp
long controlFileTime = -1;
try {
if (controlFile != null) {
if (controlFile.exists() && buildDir.exists()) {
controlFileTime = controlFile.lastModified();
ds = getDirectoryScanner(buildDir);
files = ds.getIncludedFiles();
for (int i = 0; i < files.length; i++) {
File srcFile = new File(srcDir, files[i]);
if (files[i].endsWith(".class")) {
if (controlFileTime > srcFile.lastModified()) {
if (!dirty) {
log("Control file "
+ controlFile.getAbsolutePath()
+ " has been updated. "
+ "Instrumenting all files...");
}
dirty = true;
instrumentall = true;
}
}
}
}
}
} catch (Throwable t) {
throw new BuildException("Got an interesting exception:"
+ t.getMessage());
}
}
/**
* Creates the -m option based on the values of controlFile, pre, post and
* invariant.
*/
private final String directiveString() {
StringBuffer sb = new StringBuffer();
boolean comma = false;
boolean useControlFile = (controlFile != null) && controlFile.exists();
if (useControlFile || pre || post || invariant) {
sb.append("-m");
}
if (useControlFile) {
sb.append("@").append(controlFile);
comma = true;
}
if (pre) {
if (comma) {
sb.append(",");
}
sb.append("pre");
comma = true;
}
if (post) {
if (comma) {
sb.append(",");
}
sb.append("post");
comma = true;
}
if (invariant) {
if (comma) {
sb.append(",");
}
sb.append("inv");
}
sb.append(" ");
return sb.toString();
}
/**
* BuildListener that sets the iContractMissing flag to true if a message
* about missing iContract is missing. Used to indicate a more verbose
* error to the user, with advice about how to solve the problem
*
*/
private class IContractPresenceDetector implements BuildListener {
public void buildFinished(BuildEvent event) {
}
public void buildStarted(BuildEvent event) {
}
public void messageLogged(BuildEvent event) {
if ("java.lang.NoClassDefFoundError: com/reliablesystems/iContract/Tool".equals(event.getMessage())) {
iContractMissing = true;
}
}
public void targetFinished(BuildEvent event) {
}
public void targetStarted(BuildEvent event) {
}
public void taskFinished(BuildEvent event) {
}
public void taskStarted(BuildEvent event) {
}
}
/**
* This class is a helper to set correct classpath for other compilers,
* like Jikes. It reuses the logic from DefaultCompilerAdapter, which is
* protected, so we have to subclass it.
*
*/
private class ClasspathHelper extends DefaultCompilerAdapter {
private final String compiler;
public ClasspathHelper(String compiler) {
super();
this.compiler = compiler;
}
// make it public
public void modify(Path path) {
// depending on what compiler to use, set the
// includeJavaRuntime flag
if ("jikes".equals(compiler)) {
icCompiler = compiler;
includeJavaRuntime = true;
path.append(getCompileClasspath());
}
}
// dummy implementation. Never called
public void setJavac(Javac javac) {
}
public boolean execute() {
return true;
}
}
}