Coverage Report - org.apache.maven.tools.plugin.generator.PluginHelpGenerator
 
Classes in this File Line Coverage Branch Coverage Complexity
PluginHelpGenerator
56 %
63/111
35 %
12/34
3,818
 
 1  
 package org.apache.maven.tools.plugin.generator;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 23  
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
 24  
 import org.apache.maven.project.MavenProject;
 25  
 import org.apache.maven.tools.plugin.PluginToolsRequest;
 26  
 import org.apache.velocity.VelocityContext;
 27  
 import org.codehaus.plexus.logging.AbstractLogEnabled;
 28  
 import org.codehaus.plexus.logging.Logger;
 29  
 import org.codehaus.plexus.logging.console.ConsoleLogger;
 30  
 import org.codehaus.plexus.util.FileUtils;
 31  
 import org.codehaus.plexus.util.IOUtil;
 32  
 import org.codehaus.plexus.util.PropertyUtils;
 33  
 import org.codehaus.plexus.util.StringUtils;
 34  
 import org.codehaus.plexus.velocity.VelocityComponent;
 35  
 import org.objectweb.asm.ClassReader;
 36  
 import org.objectweb.asm.ClassVisitor;
 37  
 import org.objectweb.asm.ClassWriter;
 38  
 import org.objectweb.asm.commons.Remapper;
 39  
 import org.objectweb.asm.commons.RemappingClassAdapter;
 40  
 import org.objectweb.asm.commons.SimpleRemapper;
 41  
 
 42  
 import java.io.File;
 43  
 import java.io.FileInputStream;
 44  
 import java.io.FileOutputStream;
 45  
 import java.io.IOException;
 46  
 import java.io.InputStream;
 47  
 import java.io.InputStreamReader;
 48  
 import java.io.StringWriter;
 49  
 import java.util.List;
 50  
 import java.util.Properties;
 51  
 
 52  
 /**
 53  
  * Generates an <code>HelpMojo</code> class.
 54  
  *
 55  
  * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
 56  
  * @version $Id: PluginHelpGenerator.java 1354250 2012-06-26 21:36:53Z hboutemy $
 57  
  * @since 2.4
 58  
  */
 59  
 public class PluginHelpGenerator
 60  
     extends AbstractLogEnabled
 61  
     implements Generator
 62  
 {
 63  
     /**
 64  
      * Default generated class name
 65  
      */
 66  
     private static final String HELP_MOJO_CLASS_NAME = "HelpMojo";
 67  
 
 68  
     /**
 69  
      * Help properties file, to store data about generated source.
 70  
      */
 71  
     private static final String HELP_PROPERTIES_FILENAME = "maven-plugin-help.properties";
 72  
 
 73  
     /**
 74  
      * Default goal
 75  
      */
 76  
     private static final String HELP_GOAL = "help";
 77  
 
 78  
     private String helpPackageName;
 79  
 
 80  
     private VelocityComponent velocityComponent;
 81  
 
 82  
     /**
 83  
      * Default constructor
 84  
      */
 85  
     public PluginHelpGenerator()
 86  1
     {
 87  1
         this.enableLogging( new ConsoleLogger( Logger.LEVEL_INFO, "PluginHelpGenerator" ) );
 88  1
     }
 89  
 
 90  
     // ----------------------------------------------------------------------
 91  
     // Public methods
 92  
     // ----------------------------------------------------------------------
 93  
 
 94  
     /**
 95  
      * {@inheritDoc}
 96  
      */
 97  
     public void execute( File destinationDirectory, PluginToolsRequest request )
 98  
         throws GeneratorException
 99  
     {
 100  1
         PluginDescriptor pluginDescriptor = request.getPluginDescriptor();
 101  
 
 102  1
         String helpImplementation = getImplementation( pluginDescriptor );
 103  
 
 104  
         @SuppressWarnings( "unchecked" )
 105  1
         List<MojoDescriptor> mojoDescriptors = pluginDescriptor.getMojos();
 106  
 
 107  1
         if ( mojoDescriptors != null )
 108  
         {
 109  
             // Verify that no help goal already exists
 110  1
             MojoDescriptor descriptor = pluginDescriptor.getMojo( HELP_GOAL );
 111  
 
 112  1
             if ( ( descriptor != null ) && !descriptor.getImplementation().equals( helpImplementation ) )
 113  
             {
 114  0
                 if ( getLogger().isWarnEnabled() )
 115  
                 {
 116  0
                     getLogger().warn( "\n\nA help goal (" + descriptor.getImplementation()
 117  
                                           + ") already exists in this plugin. SKIPPED THE " + helpImplementation
 118  
                                           + " GENERATION.\n" );
 119  
                 }
 120  
 
 121  0
                 return;
 122  
             }
 123  
         }
 124  
 
 125  1
         writeHelpPropertiesFile( request );
 126  
 
 127  
         try
 128  
         {
 129  1
             String sourcePath = helpImplementation.replace( '.', File.separatorChar ) + ".java";
 130  
 
 131  1
             File helpClass = new File( destinationDirectory, sourcePath );
 132  1
             helpClass.getParentFile().mkdirs();
 133  
 
 134  1
             MavenProject mavenProject = request.getProject();
 135  1
             String pluginResourcesPath = "META-INF/maven/" + mavenProject.getGroupId() + "/" + mavenProject.getArtifactId();
 136  
 
 137  1
             String helpClassSources = getHelpClassSources( pluginResourcesPath, pluginDescriptor );
 138  
 
 139  1
             FileUtils.fileWrite( helpClass, request.getEncoding(), helpClassSources );
 140  
         }
 141  0
         catch ( IOException e )
 142  
         {
 143  0
             throw new GeneratorException( e.getMessage(), e );
 144  1
         }
 145  1
     }
 146  
 
 147  
     public PluginHelpGenerator setHelpPackageName( String helpPackageName )
 148  
     {
 149  0
         this.helpPackageName = helpPackageName;
 150  0
         return this;
 151  
     }
 152  
 
 153  
     public VelocityComponent getVelocityComponent()
 154  
     {
 155  0
         return velocityComponent;
 156  
     }
 157  
 
 158  
     public PluginHelpGenerator setVelocityComponent( VelocityComponent velocityComponent )
 159  
     {
 160  1
         this.velocityComponent = velocityComponent;
 161  1
         return this;
 162  
     }
 163  
 
 164  
     // ----------------------------------------------------------------------
 165  
     // Private methods
 166  
     // ----------------------------------------------------------------------
 167  
 
 168  
     private String getHelpClassSources( String pluginResourcesPath, PluginDescriptor pluginDescriptor )
 169  
     {
 170  1
         Properties properties = new Properties();
 171  1
         VelocityContext context = new VelocityContext( properties );
 172  1
         if ( this.helpPackageName != null )
 173  
         {
 174  0
             properties.put( "helpPackageName", this.helpPackageName );
 175  
         }
 176  
         else
 177  
         {
 178  1
             properties.put( "helpPackageName", "" );
 179  
         }
 180  1
         properties.put( "pluginHelpPath", pluginResourcesPath + "/plugin-help.xml" );
 181  1
         properties.put( "artifactId", pluginDescriptor.getArtifactId() );
 182  1
         properties.put( "goalPrefix", pluginDescriptor.getGoalPrefix() );
 183  
 
 184  
         // FIXME encoding !
 185  
 
 186  1
         StringWriter stringWriter = new StringWriter();
 187  
 
 188  1
         InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream( "help-class-source.vm" );
 189  1
         InputStreamReader isReader = new InputStreamReader( is );
 190  1
         velocityComponent.getEngine().evaluate( context, stringWriter, "", isReader );
 191  
 
 192  1
         return stringWriter.toString();
 193  
     }
 194  
 
 195  
     /**
 196  
      * @param pluginDescriptor The descriptor of the plugin for which to generate a help goal, must not be
 197  
      *                         <code>null</code>.
 198  
      * @return The implementation.
 199  
      */
 200  
     private String getImplementation( PluginDescriptor pluginDescriptor )
 201  
     {
 202  1
         String packageName = helpPackageName;
 203  1
         if ( StringUtils.isEmpty( packageName ) )
 204  
         {
 205  1
             packageName = GeneratorUtils.discoverPackageName( pluginDescriptor );
 206  
         }
 207  
 
 208  1
         return StringUtils.isEmpty( packageName ) ? HELP_MOJO_CLASS_NAME : packageName + '.' + HELP_MOJO_CLASS_NAME;
 209  
     }
 210  
 
 211  
     /**
 212  
      * Write help properties files for later use to eventually rewrite Help Mojo.
 213  
      *
 214  
      * @param request
 215  
      * @throws GeneratorException
 216  
      * @see {@link #rewriteHelpMojo(PluginToolsRequest)}
 217  
      */
 218  
     private void writeHelpPropertiesFile( PluginToolsRequest request )
 219  
         throws GeneratorException
 220  
     {
 221  1
         Properties properties = new Properties();
 222  1
         properties.put( "helpPackageName", helpPackageName == null ? "" : helpPackageName );
 223  
 
 224  1
         File tmpPropertiesFile =
 225  
             new File( request.getProject().getBuild().getDirectory(), HELP_PROPERTIES_FILENAME );
 226  
 
 227  1
         if ( tmpPropertiesFile.exists() )
 228  
         {
 229  1
             tmpPropertiesFile.delete();
 230  
         }
 231  0
         else if ( !tmpPropertiesFile.getParentFile().exists() )
 232  
         {
 233  0
             tmpPropertiesFile.getParentFile().mkdirs();
 234  
         }
 235  
 
 236  1
         FileOutputStream fos = null;
 237  
         try
 238  
         {
 239  1
             fos = new FileOutputStream( tmpPropertiesFile );
 240  1
             properties.store( fos, "maven plugin help mojo generation informations" );
 241  
         }
 242  0
         catch ( IOException e )
 243  
         {
 244  0
             throw new GeneratorException( e.getMessage(), e );
 245  
         }
 246  
         finally
 247  
         {
 248  1
             IOUtil.close( fos );
 249  1
         }
 250  1
     }
 251  
 
 252  
     /**
 253  
      * Rewrite Help Mojo to match actual Mojos package name if it was not available at source generation
 254  
      * time. This is used at descriptor generation time.
 255  
      *
 256  
      * @param request
 257  
      * @throws GeneratorException
 258  
      */
 259  
     static void rewriteHelpMojo( PluginToolsRequest request )
 260  
         throws GeneratorException
 261  
     {
 262  1
         File tmpPropertiesFile =
 263  
             new File( request.getProject().getBuild().getDirectory(), HELP_PROPERTIES_FILENAME );
 264  
 
 265  1
         if ( !tmpPropertiesFile.exists() )
 266  
         {
 267  0
             return;
 268  
         }
 269  
 
 270  1
         Properties properties = PropertyUtils.loadProperties( tmpPropertiesFile );
 271  
 
 272  1
         String helpPackageName = properties.getProperty( "helpPackageName" );
 273  
 
 274  
         // if helpPackageName property is empty, we have to rewrite the class with a better package name than empty
 275  1
         if ( StringUtils.isEmpty( helpPackageName ) )
 276  
         {
 277  1
             String helpMojoImplementation = rewriteHelpClassToMojoPackage( request );
 278  
 
 279  1
             if ( helpMojoImplementation != null )
 280  
             {
 281  
                 // rewrite plugin descriptor with new HelpMojo implementation class
 282  0
                 updateHelpMojoDescriptor( request.getPluginDescriptor(), helpMojoImplementation );
 283  
             }
 284  
         }
 285  1
     }
 286  
 
 287  
     private static String rewriteHelpClassToMojoPackage( PluginToolsRequest request )
 288  
         throws GeneratorException
 289  
     {
 290  1
         String destinationPackage = GeneratorUtils.discoverPackageName( request.getPluginDescriptor() );
 291  1
         if ( StringUtils.isEmpty( destinationPackage ) )
 292  
         {
 293  0
             return null;
 294  
         }
 295  1
         String packageAsDirectory = StringUtils.replace( destinationPackage, '.', '/' );
 296  
 
 297  1
         String outputDirectory = request.getProject().getBuild().getOutputDirectory();
 298  1
         File helpClassFile = new File( outputDirectory, HELP_MOJO_CLASS_NAME + ".class" );
 299  1
         if ( !helpClassFile.exists() )
 300  
         {
 301  1
             return null;
 302  
         }
 303  
 
 304  0
         File rewriteHelpClassFile =
 305  
             new File( outputDirectory + '/' + packageAsDirectory, HELP_MOJO_CLASS_NAME + ".class" );
 306  0
         if ( !rewriteHelpClassFile.getParentFile().exists() )
 307  
         {
 308  0
             rewriteHelpClassFile.getParentFile().mkdirs();
 309  
         }
 310  
 
 311  0
         FileInputStream fileInputStream = null;
 312  0
         ClassReader cr = null;
 313  
         try
 314  
         {
 315  0
             fileInputStream = new FileInputStream( helpClassFile );
 316  0
             cr = new ClassReader( fileInputStream );
 317  
         }
 318  0
         catch ( IOException e )
 319  
         {
 320  0
             throw new GeneratorException( e.getMessage(), e );
 321  
         }
 322  
         finally
 323  
         {
 324  0
             IOUtil.close( fileInputStream );
 325  0
         }
 326  
 
 327  0
         ClassWriter cw = new ClassWriter( 0 );
 328  
 
 329  0
         Remapper packageRemapper =
 330  
             new SimpleRemapper( HELP_MOJO_CLASS_NAME, packageAsDirectory + '/' + HELP_MOJO_CLASS_NAME );
 331  0
         ClassVisitor cv = new RemappingClassAdapter( cw, packageRemapper );
 332  
 
 333  
         try
 334  
         {
 335  0
             cr.accept( cv, ClassReader.EXPAND_FRAMES );
 336  
         }
 337  0
         catch ( Throwable e )
 338  
         {
 339  0
             throw new GeneratorException( "ASM issue processing class-file " + helpClassFile.getPath(), e );
 340  0
         }
 341  
 
 342  0
         byte[] renamedClass = cw.toByteArray();
 343  0
         FileOutputStream fos = null;
 344  
         try
 345  
         {
 346  0
             fos = new FileOutputStream( rewriteHelpClassFile );
 347  0
             fos.write( renamedClass );
 348  
         }
 349  0
         catch ( IOException e )
 350  
         {
 351  0
             throw new GeneratorException( "Error rewriting help class: " + e.getMessage(), e );
 352  
         }
 353  
         finally
 354  
         {
 355  0
             IOUtil.close( fos );
 356  0
         }
 357  
 
 358  0
         helpClassFile.delete();
 359  
 
 360  0
         return destinationPackage + ".HelpMojo";
 361  
     }
 362  
 
 363  
     private static void updateHelpMojoDescriptor( PluginDescriptor pluginDescriptor, String helpMojoImplementation )
 364  
     {
 365  0
         MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( HELP_GOAL );
 366  
 
 367  0
         if ( mojoDescriptor != null )
 368  
         {
 369  0
             mojoDescriptor.setImplementation( helpMojoImplementation );
 370  
         }
 371  0
     }
 372  
 }