Coverage Report - org.apache.maven.plugins.jarsigner.AbstractJarsignerMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractJarsignerMojo
0%
0/86
0%
0/68
4,077
AbstractJarsignerMojo$1
0%
0/2
N/A
4,077
AbstractJarsignerMojo$2
0%
0/5
0%
0/2
4,077
AbstractJarsignerMojo$3
0%
0/3
N/A
4,077
 
 1  
 package org.apache.maven.plugins.jarsigner;
 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 java.io.File;
 23  
 import java.io.FileInputStream;
 24  
 import java.io.IOException;
 25  
 import java.io.InputStream;
 26  
 import java.text.MessageFormat;
 27  
 import java.util.Iterator;
 28  
 import java.util.Properties;
 29  
 import java.util.ResourceBundle;
 30  
 import java.util.zip.ZipEntry;
 31  
 import java.util.zip.ZipInputStream;
 32  
 
 33  
 import org.apache.maven.artifact.Artifact;
 34  
 import org.apache.maven.plugin.AbstractMojo;
 35  
 import org.apache.maven.plugin.MojoExecutionException;
 36  
 import org.apache.maven.project.MavenProject;
 37  
 
 38  
 import org.codehaus.plexus.util.Os;
 39  
 import org.codehaus.plexus.util.StringUtils;
 40  
 import org.codehaus.plexus.util.cli.CommandLineException;
 41  
 import org.codehaus.plexus.util.cli.CommandLineUtils;
 42  
 import org.codehaus.plexus.util.cli.Commandline;
 43  
 import org.codehaus.plexus.util.cli.StreamConsumer;
 44  
 
 45  
 /**
 46  
  * Maven Jarsigner Plugin base class.
 47  
  *
 48  
  * @author <a href="cs@schulte.it">Christian Schulte</a>
 49  
  * @version $Id: AbstractJarsignerMojo.java 795568 2009-07-19 16:41:47Z bentmann $
 50  
  */
 51  0
 public abstract class AbstractJarsignerMojo
 52  
     extends AbstractMojo
 53  
 {
 54  
 
 55  
     /**
 56  
      * See <a href="http://java.sun.com/javase/6/docs/technotes/tools/windows/jarsigner.html#Options">options</a>.
 57  
      *
 58  
      * @parameter expression="${jarsigner.verbose}" default-value="false"
 59  
      */
 60  
     private boolean verbose;
 61  
 
 62  
     /**
 63  
      * The maximum memory available to the JAR signer, e.g. <code>256M</code>. See <a
 64  
      * href="http://java.sun.com/javase/6/docs/technotes/tools/windows/java.html#Xms">-Xmx</a> for more details.
 65  
      * 
 66  
      * @parameter expression="${jarsigner.maxMemory}"
 67  
      */
 68  
     private String maxMemory;
 69  
 
 70  
     /**
 71  
      * Archive to process. If set, neither the project artifact nor any attachments are processed.
 72  
      *
 73  
      * @parameter expression="${jarsigner.archive}"
 74  
      * @optional
 75  
      */
 76  
     private File archive;
 77  
 
 78  
     /**
 79  
      * List of additional arguments to append to the jarsigner command line.
 80  
      *
 81  
      * @parameter expression="${jarsigner.arguments}"
 82  
      * @optional
 83  
      */
 84  
     private String[] arguments;
 85  
 
 86  
     /**
 87  
      * Set to {@code true} to disable the plugin.
 88  
      *
 89  
      * @parameter expression="${jarsigner.skip}" default-value="false"
 90  
      */
 91  
     private boolean skip;
 92  
 
 93  
     /**
 94  
      * Controls processing of project attachments.
 95  
      *
 96  
      * @parameter expression="${jarsigner.attachments}" default-value="true"
 97  
      */
 98  
     private boolean attachments;
 99  
 
 100  
     /**
 101  
      * The Maven project.
 102  
      *
 103  
      * @parameter default-value="${project}"
 104  
      * @required
 105  
      * @readonly
 106  
      */
 107  
     private MavenProject project;
 108  
 
 109  
     /**
 110  
      * The path to the jarsigner we are going to use.
 111  
      */
 112  
     private String executable;
 113  
 
 114  
     public final void execute()
 115  
         throws MojoExecutionException
 116  
     {
 117  0
         if ( !this.skip )
 118  
         {
 119  0
             this.executable = getExecutable();
 120  
 
 121  0
             if ( this.archive != null )
 122  
             {
 123  0
                 this.processArchive( this.archive );
 124  
             }
 125  
             else
 126  
             {
 127  0
                 this.processArtifact( this.project.getArtifact() );
 128  
 
 129  0
                 for ( Iterator it = this.project.getAttachedArtifacts().iterator(); it.hasNext(); )
 130  
                 {
 131  0
                     final Artifact artifact = (Artifact) it.next();
 132  
 
 133  0
                     if ( this.attachments )
 134  
                     {
 135  0
                         this.processArtifact( artifact );
 136  
                     }
 137  0
                     else if ( this.isJarFile( artifact ) )
 138  
                     {
 139  0
                         this.getLog().info( this.getMessage( "ignoringAttachment", new Object[]
 140  
                             {
 141  
                                 artifact.toString()
 142  
                             } ) );
 143  
 
 144  
                     }
 145  0
                 }
 146  
             }
 147  
         }
 148  
         else
 149  
         {
 150  0
             this.getLog().info( this.getMessage( "disabled", null ) );
 151  
         }
 152  0
     }
 153  
 
 154  
     /**
 155  
      * Gets the {@code Commandline} to execute for a given Java archive taking a command line prepared for executing
 156  
      * jarsigner.
 157  
      *
 158  
      * @param archive The Java archive to get a {@code Commandline} to execute for.
 159  
      * @param commandLine A {@code Commandline} prepared for executing jarsigner without any arguments.
 160  
      *
 161  
      * @return A {@code Commandline} for executing jarsigner with {@code archive}.
 162  
      *
 163  
      * @throws NullPointerException if {@code archive} or {@code commandLine} is {@code null}.
 164  
      */
 165  
     protected abstract Commandline getCommandline( final File archive, final Commandline commandLine );
 166  
 
 167  
     /**
 168  
      * Gets a string representation of a {@code Commandline}.
 169  
      * <p>This method creates the string representation by calling {@code commandLine.toString()} by default.</p>
 170  
      *
 171  
      * @param commandLine The {@code Commandline} to get a string representation of.
 172  
      *
 173  
      * @return The string representation of {@code commandLine}.
 174  
      *
 175  
      * @throws NullPointerException if {@code commandLine} is {@code null}.
 176  
      */
 177  
     protected String getCommandlineInfo( final Commandline commandLine )
 178  
     {
 179  0
         if ( commandLine == null )
 180  
         {
 181  0
             throw new NullPointerException( "commandLine" );
 182  
         }
 183  
 
 184  0
         return commandLine.toString();
 185  
     }
 186  
 
 187  
     /**
 188  
      * Checks Java language capability of an artifact.
 189  
      *
 190  
      * @param artifact The artifact to check.
 191  
      *
 192  
      * @return {@code true} if {@code artifact} is Java language capable; {@code false} if not.
 193  
      */
 194  
     private boolean isJarFile( final Artifact artifact )
 195  
     {
 196  0
         return artifact != null && artifact.getFile() != null && isJarFile( artifact.getFile() );
 197  
     }
 198  
 
 199  
     /**
 200  
      * Checks whether the specified file is a JAR file. For our purposes, a JAR file is a (non-empty) ZIP stream with a
 201  
      * META-INF directory or some class files.
 202  
      * 
 203  
      * @param file The file to check, must not be <code>null</code>.
 204  
      * @return <code>true</code> if the file looks like a JAR file, <code>false</code> otherwise.
 205  
      */
 206  
     private boolean isJarFile( final File file )
 207  
     {
 208  
         try
 209  
         {
 210  
             // NOTE: ZipFile.getEntry() might be shorter but is several factors slower on large files
 211  
 
 212  0
             ZipInputStream zis = new ZipInputStream( new FileInputStream( file ) );
 213  
             try
 214  
             {
 215  0
                 for ( ZipEntry ze = zis.getNextEntry(); ze != null; ze = zis.getNextEntry() )
 216  
                 {
 217  0
                     if ( ze.getName().startsWith( "META-INF/" ) || ze.getName().endsWith( ".class" ) )
 218  
                     {
 219  0
                         return true;
 220  
                     }
 221  
                 }
 222  
             }
 223  
             finally
 224  
             {
 225  0
                 zis.close();
 226  0
             }
 227  
         }
 228  0
         catch ( Exception e )
 229  
         {
 230  
             // ignore, will fail below
 231  0
         }
 232  
 
 233  0
         return false;
 234  
     }
 235  
 
 236  
     /**
 237  
      * Processes a given artifact.
 238  
      *
 239  
      * @param artifact The artifact to process.
 240  
      *
 241  
      * @throws NullPointerException if {@code artifact} is {@code null}.
 242  
      * @throws MojoExecutionException if processing {@code artifact} fails.
 243  
      */
 244  
     private void processArtifact( final Artifact artifact )
 245  
         throws MojoExecutionException
 246  
     {
 247  0
         if ( artifact == null )
 248  
         {
 249  0
             throw new NullPointerException( "artifact" );
 250  
         }
 251  
 
 252  0
         if ( this.isJarFile( artifact ) )
 253  
         {
 254  0
             if ( this.verbose )
 255  
             {
 256  0
                 this.getLog().info( this.getMessage( "processing", new Object[]
 257  
                     {
 258  
                         artifact.toString()
 259  
                     } ) );
 260  
 
 261  
             }
 262  0
             else if ( this.getLog().isDebugEnabled() )
 263  
             {
 264  0
                 this.getLog().debug( this.getMessage( "processing", new Object[]
 265  
                     {
 266  
                         artifact.toString()
 267  
                     } ) );
 268  
 
 269  
             }
 270  
 
 271  0
             this.processArchive( artifact.getFile() );
 272  
         }
 273  
         else
 274  
         {
 275  0
             if ( this.verbose )
 276  
             {
 277  0
                 this.getLog().info( this.getMessage( "unsupported", new Object[]
 278  
                     {
 279  
                         artifact.toString()
 280  
                     } ) );
 281  
 
 282  
             }
 283  0
             else if ( this.getLog().isDebugEnabled() )
 284  
             {
 285  0
                 this.getLog().debug( this.getMessage( "unsupported", new Object[]
 286  
                     {
 287  
                         artifact.toString()
 288  
                     } ) );
 289  
 
 290  
             }
 291  
         }
 292  0
     }
 293  
 
 294  
     /**
 295  
      * Processes a given archive.
 296  
      *
 297  
      * @param archive The archive to process.
 298  
      *
 299  
      * @throws NullPointerException if {@code archive} is {@code null}.
 300  
      * @throws MojoExecutionException if processing {@code archive} fails.
 301  
      */
 302  
     private void processArchive( final File archive )
 303  
         throws MojoExecutionException
 304  
     {
 305  0
         if ( archive == null )
 306  
         {
 307  0
             throw new NullPointerException( "archive" );
 308  
         }
 309  
 
 310  0
         Commandline commandLine = new Commandline();
 311  
 
 312  0
         commandLine.setExecutable( this.executable );
 313  
 
 314  0
         commandLine.setWorkingDirectory( this.project.getBasedir() );
 315  
 
 316  0
         if ( this.verbose )
 317  
         {
 318  0
             commandLine.createArg().setValue( "-verbose" );
 319  
         }
 320  
 
 321  0
         if ( StringUtils.isNotEmpty( maxMemory ) )
 322  
         {
 323  0
             commandLine.createArg().setValue( "-J-Xmx" + maxMemory );
 324  
         }
 325  
 
 326  0
         if ( this.arguments != null )
 327  
         {
 328  0
             commandLine.addArguments( this.arguments );
 329  
         }
 330  
 
 331  0
         commandLine = this.getCommandline( archive, commandLine );
 332  
 
 333  
         try
 334  
         {
 335  0
             if ( this.getLog().isDebugEnabled() )
 336  
             {
 337  0
                 this.getLog().debug( this.getMessage( "command", new Object[]
 338  
                     {
 339  
                         this.getCommandlineInfo( commandLine )
 340  
                     } ) );
 341  
 
 342  
             }
 343  
 
 344  0
             final int result = CommandLineUtils.executeCommandLine( commandLine,
 345  
                 new InputStream()
 346  
             {
 347  
 
 348  0
                 public int read()
 349  
                 {
 350  0
                     return -1;
 351  
                 }
 352  
 
 353  
             }, new StreamConsumer()
 354  
             {
 355  
 
 356  0
                 public void consumeLine( final String line )
 357  
                 {
 358  0
                     if ( verbose )
 359  
                     {
 360  0
                         getLog().info( line );
 361  
                     }
 362  
                     else
 363  
                     {
 364  0
                         getLog().debug( line );
 365  
                     }
 366  0
                 }
 367  
 
 368  
             }, new StreamConsumer()
 369  
             {
 370  
 
 371  0
                 public void consumeLine( final String line )
 372  
                 {
 373  0
                     getLog().warn( line );
 374  0
                 }
 375  
 
 376  
             } );
 377  
 
 378  0
             if ( result != 0 )
 379  
             {
 380  0
                 throw new MojoExecutionException( this.getMessage( "failure", new Object[]
 381  
                     {
 382  
                         this.getCommandlineInfo( commandLine ), new Integer( result )
 383  
                     } ) );
 384  
 
 385  
             }
 386  
         }
 387  0
         catch ( CommandLineException e )
 388  
         {
 389  0
             throw new MojoExecutionException( this.getMessage( "commandLineException", new Object[]
 390  
                 {
 391  
                     this.getCommandlineInfo( commandLine )
 392  
                 } ), e );
 393  
 
 394  0
         }
 395  0
     }
 396  
 
 397  
     /**
 398  
      * Locates the executable for the jarsigner tool.
 399  
      * 
 400  
      * @return The executable of the jarsigner tool, never <code>null<code>.
 401  
      */
 402  
     private String getExecutable()
 403  
     {
 404  0
         String command = "jarsigner" + ( Os.isFamily( Os.FAMILY_WINDOWS ) ? ".exe" : "" );
 405  
 
 406  0
         String executable =
 407  
             findExecutable( command, System.getProperty( "java.home" ), new String[] { "../bin", "bin", "../sh" } );
 408  
 
 409  0
         if ( executable == null )
 410  
         {
 411  
             try
 412  
             {
 413  0
                 Properties env = CommandLineUtils.getSystemEnvVars();
 414  
 
 415  0
                 String[] variables = { "JDK_HOME", "JAVA_HOME" };
 416  
 
 417  0
                 for ( int i = 0; i < variables.length && executable == null; i++ )
 418  
                 {
 419  0
                     executable =
 420  
                         findExecutable( command, env.getProperty( variables[i] ), new String[] { "bin", "sh" } );
 421  
                 }
 422  
             }
 423  0
             catch ( IOException e )
 424  
             {
 425  0
                 if ( getLog().isDebugEnabled() )
 426  
                 {
 427  0
                     getLog().warn( "Failed to retrieve environment variables, cannot search for " + command, e );
 428  
                 }
 429  
                 else
 430  
                 {
 431  0
                     getLog().warn( "Failed to retrieve environment variables, cannot search for " + command );
 432  
                 }
 433  0
             }
 434  
         }
 435  
 
 436  0
         if ( executable == null )
 437  
         {
 438  0
             executable = command;
 439  
         }
 440  
 
 441  0
         return executable;
 442  
     }
 443  
 
 444  
     /**
 445  
      * Finds the specified command in any of the given sub directories of the specified JDK/JRE home directory.
 446  
      * 
 447  
      * @param command The command to find, must not be <code>null</code>.
 448  
      * @param homeDir The home directory to search in, may be <code>null</code>.
 449  
      * @param subDirs The sub directories of the home directory to search in, must not be <code>null</code>.
 450  
      * @return The (absolute) path to the command if found, <code>null</code> otherwise.
 451  
      */
 452  
     private String findExecutable( String command, String homeDir, String[] subDirs )
 453  
     {
 454  0
         if ( StringUtils.isNotEmpty( homeDir ) )
 455  
         {
 456  0
             for ( int i = 0; i < subDirs.length; i++ )
 457  
             {
 458  0
                 File file = new File( new File( homeDir, subDirs[i] ), command );
 459  
 
 460  0
                 if ( file.isFile() )
 461  
                 {
 462  0
                     return file.getAbsolutePath();
 463  
                 }
 464  
             }
 465  
         }
 466  
 
 467  0
         return null;
 468  
     }
 469  
 
 470  
     /**
 471  
      * Gets a message for a given key from the resource bundle backing the implementation.
 472  
      *
 473  
      * @param key The key of the message to return.
 474  
      * @param args Arguments to format the message with or {@code null}.
 475  
      *
 476  
      * @return The message with key {@code key} from the resource bundle backing the implementation.
 477  
      *
 478  
      * @throws NullPointerException if {@code key} is {@code null}.
 479  
      * @throws java.util.MissingResourceException if there is no message available matching {@code key} or accessing
 480  
      * the resource bundle fails.
 481  
      */
 482  
     private String getMessage( final String key, final Object[] args )
 483  
     {
 484  0
         if ( key == null )
 485  
         {
 486  0
             throw new NullPointerException( "key" );
 487  
         }
 488  
 
 489  0
         return new MessageFormat( ResourceBundle.getBundle( "jarsigner" ).getString( key ) ).format( args );
 490  
     }
 491  
 
 492  
 }