Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Package refactoring of Cli, Ant, Maven #69

Merged
merged 6 commits into from
Sep 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
<property name="documentation-dir" location="build/docs"/>
<property name="test-results-dir" location="build/test-results"/>

<property name="cli-classname" value="de.thetaphi.forbiddenapis.CliMain"/>
<property name="cli-classname" value="de.thetaphi.forbiddenapis.cli.CliMain"/>

<!-- define Maven coordinates -->
<property name="groupId" value="de.thetaphi" />
Expand Down Expand Up @@ -333,7 +333,7 @@
<zips><fileset refid="fileset.bundle"/></zips>
</archives>
</restrict>
<keep pattern="de.thetaphi.forbiddenapis.*"/>
<keep pattern="de.thetaphi.forbiddenapis.**"/>
<rule pattern="org.objectweb.asm.**" result="de.thetaphi.forbiddenapis.asm.@1"/>
<rule pattern="org.apache.commons.**" result="de.thetaphi.forbiddenapis.commons.@1"/>
<rule pattern="org.codehaus.plexus.**" result="de.thetaphi.forbiddenapis.plexus.@1"/>
Expand Down Expand Up @@ -424,7 +424,7 @@
description="Checks bundled signatures file corresponding to the current JVM. Run after every update!"/>

<target name="-install-forbiddenapi-task" depends="compile" unless="installed.forbiddenapi-task">
<taskdef name="forbiddenapis" classname="de.thetaphi.forbiddenapis.AntTask" classpathref="path.main-run"/>
<taskdef name="forbiddenapis" classname="de.thetaphi.forbiddenapis.ant.AntTask" classpathref="path.main-run"/>
<property name="installed.forbiddenapi-task" value="true"/>
</target>

Expand Down
318 changes: 8 additions & 310 deletions src/main/java/de/thetaphi/forbiddenapis/AntTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,325 +18,23 @@
* limitations under the License.
*/

import static de.thetaphi.forbiddenapis.Checker.Option.*;
import java.util.Locale;

import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectComponent;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.FileList;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.resources.FileResource;
import org.apache.tools.ant.types.resources.Resources;
import org.apache.tools.ant.types.resources.StringResource;

import java.io.IOException;
import java.io.File;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

/**
* Task to check if a set of class files contains calls to forbidden APIs
* from a given classpath and list of API signatures (either inline or as pointer to files).
* In contrast to other ANT tasks, this tool does only visit the given classpath
* and the system classloader, not ANT's class loader.
* {@inheritDoc}
* @deprecated Use {@link de.thetaphi.forbiddenapis.ant.AntTask} instead.
*/
public final class AntTask extends Task {

private final Resources classFiles = new Resources();
private final Resources apiSignatures = new Resources();
private final List<BundledSignaturesType> bundledSignatures = new ArrayList<BundledSignaturesType>();
private final List<SuppressAnnotationType> suppressAnnotations = new ArrayList<SuppressAnnotationType>();
private Path classpath = null;
@Deprecated
public final class AntTask extends de.thetaphi.forbiddenapis.ant.AntTask {

private boolean failOnUnsupportedJava = false;
private boolean internalRuntimeForbidden = false;
private boolean restrictClassFilename = true;
private boolean failOnMissingClasses = true;
private boolean failOnUnresolvableSignatures = true;
private boolean failOnViolation = true;
private boolean ignoreEmptyFileset = false;

@Override
public void execute() throws BuildException {
AntClassLoader antLoader = null;
try {
final ClassLoader loader;
if (classpath != null) {
classpath.setProject(getProject());
loader = antLoader = getProject().createClassLoader(ClassLoader.getSystemClassLoader(), classpath);
antLoader.setParentFirst(true); // use default classloader delegation
} else {
loader = ClassLoader.getSystemClassLoader();
}
classFiles.setProject(getProject());
apiSignatures.setProject(getProject());

final EnumSet<Checker.Option> options = EnumSet.noneOf(Checker.Option.class);
if (internalRuntimeForbidden) options.add(INTERNAL_RUNTIME_FORBIDDEN);
if (failOnMissingClasses) options.add(FAIL_ON_MISSING_CLASSES);
if (failOnViolation) options.add(FAIL_ON_VIOLATION);
if (failOnUnresolvableSignatures) options.add(FAIL_ON_UNRESOLVABLE_SIGNATURES);
final Checker checker = new Checker(new Logger() {
public void error(String msg) {
log(msg, Project.MSG_ERR);
}

public void warn(String msg) {
// ANT has no real log levels printed, so prefix with "WARNING":
log("WARNING: " + msg, Project.MSG_WARN);
}

public void info(String msg) {
log(msg, Project.MSG_INFO);
}
}, loader, options);

if (!checker.isSupportedJDK) {
final String msg = String.format(Locale.ENGLISH,
"Your Java runtime (%s %s) is not supported by <%s/>. Please run the checks with a supported JDK!",
System.getProperty("java.runtime.name"), System.getProperty("java.runtime.version"), getTaskName());
if (failOnUnsupportedJava) {
throw new BuildException(msg);
} else {
log("WARNING: " + msg, Project.MSG_WARN);
return;
}
}

for (final SuppressAnnotationType a : suppressAnnotations) {
checker.addSuppressAnnotation(a.getClassname());
}

try {
for (BundledSignaturesType bs : bundledSignatures) {
final String name = bs.getName();
if (name == null) {
throw new BuildException("<bundledSignatures/> must have the mandatory attribute 'name' referring to a bundled signatures file.");
}
log("Reading bundled API signatures: " + name, Project.MSG_INFO);
checker.parseBundledSignatures(name, null);
}

@SuppressWarnings("unchecked")
final Iterator<Resource> iter = apiSignatures.iterator();
while (iter.hasNext()) {
final Resource r = iter.next();
if (r instanceof StringResource) {
final String s = ((StringResource) r).getValue();
if (s != null && s.trim().length() > 0) {
log("Reading inline API signatures...", Project.MSG_INFO);
checker.parseSignaturesString(s);
}
} else {
log("Reading API signatures: " + r, Project.MSG_INFO);
checker.parseSignaturesFile(r.getInputStream());
}
}
} catch (IOException ioe) {
throw new BuildException("IO problem while reading files with API signatures: " + ioe);
} catch (ParseException pe) {
throw new BuildException("Parsing signatures failed: " + pe.getMessage());
}

if (checker.hasNoSignatures()) {
throw new BuildException("No API signatures found; use signaturesFile=, <signaturesFileSet/>, <bundledSignatures/> or inner text to define those!");
}

log("Loading classes to check...", Project.MSG_INFO);
try {
@SuppressWarnings("unchecked")
final Iterator<Resource> iter = classFiles.iterator();
boolean foundClass = false;
while (iter.hasNext()) {
final Resource r = iter.next();
final String name = r.getName();
if (restrictClassFilename && name != null && !name.endsWith(".class")) {
continue;
}
checker.addClassToCheck(r.getInputStream());
foundClass = true;
}
if (!foundClass) {
if (ignoreEmptyFileset) {
log("There is no <fileset/> or other resource collection given, or the collection does not contain any class files to check.", Project.MSG_WARN);
log("Scanned 0 class files.", Project.MSG_INFO);
return;
} else {
throw new BuildException("There is no <fileset/> or other resource collection given, or the collection does not contain any class files to check.");
}
}
} catch (IOException ioe) {
throw new BuildException("Failed to load one of the given class files: " + ioe);
}

log("Scanning for API signatures and dependencies...", Project.MSG_INFO);
try {
checker.run();
} catch (ForbiddenApiException fae) {
throw new BuildException(fae.getMessage());
}
} finally {
if (antLoader != null) antLoader.cleanup();
}
log(String.format(Locale.ENGLISH, "DEPRECATED-WARNING: Please change your build.xml to use new task class '%s'",
getClass().getSuperclass().getName()), Project.MSG_WARN);
super.execute();
}

/** Set of class files to check */
public void add(ResourceCollection rc) {
classFiles.add(rc);
}

/** Sets a directory as base for class files. The implicit pattern '**&#47;*.class' is used to only scan class files. */
public void setDir(File dir) {
final FileSet fs = new FileSet();
fs.setProject(getProject());
fs.setDir(dir);
// needed if somebody sets restrictClassFilename=false:
fs.setIncludes("**/*.class");
classFiles.add(fs);
}

private <T extends ResourceCollection> T addSignaturesResource(T res) {
((ProjectComponent) res).setProject(getProject());
apiSignatures.add(res);
return res;
}

/** Set of files with API signatures as <signaturesFileSet/> nested element */
public FileSet createSignaturesFileSet() {
return addSignaturesResource(new FileSet());
}

/** List of files with API signatures as <signaturesFileList/> nested element */
public FileList createSignaturesFileList() {
return addSignaturesResource(new FileList());
}

/** Single file with API signatures as <signaturesFile/> nested element */
public FileResource createSignaturesFile() {
return addSignaturesResource(new FileResource());
}

/** A file with API signatures signaturesFile= attribute */
public void setSignaturesFile(File file) {
createSignaturesFile().setFile(file);
}

/** Support for API signatures list as nested text */
public void addText(String text) {
addSignaturesResource(new StringResource(text));
}

/** Creates a bundled signatures instance */
public BundledSignaturesType createBundledSignatures() {
final BundledSignaturesType s = new BundledSignaturesType();
s.setProject(getProject());
bundledSignatures.add(s);
return s;
}

/** A bundled signatures name */
public void setBundledSignatures(String name) {
createBundledSignatures().setName(name);
}

/** Creates a instance of an annotation class name that suppresses error reporting in classes/methods/fields. */
public SuppressAnnotationType createSuppressAnnotation() {
final SuppressAnnotationType s = new SuppressAnnotationType();
s.setProject(getProject());
suppressAnnotations.add(s);
return s;
}

/** Class name of annotation that suppresses error reporting in classes/methods/fields. */
public void setSuppressAnnotation(String classname) {
createSuppressAnnotation().setClassname(classname);
}

/** Classpath as classpath= attribute */
public void setClasspath(Path classpath) {
createClasspath().append(classpath);
}

/** Classpath as classpathRef= attribute */
public void setClasspathRef(Reference r) {
createClasspath().setRefid(r);
}

/** Classpath as <classpath/> nested element */
public Path createClasspath() {
if (this.classpath == null) {
this.classpath = new Path(getProject());
}
return this.classpath.createPath();
}

/**
* Fail the build, if the bundled ASM library cannot read the class file format
* of the runtime library or the runtime library cannot be discovered.
* Defaults to {@code false}.
*/
public void setFailOnUnsupportedJava(boolean failOnUnsupportedJava) {
this.failOnUnsupportedJava = failOnUnsupportedJava;
}

/**
* Fail the build, if a referenced class is missing. This requires
* that you pass the whole classpath including all dependencies.
* If you don't have all classes in the filesets, the application classes
* must be reachable through this classpath, too.
* Defaults to {@code true}.
*/
public void setFailOnMissingClasses(boolean failOnMissingClasses) {
this.failOnMissingClasses = failOnMissingClasses;
}

/**
* Fail the build if a signature is not resolving. If this parameter is set to
* to false, then such signatures are silently ignored.
* Defaults to {@code true}.
*/
public void setFailOnUnresolvableSignatures(boolean failOnUnresolvableSignatures) {
this.failOnUnresolvableSignatures = failOnUnresolvableSignatures;
}

/**
* Forbids calls to classes from the internal java runtime (like sun.misc.Unsafe)
* Defaults to {@code false}.
*/
public void setInternalRuntimeForbidden(boolean internalRuntimeForbidden) {
this.internalRuntimeForbidden = internalRuntimeForbidden;
}

/** Automatically restrict resource names included to files with a name ending in '.class'.
* This makes filesets easier, as the includes="**&#47;*.class" is not needed.
* Defaults to {@code true}.
*/
public void setRestrictClassFilename(boolean restrictClassFilename) {
this.restrictClassFilename = restrictClassFilename;
}

/** Ignore empty fileset/resource collection and print a warning instead.
* Defaults to {@code false}.
*/
public void setIgnoreEmptyFileSet(boolean ignoreEmptyFileset) {
this.ignoreEmptyFileset = ignoreEmptyFileset;
}

/**
* Fail the build if violations have been found. If this parameter is set to {@code false},
* then the build will continue even if violations have been found.
* Defaults to {@code true}.
*/
public void setFailOnViolation(boolean failOnViolation) {
this.failOnViolation = failOnViolation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/

@SuppressWarnings("serial")
public final class WrapperRuntimeException extends RuntimeException {
final class WrapperRuntimeException extends RuntimeException {

public WrapperRuntimeException(Exception e) {
super(e);
Expand Down
Loading