| package org.apache.maven.plugins.help; |
| |
| /* |
| * 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.IOException; |
| import java.io.StringWriter; |
| import java.io.Writer; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Properties; |
| |
| import org.apache.maven.model.InputLocation; |
| import org.apache.maven.model.Model; |
| import org.apache.maven.model.InputSource; |
| import org.apache.maven.model.io.xpp3.MavenXpp3Writer; |
| import org.apache.maven.model.io.xpp3.MavenXpp3WriterExOldSupport; |
| import org.apache.maven.plugin.MojoExecution; |
| import org.apache.maven.plugin.MojoExecution.Source; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugins.annotations.Mojo; |
| import org.apache.maven.plugins.annotations.Parameter; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.shared.utils.logging.MessageUtils; |
| import org.codehaus.plexus.util.StringUtils; |
| import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter; |
| import org.codehaus.plexus.util.xml.XMLWriter; |
| import org.codehaus.plexus.util.xml.XmlWriterUtil; |
| import org.codehaus.plexus.util.xml.Xpp3Dom; |
| import org.codehaus.plexus.util.xml.pull.XmlSerializer; |
| |
| /** |
| * Displays the effective POM as an XML for this build, with the active profiles factored in, or a specified artifact. |
| * If <code>verbose</code>, a comment is added to each XML element describing the origin of the line. |
| * |
| * @since 2.0 |
| */ |
| @Mojo( name = "effective-pom", aggregator = true ) |
| public class EffectivePomMojo |
| extends AbstractEffectiveMojo |
| { |
| // ---------------------------------------------------------------------- |
| // Mojo parameters |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * The Maven project. |
| * |
| * @since 2.0.2 |
| */ |
| @Parameter( defaultValue = "${project}", readonly = true, required = true ) |
| private MavenProject project; |
| |
| /** |
| * The projects in the current build. The effective-POM for |
| * each of these projects will written. |
| */ |
| @Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true ) |
| private List<MavenProject> projects; |
| |
| /** |
| * This mojo execution, used to determine if it was launched from the lifecycle or the command-line. |
| */ |
| @Parameter( defaultValue = "${mojo}", required = true, readonly = true ) |
| private MojoExecution mojoExecution; |
| |
| /** |
| * The artifact for which to display the effective POM. |
| * <br> |
| * <b>Note</b>: Should respect the Maven format, i.e. <code>groupId:artifactId[:version]</code>. The |
| * latest version of the artifact will be used when no version is specified. |
| * |
| * @since 3.0.0 |
| */ |
| @Parameter( property = "artifact" ) |
| private String artifact; |
| |
| /** |
| * Output POM input location as comments. |
| * |
| * @since 3.2.0 |
| */ |
| @Parameter( property = "verbose", defaultValue = "false" ) |
| private boolean verbose = false; |
| |
| // ---------------------------------------------------------------------- |
| // Public methods |
| // ---------------------------------------------------------------------- |
| |
| /** {@inheritDoc} */ |
| public void execute() |
| throws MojoExecutionException |
| { |
| if ( StringUtils.isNotEmpty( artifact ) ) |
| { |
| project = getMavenProject( artifact ); |
| projects = Collections.singletonList( project ); |
| } |
| |
| StringWriter w = new StringWriter(); |
| String encoding = output != null ? project.getModel().getModelEncoding() |
| : System.getProperty( "file.encoding" ); |
| XMLWriter writer = |
| new PrettyPrintXMLWriter( w, StringUtils.repeat( " ", XmlWriterUtil.DEFAULT_INDENTATION_SIZE ), |
| encoding, null ); |
| |
| writeHeader( writer ); |
| |
| if ( shouldWriteAllEffectivePOMsInReactor() ) |
| { |
| // outer root element |
| writer.startElement( "projects" ); |
| for ( MavenProject subProject : projects ) |
| { |
| writeEffectivePom( subProject, writer ); |
| } |
| writer.endElement(); |
| } |
| else |
| { |
| writeEffectivePom( project, writer ); |
| } |
| |
| String effectivePom = prettyFormat( w.toString(), encoding, false ); |
| if ( verbose ) |
| { |
| // tweak location tracking comment, that are put on a separate line by pretty print |
| effectivePom = effectivePom.replaceAll( "(?m)>\\s+<!--}", "> <!-- " ); |
| } |
| |
| if ( output != null ) |
| { |
| try |
| { |
| writeXmlFile( output, effectivePom ); |
| } |
| catch ( IOException e ) |
| { |
| throw new MojoExecutionException( "Cannot write effective-POM to output: " + output, e ); |
| } |
| |
| getLog().info( "Effective-POM written to: " + output ); |
| } |
| else |
| { |
| if ( MessageUtils.isColorEnabled() ) |
| { |
| // add color to comments |
| String comment = MessageUtils.buffer().project( "<!--.-->" ).toString(); |
| int dotIndex = comment.indexOf( "." ); |
| String commentStart = comment.substring( 0, dotIndex ); |
| String commentEnd = comment.substring( dotIndex + 1 ); |
| effectivePom = effectivePom.replace( "<!--", commentStart ).replace( "-->", commentEnd ); |
| } |
| |
| getLog().info( LS + "Effective POMs, after inheritance, interpolation, and profiles are applied:" |
| + LS + LS + effectivePom + LS ); |
| } |
| } |
| |
| /** |
| * Determines if all effective POMs of all the projects in the reactor should be written. When this goal is started |
| * on the command-line, it is always the case. However, when it is bound to a phase in the lifecycle, it is only the |
| * case when the current project being built is the head project in the reactor. |
| * |
| * @return <code>true</code> if all effective POMs should be written, <code>false</code> otherwise. |
| */ |
| private boolean shouldWriteAllEffectivePOMsInReactor() |
| { |
| Source source = mojoExecution.getSource(); |
| // [MNG-5550] For Maven < 3.2.1, the source is null, instead of LIFECYCLE: only rely on comparisons with CLI |
| return projects.size() > 1 && ( source == Source.CLI || projects.get( 0 ).equals( project ) ); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Private methods |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * Method for writing the effective pom informations of the current build. |
| * |
| * @param project the project of the current build, not null. |
| * @param writer the XML writer , not null, not null. |
| * @throws MojoExecutionException if any |
| */ |
| private void writeEffectivePom( MavenProject project, XMLWriter writer ) |
| throws MojoExecutionException |
| { |
| Model pom = project.getModel(); |
| cleanModel( pom ); |
| |
| StringWriter sWriter = new StringWriter(); |
| try |
| { |
| if ( verbose ) |
| { |
| // try to use Maven core-provided xpp3 extended writer (available since Maven 3.6.1) |
| if ( ! writeMavenXpp3WriterEx( sWriter, pom ) ) |
| { |
| // xpp3 extended writer not provided by Maven core, use local code |
| new EffectiveWriterExOldSupport().write( sWriter, pom ); |
| } |
| } |
| else |
| { |
| new MavenXpp3Writer().write( sWriter, pom ); |
| } |
| } |
| catch ( IOException e ) |
| { |
| throw new MojoExecutionException( "Cannot serialize POM to XML.", e ); |
| } |
| |
| // This removes the XML declaration written by MavenXpp3Writer |
| String effectivePom = prettyFormat( sWriter.toString(), null, true ); |
| |
| writeComment( writer, "Effective POM for project '" + project.getId() + "'" ); |
| |
| writer.writeMarkup( effectivePom ); |
| } |
| |
| /** |
| * Apply some logic to clean the model before writing it. |
| * |
| * @param pom not null |
| */ |
| private static void cleanModel( Model pom ) |
| { |
| Properties properties = new SortedProperties(); |
| properties.putAll( pom.getProperties() ); |
| pom.setProperties( properties ); |
| } |
| |
| private void warnWriteMavenXpp3WriterEx( Throwable t ) |
| { |
| getLog().warn( "Unexpected exception while running Maven Model Extended Writer, " |
| + "falling back to old internal implementation.", t ); |
| } |
| |
| private boolean writeMavenXpp3WriterEx( Writer writer, Model model ) |
| throws IOException |
| { |
| try |
| { |
| Class<?> mavenXpp3WriterExClass = Class.forName( "org.apache.maven.model.io.xpp3.MavenXpp3WriterEx" ); |
| Object mavenXpp3WriterEx = mavenXpp3WriterExClass.getDeclaredConstructor().newInstance(); |
| |
| Method setStringFormatter = |
| mavenXpp3WriterExClass.getMethod( "setStringFormatter", InputLocation.StringFormatter.class ); |
| setStringFormatter.invoke( mavenXpp3WriterEx, new InputLocationStringFormatter() ); |
| |
| Method write = mavenXpp3WriterExClass.getMethod( "write", Writer.class, Model.class ); |
| write.invoke( mavenXpp3WriterEx, writer, model ); |
| |
| return true; |
| } |
| catch ( ClassNotFoundException e ) |
| { |
| // MavenXpp3WriterEx not available in running Maven version |
| } |
| catch ( NoSuchMethodException | SecurityException | IllegalArgumentException | IllegalAccessException |
| | InstantiationException e ) |
| { |
| warnWriteMavenXpp3WriterEx( e ); |
| } |
| catch ( InvocationTargetException e ) |
| { |
| if ( e.getTargetException() instanceof IOException ) |
| { |
| throw (IOException) e.getTargetException(); |
| } |
| else if ( e.getTargetException() instanceof RuntimeException ) |
| { |
| throw (RuntimeException) e.getTargetException(); |
| } |
| warnWriteMavenXpp3WriterEx( e ); |
| } |
| return false; |
| } |
| |
| private static String toString( InputLocation location ) |
| { |
| InputSource source = location.getSource(); |
| |
| String s = source.getModelId(); // by default, display modelId |
| |
| if ( StringUtils.isBlank( s ) || s.contains( "[unknown-version]" ) ) |
| { |
| // unless it is blank or does not provide version information |
| s = source.toString(); |
| } |
| |
| return '}' + s + ( ( location.getLineNumber() >= 0 ) ? ", line " + location.getLineNumber() : "" ) + ' '; |
| } |
| |
| private static class InputLocationStringFormatter |
| extends InputLocation.StringFormatter |
| { |
| |
| public String toString( InputLocation location ) |
| { |
| return EffectivePomMojo.toString( location ); |
| } |
| |
| } |
| |
| /** |
| * Xpp3 extended writer extension to improve default InputSource display |
| */ |
| private static class EffectiveWriterExOldSupport |
| extends MavenXpp3WriterExOldSupport |
| { |
| |
| @Override |
| public String toString( InputLocation location ) |
| { |
| return EffectivePomMojo.toString( location ); |
| } |
| |
| @Override |
| protected void writeXpp3DomToSerializer( Xpp3Dom dom, XmlSerializer serializer ) |
| throws java.io.IOException |
| { |
| // default method uses Xpp3Dom input location tracking, not available in older Maven versions |
| // use old Xpp3Dom serialization, without input location tracking |
| dom.writeToSerializer( null, serializer ); |
| } |
| } |
| } |