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(); + } +}