Java tutorial
package org.apache.maven.plugin.jdeps; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import org.apache.commons.lang.SystemUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.DependencyResolutionRequiredException; 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.plugin.jdeps.consumers.JDepsConsumer; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.toolchain.Toolchain; import org.apache.maven.toolchain.ToolchainManager; import org.codehaus.plexus.util.MatchPatterns; 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; /** * Abstract Mojo for JDeps * * @author Robert Scholte * */ public abstract class AbstractJDepsMojo extends AbstractMojo { @Parameter(defaultValue = "${project}", readonly = true, required = true) private MavenProject project; @Parameter(defaultValue = "${session}", readonly = true, required = true) private MavenSession session; @Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true) private File outputDirectory; /** * Indicates whether the build will continue even if there are jdeps warnings. */ @Parameter(defaultValue = "true") private boolean failOnWarning; /** * Additional dependencies which should be analyzed besides the classes. * Specify as {@code groupId:artifactId}, allowing ant-pattern. * * E.g. * <pre> * <dependenciesToAnalyzeIncludes> * <include>*:*</include> * <include>org.foo.*:*</include> * <include>com.foo.bar:*</include> * <include>dot.foo.bar:utilities</include> * </dependenciesToAnalyzeIncludes> * </pre> */ @Parameter private List<String> dependenciesToAnalyzeIncludes; /** * Subset of {@link AbstractJDepsMojo#dependenciesToAnalyzeIncludes} which should be not analyzed. * Specify as {@code groupId:artifactId}, allowing ant-pattern. * * E.g. * <pre> * <dependenciesToAnalyzeExcludes> * <exclude>org.foo.*:*</exclude> * <exclude>com.foo.bar:*</exclude> * <exclude>dot.foo.bar:utilities</exclude> * </dependenciesToAnalyzeExcludes> * </pre> */ @Parameter private List<String> dependenciesToAnalyzeExcludes; /** * Destination directory for DOT file output */ @Parameter(property = "jdeps.dotOutput") private File dotOutput; // @Parameter( defaultValue = "false", property = "jdeps.summaryOnly" ) // private boolean summaryOnly; /** * <dl> * <dt>package</dt><dd>Print package-level dependencies excluding dependencies within the same archive<dd/> * <dt>class</dt><dd>Print class-level dependencies excluding dependencies within the same archive<dd/> * <dt><empty></dt><dd>Print all class level dependencies. Equivalent to -verbose:class -filter:none.<dd/> * </dl> */ @Parameter(property = "jdeps.verbose") private String verbose; // /** // * A comma-separated list to find dependences in the given package (may be given multiple times) // */ // @Parameter( property = "jdeps.pkgnames" ) // private String packageNames; // // /** // * Finds dependences in packages matching pattern (-p and -e are exclusive) // */ // @Parameter( property = "jdeps.regex" ) // private String regex; /** * Restrict analysis to classes matching pattern. This option filters the list of classes to be analyzed. It can be * used together with <code>-p</code> and <code>-e</code> which apply pattern to the dependences */ @Parameter(property = "jdeps.include") private String include; /** * Restrict analysis to APIs i.e. dependences from the signature of public and protected members of public classes * including field type, method parameter types, returned type, checked exception types etc */ @Parameter(defaultValue = "false", property = "jdeps.apionly") private boolean apiOnly; /** * Show profile or the file containing a package */ @Parameter(defaultValue = "false", property = "jdeps.profile") private boolean profile; /** * Recursively traverse all dependencies. The {@code -R} option implies {@code -filter:none}. If {@code -p}, * {@code -e}, {@code -f} option is specified, only the matching dependences are analyzed. */ @Parameter(defaultValue = "false", property = "jdeps.recursive") private boolean recursive; /** * Show module containing the package * * @since JDK 1.9.0 */ @Parameter(defaultValue = "false", property = "jdeps.module") private boolean module; @Component private ToolchainManager toolchainManager; protected MavenProject getProject() { return project; } public void execute() throws MojoExecutionException, MojoFailureException { if (!new File(getClassesDirectory()).exists()) { getLog().debug("No classes to analyze"); return; } String jExecutable; try { jExecutable = getJDepsExecutable(); } catch (IOException e) { throw new MojoFailureException("Unable to find jdeps command: " + e.getMessage(), e); } // Synopsis // jdeps [options] classes ... Commandline cmd = new Commandline(); cmd.setExecutable(jExecutable); addJDepsOptions(cmd); addJDepsClasses(cmd); JDepsConsumer consumer = new JDepsConsumer(); executeJDepsCommandLine(cmd, outputDirectory, consumer); // @ TODO if there will be more goals, this should be pushed down to AbstractJDKInternals if (consumer.getOffendingPackages().size() > 0) { final String ls = System.getProperty("line.separator"); StringBuilder msg = new StringBuilder(); msg.append("Found offending packages:").append(ls); for (Map.Entry<String, String> offendingPackage : consumer.getOffendingPackages().entrySet()) { msg.append(' ').append(offendingPackage.getKey()).append(" -> ").append(offendingPackage.getValue()) .append(ls); } if (failOnWarning) { throw new MojoExecutionException(msg.toString()); } } } protected void addJDepsOptions(Commandline cmd) throws MojoFailureException { if (dotOutput != null) { cmd.createArg().setValue("-dotoutput"); cmd.createArg().setFile(dotOutput); } // if ( summaryOnly ) // { // cmd.createArg().setValue( "-s" ); // } if (verbose != null) { if ("class".equals(verbose)) { cmd.createArg().setValue("-verbose:class"); } else if ("package".equals(verbose)) { cmd.createArg().setValue("-verbose:package"); } else { cmd.createArg().setValue("-v"); } } try { cmd.createArg().setValue("-cp"); cmd.createArg().setValue(getClassPath()); } catch (DependencyResolutionRequiredException e) { throw new MojoFailureException(e.getMessage(), e); } // if ( packageNames != null ) // { // for ( String pkgName : packageNames.split( "[,:;]" ) ) // { // cmd.createArg().setValue( "-p" ); // cmd.createArg().setValue( pkgName ); // } // } // // if ( regex != null ) // { // cmd.createArg().setValue( "-e" ); // cmd.createArg().setValue( regex ); // } if (include != null) { cmd.createArg().setValue("-include"); cmd.createArg().setValue(include); } if (profile) { cmd.createArg().setValue("-P"); } if (module) { cmd.createArg().setValue("-M"); } if (apiOnly) { cmd.createArg().setValue("-apionly"); } if (recursive) { cmd.createArg().setValue("-R"); } // cmd.createArg().setValue( "-version" ); } protected void addJDepsClasses(Commandline cmd) { // <classes> can be a pathname to a .class file, a directory, a JAR file, or a fully-qualified class name. cmd.createArg().setFile(new File(getClassesDirectory())); if (dependenciesToAnalyzeIncludes != null) { MatchPatterns includes = MatchPatterns.from(dependenciesToAnalyzeIncludes); MatchPatterns excludes; if (dependenciesToAnalyzeExcludes != null) { excludes = MatchPatterns.from(dependenciesToAnalyzeExcludes); } else { excludes = MatchPatterns.from(Collections.<String>emptyList()); } for (Artifact artifact : project.getArtifacts()) { String versionlessKey = ArtifactUtils.versionlessKey(artifact); if (includes.matchesPatternStart(versionlessKey, true) && !excludes.matchesPatternStart(versionlessKey, true)) { cmd.createArg().setFile(artifact.getFile()); } } } } private String getJDepsExecutable() throws IOException { Toolchain tc = getToolchain(); String jdepsExecutable = null; if (tc != null) { jdepsExecutable = tc.findTool("jdeps"); } String jdepsCommand = "jdeps" + (SystemUtils.IS_OS_WINDOWS ? ".exe" : ""); File jdepsExe; if (StringUtils.isNotEmpty(jdepsExecutable)) { jdepsExe = new File(jdepsExecutable); if (jdepsExe.isDirectory()) { jdepsExe = new File(jdepsExe, jdepsCommand); } if (SystemUtils.IS_OS_WINDOWS && jdepsExe.getName().indexOf('.') < 0) { jdepsExe = new File(jdepsExe.getPath() + ".exe"); } if (!jdepsExe.isFile()) { throw new IOException("The jdeps executable '" + jdepsExe + "' doesn't exist or is not a file."); } return jdepsExe.getAbsolutePath(); } // ---------------------------------------------------------------------- // 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 // ---------------------------------------------------------------------- // For IBM's JDK 1.2 if (SystemUtils.IS_OS_AIX) { jdepsExe = new File(SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "sh", jdepsCommand); } // For Apple's JDK 1.6.x (and older?) on Mac OSX // CHECKSTYLE_OFF: MagicNumber else if (SystemUtils.IS_OS_MAC_OSX && SystemUtils.JAVA_VERSION_FLOAT < 1.7f) // CHECKSTYLE_ON: MagicNumber { jdepsExe = new File(SystemUtils.getJavaHome() + File.separator + "bin", jdepsCommand); } else { jdepsExe = new File(SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "bin", jdepsCommand); } // ---------------------------------------------------------------------- // Try to find jdepsExe from JAVA_HOME environment variable // ---------------------------------------------------------------------- if (!jdepsExe.exists() || !jdepsExe.isFile()) { 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."); } if ((!new File(javaHome).getCanonicalFile().exists()) || (new File(javaHome).getCanonicalFile().isFile())) { throw new IOException("The environment variable JAVA_HOME=" + javaHome + " doesn't exist or is not a valid directory."); } jdepsExe = new File(javaHome + File.separator + "bin", jdepsCommand); } if (!jdepsExe.getCanonicalFile().exists() || !jdepsExe.getCanonicalFile().isFile()) { throw new IOException("The jdeps executable '" + jdepsExe + "' doesn't exist or is not a file. Verify the JAVA_HOME environment variable."); } return jdepsExe.getAbsolutePath(); } private void executeJDepsCommandLine(Commandline cmd, File jOutputDirectory, CommandLineUtils.StringStreamConsumer consumer) throws MojoExecutionException { if (getLog().isDebugEnabled()) { // no quoted arguments getLog().debug(CommandLineUtils.toString(cmd.getCommandline()).replaceAll("'", "")); } CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer(); CommandLineUtils.StringStreamConsumer out; if (consumer != null) { out = consumer; } else { out = new CommandLineUtils.StringStreamConsumer(); } try { int exitCode = CommandLineUtils.executeCommandLine(cmd, out, err); String output = (StringUtils.isEmpty(out.getOutput()) ? null : '\n' + out.getOutput().trim()); if (exitCode != 0) { if (StringUtils.isNotEmpty(output)) { getLog().info(output); } 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(cmd).append('\n').append('\n'); throw new MojoExecutionException(msg.toString()); } if (StringUtils.isNotEmpty(output)) { getLog().info(output); } } catch (CommandLineException e) { throw new MojoExecutionException("Unable to execute jdeps command: " + e.getMessage(), e); } // ---------------------------------------------------------------------- // Handle JDeps warnings // ---------------------------------------------------------------------- if (StringUtils.isNotEmpty(err.getOutput()) && getLog().isWarnEnabled()) { getLog().warn("JDeps Warnings"); StringTokenizer token = new StringTokenizer(err.getOutput(), "\n"); while (token.hasMoreTokens()) { String current = token.nextToken().trim(); getLog().warn(current); } } } private Toolchain getToolchain() { Toolchain tc = null; if (toolchainManager != null) { tc = toolchainManager.getToolchainFromBuildContext("jdk", session); if (tc == null) { // Maven 3.2.6 has plugin execution scoped Toolchain Support try { Method getToolchainsMethod = toolchainManager.getClass().getMethod("getToolchains", MavenSession.class, String.class, Map.class); @SuppressWarnings("unchecked") List<Toolchain> tcs = (List<Toolchain>) getToolchainsMethod.invoke(toolchainManager, session, "jdk", Collections.singletonMap("version", "[1.8,)")); if (tcs != null && tcs.size() > 0) { // pick up latest, jdeps of JDK9 has more options compared to JDK8 tc = tcs.get(tcs.size() - 1); } } catch (NoSuchMethodException e) { // ignore } catch (SecurityException e) { // ignore } catch (IllegalAccessException e) { // ignore } catch (IllegalArgumentException e) { // ignore } catch (InvocationTargetException e) { // ignore } } } return tc; } protected abstract String getClassesDirectory(); protected abstract String getClassPath() throws DependencyResolutionRequiredException; }