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             Format form = Format.getRawFormat().setEncoding( fileEncoding );
321             writer.write( model, doc, outputStreamWriter, form );
322         }
323         catch ( JDOMException exc )
324         {
325             IOException ioe = new IOException( "Cannot parse the POM by JDOM while reading " + initialPomFile + ": "
326                                                + exc.getMessage() );
327             ioe.initCause( exc );
328             throw ioe;
329         }
330         catch ( FileNotFoundException e )
331         {
332             getLogger().debug( "Creating pom file " + pomFile );
333 
334             Writer pomWriter = null;
335 
336             try
337             {
338                 pomWriter =
339                     new OutputStreamWriter( new FileOutputStream( pomFile ), fileEncoding );
340 
341                 MavenXpp3Writer writer = new MavenXpp3Writer();
342                 writer.write( pomWriter, model );
343             }
344             finally
345             {
346                 IOUtil.close( pomWriter );
347             }
348         }
349         finally
350         {
351             IOUtil.close( inputStream );
352             IOUtil.close( outputStreamWriter );
353         }
354     }
355 
356     private Map<String, Dependency> createDependencyMap( List<Dependency> dependencies )
357     {
358         Map<String, Dependency> dependencyMap = new HashMap<String, Dependency>();
359         for ( Dependency dependency : dependencies )
360         {
361             dependencyMap.put( dependency.getManagementKey(), dependency );
362         }
363 
364         return dependencyMap;
365     }
366 
367     private void mergeModelBuild( Model model, Model generatedModel )
368     {
369         if ( generatedModel.getBuild() != null )
370         {
371             if ( model.getBuild() == null )
372             {
373                 model.setBuild( new Build() );
374             }
375 
376             mergeBuildPlugins( model.getBuild(), generatedModel.getBuild() );
377         }
378     }
379 
380     private void mergeProfiles( Model model, Model generatedModel )
381     {
382         @SuppressWarnings( "unchecked" )
383         List<Profile> generatedProfiles = generatedModel.getProfiles();
384         if ( generatedProfiles != null && generatedProfiles.size() > 0 )
385         {
386             @SuppressWarnings( "unchecked" )
387             List<Profile> modelProfiles = model.getProfiles();
388             Map<String, Profile> modelProfileIdMap = new HashMap<String, Profile>();
389             if ( modelProfiles == null )
390             {
391                 modelProfiles = new ArrayList<Profile>();
392                 model.setProfiles( modelProfiles );
393             }
394             else if ( modelProfiles.size() > 0 )
395             {
396                 // add profile ids from the model for later lookups to the modelProfileIds set
397                 for ( Profile modelProfile : modelProfiles )
398                 {
399                     modelProfileIdMap.put( modelProfile.getId(), modelProfile );
400                 }
401             }
402 
403             for ( Profile generatedProfile : generatedProfiles )
404             {
405                 String generatedProfileId = generatedProfile.getId();
406                 if ( !modelProfileIdMap.containsKey( generatedProfileId ) )
407                 {
408                     model.addProfile( generatedProfile );
409                 }
410                 else
411                 {
412                     getLogger().warn( "Try to merge profiles with id " + generatedProfileId );
413                     mergeModelBase( (Profile) modelProfileIdMap.get( generatedProfileId ), generatedProfile );
414                     mergeProfileBuild( (Profile) modelProfileIdMap.get( generatedProfileId ), generatedProfile );
415                 }
416             }
417         }
418     }
419 
420     private void mergeProfileBuild( Profile modelProfile, Profile generatedProfile )
421     {
422         if ( generatedProfile.getBuild() != null )
423         {
424             if ( modelProfile.getBuild() == null )
425             {
426                 modelProfile.setBuild( new Build() );
427             }
428             mergeBuildPlugins( modelProfile.getBuild(), generatedProfile.getBuild() );
429             // TODO: merge more than just plugins in the profile...
430         }
431     }
432 
433     private void mergeModelBase( ModelBase model, ModelBase generatedModel )
434     {
435         // ModelBase can be a Model or a Profile...
436 
437         @SuppressWarnings( "unchecked" )
438         Map<String, Dependency> dependenciesByIds = createDependencyMap( model.getDependencies() );
439         @SuppressWarnings( "unchecked" )
440         Map<String, Dependency> generatedDependenciesByIds = createDependencyMap( generatedModel.getDependencies() );
441 
442         for ( String generatedDependencyId : generatedDependenciesByIds.keySet() )
443         {
444             if ( !dependenciesByIds.containsKey( generatedDependencyId ) )
445             {
446                 model.addDependency( (Dependency) generatedDependenciesByIds.get( generatedDependencyId ) );
447             }
448             else
449             {
450                 getLogger().warn( "Can not override property: " + generatedDependencyId );
451             }
452 
453         // TODO: maybe warn, if a property key gets overridden?
454         model.getProperties().putAll( generatedModel.getProperties() );
455 
456         // TODO: maybe merge more than just dependencies and properties...
457         }
458     }
459 
460     private void mergeReportPlugins( Model model, Model generatedModel )
461     {
462         if ( generatedModel.getReporting() != null )
463         {
464             if ( model.getReporting() == null )
465             {
466                 model.setReporting( new Reporting() );
467             }
468 
469             @SuppressWarnings( "unchecked" )
470             Map<String, ReportPlugin> reportPluginsByIds = model.getReporting().getReportPluginsAsMap();
471             @SuppressWarnings( "unchecked" )
472             Map<String, ReportPlugin> generatedReportPluginsByIds =
473                 generatedModel.getReporting().getReportPluginsAsMap();
474 
475             for ( String generatedReportPluginsId : generatedReportPluginsByIds.keySet() )
476             {
477                 if ( !reportPluginsByIds.containsKey( generatedReportPluginsId ) )
478                 {
479                     model.getReporting().addPlugin( generatedReportPluginsByIds.get( generatedReportPluginsId ) );
480                 }
481                 else
482                 {
483                     getLogger().warn( "Can not override report: " + generatedReportPluginsId );
484                 }
485             }
486         }
487     }
488 
489     private void mergeBuildPlugins( BuildBase modelBuild, BuildBase generatedModelBuild )
490     {
491         @SuppressWarnings( "unchecked" )
492         Map<String, Plugin> pluginsByIds = modelBuild.getPluginsAsMap();
493         @SuppressWarnings( "unchecked" )
494         List<Plugin> generatedPlugins = generatedModelBuild.getPlugins();
495 
496         for ( Plugin generatedPlugin : generatedPlugins )
497         {
498             String generatedPluginsId = generatedPlugin.getKey();
499 
500             if ( !pluginsByIds.containsKey( generatedPluginsId ) )
501             {
502                 modelBuild.addPlugin( generatedPlugin );
503             }
504             else
505             {
506                 getLogger().info( "Try to merge plugin configuration of plugins with id: " + generatedPluginsId );
507                 Plugin modelPlugin = (Plugin) pluginsByIds.get( generatedPluginsId );
508 
509                 modelPlugin.setConfiguration( Xpp3DomUtils.mergeXpp3Dom( (Xpp3Dom) generatedPlugin.getConfiguration(),
510                                                                          (Xpp3Dom) modelPlugin.getConfiguration() ) );
511             }
512         }
513     }
514 }