diff --git a/maven-core/src/main/java/org/apache/maven/internal/MessageHelper.java b/maven-core/src/main/java/org/apache/maven/internal/MessageHelper.java new file mode 100644 index 000000000000..5c117d1aeca8 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/internal/MessageHelper.java @@ -0,0 +1,101 @@ +package org.apache.maven.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 java.util.ArrayList; +import java.util.List; + +/** + * Helper class to format warning messages to the console + */ +public class MessageHelper +{ + + public static final int DEFAULT_MAX_SIZE = 65; + + private static final char BOX_CHAR = '*'; + + public static String separatorLine() + { + return separatorLine( DEFAULT_MAX_SIZE ); + } + + public static String separatorLine( int length ) + { + StringBuilder sb = new StringBuilder( length ); + repeat( sb, '*', length ); + return sb.toString(); + } + + public static List messageBox( String... lines ) + { + return messageBox( DEFAULT_MAX_SIZE, lines ); + } + + public static List messageBox( int size, String... lines ) + { + int rem = size - 4; + List result = new ArrayList<>(); + StringBuilder sb = new StringBuilder( size ); + // first line + sb.setLength( 0 ); + repeat( sb, BOX_CHAR, size ); + result.add( sb.toString() ); + // lines + for ( String line : lines ) + { + sb.setLength( 0 ); + String[] words = line.split( " " ); + for ( String word : words ) + { + if ( sb.length() >= rem - word.length() - ( sb.length() > 0 ? 1 : 0 ) ) + { + repeat( sb, ' ', rem - sb.length() ); + result.add( BOX_CHAR + " " + sb + " " + BOX_CHAR ); + sb.setLength( 0 ); + } + if ( sb.length() > 0 ) + { + sb.append( ' ' ); + } + sb.append( word ); + } + + while ( sb.length() < rem ) + { + sb.append( ' ' ); + } + result.add( BOX_CHAR + " " + sb + " " + BOX_CHAR ); + } + // last line + sb.setLength( 0 ); + repeat( sb, BOX_CHAR, size ); + result.add( sb.toString() ); + return result; + } + + private static void repeat( StringBuilder sb, char c, int nb ) + { + for ( int i = 0; i < nb; i++ ) + { + sb.append( c ); + } + } +} diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java index cf97c8cab954..67aa922b39f3 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java @@ -24,6 +24,7 @@ import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter; import org.apache.maven.execution.ExecutionEvent; import org.apache.maven.execution.MavenSession; +import org.apache.maven.internal.MessageHelper; import org.apache.maven.lifecycle.LifecycleExecutionException; import org.apache.maven.lifecycle.MissingProjectException; import org.apache.maven.plugin.BuildPluginManager; @@ -36,10 +37,13 @@ import org.apache.maven.plugin.PluginManagerException; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.project.MavenProject; +import org.apache.maven.shared.utils.logging.MessageUtils; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.util.StringUtils; import org.eclipse.aether.SessionData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Arrays; @@ -71,6 +75,8 @@ public class MojoExecutor { + private static final Logger LOGGER = LoggerFactory.getLogger( MojoExecutor.class ); + @Requirement private BuildPluginManager pluginManager; @@ -232,7 +238,18 @@ private static class ProjectLock implements AutoCloseable boolean aggregator = mojoDescriptor.isAggregator(); acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock(); acquiredProjectLock = getProjectLock( session ); - acquiredAggregatorLock.lock(); + if ( !acquiredAggregatorLock.tryLock() ) + { + int size = Math.max( MessageUtils.getTerminalWidth() - 15, MessageHelper.DEFAULT_MAX_SIZE ); + for ( String s : MessageHelper.messageBox( size, + "An aggregator Mojo is already executing in parallel build, but aggregator " + + "Mojos require exclusive access to reactor to prevent race conditions. This " + + "mojo execution will be blocked until the aggregator work is done." ) ) + { + LOGGER.warn( s ); + } + acquiredAggregatorLock.lock(); + } acquiredProjectLock.lock(); } else diff --git a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java index 25ab6a46a6e2..f910bc105915 100644 --- a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java +++ b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java @@ -24,6 +24,7 @@ import org.apache.maven.execution.ExecutionEvent; import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenSession; +import org.apache.maven.internal.MessageHelper; import org.apache.maven.lifecycle.LifecycleExecutionException; import org.apache.maven.lifecycle.LifecycleNotFoundException; import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException; @@ -43,6 +44,7 @@ import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; import org.apache.maven.plugin.version.PluginVersionResolutionException; import org.apache.maven.project.MavenProject; +import org.apache.maven.shared.utils.logging.MessageUtils; import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; @@ -103,22 +105,23 @@ public MavenExecutionPlan resolveBuildPlan( MavenSession session, MavenProject p final Set unsafePlugins = executionPlan.getNonThreadSafePlugins(); if ( !unsafePlugins.isEmpty() ) { - logger.warn( "*****************************************************************" ); - logger.warn( "* Your build is requesting parallel execution, but project *" ); - logger.warn( "* contains the following plugin(s) that have goals not marked *" ); - logger.warn( "* as @threadSafe to support parallel building. *" ); - logger.warn( "* While this /may/ work fine, please look for plugin updates *" ); - logger.warn( "* and/or request plugins be made thread-safe. *" ); - logger.warn( "* If reporting an issue, report it against the plugin in *" ); - logger.warn( "* question, not against maven-core *" ); - logger.warn( "*****************************************************************" ); + int size = Math.max( MessageUtils.getTerminalWidth() - 15, MessageHelper.DEFAULT_MAX_SIZE ); + for ( String s : MessageHelper.messageBox( size, + "Your build is requesting parallel execution, but project contains the following " + + "plugin(s) that have goals not marked as @threadSafe to support parallel building.", + "While this /may/ work fine, please look for plugin updates and/or " + + "request plugins be made thread-safe.", + "If reporting an issue, report it against the plugin in question, not against maven-core." ) ) + { + logger.warn( s ); + } if ( logger.isDebugEnabled() ) { final Set unsafeGoals = executionPlan.getNonThreadSafeMojos(); logger.warn( "The following goals are not marked @threadSafe in " + project.getName() + ":" ); for ( MojoDescriptor unsafeGoal : unsafeGoals ) { - logger.warn( unsafeGoal.getId() ); + logger.warn( " - " + unsafeGoal.getId() ); } } else @@ -126,11 +129,11 @@ public MavenExecutionPlan resolveBuildPlan( MavenSession session, MavenProject p logger.warn( "The following plugins are not marked @threadSafe in " + project.getName() + ":" ); for ( Plugin unsafePlugin : unsafePlugins ) { - logger.warn( unsafePlugin.getId() ); + logger.warn( " - " + unsafePlugin.getId() ); } logger.warn( "Enable debug to see more precisely which goals are not marked @threadSafe." ); } - logger.warn( "*****************************************************************" ); + logger.warn( MessageHelper.separatorLine( size ) ); } } diff --git a/maven-core/src/test/java/org/apache/maven/internal/MessageHelperTest.java b/maven-core/src/test/java/org/apache/maven/internal/MessageHelperTest.java new file mode 100644 index 000000000000..26432f1ef9d7 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/internal/MessageHelperTest.java @@ -0,0 +1,71 @@ +package org.apache.maven.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 java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class MessageHelperTest +{ + + @Test + public void testBuilderCommon() + { + List msgs = new ArrayList<>(); + msgs.add( "*****************************************************************" ); + msgs.add( "* Your build is requesting parallel execution, but project *" ); + msgs.add( "* contains the following plugin(s) that have goals not marked *" ); + msgs.add( "* as @threadSafe to support parallel building. *" ); + msgs.add( "* While this /may/ work fine, please look for plugin updates *" ); + msgs.add( "* and/or request plugins be made thread-safe. *" ); + msgs.add( "* If reporting an issue, report it against the plugin in *" ); + msgs.add( "* question, not against maven-core *" ); + msgs.add( "*****************************************************************" ); + + assertEquals( msgs, MessageHelper.messageBox( + "Your build is requesting parallel execution, but project contains the following " + + "plugin(s) that have goals not marked as @threadSafe to support parallel building.", + "While this /may/ work fine, please look for plugin updates and/or " + + "request plugins be made thread-safe.", + "If reporting an issue, report it against the plugin in question, not against maven-core" + ) ); + } + + @Test + public void testMojoExecutor() + { + List msgs = new ArrayList<>(); + msgs.add( "*****************************************************************" ); + msgs.add( "* An aggregator Mojo is already executing in parallel build, *" ); + msgs.add( "* but aggregator Mojos require exclusive access to reactor to *" ); + msgs.add( "* prevent race conditions. This mojo execution will be blocked *" ); + msgs.add( "* until the aggregator work is done. *" ); + msgs.add( "*****************************************************************" ); + + assertEquals( msgs, MessageHelper.messageBox( + "An aggregator Mojo is already executing in parallel build, but aggregator " + + "Mojos require exclusive access to reactor to prevent race conditions. This " + + "mojo execution will be blocked until the aggregator work is done." ) ); + } +}