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