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.jdom2.JDOMException;
47  import org.jdom2.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.jdom2.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             throw new IOException( "Cannot parse the POM by JDOM while reading " + initialPomFile + ": "
233                                                + exc.getMessage(), exc );
234         }
235 
236 
237         if ( !pomFile.exists() && !pomFile.createNewFile() )
238         {
239             getLogger().warn( "Could not create new file \"" + pomFile.getPath() + "\" or the file already exists." );
240         }
241         
242         try ( Writer outputStreamWriter = new OutputStreamWriter( new FileOutputStream( pomFile ), fileEncoding ) ) 
243         {
244             // The cdata parts of the pom are not preserved from initial to target
245             MavenJDOMWriter writer = new MavenJDOMWriter();
246 
247             final String ls = System.lineSeparator();
248             Format form = Format.getRawFormat().setEncoding( fileEncoding ).setLineSeparator( ls );
249             writer.write( model, doc, outputStreamWriter, form );
250         }
251         catch ( FileNotFoundException e )
252         {
253             getLogger().debug( "Creating pom file " + pomFile );
254 
255             try ( Writer pomWriter = new OutputStreamWriter( new FileOutputStream( pomFile ), fileEncoding ) )
256             {
257                 MavenXpp3Writer writer = new MavenXpp3Writer();
258                 writer.write( pomWriter, model );
259             }
260         }
261     }
262 
263     private Map<String, Dependency> createDependencyMap( List<Dependency> dependencies )
264     {
265         Map<String, Dependency> dependencyMap = new HashMap<>();
266         for ( Dependency dependency : dependencies )
267         {
268             dependencyMap.put( dependency.getManagementKey(), dependency );
269         }
270 
271         return dependencyMap;
272     }
273 
274     private void mergeModelBuild( Model model, Model generatedModel )
275     {
276         if ( generatedModel.getBuild() != null )
277         {
278             if ( model.getBuild() == null )
279             {
280                 model.setBuild( new Build() );
281             }
282 
283             mergeBuildPlugins( model.getBuild(), generatedModel.getBuild() );
284         }
285     }
286 
287     private void mergeProfiles( Model model, Model generatedModel )
288     {
289         List<Profile> generatedProfiles = generatedModel.getProfiles();
290         if ( generatedProfiles != null && generatedProfiles.size() > 0 )
291         {
292             List<Profile> modelProfiles = model.getProfiles();
293             Map<String, Profile> modelProfileIdMap = new HashMap<>();
294             if ( modelProfiles == null )
295             {
296                 modelProfiles = new ArrayList<>();
297                 model.setProfiles( modelProfiles );
298             }
299             else if ( modelProfiles.size() > 0 )
300             {
301                 // add profile ids from the model for later lookups to the modelProfileIds set
302                 for ( Profile modelProfile : modelProfiles )
303                 {
304                     modelProfileIdMap.put( modelProfile.getId(), modelProfile );
305                 }
306             }
307 
308             for ( Profile generatedProfile : generatedProfiles )
309             {
310                 String generatedProfileId = generatedProfile.getId();
311                 if ( !modelProfileIdMap.containsKey( generatedProfileId ) )
312                 {
313                     model.addProfile( generatedProfile );
314                 }
315                 else
316                 {
317                     getLogger().warn( "Try to merge profiles with id " + generatedProfileId );
318                     mergeModelBase( modelProfileIdMap.get( generatedProfileId ), generatedProfile );
319                     mergeProfileBuild( modelProfileIdMap.get( generatedProfileId ), generatedProfile );
320                 }
321             }
322         }
323     }
324 
325     private void mergeProfileBuild( Profile modelProfile, Profile generatedProfile )
326     {
327         if ( generatedProfile.getBuild() != null )
328         {
329             if ( modelProfile.getBuild() == null )
330             {
331                 modelProfile.setBuild( new Build() );
332             }
333             mergeBuildPlugins( modelProfile.getBuild(), generatedProfile.getBuild() );
334             // TODO: merge more than just plugins in the profile...
335         }
336     }
337 
338     private void mergeModelBase( ModelBase model, ModelBase generatedModel )
339     {
340         // ModelBase can be a Model or a Profile...
341         Map<String, Dependency> dependenciesByIds = createDependencyMap( model.getDependencies() );
342 
343         Map<String, Dependency> generatedDependenciesByIds = createDependencyMap( generatedModel.getDependencies() );
344 
345         for ( String generatedDependencyId : generatedDependenciesByIds.keySet() )
346         {
347             if ( !dependenciesByIds.containsKey( generatedDependencyId ) )
348             {
349                 model.addDependency( generatedDependenciesByIds.get( generatedDependencyId ) );
350             }
351             else
352             {
353                 getLogger().warn( "Can not override property: " + generatedDependencyId );
354             }
355 
356         // TODO: maybe warn, if a property key gets overridden?
357         model.getProperties().putAll( generatedModel.getProperties() );
358 
359         // TODO: maybe merge more than just dependencies and properties...
360         }
361     }
362 
363     private void mergeReportPlugins( Model model, Model generatedModel )
364     {
365         if ( generatedModel.getReporting() != null )
366         {
367             if ( model.getReporting() == null )
368             {
369                 model.setReporting( new Reporting() );
370             }
371 
372             Map<String, ReportPlugin> reportPluginsByIds = model.getReporting().getReportPluginsAsMap();
373 
374             Map<String, ReportPlugin> generatedReportPluginsByIds =
375                 generatedModel.getReporting().getReportPluginsAsMap();
376 
377             for ( String generatedReportPluginsId : generatedReportPluginsByIds.keySet() )
378             {
379                 if ( !reportPluginsByIds.containsKey( generatedReportPluginsId ) )
380                 {
381                     model.getReporting().addPlugin( generatedReportPluginsByIds.get( generatedReportPluginsId ) );
382                 }
383                 else
384                 {
385                     getLogger().warn( "Can not override report: " + generatedReportPluginsId );
386                 }
387             }
388         }
389     }
390 
391     private void mergeBuildPlugins( BuildBase modelBuild, BuildBase generatedModelBuild )
392     {
393         Map<String, Plugin> pluginsByIds = modelBuild.getPluginsAsMap();
394 
395         List<Plugin> generatedPlugins = generatedModelBuild.getPlugins();
396 
397         for ( Plugin generatedPlugin : generatedPlugins )
398         {
399             String generatedPluginsId = generatedPlugin.getKey();
400 
401             if ( !pluginsByIds.containsKey( generatedPluginsId ) )
402             {
403                 modelBuild.addPlugin( generatedPlugin );
404             }
405             else
406             {
407                 getLogger().info( "Try to merge plugin configuration of plugins with id: " + generatedPluginsId );
408                 Plugin modelPlugin = pluginsByIds.get( generatedPluginsId );
409 
410                 modelPlugin.setConfiguration( Xpp3DomUtils.mergeXpp3Dom( (Xpp3Dom) generatedPlugin.getConfiguration(),
411                                                                          (Xpp3Dom) modelPlugin.getConfiguration() ) );
412             }
413         }
414     }
415 }