Coverage Report - org.apache.maven.plugin.eclipse.writers.EclipseManifestWriter
 
Classes in this File Line Coverage Branch Coverage Complexity
EclipseManifestWriter
0%
0/121
0%
0/62
3.818
 
 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
         String metaInfBaseDirectory = getMetaInfBaseDirectory( this.config.getProject() );
 149  
 
 150  0
         if ( metaInfBaseDirectory == null )
 151  
         {
 152  
             // TODO: if this really is an error, shouldn't we stop the build??
 153  0
             throw new MojoExecutionException(
 154  
                                               Messages.getString(
 155  
                                                                   "EclipseCleanMojo.nofilefound",
 156  
                                                                   new Object[] { EclipseManifestWriter.META_INF_DIRECTORY } ) );
 157  
         }
 158  0
         manifestFile =
 159  
             new File( metaInfBaseDirectory + File.separatorChar + EclipseManifestWriter.META_INF_DIRECTORY
 160  
                 + File.separatorChar + EclipseManifestWriter.MANIFEST_MF_FILENAME );
 161  0
         Manifest manifest = createNewManifest();
 162  
 
 163  0
         log.info( "MANIFEST LOCATION: " + manifestFile );
 164  
 
 165  0
         if ( shouldNewManifestFileBeWritten( manifest, manifestFile ) )
 166  
         {
 167  0
             log.info( "Writing manifest..." );
 168  
 
 169  0
             manifestFile.getParentFile().mkdirs();
 170  
 
 171  
             try
 172  
             {
 173  0
                 FileOutputStream stream = new FileOutputStream( manifestFile );
 174  
 
 175  0
                 manifest.write( stream );
 176  
 
 177  0
                 stream.close();
 178  
 
 179  
             }
 180  0
             catch ( Exception e )
 181  
             {
 182  0
                 this.log.error( Messages.getString( "EclipsePlugin.cantwritetofile",
 183  
                                                     new Object[] { manifestFile.getAbsolutePath() } ) );
 184  0
             }
 185  
         }
 186  0
     }
 187  
 
 188  
     /**
 189  
      * make room for a Manifest file. use a generated resource for JARS and for WARS use the manifest in the
 190  
      * webapp/META-INF directory.
 191  
      * 
 192  
      * @throws MojoExecutionException
 193  
      */
 194  
     public static void addManifestResource( Log log, EclipseWriterConfig config )
 195  
         throws MojoExecutionException
 196  
     {
 197  
 
 198  0
         EclipseManifestWriter manifestWriter = new EclipseManifestWriter();
 199  0
         manifestWriter.init( log, config );
 200  
 
 201  0
         String packaging = config.getProject().getPackaging();
 202  
 
 203  0
         String manifestDirectory = manifestWriter.getMetaInfBaseDirectory( config.getProject() );
 204  
 
 205  0
         if ( !Constants.PROJECT_PACKAGING_EAR.equals( packaging )
 206  
             && !Constants.PROJECT_PACKAGING_WAR.equals( packaging ) && manifestDirectory == null )
 207  
         {
 208  
 
 209  0
             String generatedResourceDir =
 210  
                 config.getProject().getBasedir().getAbsolutePath() + File.separatorChar
 211  
                     + EclipseManifestWriter.GENERATED_RESOURCE_DIRNAME;
 212  
 
 213  0
             manifestDirectory = generatedResourceDir + File.separatorChar + "META-INF";
 214  
 
 215  
             try
 216  
             {
 217  0
                 new File( manifestDirectory ).mkdirs();
 218  0
                 File manifestFile = new File( manifestDirectory + File.separatorChar + "MANIFEST.MF" );
 219  0
                 if ( manifestFile.exists() )
 220  
                 {
 221  0
                     manifestFile.delete();
 222  
                 }
 223  0
                 manifestFile.createNewFile();
 224  
             }
 225  0
             catch ( IOException e )
 226  
             {
 227  0
                 log.error( Messages.getString( "EclipsePlugin.cantwritetofile", new Object[] { manifestDirectory
 228  
                     + File.separatorChar + "META-INF" + File.separatorChar + "MANIFEST.MF" } ) );
 229  0
             }
 230  
 
 231  0
             log.debug( "Adding " + EclipseManifestWriter.GENERATED_RESOURCE_DIRNAME + " to eclipse sources " );
 232  
 
 233  0
             EclipseSourceDir[] sourceDirs = config.getSourceDirs();
 234  0
             EclipseSourceDir[] newSourceDirs = new EclipseSourceDir[sourceDirs.length + 1];
 235  0
             System.arraycopy( sourceDirs, 0, newSourceDirs, 0, sourceDirs.length );
 236  0
             newSourceDirs[sourceDirs.length] =
 237  
                 new EclipseSourceDir( EclipseManifestWriter.GENERATED_RESOURCE_DIRNAME, null, true, false, null, null,
 238  
                                       false );
 239  0
             config.setSourceDirs( newSourceDirs );
 240  
         }
 241  
 
 242  0
         if ( Constants.PROJECT_PACKAGING_WAR.equals( packaging ) )
 243  
         {
 244  0
             new File( getWebContentBaseDirectory( config ) + File.separatorChar + "META-INF" ).mkdirs();
 245  
         }
 246  
 
 247  
         // special case must be done first because it can add stuff to the
 248  
         // classpath that will be
 249  
         // written by the superclass
 250  0
         manifestWriter.write();
 251  0
     }
 252  
 
 253  
     /**
 254  
      * Add one dependency to the black separated classpath stringbuffer. When the project is available in the reactor
 255  
      * (current build) then the project is used else the jar representing the artifact. System dependencies will only be
 256  
      * included if they are in this project.
 257  
      * 
 258  
      * @param classpath existing classpath to append
 259  
      * @param dependency dependency to append as jar or as project
 260  
      */
 261  
     private void addDependencyToClassPath( StringBuffer classpath, IdeDependency dependency )
 262  
     {
 263  0
         if ( !dependency.isTestDependency() && !dependency.isProvided()
 264  
             && !dependency.isSystemScopedOutsideProject( this.config.getProject() ) )
 265  
         {
 266  
 
 267  
             // blank is the separator in manifest classpath's
 268  0
             if ( classpath.length() != 0 )
 269  
             {
 270  0
                 classpath.append( ' ' );
 271  
             }
 272  
             // if the dependency is a workspace project add the project and not
 273  
             // the jar
 274  0
             if ( !dependency.isReferencedProject() )
 275  
             {
 276  0
                 classpath.append( dependency.getFile().getName() );
 277  
             }
 278  
             else
 279  
             {
 280  0
                 classpath.append( dependency.getEclipseProjectName() + ".jar" );
 281  
             }
 282  
         }
 283  0
     }
 284  
 
 285  
     /**
 286  
      * Check if the two manifests are equal. Manifest.equal can not be used because of the special case the Classpath
 287  
      * entr, witch must be comaired sorted so that a different oder in the classpath does not result in "not equal".
 288  
      * This not not realy correct but in this case it is more important to reduce the number of version-controll files.
 289  
      * 
 290  
      * @param manifest the new manifest
 291  
      * @param existingManifest to compaire the new one with
 292  
      * @return are the manifests equal
 293  
      */
 294  
     private boolean areManifestsEqual( Manifest manifest, Manifest existingManifest )
 295  
     {
 296  0
         if ( existingManifest == null )
 297  
         {
 298  0
             return false;
 299  
         }
 300  
 
 301  0
         Set keys = new HashSet();
 302  0
         Attributes existingMap = existingManifest.getMainAttributes();
 303  0
         Attributes newMap = manifest.getMainAttributes();
 304  0
         keys.addAll( existingMap.keySet() );
 305  0
         keys.addAll( newMap.keySet() );
 306  0
         Iterator iterator = keys.iterator();
 307  0
         while ( iterator.hasNext() )
 308  
         {
 309  0
             Attributes.Name key = (Attributes.Name) iterator.next();
 310  0
             String newValue = (String) newMap.get( key );
 311  0
             String existingValue = (String) existingMap.get( key );
 312  
             // special case classpath... they are qual when there entries
 313  
             // are equal
 314  0
             if ( Attributes.Name.CLASS_PATH.equals( key ) )
 315  
             {
 316  0
                 newValue = orderClasspath( newValue );
 317  0
                 existingValue = orderClasspath( existingValue );
 318  
             }
 319  0
             if ( ( newValue == null || !newValue.equals( existingValue ) )
 320  
                 && ( existingValue == null || !existingValue.equals( newValue ) ) )
 321  
             {
 322  0
                 return false;
 323  
             }
 324  0
         }
 325  0
         return true;
 326  
     }
 327  
 
 328  
     /**
 329  
      * Convert all dependencies in a blank seperated list of jars and projects representing the classpath.
 330  
      * 
 331  
      * @return the blank separeted classpath string
 332  
      */
 333  
     private String constructManifestClasspath()
 334  
     {
 335  0
         StringBuffer stringBuffer = new StringBuffer();
 336  0
         IdeDependency[] deps = this.config.getDepsOrdered();
 337  
 
 338  0
         for ( int index = 0; index < deps.length; index++ )
 339  
         {
 340  0
             addDependencyToClassPath( stringBuffer, deps[index] );
 341  
         }
 342  
 
 343  0
         return stringBuffer.toString();
 344  
     }
 345  
 
 346  
     /**
 347  
      * Create a manifest contaigning the required classpath.
 348  
      * 
 349  
      * @return the newly created manifest
 350  
      */
 351  
     private Manifest createNewManifest()
 352  
     {
 353  0
         Manifest manifest = new Manifest();
 354  0
         manifest.getMainAttributes().put( Attributes.Name.MANIFEST_VERSION, "1.0" );
 355  0
         manifest.getMainAttributes().put( Attributes.Name.CLASS_PATH, constructManifestClasspath() );
 356  0
         return manifest;
 357  
     }
 358  
 
 359  
     /**
 360  
      * Aphabeticaly sort the classpath. Do this by splitting it up, sort the entries and gleue them together again.
 361  
      * 
 362  
      * @param newValue classpath to sort
 363  
      * @return the sorted classpath
 364  
      */
 365  
     private String orderClasspath( String newValue )
 366  
     {
 367  0
         if ( newValue == null )
 368  
         {
 369  0
             return null;
 370  
         }
 371  0
         String[] entries = newValue.split( " " );
 372  0
         Arrays.sort( entries );
 373  0
         StringBuffer buffer = new StringBuffer( newValue.length() );
 374  0
         for ( int index = 0; index < entries.length; index++ )
 375  
         {
 376  0
             buffer.append( entries[index] );
 377  0
             buffer.append( ' ' );
 378  
         }
 379  0
         return buffer.toString();
 380  
     }
 381  
 
 382  
     /**
 383  
      * Read and parse the existing manifest file.
 384  
      * 
 385  
      * @param manifestFile file
 386  
      * @return the read manifest
 387  
      * @throws IOException if the file could not be read
 388  
      */
 389  
     private Manifest readExistingManifest( File manifestFile )
 390  
         throws IOException
 391  
     {
 392  0
         if ( !manifestFile.exists() )
 393  
         {
 394  0
             return null;
 395  
         }
 396  
 
 397  0
         Manifest existingManifest = new Manifest();
 398  0
         FileInputStream inputStream = new FileInputStream( manifestFile );
 399  0
         existingManifest.read( inputStream );
 400  0
         inputStream.close();
 401  0
         return existingManifest;
 402  
     }
 403  
 
 404  
     /**
 405  
      * Verify is the manifest sould be overwritten this sould take in account that the manifest should only be written
 406  
      * if the contents of the classpath was changed not the order. The classpath sorting oder should be ignored.
 407  
      * 
 408  
      * @param manifest the newly created classpath
 409  
      * @param manifestFile the file where the manifest
 410  
      * @return if the new manifest file must be written
 411  
      * @throws MojoExecutionException
 412  
      */
 413  
     private boolean shouldNewManifestFileBeWritten( Manifest manifest, File manifestFile )
 414  
         throws MojoExecutionException
 415  
     {
 416  
         try
 417  
         {
 418  0
             Manifest existingManifest = readExistingManifest( manifestFile );
 419  0
             if ( areManifestsEqual( manifest, existingManifest ) )
 420  
             {
 421  0
                 this.log.info( Messages.getString( "EclipseCleanMojo.unchanged", manifestFile.getAbsolutePath() ) );
 422  0
                 return false;
 423  
             }
 424  
         }
 425  0
         catch ( Exception e )
 426  
         {
 427  0
             throw new MojoExecutionException( Messages.getString( "EclipseCleanMojo.nofilefound",
 428  
                                                                   manifestFile.getAbsolutePath() ), e );
 429  0
         }
 430  0
         return true;
 431  
     }
 432  
 }