Coverage Report - org.apache.maven.plugin.eclipse.writers.AbstractEclipseManifestWriter
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractEclipseManifestWriter
0%
0/80
0%
0/38
3,3
 
 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;
 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.plugin.MojoExecutionException;
 34  
 import org.apache.maven.plugin.eclipse.EclipseSourceDir;
 35  
 import org.apache.maven.plugin.eclipse.Messages;
 36  
 import org.apache.maven.plugin.eclipse.writers.wtp.AbstractWtpResourceWriter;
 37  
 import org.apache.maven.plugin.ide.IdeDependency;
 38  
 import org.apache.maven.project.MavenProject;
 39  
 import org.codehaus.plexus.util.IOUtil;
 40  
 
 41  
 /**
 42  
  * Common behaviours for creating or adapting the manifest files for eclipse environments.
 43  
  * 
 44  
  * @author <a href="mailto:nir@cfc.at">Richard van Nieuwenhoven </a>
 45  
  */
 46  
 public abstract class AbstractEclipseManifestWriter
 47  
     extends AbstractEclipseWriter
 48  
 {
 49  
 
 50  
     protected static final String MANIFEST_MF_FILENAME = "MANIFEST.MF";
 51  
 
 52  
     protected static final String META_INF_DIRECTORY = "META-INF";
 53  
 
 54  
     public AbstractEclipseManifestWriter()
 55  
     {
 56  0
         super();
 57  0
     }
 58  
 
 59  
     /**
 60  
      * Aphabeticaly sort the classpath. Do this by splitting it up, sort the entries and gleue them together again.
 61  
      * 
 62  
      * @param newValue classpath to sort
 63  
      * @return the sorted classpath
 64  
      */
 65  
     protected String orderClasspath( String newValue )
 66  
     {
 67  0
         if ( newValue == null )
 68  
         {
 69  0
             return null;
 70  
         }
 71  0
         String[] entries = newValue.split( " " );
 72  0
         Arrays.sort( entries );
 73  0
         StringBuffer buffer = new StringBuffer( newValue.length() );
 74  0
         for ( int index = 0; index < entries.length; index++ )
 75  
         {
 76  0
             buffer.append( entries[index] );
 77  0
             buffer.append( ' ' );
 78  
         }
 79  0
         return buffer.toString();
 80  
     }
 81  
 
 82  
     /**
 83  
      * Read and parse the existing manifest file.
 84  
      * 
 85  
      * @param manifestFile file
 86  
      * @return the read manifest
 87  
      * @throws IOException if the file could not be read
 88  
      */
 89  
     protected Manifest readExistingManifest( File manifestFile )
 90  
         throws IOException
 91  
     {
 92  0
         if ( !manifestFile.exists() )
 93  
         {
 94  0
             return null;
 95  
         }
 96  
 
 97  0
         Manifest existingManifest = new Manifest();
 98  0
         FileInputStream inputStream = new FileInputStream( manifestFile );
 99  
         try
 100  
         {
 101  0
             existingManifest.read( inputStream );
 102  
         }
 103  
         finally
 104  
         {
 105  0
             IOUtil.close( inputStream );
 106  0
         }
 107  0
         return existingManifest;
 108  
     }
 109  
 
 110  
     /**
 111  
      * Add one dependency to the blank separated classpath stringbuffer. When the project is available in the reactor
 112  
      * (current build) then the project is used else the jar representing the artifact. System dependencies will only be
 113  
      * included if they are in this project.
 114  
      * 
 115  
      * @param classpath existing classpath to append
 116  
      * @param dependency dependency to append as jar or as project
 117  
      */
 118  
     protected void addDependencyToClassPath( StringBuffer classpath, IdeDependency dependency )
 119  
     {
 120  0
         if ( !dependency.isTestDependency() && !dependency.isProvided()
 121  
             && !dependency.isSystemScopedOutsideProject( this.config.getProject() ) )
 122  
         {
 123  
 
 124  
             // blank is the separator in manifest classpath's
 125  0
             if ( classpath.length() != 0 )
 126  
             {
 127  0
                 classpath.append( ' ' );
 128  
             }
 129  
             // if the dependency is a workspace project add the project and not
 130  
             // the jar
 131  0
             if ( !dependency.isReferencedProject() )
 132  
             {
 133  0
                 classpath.append( dependency.getFile().getName() );
 134  
             }
 135  
             else
 136  
             {
 137  0
                 classpath.append( dependency.getEclipseProjectName() + ".jar" );
 138  
             }
 139  
         }
 140  0
     }
 141  
 
 142  
     /**
 143  
      * Check if the two manifests are equal. Manifest.equal can not be used because of the special case the Classpath
 144  
      * entr, witch must be comaired sorted so that a different oder in the classpath does not result in "not equal".
 145  
      * This not not realy correct but in this case it is more important to reduce the number of version-controll files.
 146  
      * 
 147  
      * @param manifest the new manifest
 148  
      * @param existingManifest to compaire the new one with
 149  
      * @return are the manifests equal
 150  
      */
 151  
     protected boolean areManifestsEqual( Manifest manifest, Manifest existingManifest )
 152  
     {
 153  0
         if ( existingManifest == null )
 154  
         {
 155  0
             log.info( "@@@ FALSE - Manifest are not equal because existingManifest is null" );
 156  0
             return false;
 157  
         }
 158  
 
 159  0
         Set keys = new HashSet();
 160  0
         Attributes existingMap = existingManifest.getMainAttributes();
 161  0
         Attributes newMap = manifest.getMainAttributes();
 162  0
         keys.addAll( existingMap.keySet() );
 163  0
         keys.addAll( newMap.keySet() );
 164  0
         Iterator iterator = keys.iterator();
 165  0
         while ( iterator.hasNext() )
 166  
         {
 167  0
             Attributes.Name key = (Attributes.Name) iterator.next();
 168  0
             String newValue = (String) newMap.get( key );
 169  0
             String existingValue = (String) existingMap.get( key );
 170  
             // special case classpath... they are equal when there entries
 171  
             // are equal
 172  0
             if ( Attributes.Name.CLASS_PATH.equals( key ) )
 173  
             {
 174  0
                 newValue = orderClasspath( newValue );
 175  0
                 existingValue = orderClasspath( existingValue );
 176  
             }
 177  0
             if ( ( newValue == null || !newValue.equals( existingValue ) )
 178  
                 && ( existingValue == null || !existingValue.equals( newValue ) ) )
 179  
             {
 180  0
                 log.info( "@@@ FALSE - Manifest are not equal because key = " + key + " has existing value = "
 181  
                     + existingValue + " and new value = " + newValue + " are different" );
 182  0
                 return false;
 183  
             }
 184  0
         }
 185  0
         log.info( "@@@ TRUE - Manifests are equal" );
 186  0
         return true;
 187  
     }
 188  
 
 189  
     /**
 190  
      * Convert all dependencies in a blank seperated list of jars and projects representing the classpath.
 191  
      * 
 192  
      * @return the blank separeted classpath string
 193  
      */
 194  
     protected String constructManifestClasspath()
 195  
     {
 196  0
         StringBuffer stringBuffer = new StringBuffer();
 197  0
         IdeDependency[] deps = this.config.getDepsOrdered();
 198  
 
 199  0
         for ( int index = 0; index < deps.length; index++ )
 200  
         {
 201  0
             addDependencyToClassPath( stringBuffer, deps[index] );
 202  
         }
 203  
 
 204  0
         return stringBuffer.toString();
 205  
     }
 206  
 
 207  
     /**
 208  
      * Create a manifest contaigning the required classpath.
 209  
      * 
 210  
      * @return the newly created manifest
 211  
      */
 212  
     protected Manifest createNewManifest()
 213  
     {
 214  0
         Manifest manifest = new Manifest();
 215  0
         manifest.getMainAttributes().put( Attributes.Name.MANIFEST_VERSION, "1.0" );
 216  0
         manifest.getMainAttributes().put( Attributes.Name.CLASS_PATH, constructManifestClasspath() );
 217  0
         return manifest;
 218  
     }
 219  
 
 220  
     /**
 221  
      * Verify is the manifest sould be overwritten this sould take in account that the manifest should only be written
 222  
      * if the contents of the classpath was changed not the order. The classpath sorting oder should be ignored.
 223  
      * 
 224  
      * @param manifest the newly created classpath
 225  
      * @param manifestFile the file where the manifest
 226  
      * @return if the new manifest file must be written
 227  
      * @throws MojoExecutionException
 228  
      */
 229  
     protected boolean shouldNewManifestFileBeWritten( Manifest manifest, File manifestFile )
 230  
         throws MojoExecutionException
 231  
     {
 232  
         try
 233  
         {
 234  0
             Manifest existingManifest = readExistingManifest( manifestFile );
 235  0
             if ( areManifestsEqual( manifest, existingManifest ) )
 236  
             {
 237  0
                 this.log.info( Messages.getString( "EclipsePlugin.unchangedmanifest", manifestFile.getAbsolutePath() ) );
 238  0
                 return false;
 239  
             }
 240  
         }
 241  0
         catch ( Exception e )
 242  
         {
 243  0
             throw new MojoExecutionException( Messages.getString( "EclipseCleanMojo.nofilefound",
 244  
                                                                   manifestFile.getAbsolutePath() ), e );
 245  0
         }
 246  0
         return true;
 247  
     }
 248  
 
 249  
     /**
 250  
      * Search the project for the existing META-INF directory where the manifest should be located.
 251  
      * 
 252  
      * @return the absolute path to the META-INF directory
 253  
      * @throws MojoExecutionException
 254  
      */
 255  
     protected abstract String getMetaInfBaseDirectory( MavenProject project )
 256  
         throws MojoExecutionException;
 257  
 
 258  
     /**
 259  
      * If the existing manifest file located in <code>getMetaInfBaseDirectory()</code> already has a correct
 260  
      * MANIFEST_VERSION and CLASS_PATH value then do nothing.
 261  
      * <p>
 262  
      * Otherwise generate a <b>NEW</b> (i.e the old one is overwritten) which only contains values for MANIFEST_VERSION
 263  
      * and CLASS_PATH, all other previous entries are not kept.
 264  
      * 
 265  
      * @see AbstractWtpResourceWriter#write(EclipseSourceDir[], ArtifactRepository, File)
 266  
      * @param sourceDirs all eclipse source directorys
 267  
      * @param localRepository the local reposetory
 268  
      * @param buildOutputDirectory build output directory (target)
 269  
      * @throws MojoExecutionException when writing the config files was not possible
 270  
      */
 271  
     public void write()
 272  
         throws MojoExecutionException
 273  
     {
 274  0
         String metaInfBaseDirectory = getMetaInfBaseDirectory( this.config.getProject() );
 275  
 
 276  0
         if ( metaInfBaseDirectory == null )
 277  
         {
 278  
             // TODO: if this really is an error, shouldn't we stop the build??
 279  0
             throw new MojoExecutionException(
 280  
                                               Messages.getString(
 281  
                                                                   "EclipseCleanMojo.nofilefound",
 282  
                                                                   new Object[] { EclipseManifestWriter.META_INF_DIRECTORY } ) );
 283  
         }
 284  0
         File manifestFile =
 285  
             new File( metaInfBaseDirectory + File.separatorChar + EclipseManifestWriter.META_INF_DIRECTORY
 286  
                 + File.separatorChar + EclipseManifestWriter.MANIFEST_MF_FILENAME );
 287  0
         Manifest manifest = createNewManifest();
 288  
 
 289  0
         if ( shouldNewManifestFileBeWritten( manifest, manifestFile ) )
 290  
         {
 291  0
             log.info( "Writing manifest..." );
 292  
 
 293  0
             manifestFile.getParentFile().mkdirs();
 294  
 
 295  
             try
 296  
             {
 297  0
                 FileOutputStream stream = new FileOutputStream( manifestFile );
 298  
 
 299  0
                 manifest.write( stream );
 300  
 
 301  0
                 stream.close();
 302  
 
 303  
             }
 304  0
             catch ( Exception e )
 305  
             {
 306  0
                 this.log.error( Messages.getString( "EclipsePlugin.cantwritetofile",
 307  
                                                     new Object[] { manifestFile.getAbsolutePath() } ) );
 308  0
             }
 309  
         }
 310  0
     }
 311  
 
 312  
 }