This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a commit to branch MNG-7827
in repository https://gitbox.apache.org/repos/asf/maven.git

commit 74be0f4143e22e098cd03013e731498418ac8542
Author: Romain Manni-Bucau <rmannibu...@gmail.com>
AuthorDate: Mon Jun 26 19:35:00 2023 +0200

    [MNG-7827] Ensure maven 4 Log API is the primary and documented API for 
mojos
---
 maven-plugin-api/pom.xml                           |  14 +++
 .../java/org/apache/maven/plugin/AbstractMojo.java |  48 +++++++-
 .../maven/plugin/logging/SystemStreamLog.java      |  56 ++++++++--
 .../org/apache/maven/internal/impl/DefaultLog.java | 118 ++++++++++++++++++++
 .../org/apache/maven/plugin/AbstractMojoTest.java  | 123 +++++++++++++++++++++
 5 files changed, 348 insertions(+), 11 deletions(-)

diff --git a/maven-plugin-api/pom.xml b/maven-plugin-api/pom.xml
index 274a62ef6..db3000e36 100644
--- a/maven-plugin-api/pom.xml
+++ b/maven-plugin-api/pom.xml
@@ -52,6 +52,20 @@ under the License.
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-classworlds</artifactId>
     </dependency>
+
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>maven-api-core</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-jdk14</artifactId>
+      <version>${slf4jVersion}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
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 5e76c40d9..dba25a40f 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 <code>Mojo</code> except for
  * the execute method.<br>
@@ -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 abstract class AbstractMojo implements Mojo, 
ContextEnabled {
      * 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 07ff6e690..eda0f9c93 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 @@ package org.apache.maven.plugin.logging;
 
 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 class SystemStreamLog implements Log {
         print("debug", error);
     }
 
+    @Override
+    public void debug(final Supplier<String> content) {
+        debug(content.get());
+    }
+
+    @Override
+    public void debug(final Supplier<String> content, final Throwable error) {
+        debug(content.get(), error);
+    }
+
     /**
      * @see org.apache.maven.plugin.logging.Log#info(java.lang.CharSequence)
      */
@@ -72,6 +83,16 @@ public class SystemStreamLog implements Log {
         print("info", error);
     }
 
+    @Override
+    public void info(final Supplier<String> content) {
+        info(content.get());
+    }
+
+    @Override
+    public void info(final Supplier<String> content, final Throwable error) {
+        info(content.get(), error);
+    }
+
     /**
      * @see org.apache.maven.plugin.logging.Log#warn(java.lang.CharSequence)
      */
@@ -93,6 +114,16 @@ public class SystemStreamLog implements Log {
         print("warn", error);
     }
 
+    @Override
+    public void warn(final Supplier<String> content) {
+        warn(content.get());
+    }
+
+    @Override
+    public void warn(final Supplier<String> content, final Throwable error) {
+        warn(content.get(), error);
+    }
+
     /**
      * @see org.apache.maven.plugin.logging.Log#error(java.lang.CharSequence)
      */
@@ -109,8 +140,7 @@ public class SystemStreamLog implements Log {
 
         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 class SystemStreamLog implements Log {
 
         error.printStackTrace(pWriter);
 
-        System.err.println("[error] " + sWriter.toString());
+        System.err.println("[error] " + sWriter);
+    }
+
+    @Override
+    public void error(final Supplier<String> content) {
+        error(content.get());
+    }
+
+    @Override
+    public void error(final Supplier<String> content, final Throwable error) {
+        error(content.get(), error);
     }
 
     /**
@@ -164,7 +204,7 @@ public class SystemStreamLog implements Log {
 
         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 @@ public class SystemStreamLog implements Log {
 
         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 000000000..34f5b6143
--- /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<String> content) {}
+
+    @Override
+    public void debug(final Supplier<String> 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<String> content) {}
+
+    @Override
+    public void info(final Supplier<String> 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<String> content) {}
+
+    @Override
+    public void warn(final Supplier<String> 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<String> content) {}
+
+    @Override
+    public void error(final Supplier<String> 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 000000000..9f7f3f137
--- /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<org.apache.maven.api.plugin.Log>) 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<String> packageNames, final List<String> 
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<URL> 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();
+        }
+    }
+}

Reply via email to