package org.ops4j.pax.construct.bundle;
/*
* Copyright 2007 Stuart McCulloch
*
* Licensed 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 java.io.File;
import java.io.IOException;
import java.util.Iterator;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.codehaus.plexus.util.FileUtils;
import org.ops4j.pax.construct.util.DirUtils;
import org.ops4j.pax.construct.util.PomIterator;
import org.ops4j.pax.construct.util.PomUtils;
import org.ops4j.pax.construct.util.PomUtils.Pom;
/**
* Move a bundle project to a new directory, updating and creating POMs as necessary
*
* <code><pre>
* mvn pax:move-bundle -DbundleName=... -DtargetDirectory=...
* </pre></code>
*
* @goal move-bundle
* @aggregator true
*
* @requiresProject false
*/
public class MoveBundleMojo extends AbstractMojo
{
/**
* A directory in the same project tree.
*
* @parameter expression="${baseDirectory}" default-value="${project.basedir}"
*/
private File baseDirectory;
/**
* The new location for the bundle project.
*
* @parameter expression="${targetDirectory}"
* @required
*/
private File targetDirectory;
/**
* The artifactId or symbolic-name of the bundle.
*
* @parameter expression="${bundleName}"
* @required
*/
private String bundleName;
/**
* When true, repair the groupId and any references to the moved bundle.
*
* @parameter expression="${repair}" default-value="true"
*/
private boolean repair;
/**
* Locate the bundle project - try name first as a directory path, then an artifactId or symbolic-name
*
* @param baseDir base directory in the same project tree
* @param pathOrName either a path, or an artifactId or symbolic-name
* @return the matching POM
* @throws MojoExecutionException
*/
protected static Pom locateBundlePom( File baseDir, String pathOrName )
throws MojoExecutionException
{
Pom bundlePom = null;
if( null != pathOrName )
{
File path = new File( pathOrName );
try
{
bundlePom = PomUtils.readPom( path );
}
catch( IOException e )
{
bundlePom = DirUtils.findPom( baseDir, path.getName() );
}
}
if( null == bundlePom || !bundlePom.isBundleProject() )
{
throw new MojoExecutionException( "Cannot find bundle " + pathOrName );
}
return bundlePom;
}
/**
* {@inheritDoc}
*/
public void execute()
throws MojoExecutionException
{
Pom oldBundlePom = locateBundlePom( baseDirectory, bundleName );
File oldBundleDir = oldBundlePom.getBasedir();
// the main work - move files and update modules
Pom newModulesPom = moveBundleFiles( oldBundlePom );
transferBundleOwnership( oldBundleDir, newModulesPom );
if( repair )
{
// construct a groupId from the new containing POM, eliminating duplicate segments where possible
String newGroupId = PomUtils.getCompoundId( newModulesPom.getGroupId(), newModulesPom.getArtifactId() );
// need to open the recently moved POM, can't use the old one!
Pom newBundlePom = newModulesPom.getModulePom( oldBundleDir.getName() );
if( null != newBundlePom )
{
changeBundleGroup( newBundlePom, newGroupId );
}
}
}
/**
* Move a bundle directory to a new directory, creating Maven POMs as necessary to keep it connected to the project
*
* @param bundlePom current Maven POM for the bundle
* @return modules POM directly above the new bundle directory
* @throws MojoExecutionException
*/
private Pom moveBundleFiles( Pom bundlePom )
throws MojoExecutionException
{
Pom newModulesPom;
try
{
// make sure we can reach the new location from the current project tree
newModulesPom = DirUtils.createModuleTree( baseDirectory, targetDirectory );
}
catch( IOException e )
{
newModulesPom = null;
}
// sanity check
if( null == newModulesPom )
{
throw new MojoExecutionException( "targetDirectory is outside of this project" );
}
else if( !"pom".equals( newModulesPom.getPackaging() ) )
{
throw new MojoExecutionException( "targetDirectory is not a modules directory" );
}
File oldBundleDir = bundlePom.getBasedir();
File newBundleDir = new File( newModulesPom.getBasedir(), oldBundleDir.getName() );
getLog().info( "Moving " + oldBundleDir + " to " + newBundleDir );
/*
* MOVE DIRECTORY CONTENTS
*/
if( !oldBundleDir.renameTo( newBundleDir ) )
{
try
{
// fallback to copy and delete on Windows...
FileUtils.copyDirectoryStructure( oldBundleDir, newBundleDir );
FileUtils.deleteDirectory( oldBundleDir );
}
catch( IOException e )
{
throw new MojoExecutionException( "Cannot move bundle " + bundleName + " to " + targetDirectory );
}
}
updateParentDetails( bundlePom, newBundleDir );
return newModulesPom;
}
/**
* @param bundlePom current Maven POM for the bundle
* @param newBundleDir new location for the bundle
*/
private void updateParentDetails( Pom bundlePom, File newBundleDir )
{
String parentId = bundlePom.getParentId();
if( null != parentId )
{
try
{
// update the relative path, as it's probably changed
DirUtils.updateLogicalParent( newBundleDir, parentId );
}
catch( IOException e )
{
getLog().warn( "Unable to update logical parent " + parentId );
}
}
}
/**
* Transfer the bundle's module from the old modules POM to the new one
*
* @param oldBundleDir previous location of the bundle
* @param newModulesPom modules POM directly above the new bundle directory
*/
private void transferBundleOwnership( File oldBundleDir, Pom newModulesPom )
{
String moduleName = oldBundleDir.getName();
try
{
// add first, in case of problems later
newModulesPom.addModule( moduleName, true );
newModulesPom.write();
// open POM above the old directory, and remove the bundle module
Pom oldModulesPom = PomUtils.readPom( oldBundleDir.getParentFile() );
oldModulesPom.removeModule( moduleName );
oldModulesPom.write();
}
catch( IOException e )
{
getLog().warn( "Problem transferring bundle ownership" );
}
}
/**
* Update the bundle POM and any references to the bundle in the complete Maven project tree
*
* @param bundlePom bundle POM from the new directory
* @param newGroupId groupId based on the new location
*/
private void changeBundleGroup( Pom bundlePom, String newGroupId )
{
try
{
// update bundle first, in case of failure
String oldGroupId = bundlePom.getGroupId();
bundlePom.setGroupId( newGroupId );
bundlePom.write();
for( Iterator i = new PomIterator( bundlePom.getBasedir() ); i.hasNext(); )
{
Pom pom = (Pom) i.next();
if( !pom.equals( bundlePom ) )
{
updateBundleReferences( pom, oldGroupId, bundlePom.getGroupId(), bundlePom.getArtifactId() );
}
}
}
catch( IOException e )
{
getLog().warn( "Unable to update bundle groupId to " + newGroupId );
}
}
/**
* Update any references (ie. dependencies, dependencyManagement) to use the new bundle group id
*
* @param pom a Maven POM in the project tree
* @param oldGroupId old bundle group id
* @param newGroupId new bundle group id
* @param artifactId bundle artifact id
*/
private void updateBundleReferences( Pom pom, String oldGroupId, String newGroupId, String artifactId )
{
Dependency dependency = new Dependency();
dependency.setGroupId( oldGroupId );
dependency.setArtifactId( artifactId );
if( pom.updateDependencyGroup( dependency, newGroupId ) )
{
getLog().info( "Updating " + newGroupId + ':' + artifactId + " in " + pom );
try
{
pom.write();
}
catch( IOException e )
{
getLog().warn( "Problem writing Maven POM: " + pom.getFile() );
}
}
}
}