Coverage Report - org.apache.maven.plugin.eclipse.InstallPluginsMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
InstallPluginsMojo
73%
79/108
64%
32/50
7.333
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.maven.plugin.eclipse;
 20  
 
 21  
 import java.io.File;
 22  
 import java.io.IOException;
 23  
 import java.util.Collection;
 24  
 import java.util.Collections;
 25  
 import java.util.Iterator;
 26  
 import java.util.List;
 27  
 import java.util.Properties;
 28  
 import java.util.jar.Attributes;
 29  
 import java.util.jar.JarFile;
 30  
 import java.util.jar.Manifest;
 31  
 
 32  
 import org.apache.maven.artifact.Artifact;
 33  
 import org.apache.maven.artifact.repository.ArtifactRepository;
 34  
 import org.apache.maven.plugin.AbstractMojo;
 35  
 import org.apache.maven.plugin.MojoExecutionException;
 36  
 import org.apache.maven.plugin.MojoFailureException;
 37  
 import org.apache.maven.plugin.logging.Log;
 38  
 import org.apache.maven.project.MavenProject;
 39  
 import org.apache.maven.project.MavenProjectBuilder;
 40  
 import org.apache.maven.project.ProjectBuildingException;
 41  
 import org.apache.maven.shared.osgi.Maven2OsgiConverter;
 42  
 import org.codehaus.plexus.archiver.ArchiverException;
 43  
 import org.codehaus.plexus.archiver.UnArchiver;
 44  
 import org.codehaus.plexus.archiver.manager.ArchiverManager;
 45  
 import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
 46  
 import org.codehaus.plexus.components.interactivity.InputHandler;
 47  
 import org.codehaus.plexus.util.FileUtils;
 48  
 
 49  
 /**
 50  
  * Install plugins resolved from the Maven repository system into an Eclipse instance.
 51  
  * 
 52  
  * @goal install-plugins
 53  
  * @author jdcasey
 54  
  * @requiresDependencyResolution compile
 55  
  */
 56  
 public class InstallPluginsMojo
 57  
     extends AbstractMojo
 58  
 {
 59  
 
 60  
     /**
 61  
      * Set this property in a plugin POM's <properties/> section to determine whether that plugin should be
 62  
      * expanded during installation, or left as a jar file.
 63  
      */
 64  
     public static final String PROP_UNPACK_PLUGIN = "eclipse.unpack";
 65  
 
 66  
     /**
 67  
      * This is the installed base directory of the Eclipse instance you want to modify.
 68  
      * 
 69  
      * @parameter expression="${eclipseDir}"
 70  
      */
 71  
     private File eclipseDir;
 72  
 
 73  
     /**
 74  
      * Determines whether this mojo leaves existing installed plugins as-is, or overwrites them.
 75  
      * 
 76  
      * @parameter expression="${overwrite}" default-value="false"
 77  
      */
 78  
     private boolean overwrite;
 79  
 
 80  
     /**
 81  
      * The list of resolved dependencies from the current project. Since we're not resolving the dependencies by hand
 82  
      * here, the build will fail if some of these dependencies do not resolve.
 83  
      * 
 84  
      * @parameter default-value="${project.artifacts}"
 85  
      * @required
 86  
      * @readonly
 87  
      */
 88  
     private Collection artifacts;
 89  
 
 90  
     /**
 91  
      * Comma-delimited list of dependency <type/> values which will be installed in the eclipse instance's plugins
 92  
      * directory.
 93  
      * 
 94  
      * @parameter expression="${pluginDependencyTypes}" default-value="jar"
 95  
      */
 96  
     private String pluginDependencyTypes;
 97  
 
 98  
     /**
 99  
      * The location of the Maven local repository, from which to install resolved dependency plugins.
 100  
      * 
 101  
      * @parameter default-value="${localRepository}"
 102  
      * @required
 103  
      * @readonly
 104  
      */
 105  
     private ArtifactRepository localRepository;
 106  
 
 107  
     /**
 108  
      * Used to retrieve the project metadata (POM) associated with each plugin dependency, to help determine whether
 109  
      * that plugin should be installed as a jar, or expanded into a directory.
 110  
      * 
 111  
      * @component
 112  
      */
 113  
     private MavenProjectBuilder projectBuilder;
 114  
 
 115  
     /**
 116  
      * Used to configure and retrieve an appropriate tool for extracting each resolved plugin dependency. It is
 117  
      * conceivable that some resolved dependencies could be zip files, jar files, or other types, so the manager
 118  
      * approach is a convenient way to provide extensibility here.
 119  
      * 
 120  
      * @component
 121  
      */
 122  
     private ArchiverManager archiverManager;
 123  
 
 124  
     /**
 125  
      * Input handler, needed for comand line handling.
 126  
      * 
 127  
      * @component
 128  
      */
 129  
     private InputHandler inputHandler;
 130  
 
 131  
     // calculated below. Value will be ${eclipseDir}/plugins.
 132  
     private File pluginsDir;
 133  
 
 134  
     /**
 135  
      * @component
 136  
      */
 137  
     private Maven2OsgiConverter maven2OsgiConverter;
 138  
 
 139  
     public InstallPluginsMojo()
 140  0
     {
 141  
         // used for plexus init.
 142  0
     }
 143  
 
 144  
     // used primarily for testing.
 145  
     protected InstallPluginsMojo( File eclipseDir, boolean overwrite, List dependencyArtifacts,
 146  
                                   String pluginDependencyTypes, ArtifactRepository localRepository,
 147  
                                   MavenProjectBuilder projectBuilder, ArchiverManager archiverManager,
 148  
                                   InputHandler inputHandler, Log log )
 149  12
     {
 150  12
         this.eclipseDir = eclipseDir;
 151  12
         this.overwrite = overwrite;
 152  12
         artifacts = dependencyArtifacts;
 153  12
         this.pluginDependencyTypes = pluginDependencyTypes;
 154  12
         this.localRepository = localRepository;
 155  12
         this.projectBuilder = projectBuilder;
 156  12
         this.archiverManager = archiverManager;
 157  12
         this.inputHandler = inputHandler;
 158  12
         setLog( log );
 159  12
     }
 160  
 
 161  
     /**
 162  
      * Traverse the list of resolved dependency artifacts. For each one having a type that is listed in the
 163  
      * pluginDependencyTypes parameter value, resolve the associated project metadata (POM), and perform install(..) on
 164  
      * that artifact.
 165  
      */
 166  
     public void execute()
 167  
         throws MojoExecutionException, MojoFailureException
 168  
     {
 169  12
         if ( eclipseDir == null )
 170  
         {
 171  0
             getLog().info( "Eclipse directory? " );
 172  
 
 173  
             String eclipseDirString;
 174  
             try
 175  
             {
 176  0
                 eclipseDirString = inputHandler.readLine();
 177  
             }
 178  0
             catch ( IOException e )
 179  
             {
 180  0
                 throw new MojoExecutionException( "Unable to read from standard input", e );
 181  0
             }
 182  
 
 183  0
             eclipseDir = new File( eclipseDirString );
 184  
         }
 185  
 
 186  12
         if ( eclipseDir.exists() && !eclipseDir.isDirectory() )
 187  
         {
 188  0
             throw new MojoFailureException( "Invalid Eclipse directory: " + eclipseDir );
 189  
         }
 190  12
         else if ( !eclipseDir.exists() )
 191  
         {
 192  0
             eclipseDir.mkdirs();
 193  
         }
 194  
 
 195  12
         for ( Iterator it = artifacts.iterator(); it.hasNext(); )
 196  
         {
 197  12
             Artifact artifact = (Artifact) it.next();
 198  
 
 199  12
             if ( pluginDependencyTypes.indexOf( artifact.getType() ) > -1 )
 200  
             {
 201  11
                 getLog().debug( "Processing Eclipse plugin dependency: " + artifact.getId() );
 202  
 
 203  
                 MavenProject project;
 204  
 
 205  
                 try
 206  
                 {
 207  11
                     project =
 208  
                         projectBuilder.buildFromRepository( artifact, Collections.EMPTY_LIST, localRepository, true );
 209  
                 }
 210  0
                 catch ( ProjectBuildingException e )
 211  
                 {
 212  0
                     throw new MojoExecutionException( "Failed to load project metadata (POM) for: " + artifact.getId(),
 213  
                                                       e );
 214  11
                 }
 215  
 
 216  11
                 install( artifact, project );
 217  
             }
 218  
             else
 219  
             {
 220  1
                 getLog().debug(
 221  
                                 "Skipping dependency: "
 222  
                                     + artifact.getId()
 223  
                                     + ". Set pluginDependencyTypes with a comma-separated list of types to change this." );
 224  
             }
 225  
         }
 226  12
     }
 227  
 
 228  
     /**
 229  
      * <p>
 230  
      * Install the plugin into the eclipse instance's /plugins directory
 231  
      * </p>
 232  
      * <ol>
 233  
      * <li>Determine whether the plugin should be extracted into a directory or not</li>
 234  
      * <li>If the plugin's target location exists, or overwrite is set to true:
 235  
      * <ol type="a">
 236  
      * <li>if extract, ensure the plugin target location exists (mkdirs), and extract there.</li>
 237  
      * <li>copy the plugin file from the local repository to the target location</li>
 238  
      * </ol>
 239  
      * <p>
 240  
      * Warn whenever a plugin will overwrite an existing file or directory, and emit an INFO message whenever a plugin
 241  
      * installation is skipped because of an existing file and overwrite == false.
 242  
      * </p>
 243  
      * 
 244  
      * @param artifact The plugin dependency as it has been resolved.
 245  
      * @param project The project metadata for the accompanying plugin-dependency artifact, used to determine whether to
 246  
      *            install as a jar or as a directory
 247  
      * @throws MojoExecutionException In the event the plugin should be extracted but cannot, or the file copy fails (in
 248  
      *             the event it should not be extracted)
 249  
      * @throws MojoFailureException In the event that the plugins target directory (inside the Eclipse instance
 250  
      *             directory) does not exist, or is not a directory.
 251  
      */
 252  
     private void install( Artifact artifact, MavenProject project )
 253  
         throws MojoExecutionException, MojoFailureException
 254  
     {
 255  11
         if ( pluginsDir == null )
 256  
         {
 257  11
             pluginsDir = new File( eclipseDir, "plugins" );
 258  
         }
 259  
 
 260  11
         if ( !pluginsDir.exists() || !pluginsDir.isDirectory() )
 261  
         {
 262  0
             throw new MojoFailureException( "Invalid Eclipse directory: " + eclipseDir
 263  
                 + " (plugins directory is missing or not a directory)." );
 264  
         }
 265  
 
 266  11
         boolean installAsJar = true;
 267  
 
 268  11
         Properties properties = project.getProperties();
 269  11
         if ( properties != null )
 270  
         {
 271  11
             installAsJar = !Boolean.valueOf( properties.getProperty( PROP_UNPACK_PLUGIN, "false" ) ).booleanValue();
 272  
         }
 273  
 
 274  11
         Attributes attributes = null;
 275  
         try
 276  
         {
 277  
             // don't verify, plugins zipped by eclipse:make-artifacts could have a bad signature
 278  11
             JarFile jar = new JarFile( artifact.getFile(), false );
 279  11
             Manifest manifest = jar.getManifest();
 280  11
             if ( manifest == null )
 281  
             {
 282  1
                 getLog().debug(
 283  
                                 "Ignoring " + artifact.getArtifactId()
 284  
                                     + " as it is does not have a Manifest (and so is not an OSGi bundle)" );
 285  1
                 return;
 286  
             }
 287  10
             attributes = manifest.getMainAttributes();
 288  
         }
 289  0
         catch ( IOException e )
 290  
         {
 291  0
             throw new MojoExecutionException( "Unable to read manifest of plugin "
 292  
                 + artifact.getFile().getAbsolutePath(), e );
 293  10
         }
 294  
 
 295  10
         String pluginName = formatEclipsePluginName( artifact );
 296  
 
 297  10
         File pluginFile = new File( pluginsDir, pluginName + ".jar" );
 298  10
         File pluginDir = new File( pluginsDir, pluginName );
 299  
 
 300  10
         boolean skipped = true;
 301  
 
 302  
         /* check if artifact is an OSGi bundle and ignore if not */
 303  10
         Object bundleName = attributes.getValue( "Bundle-Name" );
 304  10
         Object bundleSymbolicName = attributes.getValue( "Bundle-SymbolicName" );
 305  10
         if ( bundleSymbolicName == null && bundleName == null )
 306  
         {
 307  1
             getLog().debug(
 308  
                             "Ignoring " + artifact.getArtifactId()
 309  
                                 + " as it is not an OSGi bundle (no Bundle-SymbolicName or Bundle-Name in manifest)" );
 310  1
             return;
 311  
         }
 312  
 
 313  9
         if ( overwrite )
 314  
         {
 315  2
             if ( pluginFile.exists() || pluginDir.exists() )
 316  
             {
 317  2
                 getLog().warn( "Overwriting old plugin with contents of: " + artifact.getId() );
 318  
 
 319  2
                 getLog().debug( "Removing old plugin from both: " + pluginFile + " and: " + pluginDir );
 320  
 
 321  
                 try
 322  
                 {
 323  2
                     FileUtils.forceDelete( pluginDir );
 324  2
                     FileUtils.forceDelete( pluginFile );
 325  
                 }
 326  0
                 catch ( IOException e )
 327  
                 {
 328  0
                     throw new MojoExecutionException( "Failed to remove old plugin from: " + pluginFile + " or: "
 329  
                         + pluginDir, e );
 330  2
                 }
 331  
 
 332  2
                 getLog().debug( "Removal of old plugin is complete; proceeding with plugin installation." );
 333  
             }
 334  
 
 335  2
             performFileOperations( installAsJar, artifact, pluginFile, pluginDir );
 336  
 
 337  2
             skipped = false;
 338  
         }
 339  7
         else if ( installAsJar && !pluginFile.exists() )
 340  
         {
 341  6
             performFileOperations( installAsJar, artifact, pluginFile, pluginDir );
 342  
 
 343  6
             skipped = false;
 344  
         }
 345  1
         else if ( !installAsJar && !pluginDir.exists() )
 346  
         {
 347  1
             performFileOperations( installAsJar, artifact, pluginFile, pluginDir );
 348  
 
 349  1
             skipped = false;
 350  
         }
 351  
 
 352  9
         if ( skipped )
 353  
         {
 354  0
             if ( installAsJar )
 355  
             {
 356  0
                 getLog().info(
 357  
                                "Skipping plugin installation for: " + artifact.getId() + "; file: " + pluginFile
 358  
                                    + " already exists. Set overwrite = true to override this." );
 359  
             }
 360  0
             else if ( !installAsJar )
 361  
             {
 362  0
                 getLog().info(
 363  
                                "Skipping plugin installation for: " + artifact.getId() + "; directory: " + pluginDir
 364  
                                    + " already exists. Set overwrite = true to override this." );
 365  
             }
 366  
         }
 367  9
     }
 368  
 
 369  
     private void performFileOperations( boolean installAsJar, Artifact artifact, File pluginFile, File pluginDir )
 370  
         throws MojoExecutionException
 371  
     {
 372  9
         File artifactFile = artifact.getFile();
 373  
 
 374  9
         if ( installAsJar )
 375  
         {
 376  
             try
 377  
             {
 378  7
                 getLog().debug( "Copying: " + artifact.getId() + " to: " + pluginFile );
 379  
 
 380  7
                 FileUtils.copyFile( artifactFile, pluginFile );
 381  
             }
 382  0
             catch ( IOException e )
 383  
             {
 384  0
                 throw new MojoExecutionException( "Failed to copy Eclipse plugin: " + artifact.getId() + "\nfrom: "
 385  
                     + artifact.getFile() + "\nto: " + pluginFile, e );
 386  7
             }
 387  
         }
 388  
         else
 389  
         {
 390  
             try
 391  
             {
 392  2
                 getLog().debug( "Expanding: " + artifact.getId() + " into: " + pluginDir );
 393  
 
 394  2
                 pluginDir.mkdirs();
 395  
 
 396  2
                 UnArchiver unarchiver = archiverManager.getUnArchiver( artifactFile );
 397  
 
 398  2
                 unarchiver.setSourceFile( artifactFile );
 399  2
                 unarchiver.setDestDirectory( pluginDir );
 400  2
                 unarchiver.extract();
 401  
             }
 402  0
             catch ( NoSuchArchiverException e )
 403  
             {
 404  0
                 throw new MojoExecutionException( "Could not find unarchiver for: " + artifactFile, e );
 405  
             }
 406  0
             catch ( ArchiverException e )
 407  
             {
 408  0
                 throw new MojoExecutionException( "Could not extract: " + artifactFile, e );
 409  
             }
 410  0
             catch ( IOException e )
 411  
             {
 412  0
                 throw new MojoExecutionException( "Could not extract: " + artifactFile, e );
 413  2
             }
 414  
         }
 415  9
     }
 416  
 
 417  
     /**
 418  
      * <p>
 419  
      * Format the artifact information into an Eclipse-friendly plug-in name. Delegates to maven2OsgiConverter to obtain
 420  
      * bundle symbolic name and version.
 421  
      * </p>
 422  
      */
 423  
     private String formatEclipsePluginName( Artifact artifact )
 424  
     {
 425  10
         return maven2OsgiConverter.getBundleSymbolicName( artifact ) + "_"
 426  
             + maven2OsgiConverter.getVersion( artifact.getVersion() );
 427  
     }
 428  
 
 429  
 }