Coverage Report - org.apache.maven.plugin.eclipse.writers.EclipseManifestWriter
 
Classes in this File Line Coverage Branch Coverage Complexity
EclipseManifestWriter
0%
0/124
0%
0/64
3.909
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements. See the NOTICE file distributed with this
 4  
  * work for additional information regarding copyright ownership. The ASF
 5  
  * licenses this file to you under the Apache License, Version 2.0 (the
 6  
  * "License"); you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
 9  
  * or agreed to in writing, software distributed under the License is
 10  
  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 11  
  * KIND, either express or implied. See the License for the specific language
 12  
  * governing permissions and limitations under the License.
 13  
  */
 14  
 package org.apache.maven.plugin.eclipse.writers;
 15  
 
 16  
 import java.io.File;
 17  
 import java.io.FileInputStream;
 18  
 import java.io.FileOutputStream;
 19  
 import java.io.IOException;
 20  
 import java.util.Arrays;
 21  
 import java.util.HashSet;
 22  
 import java.util.Iterator;
 23  
 import java.util.Set;
 24  
 import java.util.jar.Attributes;
 25  
 import java.util.jar.Manifest;
 26  
 
 27  
 import org.apache.maven.artifact.repository.ArtifactRepository;
 28  
 import org.apache.maven.plugin.MojoExecutionException;
 29  
 import org.apache.maven.plugin.eclipse.Constants;
 30  
 import org.apache.maven.plugin.eclipse.EclipseSourceDir;
 31  
 import org.apache.maven.plugin.eclipse.Messages;
 32  
 import org.apache.maven.plugin.eclipse.writers.wtp.AbstractWtpResourceWriter;
 33  
 import org.apache.maven.plugin.ide.IdeDependency;
 34  
 import org.apache.maven.plugin.ide.IdeUtils;
 35  
 import org.apache.maven.plugin.ide.JeeUtils;
 36  
 import org.apache.maven.plugin.logging.Log;
 37  
 import org.apache.maven.project.MavenProject;
 38  
 
 39  
 /**
 40  
  * Create or adapt the manifest files for the RAD6 runtime dependencys. attention these will not be used for the real
 41  
  * ear these are just to get the runtime enviorment using the maven dependencies. WARNING: The manifest resources added
 42  
  * here will not have the benefit of the dependencies of the project, since that's not provided in the setup() apis, one
 43  
  * of the locations from which this writer is used in the RadPlugin.
 44  
  * 
 45  
  * @author <a href="mailto:nir@cfc.at">Richard van Nieuwenhoven </a>
 46  
  */
 47  0
 public class EclipseManifestWriter
 48  
     extends AbstractEclipseWriter
 49  
 {
 50  
 
 51  
     private static final String MANIFEST_MF_FILENAME = "MANIFEST.MF";
 52  
 
 53  
     private static final String META_INF_DIRECTORY = "META-INF";
 54  
 
 55  0
     private static final String GENERATED_RESOURCE_DIRNAME =
 56  
         "target" + File.separatorChar + "generated-resources" + File.separatorChar + "eclipse";
 57  
 
 58  0
     private static final String WEBAPP_RESOURCE_DIR =
 59  
         "src" + File.separatorChar + "main" + File.separatorChar + "webapp";
 60  
 
 61  
     /**
 62  
      * Returns absolute path to the web content directory based on configuration of the war plugin or default one
 63  
      * otherwise.
 64  
      * 
 65  
      * @param project
 66  
      * @return absolute directory path as String
 67  
      * @throws MojoExecutionException
 68  
      */
 69  
     private static String getWebContentBaseDirectory( EclipseWriterConfig config )
 70  
         throws MojoExecutionException
 71  
     {
 72  
         // getting true location of web source dir from config
 73  0
         File warSourceDirectory =
 74  
             new File( IdeUtils.getPluginSetting( config.getProject(), JeeUtils.ARTIFACT_MAVEN_WAR_PLUGIN,
 75  
                                                  "warSourceDirectory", WEBAPP_RESOURCE_DIR ) );
 76  
         // getting real and correct path to the web source dir
 77  0
         String webContentDir =
 78  
             IdeUtils.toRelativeAndFixSeparator( config.getEclipseProjectDirectory(), warSourceDirectory, false );
 79  
 
 80  
         // getting the path to meta-inf base dir
 81  0
         String result = config.getProject().getBasedir().getAbsolutePath() + File.separatorChar + webContentDir;
 82  
 
 83  0
         return result;
 84  
     }
 85  
 
 86  
     /**
 87  
      * Search the project for the existing META-INF directory where the manifest should be located.
 88  
      * 
 89  
      * @return the absolute path to the META-INF directory
 90  
      * @throws MojoExecutionException
 91  
      */
 92  
     public String getMetaInfBaseDirectory( MavenProject project )
 93  
         throws MojoExecutionException
 94  
     {
 95  0
         String metaInfBaseDirectory = null;
 96  
 
 97  0
         if ( this.config.getProject().getPackaging().equals( Constants.PROJECT_PACKAGING_WAR ) )
 98  
         {
 99  
 
 100  
             // getting the path to meta-inf base dir
 101  0
             metaInfBaseDirectory = getWebContentBaseDirectory( this.config );
 102  
 
 103  0
             this.log.debug( "Attempting to use: " + metaInfBaseDirectory + " for location of META-INF in war project." );
 104  
 
 105  0
             File metaInfDirectoryFile =
 106  
                 new File( metaInfBaseDirectory + File.separatorChar + EclipseManifestWriter.META_INF_DIRECTORY );
 107  
 
 108  0
             if ( !metaInfDirectoryFile.exists()
 109  
                 || ( metaInfDirectoryFile.exists() && !metaInfDirectoryFile.isDirectory() ) )
 110  
             {
 111  0
                 metaInfBaseDirectory = null;
 112  
             }
 113  
         }
 114  
 
 115  0
         for ( int index = this.config.getSourceDirs().length - 1; metaInfBaseDirectory == null && index >= 0; index-- )
 116  
         {
 117  
 
 118  0
             File manifestFile =
 119  
                 new File( this.config.getEclipseProjectDirectory(), this.config.getSourceDirs()[index].getPath()
 120  
                     + File.separatorChar + EclipseManifestWriter.META_INF_DIRECTORY + File.separatorChar
 121  
                     + EclipseManifestWriter.MANIFEST_MF_FILENAME );
 122  
 
 123  0
             this.log.debug( "Checking for existence of META-INF/MANIFEST.MF file: " + manifestFile );
 124  
 
 125  0
             if ( manifestFile.exists() )
 126  
             {
 127  0
                 metaInfBaseDirectory = manifestFile.getParentFile().getParent();
 128  
             }
 129  
         }
 130  
 
 131  0
         return metaInfBaseDirectory;
 132  
     }
 133  
 
 134  
     /**
 135  
      * Write the manifest files use an existing one it it exists (it will be overwritten!! in a war use webapp/META-INF
 136  
      * else use the generated rad6 sourcefolder
 137  
      * 
 138  
      * @see AbstractWtpResourceWriter#write(EclipseSourceDir[], ArtifactRepository, File)
 139  
      * @param sourceDirs all eclipse source directorys
 140  
      * @param localRepository the local reposetory
 141  
      * @param buildOutputDirectory build output directory (target)
 142  
      * @throws MojoExecutionException when writing the config files was not possible
 143  
      */
 144  
     public void write()
 145  
         throws MojoExecutionException
 146  
     {
 147  0
         File manifestFile = null;
 148  0
         if ( config.getManifestFile() == null )
 149  
         {
 150  
 
 151  0
             String metaInfBaseDirectory = getMetaInfBaseDirectory( this.config.getProject() );
 152  
 
 153  0
             if ( metaInfBaseDirectory == null )
 154  
             {
 155  
                 // TODO: if this really is an error, shouldn't we stop the build??
 156  0
                 throw new MojoExecutionException(
 157  
                                                   Messages.getString(
 158  
                                                                       "EclipseCleanMojo.nofilefound",
 159  
                                                                       new Object[] { EclipseManifestWriter.META_INF_DIRECTORY } ) );
 160  
             }
 161  0
             manifestFile =
 162  
                 new File( metaInfBaseDirectory + File.separatorChar + EclipseManifestWriter.META_INF_DIRECTORY
 163  
                     + File.separatorChar + EclipseManifestWriter.MANIFEST_MF_FILENAME );
 164  
 
 165  0
         }
 166  
         else
 167  
         {
 168  0
             manifestFile = config.getManifestFile();
 169  
         }
 170  
 
 171  0
         Manifest manifest = createNewManifest();
 172  
 
 173  0
         log.info( "MANIFEST LOCATION: " + manifestFile );
 174  
 
 175  0
         if ( shouldNewManifestFileBeWritten( manifest, manifestFile ) )
 176  
         {
 177  0
             log.info( "Writing manifest..." );
 178  
 
 179  0
             manifestFile.getParentFile().mkdirs();
 180  
 
 181  
             try
 182  
             {
 183  0
                 FileOutputStream stream = new FileOutputStream( manifestFile );
 184  
 
 185  0
                 manifest.write( stream );
 186  
 
 187  0
                 stream.close();
 188  
 
 189  
             }
 190  0
             catch ( Exception e )
 191  
             {
 192  0
                 this.log.error( Messages.getString( "EclipsePlugin.cantwritetofile",
 193  
                                                     new Object[] { manifestFile.getAbsolutePath() } ) );
 194  0
             }
 195  
         }
 196  0
     }
 197  
 
 198  
     /**
 199  
      * make room for a Manifest file. use a generated resource for JARS and for WARS use the manifest in the
 200  
      * webapp/META-INF directory.
 201  
      * 
 202  
      * @throws MojoExecutionException
 203  
      */
 204  
     public static void addManifestResource( Log log, EclipseWriterConfig config )
 205  
         throws MojoExecutionException
 206  
     {
 207  
 
 208  0
         EclipseManifestWriter manifestWriter = new EclipseManifestWriter();
 209  0
         manifestWriter.init( log, config );
 210  
 
 211  0
         String packaging = config.getProject().getPackaging();
 212  
 
 213  0
         String manifestDirectory = manifestWriter.getMetaInfBaseDirectory( config.getProject() );
 214  
 
 215  0
         if ( !Constants.PROJECT_PACKAGING_EAR.equals( packaging )
 216  
             && !Constants.PROJECT_PACKAGING_WAR.equals( packaging ) && manifestDirectory == null )
 217  
         {
 218  
 
 219  0
             String generatedResourceDir =
 220  
                 config.getProject().getBasedir().getAbsolutePath() + File.separatorChar
 221  
                     + EclipseManifestWriter.GENERATED_RESOURCE_DIRNAME;
 222  
 
 223  0
             manifestDirectory = generatedResourceDir + File.separatorChar + "META-INF";
 224  
 
 225  
             try
 226  
             {
 227  0
                 new File( manifestDirectory ).mkdirs();
 228  0
                 File manifestFile = new File( manifestDirectory + File.separatorChar + "MANIFEST.MF" );
 229  0
                 if ( manifestFile.exists() )
 230  
                 {
 231  0
                     manifestFile.delete();
 232  
                 }
 233  0
                 manifestFile.createNewFile();
 234  
             }
 235  0
             catch ( IOException e )
 236  
             {
 237  0
                 log.error( Messages.getString( "EclipsePlugin.cantwritetofile", new Object[] { manifestDirectory
 238  
                     + File.separatorChar + "META-INF" + File.separatorChar + "MANIFEST.MF" } ) );
 239  0
             }
 240  
 
 241  0
             log.debug( "Adding " + EclipseManifestWriter.GENERATED_RESOURCE_DIRNAME + " to eclipse sources " );
 242  
 
 243  0
             EclipseSourceDir[] sourceDirs = config.getSourceDirs();
 244  0
             EclipseSourceDir[] newSourceDirs = new EclipseSourceDir[sourceDirs.length + 1];
 245  0
             System.arraycopy( sourceDirs, 0, newSourceDirs, 0, sourceDirs.length );
 246  0
             newSourceDirs[sourceDirs.length] =
 247  
                 new EclipseSourceDir( EclipseManifestWriter.GENERATED_RESOURCE_DIRNAME, null, true, false, null, null,
 248  
                                       false );
 249  0
             config.setSourceDirs( newSourceDirs );
 250  
         }
 251  
 
 252  0
         if ( Constants.PROJECT_PACKAGING_WAR.equals( packaging ) )
 253  
         {
 254  0
             new File( getWebContentBaseDirectory( config ) + File.separatorChar + "META-INF" ).mkdirs();
 255  
         }
 256  
 
 257  
         // special case must be done first because it can add stuff to the
 258  
         // classpath that will be
 259  
         // written by the superclass
 260  0
         manifestWriter.write();
 261  0
     }
 262  
 
 263  
     /**
 264  
      * Add one dependency to the black separated classpath stringbuffer. When the project is available in the reactor
 265  
      * (current build) then the project is used else the jar representing the artifact. System dependencies will only be
 266  
      * included if they are in this project.
 267  
      * 
 268  
      * @param classpath existing classpath to append
 269  
      * @param dependency dependency to append as jar or as project
 270  
      */
 271  
     private void addDependencyToClassPath( StringBuffer classpath, IdeDependency dependency )
 272  
     {
 273  0
         if ( !dependency.isTestDependency() && !dependency.isProvided()
 274  
             && !dependency.isSystemScopedOutsideProject( this.config.getProject() ) )
 275  
         {
 276  
 
 277  
             // blank is the separator in manifest classpath's
 278  0
             if ( classpath.length() != 0 )
 279  
             {
 280  0
                 classpath.append( ' ' );
 281  
             }
 282  
             // if the dependency is a workspace project add the project and not
 283  
             // the jar
 284  0
             if ( !dependency.isReferencedProject() )
 285  
             {
 286  0
                 classpath.append( dependency.getFile().getName() );
 287  
             }
 288  
             else
 289  
             {
 290  0
                 classpath.append( dependency.getEclipseProjectName() + ".jar" );
 291  
             }
 292  
         }
 293  0
     }
 294  
 
 295  
     /**
 296  
      * Check if the two manifests are equal. Manifest.equal can not be used because of the special case the Classpath
 297  
      * entr, witch must be comaired sorted so that a different oder in the classpath does not result in "not equal".
 298  
      * This not not realy correct but in this case it is more important to reduce the number of version-controll files.
 299  
      * 
 300  
      * @param manifest the new manifest
 301  
      * @param existingManifest to compaire the new one with
 302  
      * @return are the manifests equal
 303  
      */
 304  
     private boolean areManifestsEqual( Manifest manifest, Manifest existingManifest )
 305  
     {
 306  0
         if ( existingManifest == null )
 307  
         {
 308  0
             return false;
 309  
         }
 310  
 
 311  0
         Set keys = new HashSet();
 312  0
         Attributes existingMap = existingManifest.getMainAttributes();
 313  0
         Attributes newMap = manifest.getMainAttributes();
 314  0
         keys.addAll( existingMap.keySet() );
 315  0
         keys.addAll( newMap.keySet() );
 316  0
         Iterator iterator = keys.iterator();
 317  0
         while ( iterator.hasNext() )
 318  
         {
 319  0
             Attributes.Name key = (Attributes.Name) iterator.next();
 320  0
             String newValue = (String) newMap.get( key );
 321  0
             String existingValue = (String) existingMap.get( key );
 322  
             // special case classpath... they are qual when there entries
 323  
             // are equal
 324  0
             if ( Attributes.Name.CLASS_PATH.equals( key ) )
 325  
             {
 326  0
                 newValue = orderClasspath( newValue );
 327  0
                 existingValue = orderClasspath( existingValue );
 328  
             }
 329  0
             if ( ( newValue == null || !newValue.equals( existingValue ) )
 330  
                 && ( existingValue == null || !existingValue.equals( newValue ) ) )
 331  
             {
 332  0
                 return false;
 333  
             }
 334  0
         }
 335  0
         return true;
 336  
     }
 337  
 
 338  
     /**
 339  
      * Convert all dependencies in a blank seperated list of jars and projects representing the classpath.
 340  
      * 
 341  
      * @return the blank separeted classpath string
 342  
      */
 343  
     private String constructManifestClasspath()
 344  
     {
 345  0
         StringBuffer stringBuffer = new StringBuffer();
 346  0
         IdeDependency[] deps = this.config.getDepsOrdered();
 347  
 
 348  0
         for ( int index = 0; index < deps.length; index++ )
 349  
         {
 350  0
             addDependencyToClassPath( stringBuffer, deps[index] );
 351  
         }
 352  
 
 353  0
         return stringBuffer.toString();
 354  
     }
 355  
 
 356  
     /**
 357  
      * Create a manifest contaigning the required classpath.
 358  
      * 
 359  
      * @return the newly created manifest
 360  
      */
 361  
     private Manifest createNewManifest()
 362  
     {
 363  0
         Manifest manifest = new Manifest();
 364  0
         manifest.getMainAttributes().put( Attributes.Name.MANIFEST_VERSION, "1.0" );
 365  0
         manifest.getMainAttributes().put( Attributes.Name.CLASS_PATH, constructManifestClasspath() );
 366  0
         return manifest;
 367  
     }
 368  
 
 369  
     /**
 370  
      * Aphabeticaly sort the classpath. Do this by splitting it up, sort the entries and gleue them together again.
 371  
      * 
 372  
      * @param newValue classpath to sort
 373  
      * @return the sorted classpath
 374  
      */
 375  
     private String orderClasspath( String newValue )
 376  
     {
 377  0
         if ( newValue == null )
 378  
         {
 379  0
             return null;
 380  
         }
 381  0
         String[] entries = newValue.split( " " );
 382  0
         Arrays.sort( entries );
 383  0
         StringBuffer buffer = new StringBuffer( newValue.length() );
 384  0
         for ( int index = 0; index < entries.length; index++ )
 385  
         {
 386  0
             buffer.append( entries[index] );
 387  0
             buffer.append( ' ' );
 388  
         }
 389  0
         return buffer.toString();
 390  
     }
 391  
 
 392  
     /**
 393  
      * Read and parse the existing manifest file.
 394  
      * 
 395  
      * @param manifestFile file
 396  
      * @return the read manifest
 397  
      * @throws IOException if the file could not be read
 398  
      */
 399  
     private Manifest readExistingManifest( File manifestFile )
 400  
         throws IOException
 401  
     {
 402  0
         if ( !manifestFile.exists() )
 403  
         {
 404  0
             return null;
 405  
         }
 406  
 
 407  0
         Manifest existingManifest = new Manifest();
 408  0
         FileInputStream inputStream = new FileInputStream( manifestFile );
 409  0
         existingManifest.read( inputStream );
 410  0
         inputStream.close();
 411  0
         return existingManifest;
 412  
     }
 413  
 
 414  
     /**
 415  
      * Verify is the manifest sould be overwritten this sould take in account that the manifest should only be written
 416  
      * if the contents of the classpath was changed not the order. The classpath sorting oder should be ignored.
 417  
      * 
 418  
      * @param manifest the newly created classpath
 419  
      * @param manifestFile the file where the manifest
 420  
      * @return if the new manifest file must be written
 421  
      * @throws MojoExecutionException
 422  
      */
 423  
     private boolean shouldNewManifestFileBeWritten( Manifest manifest, File manifestFile )
 424  
         throws MojoExecutionException
 425  
     {
 426  
         try
 427  
         {
 428  0
             Manifest existingManifest = readExistingManifest( manifestFile );
 429  0
             if ( areManifestsEqual( manifest, existingManifest ) )
 430  
             {
 431  0
                 this.log.info( Messages.getString( "EclipseCleanMojo.unchanged", manifestFile.getAbsolutePath() ) );
 432  0
                 return false;
 433  
             }
 434  
         }
 435  0
         catch ( Exception e )
 436  
         {
 437  0
             throw new MojoExecutionException( Messages.getString( "EclipseCleanMojo.nofilefound",
 438  
                                                                   manifestFile.getAbsolutePath() ), e );
 439  0
         }
 440  0
         return true;
 441  
     }
 442  
 }