From aec341a7a9e292a4fc537e1042da8b9ce867580c Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Mon, 16 May 2022 11:26:49 +0200 Subject: [PATCH] [MNG-7486] Create a multiline message helper for boxed log messages This closes #746 --- .../internal/MultilineMessageHelper.java | 91 +++++++++++++++++++ .../internal/builder/BuilderCommon.java | 32 ++++--- .../internal/MultilineMessageHelperTest.java | 71 +++++++++++++++ 3 files changed, 179 insertions(+), 15 deletions(-) create mode 100644 maven-core/src/main/java/org/apache/maven/internal/MultilineMessageHelper.java create mode 100644 maven-core/src/test/java/org/apache/maven/internal/MultilineMessageHelperTest.java diff --git a/maven-core/src/main/java/org/apache/maven/internal/MultilineMessageHelper.java b/maven-core/src/main/java/org/apache/maven/internal/MultilineMessageHelper.java new file mode 100644 index 0000000000..da158295ba --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/internal/MultilineMessageHelper.java @@ -0,0 +1,91 @@ +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 multiline messages to the console + */ +public class MultilineMessageHelper +{ + + private static final int DEFAULT_MAX_SIZE = 65; + private static final char BOX_CHAR = '*'; + + public static String separatorLine() + { + StringBuilder sb = new StringBuilder( DEFAULT_MAX_SIZE ); + repeat( sb, '*', DEFAULT_MAX_SIZE ); + return sb.toString(); + } + + public static List format( String... lines ) + { + int size = DEFAULT_MAX_SIZE; + int remainder = size - 4; // 4 chars = 2 box_char + 2 spaces + 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( "\\s+" ); + for ( String word : words ) + { + if ( sb.length() >= remainder - word.length() - ( sb.length() > 0 ? 1 : 0 ) ) + { + repeat( sb, ' ', remainder - sb.length() ); + result.add( BOX_CHAR + " " + sb + " " + BOX_CHAR ); + sb.setLength( 0 ); + } + if ( sb.length() > 0 ) + { + sb.append( ' ' ); + } + sb.append( word ); + } + + while ( sb.length() < remainder ) + { + 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/builder/BuilderCommon.java b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java index 25ab6a46a6..4ee7be5241 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.BuildFailure; import org.apache.maven.execution.ExecutionEvent; import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenSession; +import org.apache.maven.internal.MultilineMessageHelper; import org.apache.maven.lifecycle.LifecycleExecutionException; import org.apache.maven.lifecycle.LifecycleNotFoundException; import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException; @@ -103,34 +104,35 @@ public class BuilderCommon 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( "*****************************************************************" ); + for ( String s : MultilineMessageHelper.format( + "Your build is requesting parallel execution, but this project contains the following " + + "plugin(s) that have goals not marked as thread-safe to support parallel execution.", + "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 Apache Maven." ) ) + { + logger.warn( s ); + } if ( logger.isDebugEnabled() ) { final Set unsafeGoals = executionPlan.getNonThreadSafeMojos(); - logger.warn( "The following goals are not marked @threadSafe in " + project.getName() + ":" ); + logger.warn( "The following goals are not marked as thread-safe in " + project.getName() + ":" ); for ( MojoDescriptor unsafeGoal : unsafeGoals ) { - logger.warn( unsafeGoal.getId() ); + logger.warn( " " + unsafeGoal.getId() ); } } else { - logger.warn( "The following plugins are not marked @threadSafe in " + project.getName() + ":" ); + logger.warn( "The following plugins are not marked as thread-safe 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( "Enable debug to see precisely which goals are not marked as thread-safe." ); } - logger.warn( "*****************************************************************" ); + logger.warn( MultilineMessageHelper.separatorLine() ); } } diff --git a/maven-core/src/test/java/org/apache/maven/internal/MultilineMessageHelperTest.java b/maven-core/src/test/java/org/apache/maven/internal/MultilineMessageHelperTest.java new file mode 100644 index 0000000000..42fe36fd58 --- /dev/null +++ b/maven-core/src/test/java/org/apache/maven/internal/MultilineMessageHelperTest.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 MultilineMessageHelperTest +{ + + @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, MultilineMessageHelper.format( + "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, MultilineMessageHelper.format( + "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." ) ); + } +}