Coverage Report - org.apache.maven.plugin.eclipse.InstallPluginsMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
InstallPluginsMojo
0%
0/107
0%
0/46
7
 
 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  0
     {
 150  0
         this.eclipseDir = eclipseDir;
 151  0
         this.overwrite = overwrite;
 152  0
         artifacts = dependencyArtifacts;
 153  0
         this.pluginDependencyTypes = pluginDependencyTypes;
 154  0
         this.localRepository = localRepository;
 155  0
         this.projectBuilder = projectBuilder;
 156  0
         this.archiverManager = archiverManager;
 157  0
         this.inputHandler = inputHandler;
 158  0
         setLog( log );
 159  0
     }
 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  0
         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  0
         if ( eclipseDir.exists() && !eclipseDir.isDirectory() )
 187  
         {
 188  0
             throw new MojoFailureException( "Invalid Eclipse directory: " + eclipseDir );
 189  
         }
 190  0
         else if ( !eclipseDir.exists() )
 191  
         {
 192  0
             eclipseDir.mkdirs();
 193  
         }
 194  
 
 195  0
         for ( Iterator it = artifacts.iterator(); it.hasNext(); )
 196  
         {
 197  0
             Artifact artifact = (Artifact) it.next();
 198  
 
 199  0
             if ( pluginDependencyTypes.indexOf( artifact.getType() ) > -1 )
 200  
             {
 201  0
                 getLog().debug( "Processing Eclipse plugin dependency: " + artifact.getId() );
 202  
 
 203  
                 MavenProject project;
 204  
 
 205  
                 try
 206  
                 {
 207  0
                     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  0
                 }
 215  
 
 216  0
                 install( artifact, project );
 217  0
             }
 218  
             else
 219  
             {
 220  0
                 getLog().debug(
 221  
                                 "Skipping dependency: " + artifact.getId() +
 222  
                                     ". Set pluginDependencyTypes with a comma-separated list of types to change this." );
 223  
             }
 224  0
         }
 225  0
     }
 226  
 
 227  
     /**
 228  
      * <p>
 229  
      * Install the plugin into the eclipse instance's /plugins directory
 230  
      * </p>
 231  
      * <ol>
 232  
      * <li>Determine whether the plugin should be extracted into a directory or not</li>
 233  
      * <li>If the plugin's target location exists, or overwrite is set to true:
 234  
      * <ol type="a">
 235  
      * <li>if extract, ensure the plugin target location exists (mkdirs), and extract there.</li>
 236  
      * <li>copy the plugin file from the local repository to the target location</li>
 237  
      * </ol>
 238  
      * <p>
 239  
      * Warn whenever a plugin will overwrite an existing file or directory, and emit an INFO message whenever a plugin
 240  
      * installation is skipped because of an existing file and overwrite == false.
 241  
      * </p>
 242  
      * 
 243  
      * @param artifact The plugin dependency as it has been resolved.
 244  
      * @param project The project metadata for the accompanying plugin-dependency artifact, used to determine whether to
 245  
      *            install as a jar or as a directory
 246  
      * @throws MojoExecutionException In the event the plugin should be extracted but cannot, or the file copy fails (in
 247  
      *             the event it should not be extracted)
 248  
      * @throws MojoFailureException In the event that the plugins target directory (inside the Eclipse instance
 249  
      *             directory) does not exist, or is not a directory.
 250  
      */
 251  
     private void install( Artifact artifact, MavenProject project )
 252  
         throws MojoExecutionException, MojoFailureException
 253  
     {
 254  0
         if ( pluginsDir == null )
 255  
         {
 256  0
             pluginsDir = new File( eclipseDir, "plugins" );
 257  
         }
 258  
 
 259  0
         if ( !pluginsDir.exists() || !pluginsDir.isDirectory() )
 260  
         {
 261  0
             throw new MojoFailureException( "Invalid Eclipse directory: " + eclipseDir +
 262  
                 " (plugins directory is missing or not a directory)." );
 263  
         }
 264  
 
 265  0
         boolean installAsJar = true;
 266  
 
 267  0
         Properties properties = project.getProperties();
 268  0
         if ( properties != null )
 269  
         {
 270  0
             installAsJar = !Boolean.valueOf( properties.getProperty( PROP_UNPACK_PLUGIN, "false" ) ).booleanValue();
 271  
         }
 272  
 
 273  0
         Attributes attributes = null;
 274  
         try
 275  
         {
 276  
             // don't verify, plugins zipped by eclipse:make-artifacts could have a bad signature
 277  0
             JarFile jar = new JarFile( artifact.getFile(), false );
 278  0
             Manifest manifest = jar.getManifest();
 279  0
             attributes = manifest.getMainAttributes();
 280  
         }
 281  0
         catch ( IOException e )
 282  
         {
 283  0
             throw new MojoExecutionException( "Unable to read manifest of plugin " +
 284  
                 artifact.getFile().getAbsolutePath(), e );
 285  0
         }
 286  
 
 287  0
         String bundleVersion = attributes.getValue( "Bundle-Version" );
 288  0
         String pluginName = formatEclipsePluginName( artifact, bundleVersion );
 289  
 
 290  0
         File pluginFile = new File( pluginsDir, pluginName + ".jar" );
 291  0
         File pluginDir = new File( pluginsDir, pluginName );
 292  
 
 293  0
         boolean skipped = true;
 294  
 
 295  
         /* check if artifact is an OSGi bundle and ignore if not */
 296  0
         Object bundleName = attributes.getValue( "Bundle-Name" );
 297  0
         if ( bundleName == null )
 298  
         {
 299  0
             getLog().debug(
 300  
                             "Ignoring " + artifact.getArtifactId() +
 301  
                                 " as it is not an OSGi bundle (no Bundle-Name in manifest)" );
 302  0
             return;
 303  
         }
 304  
 
 305  0
         if ( overwrite )
 306  
         {
 307  0
             if ( pluginFile.exists() || pluginDir.exists() )
 308  
             {
 309  0
                 getLog().warn( "Overwriting old plugin with contents of: " + artifact.getId() );
 310  
 
 311  0
                 getLog().debug( "Removing old plugin from both: " + pluginFile + " and: " + pluginDir );
 312  
 
 313  
                 try
 314  
                 {
 315  0
                     FileUtils.forceDelete( pluginDir );
 316  0
                     FileUtils.forceDelete( pluginFile );
 317  
                 }
 318  0
                 catch ( IOException e )
 319  
                 {
 320  0
                     throw new MojoExecutionException( "Failed to remove old plugin from: " + pluginFile + " or: " +
 321  
                         pluginDir, e );
 322  0
                 }
 323  
 
 324  0
                 getLog().debug( "Removal of old plugin is complete; proceeding with plugin installation." );
 325  
             }
 326  
 
 327  0
             performFileOperations( installAsJar, artifact, pluginFile, pluginDir );
 328  
 
 329  0
             skipped = false;
 330  
         }
 331  0
         else if ( installAsJar && !pluginFile.exists() )
 332  
         {
 333  0
             performFileOperations( installAsJar, artifact, pluginFile, pluginDir );
 334  
 
 335  0
             skipped = false;
 336  
         }
 337  0
         else if ( !installAsJar && !pluginDir.exists() )
 338  
         {
 339  0
             performFileOperations( installAsJar, artifact, pluginFile, pluginDir );
 340  
 
 341  0
             skipped = false;
 342  
         }
 343  
 
 344  0
         if ( skipped )
 345  
         {
 346  0
             if ( installAsJar )
 347  
             {
 348  0
                 getLog().info(
 349  
                                "Skipping plugin installation for: " + artifact.getId() + "; file: " + pluginFile +
 350  
                                    " already exists. Set overwrite = true to override this." );
 351  
             }
 352  0
             else if ( !installAsJar )
 353  
             {
 354  0
                 getLog().info(
 355  
                                "Skipping plugin installation for: " + artifact.getId() + "; directory: " + pluginDir +
 356  
                                    " already exists. Set overwrite = true to override this." );
 357  
             }
 358  
         }
 359  0
     }
 360  
 
 361  
     private void performFileOperations( boolean installAsJar, Artifact artifact, File pluginFile, File pluginDir )
 362  
         throws MojoExecutionException
 363  
     {
 364  0
         File artifactFile = artifact.getFile();
 365  
 
 366  0
         if ( installAsJar )
 367  
         {
 368  
             try
 369  
             {
 370  0
                 getLog().debug( "Copying: " + artifact.getId() + " to: " + pluginFile );
 371  
 
 372  0
                 FileUtils.copyFile( artifactFile, pluginFile );
 373  
             }
 374  0
             catch ( IOException e )
 375  
             {
 376  0
                 throw new MojoExecutionException( "Failed to copy Eclipse plugin: " + artifact.getId() + "\nfrom: " +
 377  
                     artifact.getFile() + "\nto: " + pluginFile, e );
 378  0
             }
 379  
         }
 380  
         else
 381  
         {
 382  
             try
 383  
             {
 384  0
                 getLog().debug( "Expanding: " + artifact.getId() + " into: " + pluginDir );
 385  
 
 386  0
                 pluginDir.mkdirs();
 387  
 
 388  0
                 UnArchiver unarchiver = archiverManager.getUnArchiver( artifactFile );
 389  
 
 390  0
                 unarchiver.setSourceFile( artifactFile );
 391  0
                 unarchiver.setDestDirectory( pluginDir );
 392  0
                 unarchiver.extract();
 393  
             }
 394  0
             catch ( NoSuchArchiverException e )
 395  
             {
 396  0
                 throw new MojoExecutionException( "Could not find unarchiver for: " + artifactFile, e );
 397  
             }
 398  0
             catch ( ArchiverException e )
 399  
             {
 400  0
                 throw new MojoExecutionException( "Could not extract: " + artifactFile, e );
 401  
             }
 402  0
             catch ( IOException e )
 403  
             {
 404  0
                 throw new MojoExecutionException( "Could not extract: " + artifactFile, e );
 405  0
             }
 406  
         }
 407  0
     }
 408  
 
 409  
     /**
 410  
      * <p>
 411  
      * Format the artifact information into an Eclipse-friendly plugin name. Currently, this is just:
 412  
      * <code>artifactId + "_" + bundle version</code> if bundle version is not null.
 413  
      * </p>
 414  
      */
 415  
     private String formatEclipsePluginName( Artifact artifact, String bundleVersion )
 416  
     {
 417  0
         return maven2OsgiConverter.getBundleSymbolicName( artifact ) + "_" +
 418  
             maven2OsgiConverter.getVersion( artifact.getVersion() );
 419  
     }
 420  
 
 421  
 }