This is an automated email from the ASF dual-hosted git repository. khmarbaise pushed a commit to branch MJDEPS-31 in repository https://gitbox.apache.org/repos/asf/maven-jdeps-plugin.git
commit 0577ffa76c0f8a8c73622b01dfffdf904c17dd13 Author: Karl Heinz Marbaise <khmarba...@apache.org> AuthorDate: Tue Nov 14 10:15:41 2023 +0100 [MJDEPS-31] - Upgrade maven-plugin parent to 41 --- pom.xml | 26 +- .../plugins/jdeps/AbstractJDKInternalsMojo.java | 101 +- .../maven/plugins/jdeps/AbstractJDepsMojo.java | 1231 +++++++++----------- .../maven/plugins/jdeps/JDKInternalsMojo.java | 145 +-- .../maven/plugins/jdeps/TestJDKInternalsMojo.java | 168 +-- .../plugins/jdeps/consumers/JDepsConsumer.java | 204 ++-- .../plugins/jdeps/consumers/JDepsConsumerTest.java | 211 ++-- 7 files changed, 1045 insertions(+), 1041 deletions(-) diff --git a/pom.xml b/pom.xml index 70c09c6..afe1b26 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,4 @@ -<?xml version='1.0' encoding='UTF-8'?> - +<?xml version="1.0" encoding="UTF-8"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file @@ -18,14 +17,13 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> - <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-plugins</artifactId> - <version>34</version> + <version>41</version> <relativePath /> </parent> @@ -37,15 +35,21 @@ under the License. <description>The JDeps Plugin uses the jdeps tool to analyze classes for internal API calls.</description> <inceptionYear>2015</inceptionYear> + <contributors> + <contributor> + <name>Andrea Nenni</name> + </contributor> + </contributors> + <prerequisites> <maven>${mavenVersion}</maven> </prerequisites> - + <scm> <connection>scm:git:https://gitbox.apache.org/repos/asf/maven-jdeps-plugin.git</connection> <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/maven-jdeps-plugin.git</developerConnection> - <url>https://github.com/apache/maven-jdeps-plugin/tree/${project.scm.tag}</url> <tag>HEAD</tag> + <url>https://github.com/apache/maven-jdeps-plugin/tree/${project.scm.tag}</url> </scm> <issueManagement> <system>JIRA</system> @@ -64,16 +68,10 @@ under the License. <properties> <mavenVersion>3.0</mavenVersion> - <javaVersion>7</javaVersion> + <javaVersion>8</javaVersion> <project.build.outputTimestamp>2020-04-07T21:04:00Z</project.build.outputTimestamp> </properties> - <contributors> - <contributor> - <name>Andrea Nenni</name> - </contributor> - </contributors> - <dependencies> <dependency> <groupId>org.apache.maven.plugin-tools</groupId> @@ -106,7 +104,7 @@ under the License. <artifactId>commons-lang3</artifactId> <version>3.5</version> </dependency> - + <!-- TEST --> <dependency> <groupId>junit</groupId> diff --git a/src/main/java/org/apache/maven/plugins/jdeps/AbstractJDKInternalsMojo.java b/src/main/java/org/apache/maven/plugins/jdeps/AbstractJDKInternalsMojo.java index cc43d99..24e312a 100644 --- a/src/main/java/org/apache/maven/plugins/jdeps/AbstractJDKInternalsMojo.java +++ b/src/main/java/org/apache/maven/plugins/jdeps/AbstractJDKInternalsMojo.java @@ -1,43 +1,58 @@ -package org.apache.maven.plugins.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.nio.file.Path; -import java.util.Set; - -import org.apache.maven.plugin.MojoFailureException; -import org.codehaus.plexus.util.cli.Commandline; - -/** - * Abstract Mojo for verifying code with jdkinternals - * - * @author Robert Scholte - */ -public abstract class AbstractJDKInternalsMojo extends AbstractJDepsMojo -{ - - @Override - protected void addJDepsOptions( Commandline cmd, Set<Path> dependenciesToAnalyze ) - throws MojoFailureException - { - super.addJDepsOptions( cmd, dependenciesToAnalyze ); - cmd.createArg().setValue( "-jdkinternals" ); - } -} +/* + * 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. + */ +package org.apache.maven.plugins.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.nio.file.Path; +import java.util.Set; + +import org.apache.maven.plugin.MojoFailureException; +import org.codehaus.plexus.util.cli.Commandline; + +/** + * Abstract Mojo for verifying code with jdkinternals + * + * @author Robert Scholte + */ +public abstract class AbstractJDKInternalsMojo extends AbstractJDepsMojo { + + @Override + protected void addJDepsOptions(Commandline cmd, Set<Path> dependenciesToAnalyze) throws MojoFailureException { + super.addJDepsOptions(cmd, dependenciesToAnalyze); + cmd.createArg().setValue("-jdkinternals"); + } +} diff --git a/src/main/java/org/apache/maven/plugins/jdeps/AbstractJDepsMojo.java b/src/main/java/org/apache/maven/plugins/jdeps/AbstractJDepsMojo.java index 73dd3b4..aca0132 100644 --- a/src/main/java/org/apache/maven/plugins/jdeps/AbstractJDepsMojo.java +++ b/src/main/java/org/apache/maven/plugins/jdeps/AbstractJDepsMojo.java @@ -1,653 +1,584 @@ -package org.apache.maven.plugins.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.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; +/* + * 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. + */ +package org.apache.maven.plugins.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.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; import java.util.StringTokenizer; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.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.plugins.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.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", property = "jdeps.failOnWarning" ) - private boolean failOnWarning; - - /** - * Specifies the version when processing multi-release JAR files version should be an integer >=9 or base. - * - * @since 3.1.1 - */ - @Parameter( property = "jdeps.multiRelease" ) - private String multiRelease; - - /** - * Whether only the sources need to be compatible or also every dependency on the classpath. - * - * @since 3.1.3 - */ - @Parameter( defaultValue = "true", property = "jdeps.includeClasspath" ) - private boolean includeClasspath; - - /** - * 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; - - - /** - * Finds dependences matching the specified package name. - * - * @since 3.1.1. - */ - @Parameter - private List<String> packages; - -// /** -// * 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; - - /** - * Specifies the root module for analysis. - * - * @since JDK 1.9.0 - */ - @Parameter( property = "jdeps.module" ) - private String 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 ); - - Set<Path> dependenciesToAnalyze = null; - try - { - dependenciesToAnalyze = getDependenciesToAnalyze( includeClasspath ); - } - catch ( DependencyResolutionRequiredException e ) - { - throw new MojoExecutionException( e.getMessage(), e ); - } - addJDepsOptions( cmd, dependenciesToAnalyze ); - addJDepsClasses( cmd, dependenciesToAnalyze ); - - 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 ( isFailOnWarning() ) - { - throw new MojoExecutionException( msg.toString() ); - } - } - } - - protected void addJDepsOptions( Commandline cmd, Set<Path> dependenciesToAnalyze ) - 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 - { - Collection<Path> cp = new ArrayList<>(); - - for ( Path path : getClassPath() ) - { - if ( !dependenciesToAnalyze.contains( path ) ) - { - cp.add( path ); - } - } - - if ( !cp.isEmpty() ) - { - cmd.createArg().setValue( "-cp" ); - - cmd.createArg().setValue( StringUtils.join( cp.iterator(), File.pathSeparator ) ); - } - - } - catch ( DependencyResolutionRequiredException e ) - { - throw new MojoFailureException( e.getMessage(), e ); - } - - if ( packages != null ) - { - for ( String pkgName : packages ) - { - cmd.createArg().setValue( "-p" ); - cmd.createArg().setValue( pkgName ); - } - } - -// 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 != null ) - { - cmd.createArg().setValue( "-m" ); - cmd.createArg().setValue( module ); - } - - if ( multiRelease != null ) - { - cmd.createArg().setValue( "--multi-release" ); - cmd.createArg().setValue( multiRelease ); - } - - if ( apiOnly ) - { - cmd.createArg().setValue( "-apionly" ); - } - - if ( recursive ) - { - cmd.createArg().setValue( "-R" ); - } - - // cmd.createArg().setValue( "-version" ); - } - - protected Set<Path> getDependenciesToAnalyze( boolean includeClasspath ) - throws DependencyResolutionRequiredException - { - Set<Path> jdepsClasses = new LinkedHashSet<>(); - - jdepsClasses.add( Paths.get( getClassesDirectory() ) ); - - if ( includeClasspath ) - { - jdepsClasses.addAll( getClassPath() ); - } - - 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 ) ) - { - jdepsClasses.add( artifact.getFile().toPath() ); - } - } - } - - return jdepsClasses; - } - - protected void addJDepsClasses( Commandline cmd, Set<Path> dependenciesToAnalyze ) - { - // <classes> can be a pathname to a .class file, a directory, a JAR file, or a fully-qualified class name. - for ( Path dependencyToAnalyze : dependenciesToAnalyze ) - { - cmd.createArg().setFile( dependencyToAnalyze.toFile() ); - } - } - - 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(); - } - - jdepsExe = new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "sh", 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( "Executing: " + CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" ) ); - } - - - CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer() - { - @Override - public void consumeLine( String line ) - { - if ( !line.startsWith( "Picked up JAVA_TOOL_OPTIONS:" ) ) - { - super.consumeLine( line ); - } - } - }; - 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 boolean isFailOnWarning() - { - return failOnWarning; - } - - protected abstract String getClassesDirectory(); - - protected abstract Collection<Path> getClassPath() throws DependencyResolutionRequiredException; -} +import org.apache.commons.lang3.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.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.jdeps.consumers.JDepsConsumer; +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.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", property = "jdeps.failOnWarning") + private boolean failOnWarning; + + /** + * Specifies the version when processing multi-release JAR files version should be an integer >=9 or base. + * + * @since 3.1.1 + */ + @Parameter(property = "jdeps.multiRelease") + private String multiRelease; + + /** + * Whether only the sources need to be compatible or also every dependency on the classpath. + * + * @since 3.1.3 + */ + @Parameter(defaultValue = "true", property = "jdeps.includeClasspath") + private boolean includeClasspath; + + /** + * 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; + + /** + * Finds dependences matching the specified package name. + * + * @since 3.1.1. + */ + @Parameter + private List<String> packages; + + // /** + // * 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; + + /** + * Specifies the root module for analysis. + * + * @since JDK 1.9.0 + */ + @Parameter(property = "jdeps.module") + private String 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); + + Set<Path> dependenciesToAnalyze = null; + try { + dependenciesToAnalyze = getDependenciesToAnalyze(includeClasspath); + } catch (DependencyResolutionRequiredException e) { + throw new MojoExecutionException(e.getMessage(), e); + } + addJDepsOptions(cmd, dependenciesToAnalyze); + addJDepsClasses(cmd, dependenciesToAnalyze); + + 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 (isFailOnWarning()) { + throw new MojoExecutionException(msg.toString()); + } + } + } + + protected void addJDepsOptions(Commandline cmd, Set<Path> dependenciesToAnalyze) 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 { + Collection<Path> cp = new ArrayList<>(); + + for (Path path : getClassPath()) { + if (!dependenciesToAnalyze.contains(path)) { + cp.add(path); + } + } + + if (!cp.isEmpty()) { + cmd.createArg().setValue("-cp"); + + cmd.createArg().setValue(StringUtils.join(cp.iterator(), File.pathSeparator)); + } + + } catch (DependencyResolutionRequiredException e) { + throw new MojoFailureException(e.getMessage(), e); + } + + if (packages != null) { + for (String pkgName : packages) { + cmd.createArg().setValue("-p"); + cmd.createArg().setValue(pkgName); + } + } + + // 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 != null) { + cmd.createArg().setValue("-m"); + cmd.createArg().setValue(module); + } + + if (multiRelease != null) { + cmd.createArg().setValue("--multi-release"); + cmd.createArg().setValue(multiRelease); + } + + if (apiOnly) { + cmd.createArg().setValue("-apionly"); + } + + if (recursive) { + cmd.createArg().setValue("-R"); + } + + // cmd.createArg().setValue( "-version" ); + } + + protected Set<Path> getDependenciesToAnalyze(boolean includeClasspath) + throws DependencyResolutionRequiredException { + Set<Path> jdepsClasses = new LinkedHashSet<>(); + + jdepsClasses.add(Paths.get(getClassesDirectory())); + + if (includeClasspath) { + jdepsClasses.addAll(getClassPath()); + } + + 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)) { + jdepsClasses.add(artifact.getFile().toPath()); + } + } + } + + return jdepsClasses; + } + + protected void addJDepsClasses(Commandline cmd, Set<Path> dependenciesToAnalyze) { + // <classes> can be a pathname to a .class file, a directory, a JAR file, or a fully-qualified class name. + for (Path dependencyToAnalyze : dependenciesToAnalyze) { + cmd.createArg().setFile(dependencyToAnalyze.toFile()); + } + } + + 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(); + } + + jdepsExe = new File(SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "sh", 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("Executing: " + + CommandLineUtils.toString(cmd.getCommandline()).replaceAll("'", "")); + } + + CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer() { + @Override + public void consumeLine(String line) { + if (!line.startsWith("Picked up JAVA_TOOL_OPTIONS:")) { + super.consumeLine(line); + } + } + }; + 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 boolean isFailOnWarning() { + return failOnWarning; + } + + protected abstract String getClassesDirectory(); + + protected abstract Collection<Path> getClassPath() throws DependencyResolutionRequiredException; +} diff --git a/src/main/java/org/apache/maven/plugins/jdeps/JDKInternalsMojo.java b/src/main/java/org/apache/maven/plugins/jdeps/JDKInternalsMojo.java index 1cd2d72..ec45ac5 100644 --- a/src/main/java/org/apache/maven/plugins/jdeps/JDKInternalsMojo.java +++ b/src/main/java/org/apache/maven/plugins/jdeps/JDKInternalsMojo.java @@ -1,65 +1,80 @@ -package org.apache.maven.plugins.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.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.apache.maven.artifact.DependencyResolutionRequiredException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.ResolutionScope; - -/** - * Check if main classes depend on internal JDK classes - * - * @author Robert Scholte - * - */ -@Mojo( name = "jdkinternals", - requiresDependencyResolution = ResolutionScope.COMPILE, - defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = true ) -public class JDKInternalsMojo - extends AbstractJDepsMojo -{ - - @Override - protected String getClassesDirectory() - { - return getProject().getBuild().getOutputDirectory(); - } - - @Override - protected Collection<Path> getClassPath() - throws DependencyResolutionRequiredException - { - Set<Path> classPath = new LinkedHashSet<>( getProject().getCompileClasspathElements().size() ); - - for ( String elm : getProject().getCompileClasspathElements() ) - { - classPath.add( Paths.get( elm ) ); - } - - return classPath; - } -} +/* + * 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. + */ +package org.apache.maven.plugins.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.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.apache.maven.artifact.DependencyResolutionRequiredException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; + +/** + * Check if main classes depend on internal JDK classes + * + * @author Robert Scholte + * + */ +@Mojo( + name = "jdkinternals", + requiresDependencyResolution = ResolutionScope.COMPILE, + defaultPhase = LifecyclePhase.PROCESS_CLASSES, + threadSafe = true) +public class JDKInternalsMojo extends AbstractJDepsMojo { + + @Override + protected String getClassesDirectory() { + return getProject().getBuild().getOutputDirectory(); + } + + @Override + protected Collection<Path> getClassPath() throws DependencyResolutionRequiredException { + Set<Path> classPath = + new LinkedHashSet<>(getProject().getCompileClasspathElements().size()); + + for (String elm : getProject().getCompileClasspathElements()) { + classPath.add(Paths.get(elm)); + } + + return classPath; + } +} diff --git a/src/main/java/org/apache/maven/plugins/jdeps/TestJDKInternalsMojo.java b/src/main/java/org/apache/maven/plugins/jdeps/TestJDKInternalsMojo.java index 966a7f2..0b53196 100644 --- a/src/main/java/org/apache/maven/plugins/jdeps/TestJDKInternalsMojo.java +++ b/src/main/java/org/apache/maven/plugins/jdeps/TestJDKInternalsMojo.java @@ -1,77 +1,91 @@ -package org.apache.maven.plugins.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.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.apache.maven.artifact.DependencyResolutionRequiredException; -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; - -/** - * Check if test classes depend on internal JDK classes - * - * @author Robert Scholte - * - */ -@Mojo( name = "test-jdkinternals", - requiresDependencyResolution = ResolutionScope.TEST, - defaultPhase = LifecyclePhase.PROCESS_TEST_CLASSES, threadSafe = true ) -public class TestJDKInternalsMojo - extends AbstractJDepsMojo -{ - /** - * Indicates whether the build will continue even if there are jdeps warnings. - */ - @Parameter( defaultValue = "true", property = "jdeps.test.failOnWarning" ) - private boolean failOnWarning; - - @Override - protected boolean isFailOnWarning() - { - return failOnWarning; - } - - @Override - protected String getClassesDirectory() - { - return getProject().getBuild().getTestOutputDirectory(); - } - - @Override - protected Collection<Path> getClassPath() - throws DependencyResolutionRequiredException - { - Set<Path> classPath = new LinkedHashSet<>( getProject().getTestClasspathElements().size() ); - - for ( String elm : getProject().getTestClasspathElements() ) - { - classPath.add( Paths.get( elm ) ); - } - - return classPath; - } -} +/* + * 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. + */ +package org.apache.maven.plugins.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.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.apache.maven.artifact.DependencyResolutionRequiredException; +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; + +/** + * Check if test classes depend on internal JDK classes + * + * @author Robert Scholte + * + */ +@Mojo( + name = "test-jdkinternals", + requiresDependencyResolution = ResolutionScope.TEST, + defaultPhase = LifecyclePhase.PROCESS_TEST_CLASSES, + threadSafe = true) +public class TestJDKInternalsMojo extends AbstractJDepsMojo { + /** + * Indicates whether the build will continue even if there are jdeps warnings. + */ + @Parameter(defaultValue = "true", property = "jdeps.test.failOnWarning") + private boolean failOnWarning; + + @Override + protected boolean isFailOnWarning() { + return failOnWarning; + } + + @Override + protected String getClassesDirectory() { + return getProject().getBuild().getTestOutputDirectory(); + } + + @Override + protected Collection<Path> getClassPath() throws DependencyResolutionRequiredException { + Set<Path> classPath = + new LinkedHashSet<>(getProject().getTestClasspathElements().size()); + + for (String elm : getProject().getTestClasspathElements()) { + classPath.add(Paths.get(elm)); + } + + return classPath; + } +} diff --git a/src/main/java/org/apache/maven/plugins/jdeps/consumers/JDepsConsumer.java b/src/main/java/org/apache/maven/plugins/jdeps/consumers/JDepsConsumer.java index a251785..622b892 100644 --- a/src/main/java/org/apache/maven/plugins/jdeps/consumers/JDepsConsumer.java +++ b/src/main/java/org/apache/maven/plugins/jdeps/consumers/JDepsConsumer.java @@ -1,98 +1,106 @@ -package org.apache.maven.plugins.jdeps.consumers; - -/* - * 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.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.codehaus.plexus.util.cli.CommandLineUtils; -import org.codehaus.plexus.util.cli.StreamConsumer; - -/** - * Consumes the output of the jdeps tool - * - * @author Robert Scholte - * - */ -public class JDepsConsumer - extends CommandLineUtils.StringStreamConsumer - implements StreamConsumer -{ - - /** - * JDK8 Windows: JDK internal API (rt.jar) - * JDK8 Linux: JDK internal API (JDK removed internal API) - * JDK9: JDK internal API (java.base) - */ - private static final Pattern JDKINTERNALAPI = - Pattern.compile( ".+->\\s([a-z\\.]+)\\s+(JDK (?:removed )?internal API.*)" ); - - /** - * <dl> - * <dt>key</dt><dd>The offending package</dd> - * <dt>value</dt><dd>Offending details</dd> - * </dl> - */ - private Map<String, String> offendingPackages = new HashMap<String, String>(); - - private static final Pattern PROFILE = Pattern.compile( "\\s+->\\s([a-z\\.]+)\\s+(\\S+)" ); - - /** - * <dl> - * <dt>key</dt><dd>The package</dd> - * <dt>value</dt><dd>The profile</dd> - * </dl> - */ - private Map<String, String> profiles = new HashMap<String, String>(); - - - public void consumeLine( String line ) - { - super.consumeLine( line ); - Matcher matcher; - - matcher = JDKINTERNALAPI.matcher( line ); - if ( matcher.matches() ) - { - offendingPackages.put( matcher.group( 1 ), matcher.group( 2 ) ); - return; - } - - matcher = PROFILE.matcher( line ); - if ( matcher.matches() ) - { - profiles.put( matcher.group( 1 ), matcher.group( 2 ) ); - return; - } - } - - public Map<String, String> getOffendingPackages() - { - return offendingPackages; - } - - public Map<String, String> getProfiles() - { - return profiles; - } - -} +/* + * 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. + */ +package org.apache.maven.plugins.jdeps.consumers; + +/* + * 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.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.codehaus.plexus.util.cli.CommandLineUtils; +import org.codehaus.plexus.util.cli.StreamConsumer; + +/** + * Consumes the output of the jdeps tool + * + * @author Robert Scholte + * + */ +public class JDepsConsumer extends CommandLineUtils.StringStreamConsumer implements StreamConsumer { + + /** + * JDK8 Windows: JDK internal API (rt.jar) + * JDK8 Linux: JDK internal API (JDK removed internal API) + * JDK9: JDK internal API (java.base) + */ + private static final Pattern JDKINTERNALAPI = + Pattern.compile(".+->\\s([a-z\\.]+)\\s+(JDK (?:removed )?internal API.*)"); + + /** + * <dl> + * <dt>key</dt><dd>The offending package</dd> + * <dt>value</dt><dd>Offending details</dd> + * </dl> + */ + private Map<String, String> offendingPackages = new HashMap<String, String>(); + + private static final Pattern PROFILE = Pattern.compile("\\s+->\\s([a-z\\.]+)\\s+(\\S+)"); + + /** + * <dl> + * <dt>key</dt><dd>The package</dd> + * <dt>value</dt><dd>The profile</dd> + * </dl> + */ + private Map<String, String> profiles = new HashMap<String, String>(); + + public void consumeLine(String line) { + super.consumeLine(line); + Matcher matcher; + + matcher = JDKINTERNALAPI.matcher(line); + if (matcher.matches()) { + offendingPackages.put(matcher.group(1), matcher.group(2)); + return; + } + + matcher = PROFILE.matcher(line); + if (matcher.matches()) { + profiles.put(matcher.group(1), matcher.group(2)); + return; + } + } + + public Map<String, String> getOffendingPackages() { + return offendingPackages; + } + + public Map<String, String> getProfiles() { + return profiles; + } +} diff --git a/src/test/java/org/apache/maven/plugins/jdeps/consumers/JDepsConsumerTest.java b/src/test/java/org/apache/maven/plugins/jdeps/consumers/JDepsConsumerTest.java index ed60478..479b0b5 100644 --- a/src/test/java/org/apache/maven/plugins/jdeps/consumers/JDepsConsumerTest.java +++ b/src/test/java/org/apache/maven/plugins/jdeps/consumers/JDepsConsumerTest.java @@ -1,94 +1,117 @@ -package org.apache.maven.plugins.jdeps.consumers; - -/* - * 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 static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class JDepsConsumerTest -{ - - private JDepsConsumer consumer; - - @Test - public void testJDKInterAPI() - { - - consumer = new JDepsConsumer(); - consumer.consumeLine( "test-classes -> java.base" ); - consumer.consumeLine( " <unnamed> (test-classes)" ); - consumer.consumeLine( " -> java.io " ); - consumer.consumeLine( " -> java.lang " ); - consumer.consumeLine( " -> sun.misc JDK internal API (java.base)" ); - - assertEquals( 1, consumer.getOffendingPackages().size() ); - assertEquals( "JDK internal API (java.base)", consumer.getOffendingPackages().get( "sun.misc" ) ); - assertEquals( 0, consumer.getProfiles().size() ); - } - - @Test - public void testJDKInternalAPI_Linux_Java8() - { - consumer = new JDepsConsumer(); - consumer.consumeLine( "classes -> JDK removed internal API" ); - consumer.consumeLine( "classes -> java.base" ); - consumer.consumeLine( " <unnamed> -> java.io java.base" ); - consumer.consumeLine( " <unnamed> -> java.lang java.base" ); - consumer.consumeLine( " <unnamed> -> sun.misc JDK internal API (JDK removed internal API)" ); - - assertEquals( 1, consumer.getOffendingPackages().size() ); - assertEquals( "JDK internal API (JDK removed internal API)", consumer.getOffendingPackages().get( "sun.misc" ) ); - assertEquals( 0, consumer.getProfiles().size() ); - } - - @Test - public void testJDKInternalAPI_Java8_291() - { - consumer = new JDepsConsumer(); - consumer.consumeLine( "classes -> JDK removed internal API" ); - consumer.consumeLine( "classes -> java.base" ); - consumer.consumeLine( " <unnamed> -> java.io java.base" ); - consumer.consumeLine( " <unnamed> -> java.lang java.base" ); - consumer.consumeLine( " <unnamed> -> sun.misc JDK removed internal API" ); - - assertEquals( 1, consumer.getOffendingPackages().size() ); - assertEquals( "JDK removed internal API", consumer.getOffendingPackages().get( "sun.misc" ) ); - assertEquals( 0, consumer.getProfiles().size() ); - } - - @Test - public void testProfile() - { - consumer = new JDepsConsumer(); - consumer.consumeLine( "E:\\java-workspace\\apache-maven-plugins\\maven-jdeps-plugin\\target\\classes -> " - + "C:\\Program Files\\Java\\jdk1.8.0\\jre\\lib\\rt.jar (compact1)" ); - consumer.consumeLine( " <unnamed> (classes)" ); - consumer.consumeLine( " -> java.io compact1" ); - consumer.consumeLine( " -> java.lang compact1" ); - consumer.consumeLine( " -> sun.misc JDK internal API (rt.jar)" ); - - assertEquals( 1, consumer.getOffendingPackages().size() ); - assertEquals( "JDK internal API (rt.jar)", consumer.getOffendingPackages().get( "sun.misc" ) ); - assertEquals( 2, consumer.getProfiles().size() ); - assertEquals( "compact1", consumer.getProfiles().get( "java.io" ) ); - assertEquals( "compact1", consumer.getProfiles().get( "java.lang" ) ); - } - -} +/* + * 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. + */ +package org.apache.maven.plugins.jdeps.consumers; + +/* + * 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 org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class JDepsConsumerTest { + + private JDepsConsumer consumer; + + @Test + public void testJDKInterAPI() { + + consumer = new JDepsConsumer(); + consumer.consumeLine("test-classes -> java.base"); + consumer.consumeLine(" <unnamed> (test-classes)"); + consumer.consumeLine(" -> java.io "); + consumer.consumeLine(" -> java.lang "); + consumer.consumeLine( + " -> sun.misc JDK internal API (java.base)"); + + assertEquals(1, consumer.getOffendingPackages().size()); + assertEquals( + "JDK internal API (java.base)", consumer.getOffendingPackages().get("sun.misc")); + assertEquals(0, consumer.getProfiles().size()); + } + + @Test + public void testJDKInternalAPI_Linux_Java8() { + consumer = new JDepsConsumer(); + consumer.consumeLine("classes -> JDK removed internal API"); + consumer.consumeLine("classes -> java.base"); + consumer.consumeLine( + " <unnamed> -> java.io java.base"); + consumer.consumeLine( + " <unnamed> -> java.lang java.base"); + consumer.consumeLine( + " <unnamed> -> sun.misc JDK internal API (JDK removed internal API)"); + + assertEquals(1, consumer.getOffendingPackages().size()); + assertEquals( + "JDK internal API (JDK removed internal API)", + consumer.getOffendingPackages().get("sun.misc")); + assertEquals(0, consumer.getProfiles().size()); + } + + @Test + public void testJDKInternalAPI_Java8_291() { + consumer = new JDepsConsumer(); + consumer.consumeLine("classes -> JDK removed internal API"); + consumer.consumeLine("classes -> java.base"); + consumer.consumeLine( + " <unnamed> -> java.io java.base"); + consumer.consumeLine( + " <unnamed> -> java.lang java.base"); + consumer.consumeLine( + " <unnamed> -> sun.misc JDK removed internal API"); + + assertEquals(1, consumer.getOffendingPackages().size()); + assertEquals("JDK removed internal API", consumer.getOffendingPackages().get("sun.misc")); + assertEquals(0, consumer.getProfiles().size()); + } + + @Test + public void testProfile() { + consumer = new JDepsConsumer(); + consumer.consumeLine("E:\\java-workspace\\apache-maven-plugins\\maven-jdeps-plugin\\target\\classes -> " + + "C:\\Program Files\\Java\\jdk1.8.0\\jre\\lib\\rt.jar (compact1)"); + consumer.consumeLine(" <unnamed> (classes)"); + consumer.consumeLine(" -> java.io compact1"); + consumer.consumeLine(" -> java.lang compact1"); + consumer.consumeLine(" -> sun.misc JDK internal API (rt.jar)"); + + assertEquals(1, consumer.getOffendingPackages().size()); + assertEquals( + "JDK internal API (rt.jar)", consumer.getOffendingPackages().get("sun.misc")); + assertEquals(2, consumer.getProfiles().size()); + assertEquals("compact1", consumer.getProfiles().get("java.io")); + assertEquals("compact1", consumer.getProfiles().get("java.lang")); + } +}