All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.marschall.jdeps.JDepsMojo Maven / Gradle / Ivy

There is a newer version: 0.5.1
Show newest version
package com.github.marschall.jdeps;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.lang3.SystemUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.ToolchainManager;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;

/**
 * Runs the jdeps tool.
 * 
 * @author Philippe Marschall
 */
@Mojo(name = "jdeps",
threadSafe = true,
requiresProject = true,
defaultPhase = LifecyclePhase.VERIFY,
requiresDependencyResolution = ResolutionScope.COMPILE
    )
public class JDepsMojo extends AbstractMojo {

  @Component
  private ToolchainManager toolchainManager;

  //  @Parameter(name = "${project}", readonly = true)
  @Component // deprecated for whatever reason but @Parameter doesn't work
  private MavenProject project;

  //  @Parameter(name = "${session}", readonly = true)
  @Component // deprecated for whatever reason but @Parameter doesn't work
  private MavenSession session;

  /**
   * Print dependency summary only.
   */
  @Parameter(defaultValue = "false", property = "jdeps.summary")
  private boolean summary;

  /**
   * Finds class-level dependences in JDK internal APIs.
   */
  @Parameter(defaultValue = "false", property = "jdeps.jdkInternals")
  private boolean jdkInternals;
  
  /**
   * Restricts analysis to APIs.
   */
  @Parameter(defaultValue = "false", property = "jdeps.apiOnly")
  private boolean apiOnly;

  /**
   * Print additional information.
   */
  @Parameter(defaultValue = "false", property = "jdeps.verbose")
  private boolean verbose;

  /**
   * Print package-level or class-level dependencies
   * Valid levels are: "package" and "class".
   */
  @Parameter(alias = "verbose-level", property = "jdeps.verboseLevel")
  private String verboseLevel;

  /**
   * Restrict analysis to classes in these packages.
   */
  @Parameter
  private List packages;

  /**
   * Restrict analysis to packages matching pattern.
   * ("packages" and "regex" are exclusive)
   */
  @Parameter(property = "jdeps.regex")
  private String regex;
  
  /**
   * Restricts analysis to classes matching pattern.
   * This option filters the list of classes to be analyzed.
   */
  @Parameter(property = "jdeps.include")
  private String include;

  /**
   * Show profile or the file containing a package.
   */
  @Parameter(defaultValue = "false", property = "jdeps.profile")
  private boolean profile;

  /**
   * Recursively traverse all dependencies.
   */
  @Parameter(defaultValue = "false", property = "jdeps.recursive")
  private boolean recursive;

  /**
   * Version information.
   */
  @Parameter(defaultValue = "false", property = "jdeps.version")
  private boolean version;

  /**
   * Destination directory for DOT file output. Set to something like
   * ${project.reporting.outputDirectory}/jdeps
   */
  @Parameter(property = "jdeps.dotOutputDirectory")
  private File dotOutputDirectory;
  
  @Parameter(defaultValue = "${project.build.outputDirectory}", readonly = true)
  private File outputDirectory;

  @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    String jExecutable;
    try {
      jExecutable = getJdepsExecutable();
    } catch ( IOException e ) {
      throw new MojoFailureException("Unable to find jdeps command: " + e.getMessage(), e );
    }
    Commandline cmd = new Commandline();
    cmd.setExecutable(jExecutable);

    addApiOnly(cmd);
    addClassPathArg(cmd);
    addDotOutput(cmd);
    addInclude(cmd);
    addJdkinternals(cmd);
    addPackagesArg(cmd);
    addProfileArg(cmd);
    addRegexArg(cmd);
    addRecursiveArg(cmd);
    addSummaryArg(cmd);
    addVerboseArg(cmd);
    addVerboseLevelArg(cmd);
    addVersionArg(cmd);
    
    addOutputArg(cmd);

    this.executeJDepsCommandLine(cmd);
  }

  private void addPackagesArg(Commandline cmd) {
    if (this.packages != null && !this.packages.isEmpty()) {
      for (String each : this.packages) {
        cmd.createArg().setValue("-package");
        cmd.createArg().setValue(each);
      }
    }
  }

  private void addClassPathArg(Commandline cmd) throws MojoFailureException {
    Set dependencyArtifacts = this.project.getDependencyArtifacts();
    if (!dependencyArtifacts.isEmpty()) {
      List fileNames = new ArrayList<>(dependencyArtifacts.size());
      for (Artifact artifact : dependencyArtifacts) {
        File file = artifact.getFile();
        if (file != null) {
          fileNames.add(file.getAbsolutePath());
        }
      }
      String pathSeparator = System.getProperty("path.separator");
      if (pathSeparator == null) {
        throw new MojoFailureException("Can't read path separator");

      }
      if (!fileNames.isEmpty()) {
        // jdeps doesn't like an empty classpath
        String classPath = StringUtils.join(fileNames.iterator(), pathSeparator);
        cmd.createArg().setValue("-classpath");
        cmd.createArg().setValue(classPath);
      }
    }
  }

  private void addVerboseLevelArg(Commandline cmd) {
    if (this.verboseLevel != null) {
      cmd.createArg().setValue("-verbose:" + this.verboseLevel);
    }
  }

  private void addRegexArg(Commandline cmd) {
    if (this.regex != null) {
      cmd.createArg().setValue("-regex");
      cmd.createArg().setValue(this.regex);
    }
  }
  
  private void addInclude(Commandline cmd) {
    if (this.include != null) {
      cmd.createArg().setValue("-regex");
      cmd.createArg().setValue(this.include);
    }
  }
  
  private void addDotOutput(Commandline cmd) {
    if (this.dotOutputDirectory != null) {
      cmd.createArg().setValue("-dotoutput");
      cmd.createArg().setFile(this.dotOutputDirectory);
    }
  }

  private void addVerboseArg(Commandline cmd) {
    addBooleanArg(this.verbose, "-verbose", cmd);
  }

  private void addSummaryArg(Commandline cmd) {
    addBooleanArg(this.summary, "-summary", cmd);
  }

  private void addJdkinternals(Commandline cmd) {
    addBooleanArg(this.jdkInternals, "-jdkinternals", cmd);
  }
  
  private void addApiOnly(Commandline cmd) {
    addBooleanArg(this.apiOnly, "-apionly", cmd);
  }

  private void addProfileArg(Commandline cmd) {
    addBooleanArg(this.profile, "-profile", cmd);
  }

  private void addRecursiveArg(Commandline cmd) {
    addBooleanArg(this.recursive, "-recursive", cmd);
  }

  private void addVersionArg(Commandline cmd) {
    addBooleanArg(this.version, "-version", cmd);
  }

  private void addBooleanArg(boolean flag, String name, Commandline cmd) {
    if (flag) {
      cmd.createArg().setValue(name);
    }
  }

  private void addOutputArg(Commandline cmd) {
    cmd.createArg().setFile(this.outputDirectory);
  }

  /**
   * Execute the JDeps command line
   *
   * @param cmd not null
   * @throws MojoFailureException if any errors occur
   */
  private void executeJDepsCommandLine(Commandline cmd) throws MojoFailureException {

    String cmdLine = null;

    CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
    CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer();
    try {
      int exitCode = CommandLineUtils.executeCommandLine(cmd, out, err);

      String output = StringUtils.isEmpty( out.getOutput() ) ? null : '\n' + out.getOutput().trim();
      if (StringUtils.isNotEmpty(output)) {
        getLog().info(output);
      }

      if (exitCode != 0) {
        cmdLine = CommandLineUtils.toString(cmd.getCommandline()).replaceAll("'", "");


        StringBuilder msg = new StringBuilder("\nExit code: ");
        msg.append(exitCode);
        if (StringUtils.isNotEmpty(err.getOutput())) {
          msg.append(" - ").append(err.getOutput());
        }
        msg.append('\n');
        msg.append("Command line was: ").append(cmdLine).append('\n').append('\n');

        throw new MojoFailureException(msg.toString());
      }
    }
    catch (CommandLineException e) {
      throw new MojoFailureException("Unable to execute jdeps command: " + e.getMessage(), e);
    }
  }

  /**
   * Get the path of the JDeps tool executable depending the user entry or try to find it depending the OS
   * or the java.home system property or the JAVA_HOME environment variable.
   *
   * @return the path of the JDeps tool
   * @throws IOException if not found
   */
  private String getJdepsExecutable() throws IOException {
    String jdepsExecutable = null;
    Toolchain toolchain = this.toolchainManager.getToolchainFromBuildContext("jdk", this.session);

    if (toolchain != null) {
      getLog().info("Toolchain in jdeps-maven-plugin: " + toolchain);
      jdepsExecutable = toolchain.findTool("jdeps");
    }
    String jdepsCommand = "jdeps" + (SystemUtils.IS_OS_WINDOWS ? ".exe" : "");

    Path jdepsExe;

    // ----------------------------------------------------------------------
    // The jdps executable is defined by the user
    // ----------------------------------------------------------------------
    if (StringUtils.isNotEmpty(jdepsExecutable)) {
      jdepsExe = Paths.get(jdepsExecutable);

      if (Files.isDirectory(jdepsExe)){
        jdepsExe = jdepsExe.resolve(jdepsCommand);
      }

      String fileName = jdepsExe.getFileName().toString();
      if (SystemUtils.IS_OS_WINDOWS && fileName.indexOf('.') < 0) {
        jdepsExe = jdepsExe.resolveSibling(fileName + ".exe");
      }

      if (!Files.isRegularFile(jdepsExe)) {
        throw new IOException( "The jdeps executable '" + jdepsExe + "' doesn't exist or is not a file." );
      }

      return jdepsExe.toAbsolutePath().toString();
    }

    // ----------------------------------------------------------------------
    // Try to find jdepsExe from System.getProperty( "java.home" )
    // By default, System.getProperty( "java.home" ) = JRE_HOME and JRE_HOME
    // should be in the JDK_HOME
    // ----------------------------------------------------------------------
    jdepsExe = Paths.get(SystemUtils.getJavaHome().toURI()).getParent().resolve("bin").resolve(jdepsCommand);

    // ----------------------------------------------------------------------
    // Try to find jdepsExe from JAVA_HOME environment variable
    // ----------------------------------------------------------------------
    if (!Files.exists(jdepsExe) || !Files.isRegularFile(jdepsExe)) {
      Properties env = CommandLineUtils.getSystemEnvVars();
      String javaHome = env.getProperty("JAVA_HOME");
      if (StringUtils.isEmpty(javaHome)) {
        throw new IOException( "The environment variable JAVA_HOME is not correctly set." );
      }
      Path javaHomePath = Paths.get(javaHome).resolve("bin");
      jdepsExe = javaHomePath.resolve("bin").resolve(jdepsCommand);

      if (!Files.exists(jdepsExe) || !Files.isDirectory(jdepsExe)) {
        throw new IOException("The environment variable JAVA_HOME=" + javaHome + " doesn't exist or is not a valid directory." );
      }

    }

    if (!Files.exists(jdepsExe) || !Files.isRegularFile(jdepsExe)) {
      throw new IOException( "The jdeps executable '" + jdepsExe + "' doesn't exist or is not a file. Verify the JAVA_HOME environment variable." );
    }

    return jdepsExe.toAbsolutePath().toString();
  }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy