Coverage Report - org.apache.maven.plugin.dependency.BuildClasspathMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
BuildClasspathMojo
64%
75/116
60%
30/50
2.308
 
 1  
 package org.apache.maven.plugin.dependency;
 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.artifact.Artifact;
 23  
 import org.apache.maven.plugin.MojoExecutionException;
 24  
 import org.apache.maven.plugin.dependency.utils.DependencyUtil;
 25  
 import org.apache.maven.plugins.annotations.Component;
 26  
 import org.apache.maven.plugins.annotations.LifecyclePhase;
 27  
 import org.apache.maven.plugins.annotations.Mojo;
 28  
 import org.apache.maven.plugins.annotations.Parameter;
 29  
 import org.apache.maven.plugins.annotations.ResolutionScope;
 30  
 import org.apache.maven.project.MavenProjectHelper;
 31  
 import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
 32  
 import org.codehaus.plexus.util.IOUtil;
 33  
 import org.codehaus.plexus.util.StringUtils;
 34  
 
 35  
 import java.io.BufferedReader;
 36  
 import java.io.BufferedWriter;
 37  
 import java.io.File;
 38  
 import java.io.FileReader;
 39  
 import java.io.FileWriter;
 40  
 import java.io.IOException;
 41  
 import java.io.Writer;
 42  
 import java.util.ArrayList;
 43  
 import java.util.Comparator;
 44  
 import java.util.Iterator;
 45  
 import java.util.List;
 46  
 import java.util.Set;
 47  
 import java.util.regex.Matcher;
 48  
 import java.util.regex.Pattern;
 49  
 
 50  
 /**
 51  
  * This goal will output a classpath string of dependencies from the local repository to a file or log.
 52  
  *
 53  
  * @author ankostis
 54  
  * @version $Id: BuildClasspathMojo.java 1400739 2012-10-21 23:05:22Z hboutemy $
 55  
  * @since 2.0-alpha-2
 56  
  */
 57  0
 @Mojo( name = "build-classpath", requiresDependencyResolution = ResolutionScope.TEST,
 58  
        defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true )
 59  2
 public class BuildClasspathMojo
 60  
     extends AbstractDependencyFilterMojo
 61  
     implements Comparator<Artifact>
 62  
 {
 63  
 
 64  
     /**
 65  
      * Strip artifact version during copy (only works if prefix is set)
 66  
      */
 67  2
     @Parameter( property = "mdep.stripVersion", defaultValue = "false" )
 68  
     private boolean stripVersion = false;
 69  
 
 70  
     /**
 71  
      * The prefix to prepend on each dependent artifact. If undefined, the paths refer to the actual files store in the
 72  
      * local repository (the stripVersion parameter does nothing then).
 73  
      */
 74  
     @Parameter( property = "mdep.prefix" )
 75  
     private String prefix;
 76  
 
 77  
     /**
 78  
      * The file to write the classpath string. If undefined, it just prints the classpath as [INFO].
 79  
      * This parameter is deprecated. Use outputFile instead.
 80  
      *
 81  
      * @since 2.0
 82  
      * @deprecated use outputFile instead
 83  
      */
 84  
     @Parameter( property = "mdep.cpFile" )
 85  
     private File cpFile;
 86  
 
 87  
     /**
 88  
      * The file to write the classpath string. If undefined, it just prints the classpath as [INFO].
 89  
      */
 90  
     @Parameter( property = "mdep.outputFile" )
 91  
     private File outputFile;
 92  
 
 93  
     /**
 94  
      * If 'true', it skips the up-to-date-check, and always regenerates the classpath file.
 95  
      */
 96  
     @Parameter( property = "mdep.regenerateFile", defaultValue = "false" )
 97  
     private boolean regenerateFile;
 98  
 
 99  
     /**
 100  
      * Override the char used between the paths. This field is initialized to contain the first character of the value
 101  
      * of the system property file.separator. On UNIX systems the value of this field is '/'; on Microsoft Windows
 102  
      * systems it is '\'. The default is File.separator
 103  
      *
 104  
      * @since 2.0
 105  
      */
 106  
     @Parameter( property = "mdep.fileSeparator", defaultValue = "" )
 107  
     private String fileSeparator;
 108  
 
 109  
     /**
 110  
      * Override the char used between path folders. The system-dependent path-separator character. This field is
 111  
      * initialized to contain the first character of the value of the system property path.separator. This character is
 112  
      * used to separate filenames in a sequence of files given as a path list. On UNIX systems, this character is ':';
 113  
      * on Microsoft Windows systems it is ';'.
 114  
      *
 115  
      * @since 2.0
 116  
      */
 117  
     @Parameter( property = "mdep.pathSeparator", defaultValue = "" )
 118  
     private String pathSeparator;
 119  
 
 120  
     /**
 121  
      * Replace the absolute path to the local repo with this property. This field is ignored it prefix is declared. The
 122  
      * value will be forced to "${M2_REPO}" if no value is provided AND the attach flag is true.
 123  
      *
 124  
      * @since 2.0
 125  
      */
 126  
     @Parameter( property = "mdep.localRepoProperty", defaultValue = "" )
 127  
     private String localRepoProperty;
 128  
 
 129  
     /**
 130  
      * Attach the classpath file to the main artifact so it can be installed and deployed.
 131  
      *
 132  
      * @since 2.0
 133  
      */
 134  
     @Parameter( defaultValue = "false" )
 135  
     boolean attach;
 136  
 
 137  
     /**
 138  
      * Write out the classpath in a format compatible with filtering (classpath=xxxxx)
 139  
      *
 140  
      * @since 2.0
 141  
      */
 142  
     @Parameter( property = "mdep.outputFilterFile", defaultValue = "false" )
 143  
     boolean outputFilterFile;
 144  
 
 145  
     /**
 146  
      * Either append the artifact's baseVersion or uniqueVersion to the filename.
 147  
      * Will only be used if {@link #isStripVersion()} is {@code false}.
 148  
      * @since 2.6
 149  
      */
 150  2
     @Parameter( property = "mdep.useBaseVersion", defaultValue = "true" )
 151  
     protected boolean useBaseVersion = true;
 152  
 
 153  
     /**
 154  
      * Maven ProjectHelper
 155  
      */
 156  
     @Component
 157  
     private MavenProjectHelper projectHelper;
 158  
 
 159  2
     boolean isFileSepSet = true;
 160  
 
 161  2
     boolean isPathSepSet = true;
 162  
 
 163  
     /**
 164  
      * Main entry into mojo. Gets the list of dependencies and iterates through calling copyArtifact.
 165  
      *
 166  
      * @throws MojoExecutionException with a message if an error occurs.
 167  
      * @see #getDependencies
 168  
      * @see #copyArtifact(Artifact, boolean)
 169  
      */
 170  
     public void execute()
 171  
         throws MojoExecutionException
 172  
     {
 173  
 
 174  3
         if ( cpFile != null )
 175  
         {
 176  0
             getLog().warn( "The parameter cpFile is deprecated. Use outputFile instead." );
 177  0
             this.outputFile = cpFile;
 178  
         }
 179  
 
 180  
         // initialize the separators.
 181  3
         isFileSepSet = StringUtils.isNotEmpty( fileSeparator );
 182  3
         isPathSepSet = StringUtils.isNotEmpty( pathSeparator );
 183  
 
 184  
         //don't allow them to have absolute paths when they attach.
 185  3
         if ( attach && StringUtils.isEmpty( localRepoProperty ) )
 186  
         {
 187  0
             localRepoProperty = "${M2_REPO}";
 188  
         }
 189  
 
 190  3
         Set<Artifact> artifacts = getResolvedDependencies( true );
 191  
 
 192  3
         if ( artifacts == null || artifacts.isEmpty() )
 193  
         {
 194  0
             getLog().info( "No dependencies found." );
 195  
         }
 196  
 
 197  3
         List<Artifact> artList = new ArrayList<Artifact>( artifacts );
 198  
 
 199  3
         StringBuilder sb = new StringBuilder();
 200  3
         Iterator<Artifact> i = artList.iterator();
 201  
 
 202  3
         if ( i.hasNext() )
 203  
         {
 204  3
             appendArtifactPath( i.next(), sb );
 205  
 
 206  21
             while ( i.hasNext() )
 207  
             {
 208  18
                 sb.append( isPathSepSet ? this.pathSeparator : File.pathSeparator );
 209  18
                 appendArtifactPath( (Artifact) i.next(), sb );
 210  
             }
 211  
         }
 212  
 
 213  3
         String cpString = sb.toString();
 214  
 
 215  
         // if file separator is set, I need to replace the default one from all
 216  
         // the file paths that were pulled from the artifacts
 217  3
         if ( isFileSepSet )
 218  
         {
 219  
             // Escape file separators to be used as literal strings
 220  1
             final String pattern = Pattern.quote( File.separator );
 221  1
             final String replacement = Matcher.quoteReplacement( fileSeparator );
 222  1
             cpString = cpString.replaceAll( pattern, replacement );
 223  
         }
 224  
 
 225  
         //make the string valid for filtering
 226  3
         if ( outputFilterFile )
 227  
         {
 228  0
             cpString = "classpath=" + cpString;
 229  
         }
 230  
 
 231  3
         if ( outputFile == null )
 232  
         {
 233  1
             getLog().info( "Dependencies classpath:\n" + cpString );
 234  
         }
 235  
         else
 236  
         {
 237  2
             if ( regenerateFile || !isUpdToDate( cpString ) )
 238  
             {
 239  2
                 storeClasspathFile( cpString, outputFile );
 240  
             }
 241  
             else
 242  
             {
 243  0
                 this.getLog().info( "Skipped writing classpath file '" + outputFile + "'.  No changes found." );
 244  
             }
 245  
         }
 246  3
         if ( attach )
 247  
         {
 248  0
             attachFile( cpString );
 249  
         }
 250  3
     }
 251  
 
 252  
     protected void attachFile( String cpString )
 253  
         throws MojoExecutionException
 254  
     {
 255  0
         File attachedFile = new File( project.getBuild().getDirectory(), "classpath" );
 256  0
         storeClasspathFile( cpString, attachedFile );
 257  
 
 258  0
         projectHelper.attachArtifact( project, attachedFile, "classpath" );
 259  0
     }
 260  
 
 261  
     /**
 262  
      * Appends the artifact path into the specified StringBuilder.
 263  
      *
 264  
      * @param art
 265  
      * @param sb
 266  
      */
 267  
     protected void appendArtifactPath( Artifact art, StringBuilder sb )
 268  
     {
 269  28
         if ( prefix == null )
 270  
         {
 271  25
             String file = art.getFile().getPath();
 272  
             // substitute the property for the local repo path to make the classpath file portable.
 273  25
             if ( StringUtils.isNotEmpty( localRepoProperty ) )
 274  
             {
 275  3
                 file = StringUtils.replace( file, getLocal().getBasedir(), localRepoProperty );
 276  
             }
 277  25
             sb.append( file );
 278  25
         }
 279  
         else
 280  
         {
 281  
             // TODO: add param for prepending groupId and version.
 282  3
             sb.append( prefix );
 283  3
             sb.append( File.separator );
 284  3
             sb.append( DependencyUtil.getFormattedFileName( art, this.stripVersion, this.prependGroupId, this.useBaseVersion ) );
 285  
         }
 286  28
     }
 287  
 
 288  
     /**
 289  
      * Checks that new classpath differs from that found inside the old classpathFile.
 290  
      *
 291  
      * @param cpString
 292  
      * @return true if the specified classpath equals to that found inside the file, false otherwise (including when
 293  
      *         file does not exists but new classpath does).
 294  
      */
 295  
     private boolean isUpdToDate( String cpString )
 296  
     {
 297  
         try
 298  
         {
 299  2
             String oldCp = readClasspathFile();
 300  2
             return ( cpString == oldCp || ( cpString != null && cpString.equals( oldCp ) ) );
 301  
         }
 302  0
         catch ( Exception ex )
 303  
         {
 304  0
             this.getLog().warn(
 305  
                 "Error while reading old classpath file '" + outputFile + "' for up-to-date check: " + ex );
 306  
 
 307  0
             return false;
 308  
         }
 309  
     }
 310  
 
 311  
     /**
 312  
      * It stores the specified string into that file.
 313  
      *
 314  
      * @param cpString the string to be written into the file.
 315  
      * @throws MojoExecutionException
 316  
      */
 317  
     private void storeClasspathFile( String cpString, File out )
 318  
         throws MojoExecutionException
 319  
     {
 320  
         //make sure the parent path exists.
 321  2
         out.getParentFile().mkdirs();
 322  
 
 323  2
         Writer w = null;
 324  
         try
 325  
         {
 326  2
             w = new BufferedWriter( new FileWriter( out ) );
 327  2
             w.write( cpString );
 328  2
             getLog().info( "Wrote classpath file '" + out + "'." );
 329  
         }
 330  0
         catch ( IOException ex )
 331  
         {
 332  0
             throw new MojoExecutionException( "Error while writting to classpath file '" + out + "': " + ex.toString(),
 333  
                                               ex );
 334  
         }
 335  
         finally
 336  
         {
 337  2
             IOUtil.close( w );
 338  2
         }
 339  2
     }
 340  
 
 341  
     /**
 342  
      * Reads into a string the file specified by the mojo param 'outputFile'. Assumes, the instance variable
 343  
      * 'outputFile' is not null.
 344  
      * 
 345  
      * @return the string contained in the classpathFile, if exists, or null otherwise.
 346  
      * @throws MojoExecutionException
 347  
      */
 348  
     protected String readClasspathFile()
 349  
         throws IOException
 350  
     {
 351  5
         if ( outputFile == null )
 352  
         {
 353  1
             throw new IllegalArgumentException(
 354  
                 "The outputFile parameter cannot be null if the file is intended to be read." );
 355  
         }
 356  
 
 357  4
         if ( !outputFile.isFile() )
 358  
         {
 359  1
             return null;
 360  
         }
 361  3
         StringBuilder sb = new StringBuilder();
 362  3
         BufferedReader r = null;
 363  
 
 364  
         try
 365  
         {
 366  3
             r = new BufferedReader( new FileReader( outputFile ) );
 367  
             String l;
 368  6
             while ( ( l = r.readLine() ) != null )
 369  
             {
 370  3
                 sb.append( l );
 371  
             }
 372  
 
 373  3
             return sb.toString();
 374  
         }
 375  
         finally
 376  
         {
 377  3
             IOUtil.close( r );
 378  
         }
 379  
     }
 380  
 
 381  
     /**
 382  
      * Compares artifacts lexicographically, using pattern [group_id][artifact_id][version].
 383  
      *
 384  
      * @param art1 first object
 385  
      * @param art2 second object
 386  
      * @return the value <code>0</code> if the argument string is equal to this string; a value less than
 387  
      *         <code>0</code> if this string is lexicographically less than the string argument; and a value greater
 388  
      *         than <code>0</code> if this string is lexicographically greater than the string argument.
 389  
      */
 390  
     public int compare( Artifact art1, Artifact art2 )
 391  
     {
 392  0
         if ( art1 == art2 )
 393  
         {
 394  0
             return 0;
 395  
         }
 396  0
         else if ( art1 == null )
 397  
         {
 398  0
             return -1;
 399  
         }
 400  0
         else if ( art2 == null )
 401  
         {
 402  0
             return +1;
 403  
         }
 404  
 
 405  0
         String s1 = art1.getGroupId() + art1.getArtifactId() + art1.getVersion();
 406  0
         String s2 = art2.getGroupId() + art2.getArtifactId() + art2.getVersion();
 407  
 
 408  0
         return s1.compareTo( s2 );
 409  
     }
 410  
 
 411  
     protected ArtifactsFilter getMarkedArtifactFilter()
 412  
     {
 413  3
         return null;
 414  
     }
 415  
 
 416  
     /**
 417  
      * @return the outputFile
 418  
      */
 419  
     public File getCpFile()
 420  
     {
 421  0
         return this.outputFile;
 422  
     }
 423  
 
 424  
     /**
 425  
      * @param theCpFile the outputFile to set
 426  
      */
 427  
     public void setCpFile( File theCpFile )
 428  
     {
 429  1
         this.outputFile = theCpFile;
 430  1
     }
 431  
 
 432  
     /**
 433  
      * @return the fileSeparator
 434  
      */
 435  
     public String getFileSeparator()
 436  
     {
 437  0
         return this.fileSeparator;
 438  
     }
 439  
 
 440  
     /**
 441  
      * @param theFileSeparator the fileSeparator to set
 442  
      */
 443  
     public void setFileSeparator( String theFileSeparator )
 444  
     {
 445  1
         this.fileSeparator = theFileSeparator;
 446  1
     }
 447  
 
 448  
     /**
 449  
      * @return the pathSeparator
 450  
      */
 451  
     public String getPathSeparator()
 452  
     {
 453  0
         return this.pathSeparator;
 454  
     }
 455  
 
 456  
     /**
 457  
      * @param thePathSeparator the pathSeparator to set
 458  
      */
 459  
     public void setPathSeparator( String thePathSeparator )
 460  
     {
 461  1
         this.pathSeparator = thePathSeparator;
 462  1
     }
 463  
 
 464  
     /**
 465  
      * @return the prefix
 466  
      */
 467  
     public String getPrefix()
 468  
     {
 469  0
         return this.prefix;
 470  
     }
 471  
 
 472  
     /**
 473  
      * @param thePrefix the prefix to set
 474  
      */
 475  
     public void setPrefix( String thePrefix )
 476  
     {
 477  4
         this.prefix = thePrefix;
 478  4
     }
 479  
 
 480  
     /**
 481  
      * @return the regenerateFile
 482  
      */
 483  
     public boolean isRegenerateFile()
 484  
     {
 485  0
         return this.regenerateFile;
 486  
     }
 487  
 
 488  
     /**
 489  
      * @param theRegenerateFile the regenerateFile to set
 490  
      */
 491  
     public void setRegenerateFile( boolean theRegenerateFile )
 492  
     {
 493  0
         this.regenerateFile = theRegenerateFile;
 494  0
     }
 495  
 
 496  
     /**
 497  
      * @return the stripVersion
 498  
      */
 499  
     public boolean isStripVersion()
 500  
     {
 501  0
         return this.stripVersion;
 502  
     }
 503  
 
 504  
     /**
 505  
      * @param theStripVersion the stripVersion to set
 506  
      */
 507  
     public void setStripVersion( boolean theStripVersion )
 508  
     {
 509  2
         this.stripVersion = theStripVersion;
 510  2
     }
 511  
 
 512  
     public String getLocalRepoProperty()
 513  
     {
 514  0
         return localRepoProperty;
 515  
     }
 516  
 
 517  
     public void setLocalRepoProperty( String localRepoProperty )
 518  
     {
 519  5
         this.localRepoProperty = localRepoProperty;
 520  5
     }
 521  
 
 522  
     public boolean isFileSepSet()
 523  
     {
 524  0
         return isFileSepSet;
 525  
     }
 526  
 
 527  
     public void setFileSepSet( boolean isFileSepSet )
 528  
     {
 529  0
         this.isFileSepSet = isFileSepSet;
 530  0
     }
 531  
 
 532  
     public boolean isPathSepSet()
 533  
     {
 534  0
         return isPathSepSet;
 535  
     }
 536  
 
 537  
     public void setPathSepSet( boolean isPathSepSet )
 538  
     {
 539  0
         this.isPathSepSet = isPathSepSet;
 540  0
     }
 541  
 }