tags:

views:

525

answers:

3

Given a maven project with a parent POM - including mostly plugin and dependency versions - how can I generate a POM which takes the information from the parent, places it into the child and removes the reference to the parent?

Ideally this would be done with the maven-assembly-plugin.


Update: I need this done automatically, since manually it's boring and tedious.

Update 2: I'm preparing the source code for an external and want to deliver only one project, not the whole family.

+1  A: 

If you don't want a parent you can try the BOM (Bill of materials) pattern and select that pom for import in your dependancy management section.

Since you want no references to anything else, you will need to build an app to parse the xml from the "parents" and then write them to the target pom file in the dependencies section. I don't think there is any plugin to do what you want since it seems to go against the very core of how maven is supposed to help you. The whole point of maven is so that you can include all your dependancies using inheritance or importing them with the import scope.

Another option, but I think you've excluded it, is to maintain your releasable POM file separately. Though I think you may find an easier time with this if you reference LATEST or SNAPSHOTS and then use the release plugin to resolve them to released versions.

Mike Pone
Updated description: I'm preparing the source code for an external and want to deliver only one project, not the whole family. I need it not to depend on other artifacts.
Robert Munteanu
+1  A: 

Would mvn help:effective-pom do what you need? You can send the output to a file with -Doutput=new-pom.xml.

Dominic Mitchell
That's helpful, thank you. Unfortunately all paths are converted to absolute.
Robert Munteanu
+3  A: 

I had a similar issue a while back, you can avoid Maven substituting for the properties and resolving paths etc. by defining a new Maven plugin to do the following.

  • Resolve each parent using the standard artifact factory
  • Read each pom file (without resolving it) using the MavenXpp3Reader
  • Merge the unresolved projects
  • Write the merged project to a file

Here is some test code I used to prove the process for myself, you'd obviously need to wrap this up in a Maven plugin and bind that to some phase of your process. The resolved pom is output to the output directory (i.e. target) with the name resolved-pom.xml by default, these two properties can be overridden with the usual Maven plugin configuration approach by setting the "outputDir" and/or "pomfileName" properties.

package name.seller.rich;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Stack;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.inheritance.ModelInheritanceAssembler;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

/**
 * @goal output-project
 * @phase process-resources
 * @requiresProject true
 */
public class OutputResolvedProjectMojo extends AbstractMojo {

    /**
     * Used to look up overlay the parent models on the project's model.
     * 
     * @parameter expression=
     *            "${component.org.apache.maven.project.inheritance.ModelInheritanceAssembler}"
     * @required
     * @readonly
     */
    private ModelInheritanceAssembler modelInheritanceAssembler;

    /**
     * Used to look up Artifacts in the remote repository.
     * 
     * @parameter expression=
     *            "${component.org.apache.maven.artifact.factory.ArtifactFactory}"
     * @required
     * @readonly
     */
    protected org.apache.maven.artifact.factory.ArtifactFactory factory;

    /**
     * Used to look up Artifacts in the remote repository.
     * 
     * @parameter expression=
     *            "${component.org.apache.maven.artifact.resolver.ArtifactResolver}"
     * @required
     * @readonly
     */
    protected org.apache.maven.artifact.resolver.ArtifactResolver artifactResolver;

    /**
     * List of Remote Repositories used by the resolver
     * 
     * @parameter expression="${project.remoteArtifactRepositories}"
     * @readonly
     * @required
     */
    protected java.util.List remoteRepos;

    /**
     * Location of the local repository.
     * 
     * @parameter expression="${localRepository}"
     * @readonly
     * @required
     */
    protected org.apache.maven.artifact.repository.ArtifactRepository local;

    /**
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    private MavenProject mavenProject;

    /**
     * The directory to output the resolved project to.
     * 
     * @parameter expression="${project.build.directory}"
     */
    private File outputDir;

    /**
     * The directory to output the resolved project to.
     * 
     * @parameter expression="resolved-pom.xml"
     */
    private String pomfileName;

    public void execute() throws MojoExecutionException, MojoFailureException {
     MavenProject parentProject = mavenProject.getParent();

     // get the unresolved project by reading the file
     MavenProject bareProject = readBareProject(mavenProject.getFile());

     Stack hierarchy = new Stack();
     hierarchy.push(bareProject);

     try {
      while (parentProject != null) {

       try {
        // get Maven to resolve the parent artifact (download if
        // needed)
        Artifact pomArtifact = this.factory.createArtifact(
          parentProject.getGroupId(), parentProject
            .getArtifactId(), parentProject
            .getVersion(), "", "pom");

        artifactResolver.resolve(pomArtifact, this.remoteRepos,
          this.local);

        // get the file from the local repository and read the bare
        // project
        File parentPomFile = pomArtifact.getFile();

        parentProject = readBareProject(parentPomFile);

        hierarchy.push(parentProject);

        parentProject = parentProject.getParent();
       } catch (ArtifactResolutionException e) {
        getLog().error("can't resolve parent pom", e);
       } catch (ArtifactNotFoundException e) {
        getLog().error("can't resolve parent pom", e);
       }
      }

      // merge each model starting with the oldest ancestors
      MavenProject currentParent = (MavenProject) hierarchy.pop();
      MavenProject currentProject = null;
      while (hierarchy.size() != 0) {
       currentProject = (MavenProject) hierarchy.pop();
       modelInheritanceAssembler.assembleModelInheritance(
         currentProject.getModel(), currentParent.getModel());
       currentParent = currentProject;
      }

      // spit the merged model to the output file.
      Writer writer = getWriter(outputDir, pomfileName);

      if (writer != null) {
       currentProject.writeModel(writer);
       writer.close();
      }
     } catch (IOException e) {
      getLog().error("can't write resolved pom", e);
     }

    }

    /**
     * Creates and returns a writer for outputting the project to a pom file.
     * 
     * @param logDir
     *            the directory to output the file to.
     * @param logFileName
     *            name of the log file
     * @return the writer.
     * @throws IOException
     *             if the writer cannot be created.
     */
    private Writer getWriter(final File logDir, final String logFileName)
      throws IOException {
     if (!logDir.exists()) {
      logDir.mkdirs();
     }

     File pomLog = new File(logDir, logFileName);

     if (!pomLog.exists()) {
      pomLog.createNewFile();
     }

     return new FileWriter(pomLog);
    }

    /**
     * Read the mavenProject without resolving any inherited settings.
     * 
     * @return the MavenProject for the project's POM
     * @throws MojoExecutionException
     *             if the POM can't be parsed.
     */
    MavenProject readBareProject(final File file) {
     MavenXpp3Reader reader = new MavenXpp3Reader();
     Model model = null;
     try {
      model = reader.read(new FileReader(file));
     } catch (IOException e) {
      getLog().error("can't read pom file", e);
     } catch (XmlPullParserException e) {
      getLog().error("can't read pom file", e);
     }

     return new MavenProject(model);
    }
}
Rich Seller
That's neat, thank you! I'll give this a try as soon as I can.
Robert Munteanu
You sir, are brilliant.
Robert Munteanu