Package de.thetaphi.forbiddenapis

Source Code of de.thetaphi.forbiddenapis.AbstractCheckMojo

package de.thetaphi.forbiddenapis;

/*
* (C) Copyright 2013 Uwe Schindler (Generics Policeman) and others.
*
* 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.
*/

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Parameter;
import org.codehaus.plexus.util.DirectoryScanner;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLClassLoader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

/**
* Base class for forbiddenapis Mojos.
* @since 1.0
*/
public abstract class AbstractCheckMojo extends AbstractMojo {

  /**
   * Lists all files, which contain signatures and comments for forbidden API calls.
   * The signatures are resolved against the compile classpath.
   * @since 1.0
   */
  @Parameter(required = false)
  private File[] signaturesFiles;

  /**
   * Gives a multiline list of signatures, inline in the pom.xml. Use an XML CDATA section to do that!
   * The signatures are resolved against the compile classpath.
   * @since 1.0
   */
  @Parameter(required = false)
  private String signatures;

  /**
   * Specifies built in signatures files (e.g., deprecated APIs for specific Java versions,
   * unsafe method calls using default locale, default charset,...)
   * @since 1.0
   */
  @Parameter(required = false)
  private String[] bundledSignatures;

  /**
   * Forbids calls to classes from the internal java runtime (like sun.misc.Unsafe)
   * @since 1.0
   */
  @Parameter(required = false, defaultValue = "false")
  private boolean internalRuntimeForbidden;

  /**
   * 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.
   * @since 1.0
   */
  @Parameter(required = false, defaultValue = "false")
  private boolean failOnUnsupportedJava;
 
  /**
   * Fail the build, if a class referenced in the scanned code is missing. This requires
   * that you pass the whole classpath including all dependencies to this Mojo
   * (Maven does this by default).
   * @since 1.0
   */
  @Parameter(required = false, defaultValue = "true")
  private boolean failOnMissingClasses;
 
  /**
   * Fail the build if a class referenced in a signature is missing. If this parameter is set to
   * to false, then such signatures are silently ignored. This is useful in multi-module Maven
   * projects where only some modules have the dependency to which the signature file(s) apply.
   * @since 1.4
   */
  @Parameter(required = false, defaultValue = "true")
  private boolean failOnUnresolvableSignatures;
 
  /**
   * The default compiler target version used to expand references to bundled JDK signatures.
   * E.g., if you use "jdk-deprecated", it will expand to this version.
   * This setting should be identical to the target version used in the compiler plugin.
   * @since 1.0
   */
  @Parameter(required = false, defaultValue = "${maven.compiler.target}")
  private String targetVersion;

  /**
   * List of patterns matching all class files to be parsed from the classesDirectory.
   * Can be changed to e.g. exclude several files (using excludes).
   * The default is a single include with pattern '**/*.class'
   * @see #excludes
   * @since 1.0
   */
  @Parameter(required = false)
  private String[] includes;

  /**
   * List of patterns matching class files to be excluded from checking.
   * @see #includes
   * @since 1.0
   */
  @Parameter(required = false)
  private String[] excludes;

  /**
   * Skip entire check. Most useful on the command line via "-Dforbiddenapis.skip=true".
   * @since 1.6
   */
  @Parameter(required = false, property="forbiddenapis.skip", defaultValue="false")
  private boolean skip;

  /** The project packaging (pom, jar, etc.). */
  @Parameter(defaultValue = "${project.packaging}", readonly = true, required = true)
  private String packaging;

  /** provided by the concrete Mojos for compile and test classes processing */
  protected abstract List<String> getClassPathElements();
 
  /** provided by the concrete Mojos for compile and test classes processing */
  protected abstract File getClassesDirectory();

  /** gets overridden for test, because it uses testTargetVersion as optional name to override */
  protected String getTargetVersion() {
    return targetVersion;
  }

  // Not in Java 5: @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    final Log log = getLog();
   
    if (skip) {
      log.info("Skipping forbidden-apis checks.");
      return;
    }
   
    // In multi-module projects, one may want to configure the plugin in the parent/root POM.
    // However, it should not be executed for this type of POMs.
    if ("pom".equals(packaging)) {
      log.info("Skipping execution for packaging \"" + packaging + "\"");
      return;
    }

    // set default param:
    if (includes == null) includes = new String[] {"**/*.class"};
   
    final URL[] urls;
    try {
      final List<String> cp = getClassPathElements();
      urls = new URL[cp.size()];
      int i = 0;
      for (final String cpElement : cp) {
        urls[i++] = new File(cpElement).toURI().toURL();
      }
      assert i == urls.length;
      if (log.isDebugEnabled()) log.debug("Compile Classpath: " + Arrays.toString(urls));
    } catch (MalformedURLException e) {
      throw new MojoExecutionException("Failed to build classpath: " + e);
    }

    URLClassLoader urlLoader = null;
    final ClassLoader loader = (urls.length > 0) ?
      (urlLoader = URLClassLoader.newInstance(urls, ClassLoader.getSystemClassLoader())) :
      ClassLoader.getSystemClassLoader();
   
    try {
      final Checker checker = new Checker(loader, internalRuntimeForbidden, failOnMissingClasses, failOnUnresolvableSignatures) {
        @Override
        protected void logError(String msg) {
          log.error(msg);
        }
       
        @Override
        protected void logWarn(String msg) {
          log.warn(msg);
        }
       
        @Override
        protected void logInfo(String msg) {
          log.info(msg);
        }
      };
     
      if (!checker.isSupportedJDK) {
        final String msg = String.format(Locale.ENGLISH,
          "Your Java runtime (%s %s) is not supported by the forbiddenapis MOJO. Please run the checks with a supported JDK!",
          System.getProperty("java.runtime.name"), System.getProperty("java.runtime.version"));
        if (failOnUnsupportedJava) {
          throw new MojoExecutionException(msg);
        } else {
          log.warn(msg);
          return;
        }
      }
     
      log.info("Scanning for classes to check...");
      final File classesDirectory = getClassesDirectory();
      if (!classesDirectory.exists()) {
        log.warn("Classes directory does not exist, forbiddenapis check skipped: " + classesDirectory);
        return;
      }
      final DirectoryScanner ds = new DirectoryScanner();
      ds.setBasedir(classesDirectory);
      ds.setCaseSensitive(true);
      ds.setIncludes(includes);
      ds.setExcludes(excludes);
      ds.addDefaultExcludes();
      ds.scan();
      final String[] files = ds.getIncludedFiles();
      if (files.length == 0) {
        log.warn(String.format(Locale.ENGLISH,
          "No classes found in '%s' (includes=%s, excludes=%s), forbiddenapis check skipped.",
          classesDirectory.toString(), Arrays.toString(includes), Arrays.toString(excludes)));
        return;
      }
     
      try {
        final String sig = (signatures != null) ? signatures.trim() : null;
        if (sig != null && sig.length() != 0) {
          log.info("Reading inline API signatures...");
          checker.parseSignaturesString(sig);
        }
        if (bundledSignatures != null) {
          String targetVersion = getTargetVersion();
          if ("".equals(targetVersion)) targetVersion = null;
          if (targetVersion == null) {
            log.warn("The 'targetVersion' parameter or '${maven.compiler.target}' property is missing. " +
              "Trying to read bundled JDK signatures without compiler target. " +
              "You have to explicitely specify the version in the resource name.");
          }
          for (String bs : bundledSignatures) {
            log.info("Reading bundled API signatures: " + bs);
            checker.parseBundledSignatures(bs, targetVersion);
          }
        }
        if (signaturesFiles != null) for (final File f : signaturesFiles) {
          log.info("Reading API signatures: " + f);
          checker.parseSignaturesFile(new FileInputStream(f));
        }
      } catch (IOException ioe) {
        throw new MojoExecutionException("IO problem while reading files with API signatures: " + ioe);
      } catch (ParseException pe) {
        throw new MojoExecutionException("Parsing signatures failed: " + pe.getMessage());
      }

      if (checker.hasNoSignatures()) {
        if (failOnUnresolvableSignatures) {
          throw new MojoExecutionException("No API signatures found; use parameters 'signatures', 'bundledSignatures', and/or 'signaturesFiles' to define those!");
        } else {
          log.info("Skipping execution because no API signatures are available.");
          return;
        }
      }

      log.info("Loading classes to check...");
      try {
        for (String f : files) {
          checker.addClassToCheck(new FileInputStream(new File(classesDirectory, f)));
        }
      } catch (IOException ioe) {
        throw new MojoExecutionException("Failed to load one of the given class files: " + ioe);
      }

      log.info("Scanning for API signatures and dependencies...");
      try {
        checker.run();
      } catch (ForbiddenApiException fae) {
        throw new MojoFailureException(fae.getMessage());
      }
    } finally {
      // Java 7 supports closing URLClassLoader, so check for Closeable interface:
      if (urlLoader instanceof Closeable) try {
        ((Closeable) urlLoader).close();
      } catch (IOException ioe) {
        // ignore
      }
    }
  }
 
}
TOP

Related Classes of de.thetaphi.forbiddenapis.AbstractCheckMojo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.