diff --git a/pom.xml b/pom.xml
index c0fbfc7ac..2eddc41a3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,7 +65,7 @@ under the License.
- 3.6.3
+ ${mavenVersion}
@@ -95,6 +95,9 @@ under the License.
1.9.24
1.7.36
9.4.58.v20250814
+ 9.4.57.v20241219
+ 1.2.21
+ 2.0.1
4.11.0
4.10.1
3.3.0
@@ -152,6 +155,11 @@ under the License.
doxia-sink-api
2.0.0
+
+ org.ow2.asm
+ asm
+ 9.8
+
@@ -327,7 +335,14 @@ under the License.
org.glassfish
jakarta.json
- 2.0.1
+ ${glassfishVersion}
+ test
+
+
+ org.apache.johnzon
+ johnzon-core
+ ${johnzonVersion}
+ jakarta
test
@@ -406,6 +421,18 @@ under the License.
${slf4jVersion}
test
+
+ org.tomitribe
+ tomitribe-util
+ 1.3.10
+ test
+
+
+ org.codehaus.plexus
+ plexus-classworlds
+ 2.9.0
+ test
+
@@ -470,6 +497,11 @@ under the License.
-Xmx512m
+
+ ${slf4jVersion}
+ ${johnzonVersion}
+ ${glassfishVersion}
+
2
true
diff --git a/src/it/projects/analyze/pom.xml b/src/it/projects/analyze/pom.xml
index e3072ae88..e3a134aea 100644
--- a/src/it/projects/analyze/pom.xml
+++ b/src/it/projects/analyze/pom.xml
@@ -52,12 +52,18 @@
maven-model
2.0.6
-
+
org.slf4j
slf4j-simple
2.0.16
+
+
+ org.glassfish
+ javax.json
+ 1.1.4
+
diff --git a/src/it/projects/analyze/verify.groovy b/src/it/projects/analyze/verify.groovy
index 4b430120a..68af167b8 100644
--- a/src/it/projects/analyze/verify.groovy
+++ b/src/it/projects/analyze/verify.groovy
@@ -36,8 +36,5 @@ assert buildLog.contains( '[WARNING] org.apache.maven:maven-repository-metada
assert buildLog.contains( '[WARNING] class org.apache.maven.artifact.repository.metadata.Metadata')
assert buildLog.contains( '[WARNING] Unused declared dependencies found:')
assert buildLog.contains( '[WARNING] org.apache.maven:maven-project:jar:2.0.6:compile')
-assert buildLog.contains( '[INFO] Ignored unused declared dependencies:')
-assert buildLog.contains( '[INFO] org.slf4j:slf4j-simple:jar:2.0.16:compile')
-assert !buildLog.contains( '[WARNING] org.slf4j:slf4j-simple:jar:2.0.16:compile')
return true
diff --git a/src/main/java/org/apache/maven/plugins/dependency/analyze/AbstractAnalyzeMojo.java b/src/main/java/org/apache/maven/plugins/dependency/analyze/AbstractAnalyzeMojo.java
index ac79d7178..fffd71819 100644
--- a/src/main/java/org/apache/maven/plugins/dependency/analyze/AbstractAnalyzeMojo.java
+++ b/src/main/java/org/apache/maven/plugins/dependency/analyze/AbstractAnalyzeMojo.java
@@ -19,15 +19,23 @@
package org.apache.maven.plugins.dependency.analyze;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Stream;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
@@ -43,6 +51,17 @@
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.ModuleVisitor;
+import org.objectweb.asm.Type;
+
+import static java.util.Collections.list;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static org.objectweb.asm.Opcodes.ASM9;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
/**
* Analyzes the dependencies of this project and determines which are: used and declared; used and undeclared; unused
@@ -214,16 +233,11 @@ public abstract class AbstractAnalyzeMojo extends AbstractMojo {
* org.apache., and :::*-SNAPSHOT matches all snapshot artifacts.
*
*
- * Certain dependencies that are known to be used and loaded by reflection
- * are always ignored. This includes {@code org.slf4j:slf4j-simple::}.
- *
* @since 2.10
*/
@Parameter
private String[] ignoredUnusedDeclaredDependencies = new String[0];
- private String[] unconditionallyIgnoredDeclaredDependencies = {"org.slf4j:slf4j-simple::"};
-
/**
* List of dependencies that are ignored if they are in not test scope but are only used in test classes.
* The filter syntax is:
@@ -241,7 +255,7 @@ public abstract class AbstractAnalyzeMojo extends AbstractMojo {
*
* @since 3.3.0
*/
- @Parameter(defaultValue = "org.slf4j:slf4j-simple::")
+ @Parameter
private String[] ignoredNonTestScopedDependencies;
/**
@@ -366,7 +380,6 @@ private boolean checkDependencies() throws MojoExecutionException {
ignoredUnusedDeclared.addAll(filterDependencies(unusedDeclared, ignoredDependencies));
ignoredUnusedDeclared.addAll(filterDependencies(unusedDeclared, ignoredUnusedDeclaredDependencies));
- ignoredUnusedDeclared.addAll(filterDependencies(unusedDeclared, unconditionallyIgnoredDeclaredDependencies));
if (ignoreAllNonTestScoped) {
ignoredNonTestScope.addAll(filterDependencies(nonTestScope, new String[] {"*"}));
@@ -398,11 +411,15 @@ private boolean checkDependencies() throws MojoExecutionException {
}
if (!unusedDeclared.isEmpty()) {
- logDependencyWarning("Unused declared dependencies found:");
-
- logArtifacts(unusedDeclared, true);
- reported = true;
- warning = true;
+ final Set declaredSpi = scanForSpiUsage(usedDeclared, usedUndeclaredWithClasses);
+ cleanupUnused(declaredSpi, unusedDeclared);
+ if (!unusedDeclared.isEmpty()) {
+ logDependencyWarning("Unused declared dependencies found:");
+
+ logArtifacts(unusedDeclared, true);
+ reported = true;
+ warning = true;
+ }
}
if (!nonTestScope.isEmpty()) {
@@ -449,6 +466,125 @@ private boolean checkDependencies() throws MojoExecutionException {
return warning;
}
+ // todo: enhance analyzer (dependency) to do it since it already visits classes
+ // will save some time
+ private Set scanForSpiUsage(
+ final Set usedDeclared, final Map> usedUndeclaredWithClasses) {
+ return Stream.concat(
+ usedDeclared.stream().flatMap(this::findUsedSpi),
+ usedUndeclaredWithClasses.keySet().stream().flatMap(this::findUsedSpi))
+ .collect(toSet());
+ }
+
+ private Stream findUsedSpi(final Artifact artifact) {
+ try (JarFile jar = new JarFile(artifact.getFile())) {
+ return list(jar.entries()).stream()
+ .filter(entry -> entry.getName().endsWith(".class"))
+ .flatMap(entry -> {
+ final ClassReader classReader;
+ try {
+ classReader = new ClassReader(jar.getInputStream(entry));
+ } catch (IOException e) {
+ return Stream.empty();
+ }
+ final Set spi = new HashSet<>();
+ classReader.accept(
+ new ClassVisitor(ASM9) {
+ @Override
+ public MethodVisitor visitMethod(
+ final int access,
+ final String name,
+ final String descriptor,
+ final String signature,
+ final String[] exceptions) {
+ return new MethodVisitor(ASM9) {
+ private Type lastType = null;
+
+ @Override
+ public void visitLdcInsn(final Object value) {
+ if (value instanceof Type) {
+ lastType = (Type) value;
+ }
+ }
+
+ @Override
+ public void visitMethodInsn(
+ final int opcode,
+ final String owner,
+ final String name,
+ final String descriptor,
+ final boolean isInterface) {
+ if (opcode == INVOKESTATIC
+ && Objects.equals(owner, "java/util/ServiceLoader")
+ && Objects.equals(name, "load")) {
+ spi.add(lastType.getClassName());
+ }
+ lastType = null;
+ }
+ };
+ }
+ },
+ 0);
+ return spi.stream();
+ })
+ .collect(toList()) // materialize before closing the jar
+ .stream();
+ } catch (final IOException ioe) {
+ return Stream.empty();
+ }
+ }
+
+ // here we try to detect "well-known" indirect patterns to remove false warnings
+ private void cleanupUnused(final Set spi, final Set unusedDeclared) {
+ unusedDeclared.removeIf(this::isSlf4jBinding);
+ unusedDeclared.removeIf(it -> hasUsedSPIImpl(spi, it));
+ }
+
+ // mainly for v1.x line but doesn't hurt much for v2
+ // TODO: enhance to ensure there is a single binding else just log a warning for all
+ // and maybe even handle version?
+ private boolean isSlf4jBinding(final Artifact artifact) {
+ try (JarFile file = new JarFile(artifact.getFile())) {
+ return file.getEntry("org/slf4j/impl/StaticLoggerBinder.class") != null;
+ } catch (final IOException e) {
+ return false;
+ }
+ }
+
+ private boolean hasUsedSPIImpl(final Set usedSpi, final Artifact artifact) {
+ final Set spi;
+ try (JarFile file = new JarFile(artifact.getFile())) {
+ spi = list(file.entries()).stream()
+ .filter(it -> it.getName().startsWith("META-INF/services/") && !it.isDirectory())
+ .map(it -> it.getName().substring("META-INF/services/".length()))
+ .collect(toSet());
+
+ // java >= 9
+ final JarEntry moduleEntry = file.getJarEntry("module-info.class");
+ if (moduleEntry != null) {
+ try (InputStream in = file.getInputStream(moduleEntry)) {
+ final ClassReader cr = new ClassReader(in);
+ cr.accept(
+ new ClassVisitor(ASM9) {
+ @Override
+ public ModuleVisitor visitModule(String name, int access, String version) {
+ return new ModuleVisitor(ASM9) {
+ @Override
+ public void visitProvide(final String service, final String[] providers) {
+ spi.add(service.replace('/', '.'));
+ }
+ };
+ }
+ },
+ 0);
+ }
+ }
+ } catch (final IOException e) {
+ return false;
+ }
+ return usedSpi.stream().anyMatch(spi::contains);
+ }
+
private void filterArtifactsByScope(Set artifacts, String scope) {
artifacts.removeIf(artifact -> artifact.getScope().equals(scope));
}
@@ -559,7 +695,8 @@ private void writeScriptableOutput(Set artifacts) {
}
private List filterDependencies(Set artifacts, String[] excludes) {
- ArtifactFilter filter = new StrictPatternExcludesArtifactFilter(Arrays.asList(excludes));
+ ArtifactFilter filter = new StrictPatternExcludesArtifactFilter(
+ excludes == null ? Collections.emptyList() : Arrays.asList(excludes));
List result = new ArrayList<>();
for (Iterator it = artifacts.iterator(); it.hasNext(); ) {
diff --git a/src/test/java/org/apache/maven/plugins/dependency/analyze/TestAnalyzeIndirectDependency.java b/src/test/java/org/apache/maven/plugins/dependency/analyze/TestAnalyzeIndirectDependency.java
new file mode 100644
index 000000000..dc2882d83
--- /dev/null
+++ b/src/test/java/org/apache/maven/plugins/dependency/analyze/TestAnalyzeIndirectDependency.java
@@ -0,0 +1,170 @@
+/*
+ * 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.dependency.analyze;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.HashSet;
+
+import jakarta.json.spi.JsonProvider;
+import org.apache.johnzon.core.JsonProviderImpl;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.artifact.handler.DefaultArtifactHandler;
+import org.apache.maven.model.Build;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.plugin.testing.ResolverExpressionEvaluatorStub;
+import org.apache.maven.plugins.dependency.analyze.main.JsonpMain;
+import org.apache.maven.plugins.dependency.analyze.main.Slf4jMain;
+import org.apache.maven.plugins.dependency.testUtils.TestLog;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.shared.dependency.analyzer.DefaultClassAnalyzer;
+import org.apache.maven.shared.dependency.analyzer.DefaultProjectDependencyAnalyzer;
+import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzer;
+import org.apache.maven.shared.dependency.analyzer.asm.ASMDependencyAnalyzer;
+import org.codehaus.plexus.component.configurator.BasicComponentConfigurator;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
+import org.codehaus.plexus.configuration.DefaultPlexusConfiguration;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.slf4j.LoggerFactory;
+import org.slf4j.impl.SimpleLoggerFactory;
+
+import static java.util.Arrays.asList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.tomitribe.util.JarLocation.jarLocation;
+
+class TestAnalyzeIndirectDependency {
+ @Test
+ void jakartaJavaxEE(@TempDir final Path work) throws Exception {
+ final String glassfishVersion = System.getProperty("glassfishVersion", "2.0.1");
+ final String johnzonVersion = System.getProperty("johnzonVersion", "2.0.1");
+
+ assertEquals(
+ "[info] No dependency problems found",
+ exec(
+ work,
+ base -> {
+ final String resource = JsonpMain.class.getName().replace('.', '/') + ".class";
+ final Path target = base.resolve(resource);
+ Files.createDirectories(target.getParent());
+ try (InputStream is = Thread.currentThread()
+ .getContextClassLoader()
+ .getResourceAsStream(resource)) {
+ Files.copy(is, target, StandardCopyOption.REPLACE_EXISTING);
+ }
+ },
+ artifact("org.glassfish", "jakarta.json", glassfishVersion, JsonProvider.class),
+ artifact("org.apache.johnzon", "johnzon-core", johnzonVersion, JsonProviderImpl.class)));
+ }
+
+ /* Ensure we do not have
+ [warn] Unused declared dependencies found:
+ [warn] org.slf4j:slf4j-simple:jar:1.7.36:compile
+ */
+ @Test
+ void slf4j(@TempDir final Path work) throws Exception {
+ final String slf4jVersion = System.getProperty("slf4jVersion", "1.7.36");
+ assertEquals(
+ "[info] No dependency problems found",
+ exec(
+ work,
+ base -> {
+ final String resource = Slf4jMain.class.getName().replace('.', '/') + ".class";
+ final Path target = base.resolve(resource);
+ Files.createDirectories(target.getParent());
+ try (InputStream is = Thread.currentThread()
+ .getContextClassLoader()
+ .getResourceAsStream(resource)) {
+ Files.copy(is, target, StandardCopyOption.REPLACE_EXISTING);
+ }
+ },
+ artifact("org.slf4j", "slf4j-api", slf4jVersion, LoggerFactory.class),
+ artifact("org.slf4j", "slf4j-simple", slf4jVersion, SimpleLoggerFactory.class)));
+ }
+
+ private String exec(final Path work, final IOConsumer classesFiller, final Artifact... artifacts)
+ throws Exception {
+ final Path classes = Files.createDirectories(work.resolve("target/classes"));
+ final Build build = new Build();
+ build.setOutputDirectory(classes.toString());
+ build.setTestOutputDirectory(
+ Files.createDirectories(work.resolve("target/test-classes")).toString());
+
+ final MavenProject project = new MavenProject();
+ project.setGroupId("g");
+ project.setArtifactId("test");
+ project.setVersion("build");
+ project.setBuild(build);
+ project.setArtifacts(new HashSet<>(asList(artifacts)));
+ project.setDependencyArtifacts(project.getArtifacts());
+ classesFiller.accept(classes);
+
+ final Log log = new TestLog();
+
+ final DefaultProjectDependencyAnalyzer analyzer = new DefaultProjectDependencyAnalyzer();
+ final Field classAnalyzer = DefaultProjectDependencyAnalyzer.class.getDeclaredField("classAnalyzer");
+ classAnalyzer.setAccessible(true);
+ classAnalyzer.set(analyzer, new DefaultClassAnalyzer());
+ final Field dependencyAnalyzer = DefaultProjectDependencyAnalyzer.class.getDeclaredField("dependencyAnalyzer");
+ dependencyAnalyzer.setAccessible(true);
+ dependencyAnalyzer.set(analyzer, new ASMDependencyAnalyzer());
+
+ final AnalyzeMojo mojo = new AnalyzeMojo(null, project) {
+ @Override
+ protected ProjectDependencyAnalyzer createProjectDependencyAnalyzer() throws MojoExecutionException {
+ return analyzer;
+ }
+
+ @Override
+ public Log getLog() {
+ return log;
+ }
+ };
+
+ final ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
+
+ final DefaultPlexusConfiguration configuration = new DefaultPlexusConfiguration("configuration");
+ configuration.addChild("ignoredPackagings", "pom");
+ configuration.addChild("outputDirectory", project.getBuild().getOutputDirectory());
+
+ new BasicComponentConfigurator().configureComponent(mojo, configuration, evaluator, null);
+
+ mojo.execute();
+
+ return log.toString().trim();
+ }
+
+ private DefaultArtifact artifact(
+ final String groupId, final String artifactId, final String slf4jVersion, final Class> marker) {
+ final DefaultArtifact artifact = new DefaultArtifact(
+ groupId, artifactId, slf4jVersion, "compile", "jar", "", new DefaultArtifactHandler());
+ artifact.setFile(jarLocation(marker));
+ return artifact;
+ }
+
+ private interface IOConsumer {
+ void accept(T arg) throws IOException;
+ }
+}
diff --git a/src/test/java/org/apache/maven/plugins/dependency/analyze/main/JsonpMain.java b/src/test/java/org/apache/maven/plugins/dependency/analyze/main/JsonpMain.java
new file mode 100644
index 000000000..2af60894b
--- /dev/null
+++ b/src/test/java/org/apache/maven/plugins/dependency/analyze/main/JsonpMain.java
@@ -0,0 +1,31 @@
+/*
+ * 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.dependency.analyze.main;
+
+import jakarta.json.Json;
+
+public final class JsonpMain {
+ private JsonpMain() {
+ // no-op
+ }
+
+ public static void main(final String... args) {
+ Json.createValue("");
+ }
+}
diff --git a/src/test/java/org/apache/maven/plugins/dependency/analyze/main/Slf4jMain.java b/src/test/java/org/apache/maven/plugins/dependency/analyze/main/Slf4jMain.java
new file mode 100644
index 000000000..b2aa4f34c
--- /dev/null
+++ b/src/test/java/org/apache/maven/plugins/dependency/analyze/main/Slf4jMain.java
@@ -0,0 +1,31 @@
+/*
+ * 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.dependency.analyze.main;
+
+import org.slf4j.LoggerFactory;
+
+public final class Slf4jMain {
+ private Slf4jMain() {
+ // no-op
+ }
+
+ public static void main(final String... args) {
+ LoggerFactory.getLogger(Slf4jMain.class).info("");
+ }
+}
diff --git a/src/test/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojoTest.java b/src/test/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojoTest.java
index d0da7947a..3f9621982 100644
--- a/src/test/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojoTest.java
+++ b/src/test/java/org/apache/maven/plugins/dependency/exclusion/AnalyzeExclusionsMojoTest.java
@@ -19,8 +19,6 @@
package org.apache.maven.plugins.dependency.exclusion;
import java.io.File;
-import java.io.PrintWriter;
-import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -35,8 +33,8 @@
import org.apache.maven.model.InputLocation;
import org.apache.maven.model.InputSource;
import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.dependency.AbstractDependencyMojoTestCase;
+import org.apache.maven.plugins.dependency.testUtils.TestLog;
import org.apache.maven.plugins.dependency.testUtils.stubs.DependencyProjectStub;
import org.apache.maven.plugins.dependency.utils.ResolverUtil;
import org.apache.maven.project.MavenProject;
@@ -234,183 +232,4 @@ private Exclusion exclusion(String groupId, String artifactId) {
exclusion.setLocation("", new InputLocation(1, 1, inputSource));
return exclusion;
}
-
- static class TestLog implements Log {
- StringBuilder sb = new StringBuilder();
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void debug(CharSequence content) {
- print("debug", content);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void debug(CharSequence content, Throwable error) {
- print("debug", content, error);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void debug(Throwable error) {
- print("debug", error);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void info(CharSequence content) {
- print("info", content);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void info(CharSequence content, Throwable error) {
- print("info", content, error);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void info(Throwable error) {
- print("info", error);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void warn(CharSequence content) {
- print("warn", content);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void warn(CharSequence content, Throwable error) {
- print("warn", content, error);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void warn(Throwable error) {
- print("warn", error);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void error(CharSequence content) {
- print("error", content);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void error(CharSequence content, Throwable error) {
- StringWriter sWriter = new StringWriter();
- PrintWriter pWriter = new PrintWriter(sWriter);
-
- error.printStackTrace(pWriter);
-
- System.err.println(
- "[error] " + content.toString() + System.lineSeparator() + System.lineSeparator() + sWriter);
- }
-
- /**
- * @see org.apache.maven.plugin.logging.Log#error(java.lang.Throwable)
- */
- @Override
- public void error(Throwable error) {
- StringWriter sWriter = new StringWriter();
- PrintWriter pWriter = new PrintWriter(sWriter);
-
- error.printStackTrace(pWriter);
-
- System.err.println("[error] " + sWriter);
- }
-
- /**
- * @see org.apache.maven.plugin.logging.Log#isDebugEnabled()
- */
- @Override
- public boolean isDebugEnabled() {
- return false;
- }
-
- /**
- * @see org.apache.maven.plugin.logging.Log#isInfoEnabled()
- */
- @Override
- public boolean isInfoEnabled() {
- return true;
- }
-
- /**
- * @see org.apache.maven.plugin.logging.Log#isWarnEnabled()
- */
- @Override
- public boolean isWarnEnabled() {
- return true;
- }
-
- /**
- * @see org.apache.maven.plugin.logging.Log#isErrorEnabled()
- */
- @Override
- public boolean isErrorEnabled() {
- return true;
- }
-
- private void print(String prefix, CharSequence content) {
- sb.append("[")
- .append(prefix)
- .append("] ")
- .append(content.toString())
- .append(System.lineSeparator());
- }
-
- private void print(String prefix, Throwable error) {
- StringWriter sWriter = new StringWriter();
- PrintWriter pWriter = new PrintWriter(sWriter);
-
- error.printStackTrace(pWriter);
-
- sb.append("[").append(prefix).append("] ").append(sWriter).append(System.lineSeparator());
- }
-
- private void print(String prefix, CharSequence content, Throwable error) {
- StringWriter sWriter = new StringWriter();
- PrintWriter pWriter = new PrintWriter(sWriter);
-
- error.printStackTrace(pWriter);
-
- sb.append("[")
- .append(prefix)
- .append("] ")
- .append(content.toString())
- .append(System.lineSeparator())
- .append(System.lineSeparator());
- sb.append(sWriter).append(System.lineSeparator());
- }
-
- protected String getContent() {
- return sb.toString();
- }
- }
}
diff --git a/src/test/java/org/apache/maven/plugins/dependency/testUtils/TestLog.java b/src/test/java/org/apache/maven/plugins/dependency/testUtils/TestLog.java
new file mode 100644
index 000000000..58b58e440
--- /dev/null
+++ b/src/test/java/org/apache/maven/plugins/dependency/testUtils/TestLog.java
@@ -0,0 +1,203 @@
+/*
+ * 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.dependency.testUtils;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.apache.maven.plugin.logging.Log;
+
+public class TestLog implements Log {
+ private final StringBuilder sb = new StringBuilder();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void debug(CharSequence content) {
+ print("debug", content);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void debug(CharSequence content, Throwable error) {
+ print("debug", content, error);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void debug(Throwable error) {
+ print("debug", error);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void info(CharSequence content) {
+ print("info", content);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void info(CharSequence content, Throwable error) {
+ print("info", content, error);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void info(Throwable error) {
+ print("info", error);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void warn(CharSequence content) {
+ print("warn", content);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void warn(CharSequence content, Throwable error) {
+ print("warn", content, error);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void warn(Throwable error) {
+ print("warn", error);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void error(CharSequence content) {
+ print("error", content);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void error(CharSequence content, Throwable error) {
+ StringWriter sWriter = new StringWriter();
+ PrintWriter pWriter = new PrintWriter(sWriter);
+
+ error.printStackTrace(pWriter);
+
+ System.err.println("[error] " + content.toString() + System.lineSeparator() + System.lineSeparator() + sWriter);
+ }
+
+ /**
+ * @see org.apache.maven.plugin.logging.Log#error(java.lang.Throwable)
+ */
+ @Override
+ public void error(Throwable error) {
+ StringWriter sWriter = new StringWriter();
+ PrintWriter pWriter = new PrintWriter(sWriter);
+
+ error.printStackTrace(pWriter);
+
+ System.err.println("[error] " + sWriter);
+ }
+
+ /**
+ * @see org.apache.maven.plugin.logging.Log#isDebugEnabled()
+ */
+ @Override
+ public boolean isDebugEnabled() {
+ return false;
+ }
+
+ /**
+ * @see org.apache.maven.plugin.logging.Log#isInfoEnabled()
+ */
+ @Override
+ public boolean isInfoEnabled() {
+ return true;
+ }
+
+ /**
+ * @see org.apache.maven.plugin.logging.Log#isWarnEnabled()
+ */
+ @Override
+ public boolean isWarnEnabled() {
+ return true;
+ }
+
+ /**
+ * @see org.apache.maven.plugin.logging.Log#isErrorEnabled()
+ */
+ @Override
+ public boolean isErrorEnabled() {
+ return true;
+ }
+
+ private void print(String prefix, CharSequence content) {
+ sb.append("[").append(prefix).append("] ").append(content.toString()).append(System.lineSeparator());
+ }
+
+ private void print(String prefix, Throwable error) {
+ StringWriter sWriter = new StringWriter();
+ PrintWriter pWriter = new PrintWriter(sWriter);
+
+ error.printStackTrace(pWriter);
+
+ sb.append("[").append(prefix).append("] ").append(sWriter).append(System.lineSeparator());
+ }
+
+ private void print(String prefix, CharSequence content, Throwable error) {
+ StringWriter sWriter = new StringWriter();
+ PrintWriter pWriter = new PrintWriter(sWriter);
+
+ error.printStackTrace(pWriter);
+
+ sb.append("[")
+ .append(prefix)
+ .append("] ")
+ .append(content.toString())
+ .append(System.lineSeparator())
+ .append(System.lineSeparator());
+ sb.append(sWriter).append(System.lineSeparator());
+ }
+
+ @Override
+ public String toString() {
+ return getContent();
+ }
+
+ public String getContent() {
+ return sb.toString();
+ }
+}