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