This is an automated email from the ASF dual-hosted git repository. sjaranowski pushed a commit to branch MSHARED-1393 in repository https://gitbox.apache.org/repos/asf/maven-dependency-analyzer.git
commit 2e4b082690a91f530bac4c0490ba42ee52cfb79b Author: Slawomir Jaranowski <[email protected]> AuthorDate: Thu May 9 21:42:36 2024 +0200 [MSHARED-1393] Allow to exclude classes from verification --- pom.xml | 6 ++ src/it/excludeClassFromJar/pom.xml | 64 ++++++++++++++++++++++ .../jarWithXmlTransitiveDependency/Project.java} | 36 ++++++------ .../excludeClassFromJar/verify.groovy} | 31 ++++------- src/it/excludeClassFromProject/pom.xml | 56 +++++++++++++++++++ .../excludeClassFromProject/setup.groovy} | 24 ++------ .../excludeClassFromProject/verify.groovy} | 30 ++++------ .../src/main/java/it/test/MockAnalyzeMojo.java | 6 +- .../shared/dependency/analyzer/ClassAnalyzer.java | 13 ++++- ...rClassFileVisitor.java => ClassesPatterns.java} | 50 ++++++++--------- .../analyzer/CollectorClassFileVisitor.java | 9 ++- .../dependency/analyzer/DefaultClassAnalyzer.java | 5 +- .../analyzer/DefaultProjectDependencyAnalyzer.java | 34 +++++++----- .../dependency/analyzer/DependencyAnalyzer.java | 16 +++++- .../analyzer/ProjectDependencyAnalyzer.java | 18 +++++- .../analyzer/asm/ASMDependencyAnalyzer.java | 7 ++- .../analyzer/asm/DependencyClassFileVisitor.java | 32 ++++++++--- .../VisitClassException.java} | 25 +++------ .../dependency/analyzer/ClassesPatternsTest.java} | 47 ++++++++++------ .../analyzer/asm/ASMDependencyAnalyzerTest.java | 15 +++++ .../analyzer/asm/ResultCollectorTest.java | 62 +++++++-------------- .../testcases/analyze/ClassToExclude.java} | 22 ++------ 22 files changed, 379 insertions(+), 229 deletions(-) diff --git a/pom.xml b/pom.xml index 7df05e5..0887f4e 100644 --- a/pom.xml +++ b/pom.xml @@ -122,6 +122,11 @@ <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> @@ -169,6 +174,7 @@ <localRepositoryPath>target/local-repo</localRepositoryPath> <settingsFile>src/it/settings.xml</settingsFile> <postBuildHookScript>verify</postBuildHookScript> + <preBuildHookScript>setup</preBuildHookScript> <goals> <goal>verify</goal> </goals> diff --git a/src/it/excludeClassFromJar/pom.xml b/src/it/excludeClassFromJar/pom.xml new file mode 100644 index 0000000..7165d0a --- /dev/null +++ b/src/it/excludeClassFromJar/pom.xml @@ -0,0 +1,64 @@ +<?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 + ~ 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. + --> + +<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> + + <groupId>org.apache.maven.shared.dependency-analyzer.tests</groupId> + <artifactId>jarWithXercesDependencies</artifactId> + <version>1.0</version> + <packaging>jar</packaging> + + <dependencies> + <dependency> + <groupId>dom4j</groupId> + <artifactId>dom4j</artifactId> + <version>1.6.1</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.shared.dependency-analyzer.tests</groupId> + <artifactId>maven-mock-plugin</artifactId> + <version>1.0</version> + <executions> + <execution> + <goals> + <goal>mock-analyze</goal> + </goals> + </execution> + </executions> + <configuration> + <excludedClasses> + <exclude>org.xml.sax.*</exclude> + </excludedClasses> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java b/src/it/excludeClassFromJar/src/main/java/jarWithXmlTransitiveDependency/Project.java similarity index 55% copy from src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java copy to src/it/excludeClassFromJar/src/main/java/jarWithXmlTransitiveDependency/Project.java index 679b4d6..35cd164 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java +++ b/src/it/excludeClassFromJar/src/main/java/jarWithXmlTransitiveDependency/Project.java @@ -1,3 +1,5 @@ +package jarWithXmlTransitiveDependency; + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -7,7 +9,7 @@ * "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 + * 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 @@ -16,25 +18,25 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.shared.dependency.analyzer; -import java.io.IOException; -import java.net.URL; -import java.util.Set; +import org.dom4j.Text; +import org.xml.sax.Parser; /** - * Gets the set of classes contained in a library given either as a jar file or an exploded directory. - * - * @author <a href="mailto:[email protected]">Mark Hobson</a> + * Dependency on dom4j gives xml-apis transitive dependency, which contains SAX Parser class. But SAX Parser is available in + * JDK: no need to declare a direct dependency. + * */ -public interface ClassAnalyzer { +public class Project +{ + public Text text; + + public Parser parser; + + // constructors ----------------------------------------------------------- - /** - * <p>analyze.</p> - * - * @param url the JAR file or directory to analyze - * @return a {@link java.util.Set} object - * @throws java.io.IOException if any - */ - Set<String> analyze(URL url) throws IOException; + public Project() + { + // no op + } } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java b/src/it/excludeClassFromJar/verify.groovy similarity index 58% copy from src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java copy to src/it/excludeClassFromJar/verify.groovy index 679b4d6..088383c 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java +++ b/src/it/excludeClassFromJar/verify.groovy @@ -16,25 +16,18 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.shared.dependency.analyzer; -import java.io.IOException; -import java.net.URL; -import java.util.Set; +def analysis = new File( basedir, 'target/analysis.txt' ).text -/** - * Gets the set of classes contained in a library given either as a jar file or an exploded directory. - * - * @author <a href="mailto:[email protected]">Mark Hobson</a> - */ -public interface ClassAnalyzer { +def expected = ''' +UsedDeclaredArtifacts: + dom4j:dom4j:jar:1.6.1:compile + +UsedUndeclaredArtifactsWithClasses: + +UnusedDeclaredArtifacts: + +TestArtifactsWithNonTestScope: +''' - /** - * <p>analyze.</p> - * - * @param url the JAR file or directory to analyze - * @return a {@link java.util.Set} object - * @throws java.io.IOException if any - */ - Set<String> analyze(URL url) throws IOException; -} +assert analysis == expected diff --git a/src/it/excludeClassFromProject/pom.xml b/src/it/excludeClassFromProject/pom.xml new file mode 100644 index 0000000..3885761 --- /dev/null +++ b/src/it/excludeClassFromProject/pom.xml @@ -0,0 +1,56 @@ +<?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 + ~ 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. + --> + +<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> + + <groupId>org.apache.maven.shared.dependency-analyzer.tests</groupId> + <artifactId>jarWithXercesDependencies</artifactId> + <version>1.0</version> + <packaging>jar</packaging> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.shared.dependency-analyzer.tests</groupId> + <artifactId>maven-mock-plugin</artifactId> + <version>1.0</version> + <executions> + <execution> + <goals> + <goal>mock-analyze</goal> + </goals> + </execution> + </executions> + <configuration> + <excludedClasses> + <exclude>org.example.BadClass</exclude> + </excludedClasses> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java b/src/it/excludeClassFromProject/setup.groovy similarity index 58% copy from src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java copy to src/it/excludeClassFromProject/setup.groovy index 679b4d6..f6eec9b 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java +++ b/src/it/excludeClassFromProject/setup.groovy @@ -16,25 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.shared.dependency.analyzer; +def badClass = new File(basedir, 'target/classes/org/example/BadClass.class') -import java.io.IOException; -import java.net.URL; -import java.util.Set; +badClass.getParentFile().mkdirs() +badClass << 'some content' -/** - * Gets the set of classes contained in a library given either as a jar file or an exploded directory. - * - * @author <a href="mailto:[email protected]">Mark Hobson</a> - */ -public interface ClassAnalyzer { - - /** - * <p>analyze.</p> - * - * @param url the JAR file or directory to analyze - * @return a {@link java.util.Set} object - * @throws java.io.IOException if any - */ - Set<String> analyze(URL url) throws IOException; -} +assert badClass.isFile() diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java b/src/it/excludeClassFromProject/verify.groovy similarity index 58% copy from src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java copy to src/it/excludeClassFromProject/verify.groovy index 679b4d6..e545857 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java +++ b/src/it/excludeClassFromProject/verify.groovy @@ -16,25 +16,17 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.shared.dependency.analyzer; -import java.io.IOException; -import java.net.URL; -import java.util.Set; +def analysis = new File( basedir, 'target/analysis.txt' ).text -/** - * Gets the set of classes contained in a library given either as a jar file or an exploded directory. - * - * @author <a href="mailto:[email protected]">Mark Hobson</a> - */ -public interface ClassAnalyzer { +def expected = ''' +UsedDeclaredArtifacts: + +UsedUndeclaredArtifactsWithClasses: + +UnusedDeclaredArtifacts: + +TestArtifactsWithNonTestScope: +''' - /** - * <p>analyze.</p> - * - * @param url the JAR file or directory to analyze - * @return a {@link java.util.Set} object - * @throws java.io.IOException if any - */ - Set<String> analyze(URL url) throws IOException; -} +assert analysis == expected diff --git a/src/it/setup-mock-plugin/src/main/java/it/test/MockAnalyzeMojo.java b/src/it/setup-mock-plugin/src/main/java/it/test/MockAnalyzeMojo.java index 8d9fe65..27d78ee 100644 --- a/src/it/setup-mock-plugin/src/main/java/it/test/MockAnalyzeMojo.java +++ b/src/it/setup-mock-plugin/src/main/java/it/test/MockAnalyzeMojo.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.nio.file.Files; +import java.util.Set; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; @@ -63,12 +64,15 @@ public class MockAnalyzeMojo extends AbstractMojo @Parameter( defaultValue = "${project.build.directory}/analysis.txt", readonly = true ) private File output; + @Parameter + private Set<String> excludedClasses; + @Override public void execute() throws MojoExecutionException, MojoFailureException { try { - ProjectDependencyAnalysis analysis = analyzer.analyze( project ); + ProjectDependencyAnalysis analysis = analyzer.analyze( project, excludedClasses ); Files.createDirectories( output.toPath().getParent() ); try ( PrintWriter printWriter = new UnixPrintWiter( output ) ) diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java index 679b4d6..af2b154 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java @@ -36,5 +36,16 @@ public interface ClassAnalyzer { * @return a {@link java.util.Set} object * @throws java.io.IOException if any */ - Set<String> analyze(URL url) throws IOException; + default Set<String> analyze(URL url) throws IOException { + return analyze(url, new ClassesPatterns()); + } + + /** + * <p>analyze.</p> + * + * @param url the JAR file or directory to analyze + * @return a {@link java.util.Set} object + * @throws java.io.IOException if any + */ + Set<String> analyze(URL url, ClassesPatterns excludedClasses) throws IOException; } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitor.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassesPatterns.java similarity index 50% copy from src/main/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitor.java copy to src/main/java/org/apache/maven/shared/dependency/analyzer/ClassesPatterns.java index 4adf2d2..ce69930 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitor.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassesPatterns.java @@ -18,41 +18,39 @@ */ package org.apache.maven.shared.dependency.analyzer; -import java.io.InputStream; -import java.util.HashSet; -import java.util.Set; +import java.util.Collection; +import java.util.Collections; +import java.util.regex.Pattern; +import java.util.stream.Collectors; /** - * Simply collects the set of visited classes. - * - * @author <a href="mailto:[email protected]">Mark Hobson</a> - * @see #getClasses() + * Patterns for classes */ -public class CollectorClassFileVisitor implements ClassFileVisitor { - private final Set<String> classes; +public class ClassesPatterns { + + private final Collection<Pattern> patterns; /** - * <p>Constructor for CollectorClassFileVisitor.</p> + * Default constructor. + * + * @param patterns a patterns to mach */ - public CollectorClassFileVisitor() { - classes = new HashSet<>(); + public ClassesPatterns(Collection<String> patterns) { + if (patterns == null) { + this.patterns = Collections.emptyList(); + } else { + this.patterns = patterns.stream().map(Pattern::compile).collect(Collectors.toSet()); + } } - /** {@inheritDoc} */ - @Override - public void visitClass(String className, InputStream in) { - // inner classes have equivalent compilation requirement as container class - if (className.indexOf('$') < 0) { - classes.add(className); - } + public ClassesPatterns() { + this.patterns = Collections.emptySet(); } - /** - * <p>Getter for the field <code>classes</code>.</p> - * - * @return a {@link java.util.Set} object. - */ - public Set<String> getClasses() { - return classes; + public boolean isMatch(String className) { + if (patterns.isEmpty()) { + return false; + } + return patterns.stream().anyMatch(pattern -> pattern.matcher(className).matches()); } } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitor.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitor.java index 4adf2d2..4af2ff4 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitor.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/CollectorClassFileVisitor.java @@ -31,18 +31,25 @@ import java.util.Set; public class CollectorClassFileVisitor implements ClassFileVisitor { private final Set<String> classes; + private final ClassesPatterns excludedClasses; + /** * <p>Constructor for CollectorClassFileVisitor.</p> */ public CollectorClassFileVisitor() { + this(new ClassesPatterns()); + } + + public CollectorClassFileVisitor(ClassesPatterns excludedClasses) { classes = new HashSet<>(); + this.excludedClasses = excludedClasses; } /** {@inheritDoc} */ @Override public void visitClass(String className, InputStream in) { // inner classes have equivalent compilation requirement as container class - if (className.indexOf('$') < 0) { + if (className.indexOf('$') < 0 && !excludedClasses.isMatch(className)) { classes.add(className); } } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultClassAnalyzer.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultClassAnalyzer.java index dfbed05..4302279 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultClassAnalyzer.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultClassAnalyzer.java @@ -35,10 +35,9 @@ import java.util.zip.ZipException; @Singleton public class DefaultClassAnalyzer implements ClassAnalyzer { - /** {@inheritDoc} */ @Override - public Set<String> analyze(URL url) throws IOException { - CollectorClassFileVisitor visitor = new CollectorClassFileVisitor(); + public Set<String> analyze(URL url, ClassesPatterns excludedClasses) throws IOException { + CollectorClassFileVisitor visitor = new CollectorClassFileVisitor(excludedClasses); try { ClassFileVisitorUtils.accept(url, visitor); diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzer.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzer.java index 489afd1..c792571 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzer.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/DefaultProjectDependencyAnalyzer.java @@ -25,6 +25,7 @@ import javax.inject.Singleton; import java.io.File; import java.io.IOException; import java.net.URL; +import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -61,12 +62,14 @@ public class DefaultProjectDependencyAnalyzer implements ProjectDependencyAnalyz /** {@inheritDoc} */ @Override - public ProjectDependencyAnalysis analyze(MavenProject project) throws ProjectDependencyAnalyzerException { + public ProjectDependencyAnalysis analyze(MavenProject project, Collection<String> excludedClasses) + throws ProjectDependencyAnalyzerException { try { - Map<Artifact, Set<String>> artifactClassMap = buildArtifactClassMap(project); + ClassesPatterns excludedClassesPatterns = new ClassesPatterns(excludedClasses); + Map<Artifact, Set<String>> artifactClassMap = buildArtifactClassMap(project, excludedClassesPatterns); - Set<String> mainDependencyClasses = buildMainDependencyClasses(project); - Set<String> testDependencyClasses = buildTestDependencyClasses(project); + Set<String> mainDependencyClasses = buildMainDependencyClasses(project, excludedClassesPatterns); + Set<String> testDependencyClasses = buildTestDependencyClasses(project, excludedClassesPatterns); Set<String> dependencyClasses = new HashSet<>(); dependencyClasses.addAll(mainDependencyClasses); @@ -146,7 +149,8 @@ public class DefaultProjectDependencyAnalyzer implements ProjectDependencyAnalyz return nonTestScopeArtifacts; } - private Map<Artifact, Set<String>> buildArtifactClassMap(MavenProject project) throws IOException { + private Map<Artifact, Set<String>> buildArtifactClassMap(MavenProject project, ClassesPatterns excludedClasses) + throws IOException { Map<Artifact, Set<String>> artifactClassMap = new LinkedHashMap<>(); Set<Artifact> dependencyArtifacts = project.getArtifacts(); @@ -167,7 +171,9 @@ public class DefaultProjectDependencyAnalyzer implements ProjectDependencyAnalyz if (entry.endsWith(".class")) { String className = entry.replace('/', '.'); className = className.substring(0, className.length() - ".class".length()); - classes.add(className); + if (!excludedClasses.isMatch(className)) { + classes.add(className); + } } } @@ -175,7 +181,7 @@ public class DefaultProjectDependencyAnalyzer implements ProjectDependencyAnalyz } } else if (file != null && file.isDirectory()) { URL url = file.toURI().toURL(); - Set<String> classes = classAnalyzer.analyze(url); + Set<String> classes = classAnalyzer.analyze(url, excludedClasses); artifactClassMap.put(artifact, classes); } @@ -191,20 +197,22 @@ public class DefaultProjectDependencyAnalyzer implements ProjectDependencyAnalyz return testOnlyDependencyClasses; } - private Set<String> buildMainDependencyClasses(MavenProject project) throws IOException { + private Set<String> buildMainDependencyClasses(MavenProject project, ClassesPatterns excludedClasses) + throws IOException { String outputDirectory = project.getBuild().getOutputDirectory(); - return buildDependencyClasses(outputDirectory); + return buildDependencyClasses(outputDirectory, excludedClasses); } - private Set<String> buildTestDependencyClasses(MavenProject project) throws IOException { + private Set<String> buildTestDependencyClasses(MavenProject project, ClassesPatterns excludedClasses) + throws IOException { String testOutputDirectory = project.getBuild().getTestOutputDirectory(); - return buildDependencyClasses(testOutputDirectory); + return buildDependencyClasses(testOutputDirectory, excludedClasses); } - private Set<String> buildDependencyClasses(String path) throws IOException { + private Set<String> buildDependencyClasses(String path, ClassesPatterns excludedClasses) throws IOException { URL url = new File(path).toURI().toURL(); - return dependencyAnalyzer.analyze(url); + return dependencyAnalyzer.analyze(url, excludedClasses); } private static Set<Artifact> buildDeclaredArtifacts(MavenProject project) { diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyAnalyzer.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyAnalyzer.java index d366c6b..22a6a27 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyAnalyzer.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyAnalyzer.java @@ -32,9 +32,21 @@ public interface DependencyAnalyzer { /** * <p>analyze.</p> * - * @param url the JAR file or directory to analyze + * @param url the JAR file or directory to analyze * @return the set of class names referenced by the library * @throws IOException if an error occurs reading a JAR or .class file */ - Set<String> analyze(URL url) throws IOException; + default Set<String> analyze(URL url) throws IOException { + return analyze(url, new ClassesPatterns()); + } + + /** + * <p>analyze.</p> + * + * @param url the JAR file or directory to analyze + * @param excludeClasses a class list to exclude + * @return the set of class names referenced by the library + * @throws IOException if an error occurs reading a JAR or .class file + */ + Set<String> analyze(URL url, ClassesPatterns excludeClasses) throws IOException; } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/ProjectDependencyAnalyzer.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/ProjectDependencyAnalyzer.java index 9503ea3..4b0d84a 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/ProjectDependencyAnalyzer.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/ProjectDependencyAnalyzer.java @@ -18,6 +18,8 @@ */ package org.apache.maven.shared.dependency.analyzer; +import java.util.Collection; + import org.apache.maven.project.MavenProject; /** @@ -32,12 +34,26 @@ import org.apache.maven.project.MavenProject; * @author <a href="mailto:[email protected]">Mark Hobson</a> */ public interface ProjectDependencyAnalyzer { + + /** + * <p>analyze.</p> + * + * @param project a {@link org.apache.maven.project.MavenProject} object + * @return a {@link org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalysis} object + * @throws org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzerException if any + */ + default ProjectDependencyAnalysis analyze(MavenProject project) throws ProjectDependencyAnalyzerException { + return analyze(project, null); + } + /** * <p>analyze.</p> * * @param project a {@link org.apache.maven.project.MavenProject} object + * @param excludedClasses collection of regular expression of classes name to exclude * @return a {@link org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalysis} object * @throws org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzerException if any */ - ProjectDependencyAnalysis analyze(MavenProject project) throws ProjectDependencyAnalyzerException; + ProjectDependencyAnalysis analyze(MavenProject project, Collection<String> excludedClasses) + throws ProjectDependencyAnalyzerException; } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzer.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzer.java index efd4869..7b542cd 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzer.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzer.java @@ -26,6 +26,7 @@ import java.net.URL; import java.util.Set; import org.apache.maven.shared.dependency.analyzer.ClassFileVisitorUtils; +import org.apache.maven.shared.dependency.analyzer.ClassesPatterns; import org.apache.maven.shared.dependency.analyzer.DependencyAnalyzer; /** @@ -36,10 +37,10 @@ import org.apache.maven.shared.dependency.analyzer.DependencyAnalyzer; @Named @Singleton public class ASMDependencyAnalyzer implements DependencyAnalyzer { - /** {@inheritDoc} */ + @Override - public Set<String> analyze(URL url) throws IOException { - DependencyClassFileVisitor visitor = new DependencyClassFileVisitor(); + public Set<String> analyze(URL url, ClassesPatterns excludeClasses) throws IOException { + DependencyClassFileVisitor visitor = new DependencyClassFileVisitor(excludeClasses); ClassFileVisitorUtils.accept(url, visitor); diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyClassFileVisitor.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyClassFileVisitor.java index 8f70116..084022a 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyClassFileVisitor.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/DependencyClassFileVisitor.java @@ -20,18 +20,18 @@ package org.apache.maven.shared.dependency.analyzer.asm; import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; import java.util.Set; import org.apache.commons.io.IOUtils; import org.apache.maven.shared.dependency.analyzer.ClassFileVisitor; +import org.apache.maven.shared.dependency.analyzer.ClassesPatterns; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.signature.SignatureVisitor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Computes the set of classes referenced by visited class files, using @@ -43,18 +43,33 @@ import org.slf4j.LoggerFactory; public class DependencyClassFileVisitor implements ClassFileVisitor { private final ResultCollector resultCollector = new ResultCollector(); - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final ClassesPatterns excludedClasses; /** * <p>Constructor for DependencyClassFileVisitor.</p> */ - public DependencyClassFileVisitor() {} + public DependencyClassFileVisitor(ClassesPatterns excludedClasses) { + + this.excludedClasses = excludedClasses; + } + + /** + * <p>Constructor for DependencyClassFileVisitor.</p> + */ + public DependencyClassFileVisitor() { + this(new ClassesPatterns()); + } /** {@inheritDoc} */ @Override public void visitClass(String className, InputStream in) { try { byte[] byteCode = IOUtils.toByteArray(in); + + if (excludedClasses.isMatch(className)) { + return; + } + ClassReader reader = new ClassReader(byteCode); final Set<String> constantPoolClassRefs = ConstantPoolParser.getConstantPoolClassReferences(byteCode); @@ -71,14 +86,13 @@ public class DependencyClassFileVisitor implements ClassFileVisitor { reader.accept(classVisitor, 0); } catch (IOException exception) { - exception.printStackTrace(); + throw new UncheckedIOException(exception); } catch (IndexOutOfBoundsException e) { - // some bug inside ASM causes an IOB exception. Log it and move on? + // some bug inside ASM causes an IOB exception. // this happens when the class isn't valid. - logger.warn("Unable to process: " + className, e); + throw new VisitClassException("Unable to process: " + className, e); } catch (IllegalArgumentException e) { - // [MSHARED-1248] should log instead of failing when analyzing a corrupted jar file - logger.warn("Byte code of '" + className + "' is corrupt", e); + throw new VisitClassException("Byte code of '" + className + "' is corrupt", e); } } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/VisitClassException.java similarity index 60% copy from src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java copy to src/main/java/org/apache/maven/shared/dependency/analyzer/asm/VisitClassException.java index 679b4d6..d68bf99 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java +++ b/src/main/java/org/apache/maven/shared/dependency/analyzer/asm/VisitClassException.java @@ -16,25 +16,18 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.shared.dependency.analyzer; - -import java.io.IOException; -import java.net.URL; -import java.util.Set; +package org.apache.maven.shared.dependency.analyzer.asm; /** - * Gets the set of classes contained in a library given either as a jar file or an exploded directory. - * - * @author <a href="mailto:[email protected]">Mark Hobson</a> + * Exception for processing class. */ -public interface ClassAnalyzer { - +public class VisitClassException extends RuntimeException { /** - * <p>analyze.</p> - * - * @param url the JAR file or directory to analyze - * @return a {@link java.util.Set} object - * @throws java.io.IOException if any + * A constructor + * @param message message + * @param cause cause of exception */ - Set<String> analyze(URL url) throws IOException; + public VisitClassException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyAnalyzer.java b/src/test/java/org/apache/maven/shared/dependency/analyzer/ClassesPatternsTest.java similarity index 50% copy from src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyAnalyzer.java copy to src/test/java/org/apache/maven/shared/dependency/analyzer/ClassesPatternsTest.java index d366c6b..19f6d4c 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/DependencyAnalyzer.java +++ b/src/test/java/org/apache/maven/shared/dependency/analyzer/ClassesPatternsTest.java @@ -18,23 +18,34 @@ */ package org.apache.maven.shared.dependency.analyzer; -import java.io.IOException; -import java.net.URL; -import java.util.Set; +import java.util.Arrays; -/** - * Gets the set of classes referenced by a library given either as a jar file or an exploded directory. - * - * @author <a href="mailto:[email protected]">Mark Hobson</a> - */ -public interface DependencyAnalyzer { - - /** - * <p>analyze.</p> - * - * @param url the JAR file or directory to analyze - * @return the set of class names referenced by the library - * @throws IOException if an error occurs reading a JAR or .class file - */ - Set<String> analyze(URL url) throws IOException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ClassesPatternsTest { + + @Test + void classPatternsTest() { + ClassesPatterns classesPatterns = new ClassesPatterns(Arrays.asList("Test1.*", "io.example.test.Test2")); + + assertTrue(classesPatterns.isMatch("Test1.Test2")); + assertFalse(classesPatterns.isMatch("Test2.Test2")); + assertTrue(classesPatterns.isMatch("io.example.test.Test2")); + } + + @Test + void emptyClassPatternsTest() { + ClassesPatterns classesPatterns = new ClassesPatterns(); + + assertFalse(classesPatterns.isMatch("Test")); + } + + @Test + void nullClassPatternsTest() { + ClassesPatterns classesPatterns = new ClassesPatterns(null); + + assertFalse(classesPatterns.isMatch("Test")); + } } diff --git a/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzerTest.java b/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzerTest.java index 0578331..30d19c5 100644 --- a/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzerTest.java +++ b/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ASMDependencyAnalyzerTest.java @@ -22,8 +22,10 @@ import java.io.IOException; import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.Set; +import org.apache.maven.shared.dependency.analyzer.ClassesPatterns; import org.apache.maven.shared.dependency.analyzer.DependencyAnalyzer; import org.junit.jupiter.api.Test; @@ -50,5 +52,18 @@ class ASMDependencyAnalyzerTest { Set<String> result = analyzer.analyze(file.toUri().toURL()); assertThat(result).contains("org.apache.maven.artifact.resolver.ArtifactResolutionRequest"); + assertThat(result).contains("java.util.regex.Pattern"); + } + + @Test + void verify_excluded_classes() throws IOException { + Path file = Paths.get("target/test-classes/org/apache/maven/shared/dependency/analyzer/testcases/analyze"); + + Set<String> result = + analyzer.analyze(file.toUri().toURL(), new ClassesPatterns(Collections.singleton("ClassToExclude"))); + assertThat(result).contains("org.apache.maven.artifact.resolver.ArtifactResolutionRequest"); + assertThat(result).doesNotContain("java.util.regex.Pattern"); + assertThat(result) + .doesNotContain("org.apache.maven.shared.dependency.analyzer.testcases.analyze.ClassToExclude"); } } diff --git a/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollectorTest.java b/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollectorTest.java index 791d593..7d8dc86 100644 --- a/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollectorTest.java +++ b/src/test/java/org/apache/maven/shared/dependency/analyzer/asm/ResultCollectorTest.java @@ -29,8 +29,11 @@ import org.apache.maven.shared.dependency.analyzer.testcases.ArrayCases; import org.apache.maven.shared.dependency.analyzer.testcases.InnerClassCase; import org.apache.maven.shared.dependency.analyzer.testcases.MethodHandleCases; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; class ResultCollectorTest { @@ -56,50 +59,23 @@ class ResultCollectorTest { } } - @Test - public void testOssFuzz51980() throws IOException { + @ParameterizedTest + @ValueSource( + strings = { + "issue51980", + "issue51989", + "issue52168", + "issue53543", + "issue53544a", + "issue53620", + "issue53676", + "issue54119", + "issue54254" + }) + void testOssFuzz(String name) { // Add a non-"class" suffix so that surefire does not try to read the file and fail the build - visitClass(ROOT + "/ossfuzz/issue51980/Test.class.clazz"); - } - - @Test - public void testOssFuzz51989() throws IOException { - visitClass(ROOT + "/ossfuzz/issue51989/Test.class.clazz"); - } - - @Test - public void testOssFuzz52168() throws IOException { - visitClass(ROOT + "/ossfuzz/issue52168/Test.class.clazz"); - } - - @Test - public void testOssFuzz53543() throws IOException { - visitClass(ROOT + "/ossfuzz/issue53543/Test.class.clazz"); - } - - @Test - public void testOssFuzz53544a() throws IOException { - visitClass(ROOT + "/ossfuzz/issue53544a/Test.class.clazz"); - } - - @Test - public void testOssFuzz53620() throws IOException { - visitClass(ROOT + "/ossfuzz/issue53620/Test.class.clazz"); - } - - @Test - public void testOssFuzz53676() throws IOException { - visitClass(ROOT + "/ossfuzz/issue53676/Test.class.clazz"); - } - - @Test - public void testOssFuzz54199() throws IOException { - visitClass(ROOT + "/ossfuzz/issue54119/Test.class.clazz"); - } - - @Test - public void testOssFuzz54254() throws IOException { - visitClass(ROOT + "/ossfuzz/issue54254/Test.class.clazz"); + assertThatCode(() -> visitClass(ROOT + "/ossfuzz/" + name + "/Test.class.clazz")) + .isExactlyInstanceOf(VisitClassException.class); } private void visitClass(String location) throws IOException { diff --git a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java b/src/test/java/org/apache/maven/shared/dependency/analyzer/testcases/analyze/ClassToExclude.java similarity index 58% copy from src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java copy to src/test/java/org/apache/maven/shared/dependency/analyzer/testcases/analyze/ClassToExclude.java index 679b4d6..bf1771d 100644 --- a/src/main/java/org/apache/maven/shared/dependency/analyzer/ClassAnalyzer.java +++ b/src/test/java/org/apache/maven/shared/dependency/analyzer/testcases/analyze/ClassToExclude.java @@ -16,25 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.maven.shared.dependency.analyzer; +package org.apache.maven.shared.dependency.analyzer.testcases.analyze; -import java.io.IOException; -import java.net.URL; -import java.util.Set; +import java.util.regex.Pattern; /** - * Gets the set of classes contained in a library given either as a jar file or an exploded directory. - * - * @author <a href="mailto:[email protected]">Mark Hobson</a> + * Class to be skipped during analyzed in unit test. */ -public interface ClassAnalyzer { - - /** - * <p>analyze.</p> - * - * @param url the JAR file or directory to analyze - * @return a {@link java.util.Set} object - * @throws java.io.IOException if any - */ - Set<String> analyze(URL url) throws IOException; +public class ClassToExclude { + private void doNothing(final Pattern pattern) {} }
