View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.help;
20  
21  import java.io.IOException;
22  import java.io.StringWriter;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.Properties;
26  
27  import org.apache.maven.model.InputLocation;
28  import org.apache.maven.model.InputSource;
29  import org.apache.maven.model.Model;
30  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
31  import org.apache.maven.model.io.xpp3.MavenXpp3WriterEx;
32  import org.apache.maven.plugin.MojoExecution;
33  import org.apache.maven.plugin.MojoExecution.Source;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugins.annotations.Mojo;
36  import org.apache.maven.plugins.annotations.Parameter;
37  import org.apache.maven.project.MavenProject;
38  import org.apache.maven.shared.utils.logging.MessageUtils;
39  import org.codehaus.plexus.util.StringUtils;
40  import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
41  import org.codehaus.plexus.util.xml.XMLWriter;
42  import org.codehaus.plexus.util.xml.XmlWriterUtil;
43  
44  /**
45   * Displays the effective POM as an XML for this build, with the active profiles factored in, or a specified artifact.
46   * If <code>verbose</code>, a comment is added to each XML element describing the origin of the line.
47   *
48   * @since 2.0
49   */
50  @Mojo(name = "effective-pom", aggregator = true)
51  public class EffectivePomMojo extends AbstractEffectiveMojo {
52      // ----------------------------------------------------------------------
53      // Mojo parameters
54      // ----------------------------------------------------------------------
55  
56      /**
57       * The projects in the current build. The effective-POM for
58       * each of these projects will written.
59       */
60      @Parameter(defaultValue = "${reactorProjects}", required = true, readonly = true)
61      private List<MavenProject> projects;
62  
63      /**
64       * This mojo execution, used to determine if it was launched from the lifecycle or the command-line.
65       */
66      @Parameter(defaultValue = "${mojo}", required = true, readonly = true)
67      private MojoExecution mojoExecution;
68  
69      /**
70       * The artifact for which to display the effective POM.
71       * <br>
72       * <b>Note</b>: Should respect the Maven format, i.e. <code>groupId:artifactId[:version]</code>. The
73       * latest version of the artifact will be used when no version is specified.
74       *
75       * @since 3.0.0
76       */
77      @Parameter(property = "artifact")
78      private String artifact;
79  
80      /**
81       * Output POM input location as comments.
82       *
83       * @since 3.2.0
84       */
85      @Parameter(property = "verbose", defaultValue = "false")
86      private boolean verbose = false;
87  
88      // ----------------------------------------------------------------------
89      // Public methods
90      // ----------------------------------------------------------------------
91  
92      /** {@inheritDoc} */
93      public void execute() throws MojoExecutionException {
94          if (StringUtils.isNotEmpty(artifact)) {
95              project = getMavenProject(artifact);
96              projects = Collections.singletonList(project);
97          }
98  
99          StringWriter w = new StringWriter();
100         String encoding = output != null ? project.getModel().getModelEncoding() : System.getProperty("file.encoding");
101         XMLWriter writer = new PrettyPrintXMLWriter(
102                 w, StringUtils.repeat(" ", XmlWriterUtil.DEFAULT_INDENTATION_SIZE), encoding, null);
103 
104         writeHeader(writer);
105 
106         if (shouldWriteAllEffectivePOMsInReactor()) {
107             // outer root element
108             writer.startElement("projects");
109             for (MavenProject subProject : projects) {
110                 writeEffectivePom(subProject, writer);
111             }
112             writer.endElement();
113         } else {
114             writeEffectivePom(project, writer);
115         }
116 
117         String effectivePom = prettyFormat(w.toString(), encoding, false);
118         if (verbose) {
119             // tweak location tracking comment, that are put on a separate line by pretty print
120             effectivePom = effectivePom.replaceAll("(?m)>\\s+<!--}", ">  <!-- ");
121         }
122 
123         if (output != null) {
124             try {
125                 writeXmlFile(output, effectivePom);
126             } catch (IOException e) {
127                 throw new MojoExecutionException("Cannot write effective-POM to output: " + output, e);
128             }
129 
130             getLog().info("Effective-POM written to: " + output);
131         } else {
132             if (MessageUtils.isColorEnabled()) {
133                 // add color to comments
134                 String comment = MessageUtils.buffer().project("<!--.-->").toString();
135                 int dotIndex = comment.indexOf(".");
136                 String commentStart = comment.substring(0, dotIndex);
137                 String commentEnd = comment.substring(dotIndex + 1);
138                 effectivePom = effectivePom.replace("<!--", commentStart).replace("-->", commentEnd);
139             }
140 
141             getLog().info(LS + "Effective POMs, after inheritance, interpolation, and profiles are applied:" + LS + LS
142                     + effectivePom + LS);
143         }
144     }
145 
146     /**
147      * Determines if all effective POMs of all the projects in the reactor should be written. When this goal is started
148      * on the command-line, it is always the case. However, when it is bound to a phase in the lifecycle, it is only the
149      * case when the current project being built is the head project in the reactor.
150      *
151      * @return <code>true</code> if all effective POMs should be written, <code>false</code> otherwise.
152      */
153     private boolean shouldWriteAllEffectivePOMsInReactor() {
154         Source source = mojoExecution.getSource();
155         // [MNG-5550] For Maven < 3.2.1, the source is null, instead of LIFECYCLE: only rely on comparisons with CLI
156         return projects.size() > 1 && (source == Source.CLI || projects.get(0).equals(project));
157     }
158 
159     // ----------------------------------------------------------------------
160     // Private methods
161     // ----------------------------------------------------------------------
162 
163     /**
164      * Method for writing the effective pom informations of the current build.
165      *
166      * @param project the project of the current build, not null.
167      * @param writer the XML writer , not null, not null.
168      * @throws MojoExecutionException if any
169      */
170     private void writeEffectivePom(MavenProject project, XMLWriter writer) throws MojoExecutionException {
171         Model pom = project.getModel();
172         cleanModel(pom);
173 
174         StringWriter sWriter = new StringWriter();
175         try {
176             if (verbose) {
177                 MavenXpp3WriterEx mavenXpp3WriterEx = new MavenXpp3WriterEx();
178                 mavenXpp3WriterEx.setStringFormatter(new InputLocationStringFormatter());
179                 mavenXpp3WriterEx.write(sWriter, pom);
180             } else {
181                 new MavenXpp3Writer().write(sWriter, pom);
182             }
183         } catch (IOException e) {
184             throw new MojoExecutionException("Cannot serialize POM to XML.", e);
185         }
186 
187         // This removes the XML declaration written by MavenXpp3Writer
188         String effectivePom = prettyFormat(sWriter.toString(), null, true);
189 
190         writeComment(writer, "Effective POM for project '" + project.getId() + "'");
191 
192         writer.writeMarkup(effectivePom);
193     }
194 
195     /**
196      * Apply some logic to clean the model before writing it.
197      *
198      * @param pom not null
199      */
200     private static void cleanModel(Model pom) {
201         Properties properties = new SortedProperties();
202         properties.putAll(pom.getProperties());
203         pom.setProperties(properties);
204     }
205 
206     private static class InputLocationStringFormatter extends InputLocation.StringFormatter {
207         @Override
208         public String toString(InputLocation location) {
209             InputSource source = location.getSource();
210 
211             String s = source.getModelId(); // by default, display modelId
212 
213             if (StringUtils.isBlank(s) || s.contains("[unknown-version]")) {
214                 // unless it is blank or does not provide version information
215                 s = source.toString();
216             }
217 
218             return '}' + s + ((location.getLineNumber() >= 0) ? ", line " + location.getLineNumber() : "") + ' ';
219         }
220     }
221 }