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.exception.InvalidPackaging;
24  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
25  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
26  import org.apache.maven.model.Model;
27  import org.apache.maven.model.Parent;
28  import org.apache.maven.model.Dependency;
29  import org.apache.maven.model.Build;
30  import org.apache.maven.model.Profile;
31  import org.apache.maven.model.ModelBase;
32  import org.apache.maven.model.Reporting;
33  import org.apache.maven.model.ReportPlugin;
34  import org.apache.maven.model.BuildBase;
35  import org.apache.maven.model.Plugin;
36  import org.codehaus.plexus.component.annotations.Component;
37  import org.codehaus.plexus.logging.AbstractLogEnabled;
38  import org.codehaus.plexus.util.FileUtils;
39  import org.codehaus.plexus.util.IOUtil;
40  import org.codehaus.plexus.util.ReaderFactory;
41  import org.codehaus.plexus.util.StringUtils;
42  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
43  import org.codehaus.plexus.util.xml.Xpp3DomUtils;
44  import org.codehaus.plexus.util.xml.Xpp3Dom;
45  import org.dom4j.Document;
46  import org.dom4j.DocumentException;
47  import org.dom4j.Element;
48  import org.dom4j.Node;
49  import org.dom4j.io.SAXReader;
50  import org.dom4j.io.XMLWriter;
51  import org.jdom.JDOMException;
52  import org.jdom.input.SAXBuilder;
53  
54  import java.io.File;
55  import java.io.FileInputStream;
56  import java.io.FileNotFoundException;
57  import java.io.FileOutputStream;
58  import java.io.IOException;
59  import java.io.InputStream;
60  import java.io.OutputStreamWriter;
61  import java.io.Reader;
62  import java.io.StringWriter;
63  import java.io.Writer;
64  import java.util.ArrayList;
65  import java.util.HashMap;
66  import java.util.Iterator;
67  import java.util.List;
68  import java.util.Map;
69  
70  @Component( role = PomManager.class )
71  public class DefaultPomManager
72      extends AbstractLogEnabled
73      implements PomManager
74  {
75      public void addModule( File pom, String artifactId )
76          throws IOException, XmlPullParserException, DocumentException, InvalidPackaging
77      {
78          boolean found = false;
79  
80          StringWriter writer = new StringWriter();
81          Reader fileReader = null;
82  
83          try
84          {
85              fileReader = ReaderFactory.newXmlReader( pom );
86  
87              SAXReader reader = new SAXReader();
88              Document document = reader.read( fileReader );
89              Element project = document.getRootElement();
90  
91              String packaging = null;
92              Element packagingElement = project.element( "packaging" );
93              if ( packagingElement != null )
94              {
95                  packaging = packagingElement.getStringValue();
96              }
97              if ( !"pom".equals( packaging ) )
98              {
99                  throw new InvalidPackaging(
100                     "Unable to add module to the current project as it is not of packaging type 'pom'"
101                 );
102             }
103 
104             Element modules = project.element( "modules" );
105             if ( modules == null )
106             {
107                 modules = project.addText( "  " ).addElement( "modules" );
108                 modules.setText( "\n  " );
109                 project.addText( "\n" );
110             }
111             // TODO: change to while loop
112             for ( @SuppressWarnings( "unchecked" )
113             Iterator<Element> i = modules.elementIterator( "module" ); i.hasNext() && !found; )
114             {
115                 Element module = i.next();
116                 if ( module.getText().equals( artifactId ) )
117                 {
118                     found = true;
119                 }
120             }
121             if ( !found )
122             {
123                 Node lastTextNode = null;
124                 for ( @SuppressWarnings( "unchecked" )
125                 Iterator<Node> i = modules.nodeIterator(); i.hasNext(); )
126                 {
127                     Node node = i.next();
128                     if ( node.getNodeType() == Node.ELEMENT_NODE )
129                     {
130                         lastTextNode = null;
131                     }
132                     else if ( node.getNodeType() == Node.TEXT_NODE )
133                     {
134                         lastTextNode = node;
135                     }
136                 }
137 
138                 if ( lastTextNode != null )
139                 {
140                     modules.remove( lastTextNode );
141                 }
142 
143                 modules.addText( "\n    " );
144                 modules.addElement( "module" ).setText( artifactId );
145                 modules.addText( "\n  " );
146 
147                 XMLWriter xmlWriter = new XMLWriter( writer );
148                 xmlWriter.write( document );
149 
150                 FileUtils.fileWrite( pom.getAbsolutePath(), writer.toString() );
151             } // end if
152         }
153         finally
154         {
155             IOUtil.close( fileReader );
156         }
157     }
158 
159     public void addParent( File pom, File parentPom )
160         throws IOException, XmlPullParserException
161     {
162         Model generatedModel = readPom( pom );
163         if ( null != generatedModel.getParent() )
164         {
165             getLogger().info( "Parent element not overwritten in " + pom );
166             return;
167         }
168 
169         Model parentModel = readPom( parentPom );
170 
171         Parent parent = new Parent();
172         parent.setGroupId( parentModel.getGroupId() );
173         if ( parent.getGroupId() == null )
174         {
175             parent.setGroupId( parentModel.getParent().getGroupId() );
176         }
177         parent.setArtifactId( parentModel.getArtifactId() );
178         parent.setVersion( parentModel.getVersion() );
179         if ( parent.getVersion() == null )
180         {
181             parent.setVersion( parentModel.getParent().getVersion() );
182         }
183         generatedModel.setParent( parent );
184 
185         writePom( generatedModel, pom, pom );
186     }
187 
188     public void mergePoms( File pom, File temporaryPom )
189         throws IOException, XmlPullParserException
190     {
191         Model model = readPom( pom );
192         Model generatedModel = readPom( temporaryPom );
193 
194         model.getProperties().putAll( generatedModel.getProperties() );
195 
196         mergeModelBase( model, generatedModel );
197         mergeModelBuild( model, generatedModel );
198         mergeProfiles( model, generatedModel );
199         mergeReportPlugins( model, generatedModel );
200 
201 //
202 //        // Potential merging
203 //
204 //        model.getModelEncoding ();
205 //        model.getModelVersion ();
206 //
207 //        model.getGroupId ();
208 //        model.getArtifactId ();
209 //        model.getVersion ();
210 //        model.getParent ();
211 //
212 //        model.getId ();
213 //        model.getName ();
214 //        model.getInceptionYear ();
215 //        model.getDescription ();
216 //        model.getUrl ();
217 //        model.getLicenses ();
218 //        model.getProperties ();
219 //
220 //        model.getOrganization ();
221 //        model.getMailingLists ();
222 //        model.getContributors ();
223 //        model.getDevelopers ();
224 //
225 //        model.getScm ();
226 //        model.getCiManagement ();
227 //        model.getDistributionManagement ();
228 //        model.getIssueManagement ();
229 //
230 //        model.getPackaging ();
231 ////        model.getDependencies (); // done
232 //        model.getDependencyManagement ();
233 //        model.getPrerequisites ().getMaven ();
234 //        model.getPrerequisites ().getModelEncoding ();
235 //
236 //        model.getProfiles ();
237 //        model.getModules ();
238 //        model.getRepositories ();
239 //        model.getPluginRepositories ();
240 //
241 //        model.getBuild ().getDefaultGoal ();
242 //        model.getBuild ().getFinalName ();
243 //        model.getBuild ().getModelEncoding ();
244 //        model.getBuild ().getFilters ();
245 //        model.getBuild ().getDirectory ();
246 //        model.getBuild ().getOutputDirectory ();
247 //        model.getBuild ().getSourceDirectory ();
248 //        model.getBuild ().getResources ();
249 //        model.getBuild ().getScriptSourceDirectory ();
250 //        model.getBuild ().getTestOutputDirectory ();
251 //        model.getBuild ().getTestResources ();
252 //        model.getBuild ().getTestSourceDirectory ();
253 //        model.getBuild ().getPluginManagement ();
254 //        model.getBuild ().getExtensions ();
255 ////        model.getBuild ().getPluginsAsMap (); // done
256 //
257 //        model.getReporting ().getModelEncoding ();
258 //        model.getReporting ().getOutputDirectory ();
259 ////        model.getReporting ().getReportPluginsAsMap (); // done
260 //
261 
262         writePom( model, pom, pom );
263     }
264 
265     public Model readPom( final File pomFile )
266         throws IOException, XmlPullParserException
267     {
268         Model model;
269         Reader pomReader = null;
270         try
271         {
272             pomReader = ReaderFactory.newXmlReader( pomFile );
273 
274             MavenXpp3Reader reader = new MavenXpp3Reader();
275 
276             model = reader.read( pomReader );
277         }
278         finally
279         {
280             IOUtil.close( pomReader );
281         }
282         return model;
283     }
284 
285 
286     public Model readPom( InputStream pomStream )
287         throws IOException, XmlPullParserException
288     {
289         Reader pomReader = ReaderFactory.newXmlReader( pomStream );
290 
291         MavenXpp3Reader reader = new MavenXpp3Reader();
292 
293         return reader.read( pomReader );
294     }
295 
296     public void writePom( final Model model, final File pomFile, final File initialPomFile )
297         throws IOException
298     {
299         InputStream inputStream = null;
300         Writer outputStreamWriter = null;
301 
302         String fileEncoding =
303             StringUtils.isEmpty( model.getModelEncoding() ) ? "UTF-8" : model.getModelEncoding();
304 
305         try
306         {
307             inputStream = new FileInputStream( initialPomFile );
308 
309             SAXBuilder builder = new SAXBuilder();
310             org.jdom.Document doc = builder.build( inputStream );
311             inputStream.close();
312             inputStream = null;
313 
314             // The cdata parts of the pom are not preserved from initial to target
315             MavenJDOMWriter writer = new MavenJDOMWriter();
316 
317             outputStreamWriter =
318                 new OutputStreamWriter( new FileOutputStream( pomFile ), fileEncoding );
319 
320             final String ls = System.getProperty( "line.separator" );
321             Format form = Format.getRawFormat().setEncoding( fileEncoding ).setLineSeparator( ls );
322             writer.write( model, doc, outputStreamWriter, form );
323         }
324         catch ( JDOMException exc )
325         {
326             IOException ioe = new IOException( "Cannot parse the POM by JDOM while reading " + initialPomFile + ": "
327                                                + exc.getMessage() );
328             ioe.initCause( exc );
329             throw ioe;
330         }
331         catch ( FileNotFoundException e )
332         {
333             getLogger().debug( "Creating pom file " + pomFile );
334 
335             Writer pomWriter = null;
336 
337             try
338             {
339                 pomWriter =
340                     new OutputStreamWriter( new FileOutputStream( pomFile ), fileEncoding );
341 
342                 MavenXpp3Writer writer = new MavenXpp3Writer();
343                 writer.write( pomWriter, model );
344             }
345             finally
346             {
347                 IOUtil.close( pomWriter );
348             }
349         }
350         finally
351         {
352             IOUtil.close( inputStream );
353             IOUtil.close( outputStreamWriter );
354         }
355     }
356 
357     private Map<String, Dependency> createDependencyMap( List<Dependency> dependencies )
358     {
359         Map<String, Dependency> dependencyMap = new HashMap<String, Dependency>();
360         for ( Dependency dependency : dependencies )
361         {
362             dependencyMap.put( dependency.getManagementKey(), dependency );
363         }
364 
365         return dependencyMap;
366     }
367 
368     private void mergeModelBuild( Model model, Model generatedModel )
369     {
370         if ( generatedModel.getBuild() != null )
371         {
372             if ( model.getBuild() == null )
373             {
374                 model.setBuild( new Build() );
375             }
376 
377             mergeBuildPlugins( model.getBuild(), generatedModel.getBuild() );
378         }
379     }
380 
381     private void mergeProfiles( Model model, Model generatedModel )
382     {
383         @SuppressWarnings( "unchecked" )
384         List<Profile> generatedProfiles = generatedModel.getProfiles();
385         if ( generatedProfiles != null && generatedProfiles.size() > 0 )
386         {
387             @SuppressWarnings( "unchecked" )
388             List<Profile> modelProfiles = model.getProfiles();
389             Map<String, Profile> modelProfileIdMap = new HashMap<String, Profile>();
390             if ( modelProfiles == null )
391             {
392                 modelProfiles = new ArrayList<Profile>();
393                 model.setProfiles( modelProfiles );
394             }
395             else if ( modelProfiles.size() > 0 )
396             {
397                 // add profile ids from the model for later lookups to the modelProfileIds set
398                 for ( Profile modelProfile : modelProfiles )
399                 {
400                     modelProfileIdMap.put( modelProfile.getId(), modelProfile );
401                 }
402             }
403 
404             for ( Profile generatedProfile : generatedProfiles )
405             {
406                 String generatedProfileId = generatedProfile.getId();
407                 if ( !modelProfileIdMap.containsKey( generatedProfileId ) )
408                 {
409                     model.addProfile( generatedProfile );
410                 }
411                 else
412                 {
413                     getLogger().warn( "Try to merge profiles with id " + generatedProfileId );
414                     mergeModelBase( (Profile) modelProfileIdMap.get( generatedProfileId ), generatedProfile );
415                     mergeProfileBuild( (Profile) modelProfileIdMap.get( generatedProfileId ), generatedProfile );
416                 }
417             }
418         }
419     }
420 
421     private void mergeProfileBuild( Profile modelProfile, Profile generatedProfile )
422     {
423         if ( generatedProfile.getBuild() != null )
424         {
425             if ( modelProfile.getBuild() == null )
426             {
427                 modelProfile.setBuild( new Build() );
428             }
429             mergeBuildPlugins( modelProfile.getBuild(), generatedProfile.getBuild() );
430             // TODO: merge more than just plugins in the profile...
431         }
432     }
433 
434     private void mergeModelBase( ModelBase model, ModelBase generatedModel )
435     {
436         // ModelBase can be a Model or a Profile...
437 
438         @SuppressWarnings( "unchecked" )
439         Map<String, Dependency> dependenciesByIds = createDependencyMap( model.getDependencies() );
440         @SuppressWarnings( "unchecked" )
441         Map<String, Dependency> generatedDependenciesByIds = createDependencyMap( generatedModel.getDependencies() );
442 
443         for ( String generatedDependencyId : generatedDependenciesByIds.keySet() )
444         {
445             if ( !dependenciesByIds.containsKey( generatedDependencyId ) )
446             {
447                 model.addDependency( (Dependency) generatedDependenciesByIds.get( generatedDependencyId ) );
448             }
449             else
450             {
451                 getLogger().warn( "Can not override property: " + generatedDependencyId );
452             }
453 
454         // TODO: maybe warn, if a property key gets overridden?
455         model.getProperties().putAll( generatedModel.getProperties() );
456 
457         // TODO: maybe merge more than just dependencies and properties...
458         }
459     }
460 
461     private void mergeReportPlugins( Model model, Model generatedModel )
462     {
463         if ( generatedModel.getReporting() != null )
464         {
465             if ( model.getReporting() == null )
466             {
467                 model.setReporting( new Reporting() );
468             }
469 
470             @SuppressWarnings( "unchecked" )
471             Map<String, ReportPlugin> reportPluginsByIds = model.getReporting().getReportPluginsAsMap();
472             @SuppressWarnings( "unchecked" )
473             Map<String, ReportPlugin> generatedReportPluginsByIds =
474                 generatedModel.getReporting().getReportPluginsAsMap();
475 
476             for ( String generatedReportPluginsId : generatedReportPluginsByIds.keySet() )
477             {
478                 if ( !reportPluginsByIds.containsKey( generatedReportPluginsId ) )
479                 {
480                     model.getReporting().addPlugin( generatedReportPluginsByIds.get( generatedReportPluginsId ) );
481                 }
482                 else
483                 {
484                     getLogger().warn( "Can not override report: " + generatedReportPluginsId );
485                 }
486             }
487         }
488     }
489 
490     private void mergeBuildPlugins( BuildBase modelBuild, BuildBase generatedModelBuild )
491     {
492         @SuppressWarnings( "unchecked" )
493         Map<String, Plugin> pluginsByIds = modelBuild.getPluginsAsMap();
494         @SuppressWarnings( "unchecked" )
495         List<Plugin> generatedPlugins = generatedModelBuild.getPlugins();
496 
497         for ( Plugin generatedPlugin : generatedPlugins )
498         {
499             String generatedPluginsId = generatedPlugin.getKey();
500 
501             if ( !pluginsByIds.containsKey( generatedPluginsId ) )
502             {
503                 modelBuild.addPlugin( generatedPlugin );
504             }
505             else
506             {
507                 getLogger().info( "Try to merge plugin configuration of plugins with id: " + generatedPluginsId );
508                 Plugin modelPlugin = (Plugin) pluginsByIds.get( generatedPluginsId );
509 
510                 modelPlugin.setConfiguration( Xpp3DomUtils.mergeXpp3Dom( (Xpp3Dom) generatedPlugin.getConfiguration(),
511                                                                          (Xpp3Dom) modelPlugin.getConfiguration() ) );
512             }
513         }
514     }
515 }