View Javadoc
1   package org.apache.maven.archetype.common;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.archetype.common.util.Format;
23  import org.apache.maven.archetype.common.util.PomUtils;
24  import org.apache.maven.archetype.exception.InvalidPackaging;
25  import org.apache.maven.archetype.old.ArchetypeTemplateProcessingException;
26  import org.apache.maven.model.Build;
27  import org.apache.maven.model.BuildBase;
28  import org.apache.maven.model.Dependency;
29  import org.apache.maven.model.Model;
30  import org.apache.maven.model.ModelBase;
31  import org.apache.maven.model.Parent;
32  import org.apache.maven.model.Plugin;
33  import org.apache.maven.model.Profile;
34  import org.apache.maven.model.ReportPlugin;
35  import org.apache.maven.model.Reporting;
36  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
37  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
38  import org.codehaus.plexus.component.annotations.Component;
39  import org.codehaus.plexus.logging.AbstractLogEnabled;
40  import org.codehaus.plexus.util.FileUtils;
41  import org.codehaus.plexus.util.ReaderFactory;
42  import org.codehaus.plexus.util.StringUtils;
43  import org.codehaus.plexus.util.xml.Xpp3Dom;
44  import org.codehaus.plexus.util.xml.Xpp3DomUtils;
45  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
46  import org.jdom.JDOMException;
47  import org.jdom.input.SAXBuilder;
48  import org.xml.sax.SAXException;
49  
50  import javax.xml.parsers.ParserConfigurationException;
51  import javax.xml.transform.TransformerException;
52  import java.io.File;
53  import java.io.FileInputStream;
54  import java.io.FileNotFoundException;
55  import java.io.FileOutputStream;
56  import java.io.IOException;
57  import java.io.InputStream;
58  import java.io.OutputStreamWriter;
59  import java.io.Reader;
60  import java.io.StringWriter;
61  import java.io.Writer;
62  import java.util.ArrayList;
63  import java.util.HashMap;
64  import java.util.List;
65  import java.util.Map;
66  
67  @Component( role = PomManager.class )
68  public class DefaultPomManager
69      extends AbstractLogEnabled
70      implements PomManager
71  {
72      @Override
73      public void addModule( File pom, String artifactId ) throws IOException, ParserConfigurationException,
74              TransformerException, SAXException, InvalidPackaging, ArchetypeTemplateProcessingException
75      {
76          StringWriter out = new StringWriter();
77          boolean found = PomUtils.addNewModule( artifactId, ReaderFactory.newXmlReader( pom ), out );
78          if ( found )
79          {
80              FileUtils.fileWrite( pom.getAbsolutePath(), out.toString() );
81          }
82      }
83  
84      @Override
85      public void addParent( File pom, File parentPom )
86          throws IOException, XmlPullParserException
87      {
88          Model generatedModel = readPom( pom );
89          if ( null != generatedModel.getParent() )
90          {
91              getLogger().info( "Parent element not overwritten in " + pom );
92              return;
93          }
94  
95          Model parentModel = readPom( parentPom );
96  
97          Parent parent = new Parent();
98          parent.setGroupId( parentModel.getGroupId() );
99          if ( parent.getGroupId() == null )
100         {
101             parent.setGroupId( parentModel.getParent().getGroupId() );
102         }
103         parent.setArtifactId( parentModel.getArtifactId() );
104         parent.setVersion( parentModel.getVersion() );
105         if ( parent.getVersion() == null )
106         {
107             parent.setVersion( parentModel.getParent().getVersion() );
108         }
109         generatedModel.setParent( parent );
110 
111         writePom( generatedModel, pom, pom );
112     }
113 
114     @Override
115     public void mergePoms( File pom, File temporaryPom )
116         throws IOException, XmlPullParserException
117     {
118         Model model = readPom( pom );
119         Model generatedModel = readPom( temporaryPom );
120 
121         model.getProperties().putAll( generatedModel.getProperties() );
122 
123         mergeModelBase( model, generatedModel );
124         mergeModelBuild( model, generatedModel );
125         mergeProfiles( model, generatedModel );
126         mergeReportPlugins( model, generatedModel );
127 
128 //
129 //        // Potential merging
130 //
131 //        model.getModelEncoding ();
132 //        model.getModelVersion ();
133 //
134 //        model.getGroupId ();
135 //        model.getArtifactId ();
136 //        model.getVersion ();
137 //        model.getParent ();
138 //
139 //        model.getId ();
140 //        model.getName ();
141 //        model.getInceptionYear ();
142 //        model.getDescription ();
143 //        model.getUrl ();
144 //        model.getLicenses ();
145 //        model.getProperties ();
146 //
147 //        model.getOrganization ();
148 //        model.getMailingLists ();
149 //        model.getContributors ();
150 //        model.getDevelopers ();
151 //
152 //        model.getScm ();
153 //        model.getCiManagement ();
154 //        model.getDistributionManagement ();
155 //        model.getIssueManagement ();
156 //
157 //        model.getPackaging ();
158 ////        model.getDependencies (); // done
159 //        model.getDependencyManagement ();
160 //        model.getPrerequisites ().getMaven ();
161 //        model.getPrerequisites ().getModelEncoding ();
162 //
163 //        model.getProfiles ();
164 //        model.getModules ();
165 //        model.getRepositories ();
166 //        model.getPluginRepositories ();
167 //
168 //        model.getBuild ().getDefaultGoal ();
169 //        model.getBuild ().getFinalName ();
170 //        model.getBuild ().getModelEncoding ();
171 //        model.getBuild ().getFilters ();
172 //        model.getBuild ().getDirectory ();
173 //        model.getBuild ().getOutputDirectory ();
174 //        model.getBuild ().getSourceDirectory ();
175 //        model.getBuild ().getResources ();
176 //        model.getBuild ().getScriptSourceDirectory ();
177 //        model.getBuild ().getTestOutputDirectory ();
178 //        model.getBuild ().getTestResources ();
179 //        model.getBuild ().getTestSourceDirectory ();
180 //        model.getBuild ().getPluginManagement ();
181 //        model.getBuild ().getExtensions ();
182 ////        model.getBuild ().getPluginsAsMap (); // done
183 //
184 //        model.getReporting ().getModelEncoding ();
185 //        model.getReporting ().getOutputDirectory ();
186 ////        model.getReporting ().getReportPluginsAsMap (); // done
187 //
188 
189         writePom( model, pom, pom );
190     }
191 
192     @Override
193     public Model readPom( final File pomFile )
194         throws IOException, XmlPullParserException
195     {
196         try ( Reader pomReader = ReaderFactory.newXmlReader( pomFile ) )
197         {
198             MavenXpp3Reader reader = new MavenXpp3Reader();
199 
200             return reader.read( pomReader );
201         }
202     }
203 
204 
205     @Override
206     public Model readPom( InputStream pomStream )
207         throws IOException, XmlPullParserException
208     {
209         try ( Reader pomReader = ReaderFactory.newXmlReader( pomStream ) )
210         {
211             MavenXpp3Reader reader = new MavenXpp3Reader();
212 
213             return reader.read( pomReader );
214         }
215     }
216 
217     @Override
218     public void writePom( final Model model, final File pomFile, final File initialPomFile )
219         throws IOException
220     {
221         String fileEncoding =
222             StringUtils.isEmpty( model.getModelEncoding() ) ? "UTF-8" : model.getModelEncoding();
223 
224         org.jdom.Document doc;
225         try ( InputStream inputStream = new FileInputStream( initialPomFile ) )
226         {
227             SAXBuilder builder = new SAXBuilder();
228             doc = builder.build( inputStream );
229         }
230         catch ( JDOMException exc )
231         {
232             IOException ioe = new IOException( "Cannot parse the POM by JDOM while reading " + initialPomFile + ": "
233                                                + exc.getMessage() );
234             ioe.initCause( exc );
235             throw ioe;
236         }
237         
238         try ( Writer outputStreamWriter = new OutputStreamWriter( new FileOutputStream( pomFile ), fileEncoding ) ) 
239         {
240             // The cdata parts of the pom are not preserved from initial to target
241             MavenJDOMWriter writer = new MavenJDOMWriter();
242 
243             final String ls = System.lineSeparator();
244             Format form = Format.getRawFormat().setEncoding( fileEncoding ).setLineSeparator( ls );
245             writer.write( model, doc, outputStreamWriter, form );
246         }
247         catch ( FileNotFoundException e )
248         {
249             getLogger().debug( "Creating pom file " + pomFile );
250 
251             try ( Writer pomWriter = new OutputStreamWriter( new FileOutputStream( pomFile ), fileEncoding ) )
252             {
253                 MavenXpp3Writer writer = new MavenXpp3Writer();
254                 writer.write( pomWriter, model );
255             }
256         }
257     }
258 
259     private Map<String, Dependency> createDependencyMap( List<Dependency> dependencies )
260     {
261         Map<String, Dependency> dependencyMap = new HashMap<>();
262         for ( Dependency dependency : dependencies )
263         {
264             dependencyMap.put( dependency.getManagementKey(), dependency );
265         }
266 
267         return dependencyMap;
268     }
269 
270     private void mergeModelBuild( Model model, Model generatedModel )
271     {
272         if ( generatedModel.getBuild() != null )
273         {
274             if ( model.getBuild() == null )
275             {
276                 model.setBuild( new Build() );
277             }
278 
279             mergeBuildPlugins( model.getBuild(), generatedModel.getBuild() );
280         }
281     }
282 
283     private void mergeProfiles( Model model, Model generatedModel )
284     {
285         List<Profile> generatedProfiles = generatedModel.getProfiles();
286         if ( generatedProfiles != null && generatedProfiles.size() > 0 )
287         {
288             List<Profile> modelProfiles = model.getProfiles();
289             Map<String, Profile> modelProfileIdMap = new HashMap<>();
290             if ( modelProfiles == null )
291             {
292                 modelProfiles = new ArrayList<>();
293                 model.setProfiles( modelProfiles );
294             }
295             else if ( modelProfiles.size() > 0 )
296             {
297                 // add profile ids from the model for later lookups to the modelProfileIds set
298                 for ( Profile modelProfile : modelProfiles )
299                 {
300                     modelProfileIdMap.put( modelProfile.getId(), modelProfile );
301                 }
302             }
303 
304             for ( Profile generatedProfile : generatedProfiles )
305             {
306                 String generatedProfileId = generatedProfile.getId();
307                 if ( !modelProfileIdMap.containsKey( generatedProfileId ) )
308                 {
309                     model.addProfile( generatedProfile );
310                 }
311                 else
312                 {
313                     getLogger().warn( "Try to merge profiles with id " + generatedProfileId );
314                     mergeModelBase( modelProfileIdMap.get( generatedProfileId ), generatedProfile );
315                     mergeProfileBuild( modelProfileIdMap.get( generatedProfileId ), generatedProfile );
316                 }
317             }
318         }
319     }
320 
321     private void mergeProfileBuild( Profile modelProfile, Profile generatedProfile )
322     {
323         if ( generatedProfile.getBuild() != null )
324         {
325             if ( modelProfile.getBuild() == null )
326             {
327                 modelProfile.setBuild( new Build() );
328             }
329             mergeBuildPlugins( modelProfile.getBuild(), generatedProfile.getBuild() );
330             // TODO: merge more than just plugins in the profile...
331         }
332     }
333 
334     private void mergeModelBase( ModelBase model, ModelBase generatedModel )
335     {
336         // ModelBase can be a Model or a Profile...
337         Map<String, Dependency> dependenciesByIds = createDependencyMap( model.getDependencies() );
338 
339         Map<String, Dependency> generatedDependenciesByIds = createDependencyMap( generatedModel.getDependencies() );
340 
341         for ( String generatedDependencyId : generatedDependenciesByIds.keySet() )
342         {
343             if ( !dependenciesByIds.containsKey( generatedDependencyId ) )
344             {
345                 model.addDependency( generatedDependenciesByIds.get( generatedDependencyId ) );
346             }
347             else
348             {
349                 getLogger().warn( "Can not override property: " + generatedDependencyId );
350             }
351 
352         // TODO: maybe warn, if a property key gets overridden?
353         model.getProperties().putAll( generatedModel.getProperties() );
354 
355         // TODO: maybe merge more than just dependencies and properties...
356         }
357     }
358 
359     private void mergeReportPlugins( Model model, Model generatedModel )
360     {
361         if ( generatedModel.getReporting() != null )
362         {
363             if ( model.getReporting() == null )
364             {
365                 model.setReporting( new Reporting() );
366             }
367 
368             Map<String, ReportPlugin> reportPluginsByIds = model.getReporting().getReportPluginsAsMap();
369 
370             Map<String, ReportPlugin> generatedReportPluginsByIds =
371                 generatedModel.getReporting().getReportPluginsAsMap();
372 
373             for ( String generatedReportPluginsId : generatedReportPluginsByIds.keySet() )
374             {
375                 if ( !reportPluginsByIds.containsKey( generatedReportPluginsId ) )
376                 {
377                     model.getReporting().addPlugin( generatedReportPluginsByIds.get( generatedReportPluginsId ) );
378                 }
379                 else
380                 {
381                     getLogger().warn( "Can not override report: " + generatedReportPluginsId );
382                 }
383             }
384         }
385     }
386 
387     private void mergeBuildPlugins( BuildBase modelBuild, BuildBase generatedModelBuild )
388     {
389         Map<String, Plugin> pluginsByIds = modelBuild.getPluginsAsMap();
390 
391         List<Plugin> generatedPlugins = generatedModelBuild.getPlugins();
392 
393         for ( Plugin generatedPlugin : generatedPlugins )
394         {
395             String generatedPluginsId = generatedPlugin.getKey();
396 
397             if ( !pluginsByIds.containsKey( generatedPluginsId ) )
398             {
399                 modelBuild.addPlugin( generatedPlugin );
400             }
401             else
402             {
403                 getLogger().info( "Try to merge plugin configuration of plugins with id: " + generatedPluginsId );
404                 Plugin modelPlugin = pluginsByIds.get( generatedPluginsId );
405 
406                 modelPlugin.setConfiguration( Xpp3DomUtils.mergeXpp3Dom( (Xpp3Dom) generatedPlugin.getConfiguration(),
407                                                                          (Xpp3Dom) modelPlugin.getConfiguration() ) );
408             }
409         }
410     }
411 }