blob: 1de3629385525f6cb90de3986e4be0d16162c0ec [file] [log] [blame]
package org.apache.maven.archetype.common;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.maven.archetype.common.util.Format;
import org.apache.maven.archetype.exception.InvalidPackaging;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Build;
import org.apache.maven.model.Profile;
import org.apache.maven.model.ModelBase;
import org.apache.maven.model.Reporting;
import org.apache.maven.model.ReportPlugin;
import org.apache.maven.model.BuildBase;
import org.apache.maven.model.Plugin;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.codehaus.plexus.util.xml.Xpp3DomUtils;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@Component( role = PomManager.class )
public class DefaultPomManager
extends AbstractLogEnabled
implements PomManager
{
@Override
public void addModule( File pom, String artifactId )
throws IOException, XmlPullParserException, DocumentException, InvalidPackaging
{
boolean found = false;
StringWriter writer = new StringWriter();
try ( Reader fileReader = ReaderFactory.newXmlReader( pom ) )
{
SAXReader reader = new SAXReader();
Document document = reader.read( fileReader );
Element project = document.getRootElement();
String packaging = null;
Element packagingElement = project.element( "packaging" );
if ( packagingElement != null )
{
packaging = packagingElement.getStringValue();
}
if ( !"pom".equals( packaging ) )
{
throw new InvalidPackaging(
"Unable to add module to the current project as it is not of packaging type 'pom'"
);
}
Element modules = project.element( "modules" );
if ( modules == null )
{
modules = project.addText( " " ).addElement( "modules" );
modules.setText( "\n " );
project.addText( "\n" );
}
// TODO: change to while loop
for ( @SuppressWarnings( "unchecked" )
Iterator<Element> i = modules.elementIterator( "module" ); i.hasNext() && !found; )
{
Element module = i.next();
if ( module.getText().equals( artifactId ) )
{
found = true;
}
}
if ( !found )
{
Node lastTextNode = null;
for ( @SuppressWarnings( "unchecked" )
Iterator<Node> i = modules.nodeIterator(); i.hasNext(); )
{
Node node = i.next();
if ( node.getNodeType() == Node.ELEMENT_NODE )
{
lastTextNode = null;
}
else if ( node.getNodeType() == Node.TEXT_NODE )
{
lastTextNode = node;
}
}
if ( lastTextNode != null )
{
modules.remove( lastTextNode );
}
modules.addText( "\n " );
modules.addElement( "module" ).setText( artifactId );
modules.addText( "\n " );
XMLWriter xmlWriter = new XMLWriter( writer );
xmlWriter.write( document );
FileUtils.fileWrite( pom.getAbsolutePath(), writer.toString() );
} // end if
}
}
@Override
public void addParent( File pom, File parentPom )
throws IOException, XmlPullParserException
{
Model generatedModel = readPom( pom );
if ( null != generatedModel.getParent() )
{
getLogger().info( "Parent element not overwritten in " + pom );
return;
}
Model parentModel = readPom( parentPom );
Parent parent = new Parent();
parent.setGroupId( parentModel.getGroupId() );
if ( parent.getGroupId() == null )
{
parent.setGroupId( parentModel.getParent().getGroupId() );
}
parent.setArtifactId( parentModel.getArtifactId() );
parent.setVersion( parentModel.getVersion() );
if ( parent.getVersion() == null )
{
parent.setVersion( parentModel.getParent().getVersion() );
}
generatedModel.setParent( parent );
writePom( generatedModel, pom, pom );
}
@Override
public void mergePoms( File pom, File temporaryPom )
throws IOException, XmlPullParserException
{
Model model = readPom( pom );
Model generatedModel = readPom( temporaryPom );
model.getProperties().putAll( generatedModel.getProperties() );
mergeModelBase( model, generatedModel );
mergeModelBuild( model, generatedModel );
mergeProfiles( model, generatedModel );
mergeReportPlugins( model, generatedModel );
//
// // Potential merging
//
// model.getModelEncoding ();
// model.getModelVersion ();
//
// model.getGroupId ();
// model.getArtifactId ();
// model.getVersion ();
// model.getParent ();
//
// model.getId ();
// model.getName ();
// model.getInceptionYear ();
// model.getDescription ();
// model.getUrl ();
// model.getLicenses ();
// model.getProperties ();
//
// model.getOrganization ();
// model.getMailingLists ();
// model.getContributors ();
// model.getDevelopers ();
//
// model.getScm ();
// model.getCiManagement ();
// model.getDistributionManagement ();
// model.getIssueManagement ();
//
// model.getPackaging ();
//// model.getDependencies (); // done
// model.getDependencyManagement ();
// model.getPrerequisites ().getMaven ();
// model.getPrerequisites ().getModelEncoding ();
//
// model.getProfiles ();
// model.getModules ();
// model.getRepositories ();
// model.getPluginRepositories ();
//
// model.getBuild ().getDefaultGoal ();
// model.getBuild ().getFinalName ();
// model.getBuild ().getModelEncoding ();
// model.getBuild ().getFilters ();
// model.getBuild ().getDirectory ();
// model.getBuild ().getOutputDirectory ();
// model.getBuild ().getSourceDirectory ();
// model.getBuild ().getResources ();
// model.getBuild ().getScriptSourceDirectory ();
// model.getBuild ().getTestOutputDirectory ();
// model.getBuild ().getTestResources ();
// model.getBuild ().getTestSourceDirectory ();
// model.getBuild ().getPluginManagement ();
// model.getBuild ().getExtensions ();
//// model.getBuild ().getPluginsAsMap (); // done
//
// model.getReporting ().getModelEncoding ();
// model.getReporting ().getOutputDirectory ();
//// model.getReporting ().getReportPluginsAsMap (); // done
//
writePom( model, pom, pom );
}
@Override
public Model readPom( final File pomFile )
throws IOException, XmlPullParserException
{
try ( Reader pomReader = ReaderFactory.newXmlReader( pomFile ) )
{
MavenXpp3Reader reader = new MavenXpp3Reader();
return reader.read( pomReader );
}
}
@Override
public Model readPom( InputStream pomStream )
throws IOException, XmlPullParserException
{
try ( Reader pomReader = ReaderFactory.newXmlReader( pomStream ) )
{
MavenXpp3Reader reader = new MavenXpp3Reader();
return reader.read( pomReader );
}
}
@Override
public void writePom( final Model model, final File pomFile, final File initialPomFile )
throws IOException
{
String fileEncoding =
StringUtils.isEmpty( model.getModelEncoding() ) ? "UTF-8" : model.getModelEncoding();
org.jdom.Document doc;
try ( InputStream inputStream = new FileInputStream( initialPomFile ) )
{
SAXBuilder builder = new SAXBuilder();
doc = builder.build( inputStream );
}
catch ( JDOMException exc )
{
IOException ioe = new IOException( "Cannot parse the POM by JDOM while reading " + initialPomFile + ": "
+ exc.getMessage() );
ioe.initCause( exc );
throw ioe;
}
try ( Writer outputStreamWriter = new OutputStreamWriter( new FileOutputStream( pomFile ), fileEncoding ) )
{
// The cdata parts of the pom are not preserved from initial to target
MavenJDOMWriter writer = new MavenJDOMWriter();
final String ls = System.lineSeparator();
Format form = Format.getRawFormat().setEncoding( fileEncoding ).setLineSeparator( ls );
writer.write( model, doc, outputStreamWriter, form );
}
catch ( FileNotFoundException e )
{
getLogger().debug( "Creating pom file " + pomFile );
try ( Writer pomWriter = new OutputStreamWriter( new FileOutputStream( pomFile ), fileEncoding ) )
{
MavenXpp3Writer writer = new MavenXpp3Writer();
writer.write( pomWriter, model );
}
}
}
private Map<String, Dependency> createDependencyMap( List<Dependency> dependencies )
{
Map<String, Dependency> dependencyMap = new HashMap<>();
for ( Dependency dependency : dependencies )
{
dependencyMap.put( dependency.getManagementKey(), dependency );
}
return dependencyMap;
}
private void mergeModelBuild( Model model, Model generatedModel )
{
if ( generatedModel.getBuild() != null )
{
if ( model.getBuild() == null )
{
model.setBuild( new Build() );
}
mergeBuildPlugins( model.getBuild(), generatedModel.getBuild() );
}
}
private void mergeProfiles( Model model, Model generatedModel )
{
List<Profile> generatedProfiles = generatedModel.getProfiles();
if ( generatedProfiles != null && generatedProfiles.size() > 0 )
{
List<Profile> modelProfiles = model.getProfiles();
Map<String, Profile> modelProfileIdMap = new HashMap<>();
if ( modelProfiles == null )
{
modelProfiles = new ArrayList<>();
model.setProfiles( modelProfiles );
}
else if ( modelProfiles.size() > 0 )
{
// add profile ids from the model for later lookups to the modelProfileIds set
for ( Profile modelProfile : modelProfiles )
{
modelProfileIdMap.put( modelProfile.getId(), modelProfile );
}
}
for ( Profile generatedProfile : generatedProfiles )
{
String generatedProfileId = generatedProfile.getId();
if ( !modelProfileIdMap.containsKey( generatedProfileId ) )
{
model.addProfile( generatedProfile );
}
else
{
getLogger().warn( "Try to merge profiles with id " + generatedProfileId );
mergeModelBase( modelProfileIdMap.get( generatedProfileId ), generatedProfile );
mergeProfileBuild( modelProfileIdMap.get( generatedProfileId ), generatedProfile );
}
}
}
}
private void mergeProfileBuild( Profile modelProfile, Profile generatedProfile )
{
if ( generatedProfile.getBuild() != null )
{
if ( modelProfile.getBuild() == null )
{
modelProfile.setBuild( new Build() );
}
mergeBuildPlugins( modelProfile.getBuild(), generatedProfile.getBuild() );
// TODO: merge more than just plugins in the profile...
}
}
private void mergeModelBase( ModelBase model, ModelBase generatedModel )
{
// ModelBase can be a Model or a Profile...
Map<String, Dependency> dependenciesByIds = createDependencyMap( model.getDependencies() );
Map<String, Dependency> generatedDependenciesByIds = createDependencyMap( generatedModel.getDependencies() );
for ( String generatedDependencyId : generatedDependenciesByIds.keySet() )
{
if ( !dependenciesByIds.containsKey( generatedDependencyId ) )
{
model.addDependency( generatedDependenciesByIds.get( generatedDependencyId ) );
}
else
{
getLogger().warn( "Can not override property: " + generatedDependencyId );
}
// TODO: maybe warn, if a property key gets overridden?
model.getProperties().putAll( generatedModel.getProperties() );
// TODO: maybe merge more than just dependencies and properties...
}
}
private void mergeReportPlugins( Model model, Model generatedModel )
{
if ( generatedModel.getReporting() != null )
{
if ( model.getReporting() == null )
{
model.setReporting( new Reporting() );
}
Map<String, ReportPlugin> reportPluginsByIds = model.getReporting().getReportPluginsAsMap();
Map<String, ReportPlugin> generatedReportPluginsByIds =
generatedModel.getReporting().getReportPluginsAsMap();
for ( String generatedReportPluginsId : generatedReportPluginsByIds.keySet() )
{
if ( !reportPluginsByIds.containsKey( generatedReportPluginsId ) )
{
model.getReporting().addPlugin( generatedReportPluginsByIds.get( generatedReportPluginsId ) );
}
else
{
getLogger().warn( "Can not override report: " + generatedReportPluginsId );
}
}
}
}
private void mergeBuildPlugins( BuildBase modelBuild, BuildBase generatedModelBuild )
{
Map<String, Plugin> pluginsByIds = modelBuild.getPluginsAsMap();
List<Plugin> generatedPlugins = generatedModelBuild.getPlugins();
for ( Plugin generatedPlugin : generatedPlugins )
{
String generatedPluginsId = generatedPlugin.getKey();
if ( !pluginsByIds.containsKey( generatedPluginsId ) )
{
modelBuild.addPlugin( generatedPlugin );
}
else
{
getLogger().info( "Try to merge plugin configuration of plugins with id: " + generatedPluginsId );
Plugin modelPlugin = pluginsByIds.get( generatedPluginsId );
modelPlugin.setConfiguration( Xpp3DomUtils.mergeXpp3Dom( (Xpp3Dom) generatedPlugin.getConfiguration(),
(Xpp3Dom) modelPlugin.getConfiguration() ) );
}
}
}
}