diff --git a/maven-plugin-api/pom.xml b/maven-plugin-api/pom.xml
index 274a62ef63b9..db3000e3614e 100644
--- a/maven-plugin-api/pom.xml
+++ b/maven-plugin-api/pom.xml
@@ -52,6 +52,20 @@ under the License.
org.codehaus.plexus
plexus-classworlds
+
+
+ ${project.groupId}
+ maven-api-core
+ ${project.version}
+ provided
+
+
+
+ org.slf4j
+ slf4j-jdk14
+ ${slf4jVersion}
+ test
+
diff --git a/maven-plugin-api/src/main/java/org/apache/maven/plugin/AbstractMojo.java b/maven-plugin-api/src/main/java/org/apache/maven/plugin/AbstractMojo.java
index 5e76c40d9b68..dba25a40fadd 100644
--- a/maven-plugin-api/src/main/java/org/apache/maven/plugin/AbstractMojo.java
+++ b/maven-plugin-api/src/main/java/org/apache/maven/plugin/AbstractMojo.java
@@ -18,11 +18,18 @@
*/
package org.apache.maven.plugin;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.util.Map;
+import java.util.function.Supplier;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugin.logging.SystemStreamLog;
+import static java.util.Optional.ofNullable;
+
/**
* Abstract class to provide most of the infrastructure required to implement a Mojo except for
* the execute method.
@@ -147,7 +154,7 @@ public abstract class AbstractMojo implements Mojo, ContextEnabled {
private Map pluginContext;
/**
- * @deprecated Use SLF4J directly
+ * @deprecated Use an injected {@link Log} instead in the mojo or component.
*/
@Deprecated
@Override
@@ -167,13 +174,48 @@ public void setLog(Log log) {
* method directly whenever you need the logger, it is fast enough and needs no caching.
*
* @see org.apache.maven.plugin.Mojo#getLog()
- * @deprecated Use SLF4J directly
+ * @deprecated Use an injected {@link org.apache.maven.api.plugin.Log} instead in the mojo or component.
*/
@Deprecated
@Override
public Log getLog() {
if (log == null) {
- log = new SystemStreamLog();
+ // unlikely for a standard plugin, idea is to try to fallback on maven-core impl else use stdout/stderr
+ try {
+ // ensure we have slf4j
+ final ClassLoader loader = ofNullable(getClass().getClassLoader())
+ .orElseGet(() -> Thread.currentThread().getContextClassLoader());
+ final Class> lf = loader.loadClass("org.slf4j.LoggerFactory");
+ final Method getLogger = lf.getDeclaredMethod("getLogger", Class.class);
+ if (!getLogger.isAccessible()) {
+ getLogger.setAccessible(true);
+ }
+
+ // ensure we have maven-core - else we don't care to align on it
+ final Constructor> delegatingLogConstructor = loader.loadClass(
+ "org.apache.maven.internal.impl.DefaultLog")
+ .getDeclaredConstructor(getLogger.getReturnType());
+ if (!delegatingLogConstructor.isAccessible()) {
+ delegatingLogConstructor.setAccessible(true);
+ }
+
+ // load the slf4j logger and its log impl + create a facade to comply the deprecated API
+ final Object logger = getLogger.invoke(null, getClass());
+ final Object delegate = delegatingLogConstructor.newInstance(logger);
+ log = (Log) Proxy.newProxyInstance( // Supplier is mainly an "unwrap" impl for advanced cases
+ loader, new Class>[] {Log.class, Supplier.class}, (proxy, method, args) -> {
+ if (method.getDeclaringClass() == Supplier.class) {
+ return delegate;
+ }
+ try {
+ return method.invoke(delegate, args);
+ } catch (InvocationTargetException ite) {
+ throw ite.getTargetException();
+ }
+ });
+ } catch (Error | Exception e) {
+ log = new SystemStreamLog();
+ }
}
return log;
diff --git a/maven-plugin-api/src/main/java/org/apache/maven/plugin/logging/SystemStreamLog.java b/maven-plugin-api/src/main/java/org/apache/maven/plugin/logging/SystemStreamLog.java
index 07ff6e690af5..eda0f9c93e11 100644
--- a/maven-plugin-api/src/main/java/org/apache/maven/plugin/logging/SystemStreamLog.java
+++ b/maven-plugin-api/src/main/java/org/apache/maven/plugin/logging/SystemStreamLog.java
@@ -20,16 +20,17 @@
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.function.Supplier;
/**
* Logger with "standard" output and error output stream.
*
* @author jdcasey
*
- * @deprecated Use SLF4J directly
+ * @deprecated Use directly {@link org.apache.maven.api.plugin.Log} instead.
*/
@Deprecated
-public class SystemStreamLog implements Log {
+public class SystemStreamLog implements Log, org.apache.maven.api.plugin.Log {
/**
* @see org.apache.maven.plugin.logging.Log#debug(java.lang.CharSequence)
*/
@@ -51,6 +52,16 @@ public void debug(Throwable error) {
print("debug", error);
}
+ @Override
+ public void debug(final Supplier content) {
+ debug(content.get());
+ }
+
+ @Override
+ public void debug(final Supplier content, final Throwable error) {
+ debug(content.get(), error);
+ }
+
/**
* @see org.apache.maven.plugin.logging.Log#info(java.lang.CharSequence)
*/
@@ -72,6 +83,16 @@ public void info(Throwable error) {
print("info", error);
}
+ @Override
+ public void info(final Supplier content) {
+ info(content.get());
+ }
+
+ @Override
+ public void info(final Supplier content, final Throwable error) {
+ info(content.get(), error);
+ }
+
/**
* @see org.apache.maven.plugin.logging.Log#warn(java.lang.CharSequence)
*/
@@ -93,6 +114,16 @@ public void warn(Throwable error) {
print("warn", error);
}
+ @Override
+ public void warn(final Supplier content) {
+ warn(content.get());
+ }
+
+ @Override
+ public void warn(final Supplier content, final Throwable error) {
+ warn(content.get(), error);
+ }
+
/**
* @see org.apache.maven.plugin.logging.Log#error(java.lang.CharSequence)
*/
@@ -109,8 +140,7 @@ public void error(CharSequence content, Throwable error) {
error.printStackTrace(pWriter);
- System.err.println(
- "[error] " + content.toString() + System.lineSeparator() + System.lineSeparator() + sWriter.toString());
+ System.err.println("[error] " + content.toString() + System.lineSeparator() + System.lineSeparator() + sWriter);
}
/**
@@ -122,7 +152,17 @@ public void error(Throwable error) {
error.printStackTrace(pWriter);
- System.err.println("[error] " + sWriter.toString());
+ System.err.println("[error] " + sWriter);
+ }
+
+ @Override
+ public void error(final Supplier content) {
+ error(content.get());
+ }
+
+ @Override
+ public void error(final Supplier content, final Throwable error) {
+ error(content.get(), error);
}
/**
@@ -164,7 +204,7 @@ private void print(String prefix, Throwable error) {
error.printStackTrace(pWriter);
- System.out.println("[" + prefix + "] " + sWriter.toString());
+ System.out.println("[" + prefix + "] " + sWriter);
}
private void print(String prefix, CharSequence content, Throwable error) {
@@ -173,7 +213,7 @@ private void print(String prefix, CharSequence content, Throwable error) {
error.printStackTrace(pWriter);
- System.out.println("[" + prefix + "] " + content.toString() + System.lineSeparator() + System.lineSeparator()
- + sWriter.toString());
+ System.out.println(
+ "[" + prefix + "] " + content.toString() + System.lineSeparator() + System.lineSeparator() + sWriter);
}
}
diff --git a/maven-plugin-api/src/test/java/org/apache/maven/internal/impl/DefaultLog.java b/maven-plugin-api/src/test/java/org/apache/maven/internal/impl/DefaultLog.java
new file mode 100644
index 000000000000..34f5b614372f
--- /dev/null
+++ b/maven-plugin-api/src/test/java/org/apache/maven/internal/impl/DefaultLog.java
@@ -0,0 +1,118 @@
+/*
+ * 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.internal.impl;
+
+import java.util.function.Supplier;
+
+import org.apache.maven.api.plugin.Log;
+import org.slf4j.Logger;
+
+// to not depend on maven-core we copy it to pass tests
+public class DefaultLog implements Log {
+ private final Logger logger;
+
+ public DefaultLog(final Logger logger) {
+ this.logger = logger;
+ }
+
+ // @VisibleForTests
+ public Logger getLogger() {
+ return logger;
+ }
+
+ @Override
+ public boolean isDebugEnabled() {
+ return false;
+ }
+
+ @Override
+ public void debug(final CharSequence content) {}
+
+ @Override
+ public void debug(final CharSequence content, final Throwable error) {}
+
+ @Override
+ public void debug(final Throwable error) {}
+
+ @Override
+ public void debug(final Supplier content) {}
+
+ @Override
+ public void debug(final Supplier content, final Throwable error) {}
+
+ @Override
+ public boolean isInfoEnabled() {
+ return false;
+ }
+
+ @Override
+ public void info(final CharSequence content) {}
+
+ @Override
+ public void info(final CharSequence content, final Throwable error) {}
+
+ @Override
+ public void info(final Throwable error) {}
+
+ @Override
+ public void info(final Supplier content) {}
+
+ @Override
+ public void info(final Supplier content, final Throwable error) {}
+
+ @Override
+ public boolean isWarnEnabled() {
+ return false;
+ }
+
+ @Override
+ public void warn(final CharSequence content) {}
+
+ @Override
+ public void warn(final CharSequence content, final Throwable error) {}
+
+ @Override
+ public void warn(final Throwable error) {}
+
+ @Override
+ public void warn(final Supplier content) {}
+
+ @Override
+ public void warn(final Supplier content, final Throwable error) {}
+
+ @Override
+ public boolean isErrorEnabled() {
+ return false;
+ }
+
+ @Override
+ public void error(final CharSequence content) {}
+
+ @Override
+ public void error(final CharSequence content, final Throwable error) {}
+
+ @Override
+ public void error(final Throwable error) {}
+
+ @Override
+ public void error(final Supplier content) {}
+
+ @Override
+ public void error(final Supplier content, final Throwable error) {}
+}
diff --git a/maven-plugin-api/src/test/java/org/apache/maven/plugin/AbstractMojoTest.java b/maven-plugin-api/src/test/java/org/apache/maven/plugin/AbstractMojoTest.java
new file mode 100644
index 000000000000..9f7f3f137ebd
--- /dev/null
+++ b/maven-plugin-api/src/test/java/org/apache/maven/plugin/AbstractMojoTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.plugin;
+
+import java.io.IOException;
+import java.lang.reflect.Proxy;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.function.Supplier;
+
+import org.apache.maven.internal.impl.DefaultLog;
+import org.apache.maven.plugin.logging.Log;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+
+import static java.util.Collections.emptyEnumeration;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class AbstractMojoTest {
+ @Test
+ void getDefaultLogSystem() throws Throwable {
+ ignoring( // ignore slf4j to get default behavior
+ singletonList("org.slf4j."),
+ singletonList("org/slf4j/impl/StaticLoggerBinder.class"),
+ this::assertSystem);
+ }
+
+ @Test
+ void getDefaultLogSystemOnMissingMavenCore() throws Throwable {
+ ignoring(singletonList("org.apache.maven.internal.impl."), emptyList(), this::assertSystem);
+ }
+
+ @Test
+ void getDefaultLogSlf4j() {
+ final Log log = captureLog();
+ assertTrue(Proxy.isProxyClass(log.getClass()));
+ final org.apache.maven.api.plugin.Log delegate = ((Supplier) log).get();
+ assertEquals(
+ CaptureLogMojo.class.getName(),
+ assertInstanceOf(DefaultLog.class, delegate).getLogger().getName());
+ }
+
+ private void assertSystem() {
+ final Log log = captureLog();
+ assertFalse(Proxy.isProxyClass(log.getClass()));
+ assertEquals(
+ "org.apache.maven.plugin.logging.SystemStreamLog",
+ log.getClass().getName());
+ }
+
+ private Log captureLog() {
+ final CaptureLogMojo mojo = new CaptureLogMojo();
+ mojo.execute();
+ assertNotNull(mojo.ref);
+ return mojo.ref;
+ }
+
+ private void ignoring(final List packageNames, final List resources, final Executable test)
+ throws Throwable {
+ if (packageNames.isEmpty() && resources.isEmpty()) {
+ test.execute();
+ return;
+ }
+
+ final Thread thread = Thread.currentThread();
+ final ClassLoader parent = thread.getContextClassLoader();
+ final ClassLoader custom = new ClassLoader(parent) {
+ @Override
+ protected Class> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
+ if (name != null && packageNames.stream().anyMatch(name::startsWith)) {
+ throw new ClassNotFoundException(name);
+ }
+ return super.loadClass(name, resolve);
+ }
+
+ @Override
+ public Enumeration getResources(final String name) throws IOException {
+ if (name != null && resources.stream().anyMatch(name::startsWith)) {
+ return emptyEnumeration();
+ }
+ return super.getResources(name);
+ }
+ };
+ try {
+ thread.setContextClassLoader(custom);
+
+ } finally {
+ thread.setContextClassLoader(parent);
+ }
+ }
+
+ private static class CaptureLogMojo extends AbstractMojo {
+ private Log ref;
+
+ @Override
+ public void execute() {
+ ref = getLog();
+ }
+ }
+}