diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java index 7da8ea88fa29..6ecf4bda15cd 100644 --- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java +++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java @@ -143,6 +143,7 @@ public class DefaultMavenPluginManager private PluginVersionResolver pluginVersionResolver; private PluginArtifactsCache pluginArtifactsCache; private MavenPluginValidator pluginValidator; + private MojoLogFactory logFactory; private final ExtensionDescriptorBuilder extensionDescriptorBuilder = new ExtensionDescriptorBuilder(); private final PluginDescriptorBuilder builder = new PluginDescriptorBuilder(); @@ -158,7 +159,8 @@ public DefaultMavenPluginManager( ExtensionRealmCache extensionRealmCache, PluginVersionResolver pluginVersionResolver, PluginArtifactsCache pluginArtifactsCache, - MavenPluginValidator pluginValidator ) + MavenPluginValidator pluginValidator, + MojoLogFactory logFactory ) { this.container = container; this.classRealmManager = classRealmManager; @@ -170,6 +172,7 @@ public DefaultMavenPluginManager( this.pluginVersionResolver = pluginVersionResolver; this.pluginArtifactsCache = pluginArtifactsCache; this.pluginValidator = pluginValidator; + this.logFactory = logFactory; } public synchronized PluginDescriptor getPluginDescriptor( Plugin plugin, List repositories, @@ -569,8 +572,7 @@ else if ( cause instanceof LinkageError ) if ( mojo instanceof Mojo ) { - Logger mojoLogger = LoggerFactory.getLogger( mojoDescriptor.getImplementation() ); - ( (Mojo) mojo ).setLog( new MojoLogWrapper( mojoLogger ) ); + ( ( Mojo ) mojo ).setLog( logFactory.getLog( mojoDescriptor.getImplementation() ) ); } Xpp3Dom dom = mojoExecution.getConfiguration(); diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/MojoLog.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/MojoLog.java new file mode 100644 index 000000000000..e5492af9ed10 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/MojoLog.java @@ -0,0 +1,206 @@ +package org.apache.maven.plugin.internal; + +/* + * 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. + */ + +import org.apache.maven.plugin.logging.Log; +import org.slf4j.Logger; + +import javax.inject.Provider; + +import static java.util.Objects.requireNonNull; + +/** + * Mojo {@link Log} implementation that lazily creates {@link Logger} instances. To achieve "lazyness" it uses + * {@link Provider} for logger, but guarantees that {@link Provider#get()} is invoked only once, and once got + * Logger instance is reused. + * + * @since TBD + */ +public class MojoLog + implements Log +{ + private final Provider loggerProvider; + + public MojoLog( Provider loggerProvider ) + { + this.loggerProvider = memoize( loggerProvider ); + } + + private Logger getLogger() + { + return loggerProvider.get(); + } + + private String toString( CharSequence content ) + { + if ( content == null ) + { + return ""; + } + else + { + return content.toString(); + } + } + + @Override + public void debug( CharSequence content ) + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( toString( content ) ); + } + } + + @Override + public void debug( CharSequence format, Object... arguments ) + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( toString( format ), arguments ); + } + } + + @Override + public void debug( CharSequence content, Throwable error ) + { + if ( getLogger().isDebugEnabled() ) + { + getLogger().debug( toString( content ), error ); + } + } + + @Override + public void debug( Throwable error ) + { + getLogger().debug( "", error ); + } + + @Override + public void info( CharSequence content ) + { + getLogger().info( toString( content ) ); + } + + @Override + public void info( CharSequence content, Throwable error ) + { + getLogger().info( toString( content ), error ); + } + + @Override + public void info( Throwable error ) + { + getLogger().info( "", error ); + } + + @Override + public void warn( CharSequence content ) + { + getLogger().warn( toString( content ) ); + } + + @Override + public void warn( CharSequence content, Throwable error ) + { + getLogger().warn( toString( content ), error ); + } + + @Override + public void warn( Throwable error ) + { + getLogger().warn( "", error ); + } + + @Override + public void error( CharSequence content ) + { + getLogger().error( toString( content ) ); + } + + @Override + public void error( CharSequence content, Throwable error ) + { + getLogger().error( toString( content ), error ); + } + + @Override + public void error( Throwable error ) + { + getLogger().error( "", error ); + } + + @Override + public boolean isDebugEnabled() + { + return getLogger().isDebugEnabled(); + } + + @Override + public boolean isInfoEnabled() + { + return getLogger().isInfoEnabled(); + } + + @Override + public boolean isWarnEnabled() + { + return getLogger().isWarnEnabled(); + } + + @Override + public boolean isErrorEnabled() + { + return getLogger().isErrorEnabled(); + } + + /** + * A helper bit to make users of this class easier: it wraps {@link Provider} into "memoizing" provider: the + * wrapped (delegate) provider will be invoked only once, and the returned value will be stored for subsequent + * invocations of {@link Provider#get()} on wrapper. + */ + private static Provider memoize( Provider provider ) + { + requireNonNull( provider ); + return new Provider() + { + Provider delegate = this::memoize; + + volatile boolean memoized = false; + + @Override + public T get() + { + return delegate.get(); + } + + private synchronized T memoize() + { + if ( !memoized ) + { + T value = provider.get(); + delegate = () -> value; + memoized = true; + } + return delegate.get(); + } + }; + } +} diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/MojoLogFactory.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/MojoLogFactory.java new file mode 100644 index 000000000000..d8d64bd0d5e7 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/MojoLogFactory.java @@ -0,0 +1,46 @@ +package org.apache.maven.plugin.internal; + +/* + * 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. + */ + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.maven.plugin.logging.Log; +import org.slf4j.LoggerFactory; + +/** + * Implementation of Mojo {@link Log} factory backed by Maven internal logging (slf4j). + * + * @since TBD + */ +@Singleton +@Named +public class MojoLogFactory +{ + public Log getLog( final Class clazz ) + { + return new MojoLog( () -> LoggerFactory.getLogger( clazz ) ); + } + + public Log getLog( final String name ) + { + return new MojoLog( () -> LoggerFactory.getLogger( name ) ); + } +} diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/MojoLogWrapper.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/MojoLogWrapper.java deleted file mode 100644 index 060dffdbd8a0..000000000000 --- a/maven-core/src/main/java/org/apache/maven/plugin/internal/MojoLogWrapper.java +++ /dev/null @@ -1,146 +0,0 @@ -package org.apache.maven.plugin.internal; - -/* - * 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. - */ - -import org.apache.maven.plugin.logging.Log; -import org.slf4j.Logger; - -import static java.util.Objects.requireNonNull; - -/** - * @author jdcasey - */ -public class MojoLogWrapper - implements Log -{ - private final Logger logger; - - public MojoLogWrapper( Logger logger ) - { - this.logger = requireNonNull( logger ); - } - - public void debug( CharSequence content ) - { - logger.debug( toString( content ) ); - } - - private String toString( CharSequence content ) - { - if ( content == null ) - { - return ""; - } - else - { - return content.toString(); - } - } - - @Override - public void debug( CharSequence content, Throwable error ) - { - logger.debug( toString( content ), error ); - } - - @Override - public void debug( Throwable error ) - { - logger.debug( "", error ); - } - - @Override - public void info( CharSequence content ) - { - logger.info( toString( content ) ); - } - - @Override - public void info( CharSequence content, Throwable error ) - { - logger.info( toString( content ), error ); - } - - @Override - public void info( Throwable error ) - { - logger.info( "", error ); - } - - @Override - public void warn( CharSequence content ) - { - logger.warn( toString( content ) ); - } - - @Override - public void warn( CharSequence content, Throwable error ) - { - logger.warn( toString( content ), error ); - } - - @Override - public void warn( Throwable error ) - { - logger.warn( "", error ); - } - - @Override - public void error( CharSequence content ) - { - logger.error( toString( content ) ); - } - - @Override - public void error( CharSequence content, Throwable error ) - { - logger.error( toString( content ), error ); - } - - @Override - public void error( Throwable error ) - { - logger.error( "", error ); - } - - @Override - public boolean isDebugEnabled() - { - return logger.isDebugEnabled(); - } - - @Override - public boolean isInfoEnabled() - { - return logger.isInfoEnabled(); - } - - @Override - public boolean isWarnEnabled() - { - return logger.isWarnEnabled(); - } - - @Override - public boolean isErrorEnabled() - { - return logger.isErrorEnabled(); - } -} 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 4042f84dea62..835ab32e16ca 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 @@ -22,7 +22,8 @@ import java.util.Map; import org.apache.maven.plugin.logging.Log; -import org.apache.maven.plugin.logging.SystemStreamLog; + +import static java.util.Objects.requireNonNull; /** * Abstract class to provide most of the infrastructure required to implement a Mojo except for @@ -147,53 +148,40 @@ public abstract class AbstractMojo private Log log; /** Plugin container context */ - private Map pluginContext; + private Map pluginContext; - /** - * @deprecated Use SLF4J directly - */ - @Deprecated @Override public void setLog( Log log ) { - this.log = log; + this.log = requireNonNull( log ); } /** *

- * Returns the logger that has been injected into this mojo. If no logger has been setup yet, a - * SystemStreamLog logger will be created and returned. + * Returns the logger that has been injected into this mojo. Injection happens AFTER instance is constructed. *

* Note: - * The logger returned by this method must not be cached in an instance field during the construction of the mojo. - * This would cause the mojo to use a wrongly configured default logger when being run by Maven. The proper logger - * gets injected by the Plexus container after the mojo has been constructed. Therefore, simply call this - * method directly whenever you need the logger, it is fast enough and needs no caching. + * The logger returned by this method must not be cached in an instance field during the construction of the mojo, + * as it is not yet injected. The proper logger gets injected by Maven after the mojo has been constructed. + * Therefore, simply call this 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 @Override public Log getLog() { - if ( log == null ) - { - log = new SystemStreamLog(); - } - return log; } @Override - public Map getPluginContext() + public void setPluginContext( Map pluginContext ) { - return pluginContext; + this.pluginContext = requireNonNull( pluginContext ); } @Override - public void setPluginContext( Map pluginContext ) + public Map getPluginContext() { - this.pluginContext = pluginContext; + return pluginContext; } } diff --git a/maven-plugin-api/src/main/java/org/apache/maven/plugin/ContextEnabled.java b/maven-plugin-api/src/main/java/org/apache/maven/plugin/ContextEnabled.java index 68516988b5d1..d3a1820b5497 100644 --- a/maven-plugin-api/src/main/java/org/apache/maven/plugin/ContextEnabled.java +++ b/maven-plugin-api/src/main/java/org/apache/maven/plugin/ContextEnabled.java @@ -23,10 +23,13 @@ /** * Interface to allow Mojos to communicate with each others Mojos, other than - * project's source root and project's attachment.
- * The plugin manager would pull the context out of the plugin container context, and populate it into the Mojo. - * - * @author jdcasey + * project's source root and project's attachment. The plugin manager populates it into the Mojo implementing this + * interface, before executing it. Mojos also may access other contexts using Maven Session API.
+ * Word of warning: Mojos live in their own classloader that is usually destroyed after Mojo is executed, and even + * same Mojo within two different projects will different classloader. That said, (mis) using the context to store + * anything that was loaded up by Mojo classloader is wrong to do and will lead to errors.
+ * Regarding concurrency: best practice is to "write once" (for example invoked Mojo writes to its own context), + * while other Mojos MAY get (via Maven Session API) and inspect (read) other Mojos contexts. */ public interface ContextEnabled { @@ -35,10 +38,12 @@ public interface ContextEnabled * * @param pluginContext a new Map */ - void setPluginContext( Map pluginContext ); + void setPluginContext( Map pluginContext ); /** + * Gets the shared context of the mojo. + * * @return a Map stored in the plugin container's context. */ - Map getPluginContext(); + Map getPluginContext(); } diff --git a/maven-plugin-api/src/main/java/org/apache/maven/plugin/Mojo.java b/maven-plugin-api/src/main/java/org/apache/maven/plugin/Mojo.java index 0419176057a9..c1d089e8972b 100644 --- a/maven-plugin-api/src/main/java/org/apache/maven/plugin/Mojo.java +++ b/maven-plugin-api/src/main/java/org/apache/maven/plugin/Mojo.java @@ -33,7 +33,12 @@ */ public interface Mojo { - /** The component role hint for Plexus container */ + /** The component role hint for Plexus container + * + * @deprecated do not use legacy Plexus API to look up by string, use JSR330 instead (or if must, use legacy + * Plexus lookup by class instead). + */ + @Deprecated String ROLE = Mojo.class.getName(); /** @@ -54,19 +59,14 @@ void execute() * and feedback to the user. * * @param log a new logger - * - * @deprecated Use SLF4J directly */ - @Deprecated void setLog( Log log ); /** * Furnish access to the standard Maven logging mechanism which is managed in this base class. * - * @return a log4j-like logger object which allows plugins to create messages at levels of "debug", + * @return a logger object which allows plugins to create messages at levels of "debug", * "info", "warn", and "error". - * @deprecated Use SLF4J directly */ - @Deprecated Log getLog(); } diff --git a/maven-plugin-api/src/main/java/org/apache/maven/plugin/logging/Log.java b/maven-plugin-api/src/main/java/org/apache/maven/plugin/logging/Log.java index 04d85bd960cc..3968fda5fc39 100644 --- a/maven-plugin-api/src/main/java/org/apache/maven/plugin/logging/Log.java +++ b/maven-plugin-api/src/main/java/org/apache/maven/plugin/logging/Log.java @@ -29,9 +29,8 @@ * * @author jdcasey * - * @deprecated Use SLF4J directly + * TODO: Make this interface more SLF4J Logger-lookalike. */ -@Deprecated public interface Log { /** @@ -46,6 +45,16 @@ public interface Log */ void debug( CharSequence content ); + /** + * Send a message to the user in the debug error level using given format and arguments. In format use + * {@code {}} as placeholders for objects passed in as arguments. No argument is converted to string is + * given level is not enabled. + * + * @param format + * @param arguments + */ + void debug( CharSequence format, Object... arguments ); + /** * Send a message (and accompanying exception) to the user in the debug error level.
* The error's stacktrace will be output when this error level is enabled. 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 deleted file mode 100644 index f0fc6189d57f..000000000000 --- a/maven-plugin-api/src/main/java/org/apache/maven/plugin/logging/SystemStreamLog.java +++ /dev/null @@ -1,201 +0,0 @@ -package org.apache.maven.plugin.logging; - -/* - * 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. - */ - -import java.io.PrintWriter; -import java.io.StringWriter; - -/** - * Logger with "standard" output and error output stream. - * - * @author jdcasey - * - * @deprecated Use SLF4J directly - */ -@Deprecated -public class SystemStreamLog - implements Log -{ - /** - * @see org.apache.maven.plugin.logging.Log#debug(java.lang.CharSequence) - */ - public void debug( CharSequence content ) - { - print( "debug", content ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#debug(java.lang.CharSequence, java.lang.Throwable) - */ - public void debug( CharSequence content, Throwable error ) - { - print( "debug", content, error ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#debug(java.lang.Throwable) - */ - public void debug( Throwable error ) - { - print( "debug", error ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#info(java.lang.CharSequence) - */ - public void info( CharSequence content ) - { - print( "info", content ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#info(java.lang.CharSequence, java.lang.Throwable) - */ - public void info( CharSequence content, Throwable error ) - { - print( "info", content, error ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#info(java.lang.Throwable) - */ - public void info( Throwable error ) - { - print( "info", error ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#warn(java.lang.CharSequence) - */ - public void warn( CharSequence content ) - { - print( "warn", content ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#warn(java.lang.CharSequence, java.lang.Throwable) - */ - public void warn( CharSequence content, Throwable error ) - { - print( "warn", content, error ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#warn(java.lang.Throwable) - */ - public void warn( Throwable error ) - { - print( "warn", error ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#error(java.lang.CharSequence) - */ - public void error( CharSequence content ) - { - System.err.println( "[error] " + content.toString() ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#error(java.lang.CharSequence, java.lang.Throwable) - */ - 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.toString() ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#error(java.lang.Throwable) - */ - public void error( Throwable error ) - { - StringWriter sWriter = new StringWriter(); - PrintWriter pWriter = new PrintWriter( sWriter ); - - error.printStackTrace( pWriter ); - - System.err.println( "[error] " + sWriter.toString() ); - } - - /** - * @see org.apache.maven.plugin.logging.Log#isDebugEnabled() - */ - public boolean isDebugEnabled() - { - // TODO Not sure how best to set these for this implementation... - return false; - } - - /** - * @see org.apache.maven.plugin.logging.Log#isInfoEnabled() - */ - public boolean isInfoEnabled() - { - return true; - } - - /** - * @see org.apache.maven.plugin.logging.Log#isWarnEnabled() - */ - public boolean isWarnEnabled() - { - return true; - } - - /** - * @see org.apache.maven.plugin.logging.Log#isErrorEnabled() - */ - public boolean isErrorEnabled() - { - return true; - } - - private void print( String prefix, CharSequence content ) - { - System.out.println( "[" + prefix + "] " + content.toString() ); - } - - private void print( String prefix, Throwable error ) - { - StringWriter sWriter = new StringWriter(); - PrintWriter pWriter = new PrintWriter( sWriter ); - - error.printStackTrace( pWriter ); - - System.out.println( "[" + prefix + "] " + sWriter.toString() ); - } - - private void print( String prefix, CharSequence content, Throwable error ) - { - StringWriter sWriter = new StringWriter(); - PrintWriter pWriter = new PrintWriter( sWriter ); - - error.printStackTrace( pWriter ); - - System.out.println( "[" + prefix + "] " + content.toString() - + System.lineSeparator() + System.lineSeparator() + sWriter.toString() ); - } -}