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