Author: jdcasey Date: Mon Sep 22 12:16:33 2008 New Revision: 697952 URL: http://svn.apache.org/viewvc?rev=697952&view=rev Log: [MSHARED-32] Introduce custom classpath layout; see src/site/examples/classpath.apt for more information.
Modified: maven/shared/trunk/maven-archiver/pom.xml maven/shared/trunk/maven-archiver/src/main/java/org/apache/maven/archiver/ManifestConfiguration.java maven/shared/trunk/maven-archiver/src/main/java/org/apache/maven/archiver/MavenArchiver.java maven/shared/trunk/maven-archiver/src/site/apt/examples/classpath.apt maven/shared/trunk/maven-archiver/src/test/java/org/apache/maven/archiver/MavenArchiverTest.java Modified: maven/shared/trunk/maven-archiver/pom.xml URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-archiver/pom.xml?rev=697952&r1=697951&r2=697952&view=diff ============================================================================== --- maven/shared/trunk/maven-archiver/pom.xml (original) +++ maven/shared/trunk/maven-archiver/pom.xml Mon Sep 22 12:16:33 2008 @@ -88,6 +88,11 @@ <version>3.8.2</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-interpolation</artifactId> + <version>1.5</version> + </dependency> </dependencies> <reporting> <plugins> Modified: maven/shared/trunk/maven-archiver/src/main/java/org/apache/maven/archiver/ManifestConfiguration.java URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-archiver/src/main/java/org/apache/maven/archiver/ManifestConfiguration.java?rev=697952&r1=697951&r2=697952&view=diff ============================================================================== --- maven/shared/trunk/maven-archiver/src/main/java/org/apache/maven/archiver/ManifestConfiguration.java (original) +++ maven/shared/trunk/maven-archiver/src/main/java/org/apache/maven/archiver/ManifestConfiguration.java Mon Sep 22 12:16:33 2008 @@ -1,5 +1,8 @@ package org.apache.maven.archiver; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -28,6 +31,12 @@ */ public class ManifestConfiguration { + public static final String CLASSPATH_LAYOUT_TYPE_SIMPLE = "simple"; + + public static final String CLASSPATH_LAYOUT_TYPE_REPOSITORY = "repository"; + + public static final String CLASSPATH_LAYOUT_TYPE_CUSTOM = "custom"; + private String mainClass; private String packageName; @@ -60,8 +69,13 @@ * Maven 2 repository layout: * $groupId[0]/../${groupId[n]/$artifactId/$version/{fileName} * @since 2.3 + * @deprecated Use [EMAIL PROTECTED] ManifestConfiguration#classpathLayoutType} instead. */ private boolean classpathMavenRepositoryLayout = false; + + private String classpathLayoutType = CLASSPATH_LAYOUT_TYPE_SIMPLE; + + private String customClasspathLayout; public String getMainClass() { @@ -93,6 +107,10 @@ return addExtensions; } + /** + * @deprecated Use [EMAIL PROTECTED] ManifestConfiguration#getClasspathLayoutType()}, and compare to + * CLASSPATH_LAYOUT_TYPE_SIMPLE or CLASSPATH_LAYOUT_TYPE_REPOSITORY, also declared in [EMAIL PROTECTED] ManifestConfiguration}. + */ public boolean isClasspathMavenRepositoryLayout() { return classpathMavenRepositoryLayout; @@ -118,6 +136,11 @@ this.addExtensions = addExtensions; } + /** + * @deprecated Use [EMAIL PROTECTED] ManifestConfiguration#setClasspathLayoutType(String)}, and use + * CLASSPATH_LAYOUT_TYPE_SIMPLE, CLASSPATH_LAYOUT_TYPE_CUSTOM, or CLASSPATH_LAYOUT_TYPE_REPOSITORY, + * also declared in [EMAIL PROTECTED] ManifestConfiguration}. + */ public void setClasspathMavenRepositoryLayout( boolean classpathMavenRepositoryLayout ) { this.classpathMavenRepositoryLayout = classpathMavenRepositoryLayout; @@ -149,4 +172,64 @@ return cpp; } + + /** + * Return the type of layout to use when formatting classpath entries. + * Default is taken from the constant CLASSPATH_LAYOUT_TYPE_SIMPLE, declared + * in this class, which has a value of 'simple'. Other values are: 'repository' + * (CLASSPATH_LAYOUT_TYPE_REPOSITORY, or the same as a maven classpath layout), + * and 'custom' (CLASSPATH_LAYOUT_TYPE_CUSTOM). + * <br/> + * <b>NOTE:</b> If you specify a type of 'custom' you MUST set [EMAIL PROTECTED] ManifestConfiguration#setCustomClasspathLayout(String)}. + */ + public String getClasspathLayoutType() + { + return CLASSPATH_LAYOUT_TYPE_SIMPLE.equals( classpathLayoutType ) && classpathMavenRepositoryLayout ? CLASSPATH_LAYOUT_TYPE_REPOSITORY + : classpathLayoutType; + } + + /** + * Set the type of layout to use when formatting classpath entries. + * Should be one of: 'simple' (CLASSPATH_LAYOUT_TYPE_SIMPLE), 'repository' + * (CLASSPATH_LAYOUT_TYPE_REPOSITORY, or the same as a maven classpath layout), + * and 'custom' (CLASSPATH_LAYOUT_TYPE_CUSTOM). The constant names noted here + * are defined in the [EMAIL PROTECTED] ManifestConfiguration} class. + * <br/> + * <b>NOTE:</b> If you specify a type of 'custom' you MUST set [EMAIL PROTECTED] ManifestConfiguration#setCustomClasspathLayout(String)}. + */ + public void setClasspathLayoutType( String classpathLayoutType ) + { + this.classpathLayoutType = classpathLayoutType; + } + + /** + * Retrieve the layout expression for use when the layout type set in [EMAIL PROTECTED] ManifestConfiguration#setClasspathLayoutType(String)} + * has the value 'custom'. <b>The default value is null.</b> + * Expressions will be evaluated against the following ordered list of classpath-related objects: + * <ol> + * <li>The current [EMAIL PROTECTED] Artifact} instance, if one exists.</li> + * <li>The current [EMAIL PROTECTED] ArtifactHandler} instance from the artifact above.</li> + * </ol> + * <br/> + * <b>NOTE:</b> If you specify a layout type of 'custom' you MUST set this layout expression. + */ + public String getCustomClasspathLayout() + { + return customClasspathLayout; + } + + /** + * Set the layout expression for use when the layout type set in [EMAIL PROTECTED] ManifestConfiguration#setClasspathLayoutType(String)} + * has the value 'custom'. Expressions will be evaluated against the following ordered list of classpath-related objects: + * <ol> + * <li>The current [EMAIL PROTECTED] Artifact} instance, if one exists.</li> + * <li>The current [EMAIL PROTECTED] ArtifactHandler} instance from the artifact above.</li> + * </ol> + * <br/> + * <b>NOTE:</b> If you specify a layout type of 'custom' you MUST set this layout expression. + */ + public void setCustomClasspathLayout( String customClasspathLayout ) + { + this.customClasspathLayout = customClasspathLayout; + } } Modified: maven/shared/trunk/maven-archiver/src/main/java/org/apache/maven/archiver/MavenArchiver.java URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-archiver/src/main/java/org/apache/maven/archiver/MavenArchiver.java?rev=697952&r1=697951&r2=697952&view=diff ============================================================================== --- maven/shared/trunk/maven-archiver/src/main/java/org/apache/maven/archiver/MavenArchiver.java (original) +++ maven/shared/trunk/maven-archiver/src/main/java/org/apache/maven/archiver/MavenArchiver.java Mon Sep 22 12:16:33 2008 @@ -26,14 +26,25 @@ import org.codehaus.plexus.archiver.jar.JarArchiver; import org.codehaus.plexus.archiver.jar.Manifest; import org.codehaus.plexus.archiver.jar.ManifestException; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.Interpolator; +import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor; +import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; +import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; +import org.codehaus.plexus.interpolation.PrefixedValueSourceWrapper; +import org.codehaus.plexus.interpolation.RecursionInterceptor; +import org.codehaus.plexus.interpolation.StringSearchInterpolator; +import org.codehaus.plexus.interpolation.ValueSource; import org.codehaus.plexus.util.StringUtils; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; /** @@ -42,6 +53,16 @@ */ public class MavenArchiver { + private static final List ARTIFACT_EXPRESSION_PREFIXES; + + static + { + List artifactExpressionPrefixes = new ArrayList(); + artifactExpressionPrefixes.add( "artifact." ); + + ARTIFACT_EXPRESSION_PREFIXES = artifactExpressionPrefixes; + } + private JarArchiver archiver; private File archiveFile; @@ -166,26 +187,34 @@ List artifacts = project.getRuntimeClasspathElements(); String classpathPrefix = config.getClasspathPrefix(); + String layoutType = config.getClasspathLayoutType(); + String layout = config.getCustomClasspathLayout(); + + Interpolator interpolator = new StringSearchInterpolator(); for ( Iterator iter = artifacts.iterator(); iter.hasNext(); ) { File f = new File( (String) iter.next() ); - if ( f.isFile() ) + if ( f.getAbsoluteFile().isFile() ) { + Artifact artifact = findArtifactWithFile( project.getArtifacts(), f ); + if ( classpath.length() > 0 ) { classpath.append( " " ); } classpath.append( classpathPrefix ); - if ( !config.isClasspathMavenRepositoryLayout() ) + + // NOTE: If the artifact or layout type (from config) is null, give up and use the file name by itself. + if ( artifact == null || layoutType == null + || ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_SIMPLE.equals( layoutType ) ) { classpath.append( f.getName() ); } - else + else if ( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_REPOSITORY.equals( layoutType ) ) { // we use layout /$groupId[0]/../${groupId[n]/$artifactId/$version/{fileName} // here we must find the Artifact in the project Artifacts to generate the maven layout - Artifact artifact = findArtifactWithFile( project.getArtifacts(), f ); StringBuffer classpathElement = new StringBuffer(); if ( !StringUtils.isEmpty( artifact.getGroupId() ) ) { @@ -196,13 +225,77 @@ classpathElement.append( f.getName() ); classpath.append( classpathElement ); } + else if ( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM.equals( layoutType ) ) + { + if ( layout == null ) + { + throw new ManifestException( + ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM + + " layout type was declared, but custom layout expression was not specified. Check your <archive><manifest><customLayout/> element." ); + } + + // FIXME: This query method SHOULD NOT affect the internal + // state of the artifact version, but it does. + artifact.isSnapshot(); + + List valueSources = new ArrayList(); + valueSources.add( new PrefixedObjectValueSource( ARTIFACT_EXPRESSION_PREFIXES, artifact, true ) ); + valueSources.add( new PrefixedObjectValueSource( ARTIFACT_EXPRESSION_PREFIXES, artifact == null ? null : artifact.getArtifactHandler(), true ) ); + + Properties extraExpressions = new Properties(); + if ( artifact != null ) + { + extraExpressions.setProperty( "groupIdPath", artifact.getGroupId().replace( '.', '/' ) ); + if ( artifact.getClassifier() != null ) + { + extraExpressions.setProperty( "dashClassifier", "-" + artifact.getClassifier() ); + extraExpressions.setProperty( "dashClassifier?", "-" + artifact.getClassifier() ); + } + else + { + extraExpressions.setProperty( "dashClassifier", "" ); + extraExpressions.setProperty( "dashClassifier?", "" ); + } + } + valueSources.add( new PrefixedPropertiesValueSource( ARTIFACT_EXPRESSION_PREFIXES, extraExpressions, true ) ); + + for ( Iterator it = valueSources.iterator(); it.hasNext(); ) + { + ValueSource vs = (ValueSource) it.next(); + interpolator.addValueSource( vs ); + } + + RecursionInterceptor recursionInterceptor = new PrefixAwareRecursionInterceptor( ARTIFACT_EXPRESSION_PREFIXES ); + try + { + classpath.append( interpolator.interpolate( layout, recursionInterceptor ) ); + } + catch ( InterpolationException e ) + { + ManifestException error = + new ManifestException( "Error interpolating artifact path for classpath entry: " + + e.getMessage() ); + + error.initCause( e ); + throw error; + } + finally + { + for ( Iterator it = valueSources.iterator(); it.hasNext(); ) + { + ValueSource vs = (ValueSource) it.next(); + interpolator.removeValuesSource( vs ); + } + } + } + else + { + throw new ManifestException( "Unknown classpath layout type: '" + layoutType + + "'. Check your <archive><manifest><layoutType/> element." ); + } } - } - - - if ( classpath.length() > 0 ) { // Class-Path is special and should be added to manifest even if Modified: maven/shared/trunk/maven-archiver/src/site/apt/examples/classpath.apt URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-archiver/src/site/apt/examples/classpath.apt?rev=697952&r1=697951&r2=697952&view=diff ============================================================================== --- maven/shared/trunk/maven-archiver/src/site/apt/examples/classpath.apt (original) +++ maven/shared/trunk/maven-archiver/src/site/apt/examples/classpath.apt Mon Sep 22 12:16:33 2008 @@ -139,7 +139,7 @@ +-----+ -* Altering The Classpath +* Altering The Classpath: Defining a Classpath Directory Prefix Sometimes it is useful to be able to alter the classpath, for example when {{{http://maven.apache.org/plugins/maven-war-plugin/examples/skinny-wars.html}creating skinny war-files}}. @@ -180,13 +180,166 @@ </project> +-----+ - The manifest produced using the above configuration would look like this: + The manifest classpath produced using the above configuration would look like this: +-----+ -Manifest-Version: 1.0 -Archiver-Version: Plexus Archiver -Created-By: Apache Maven -Built-By: ${user.name} -Build-Jdk: ${java.version} Class-Path: lib/plexus-utils-1.1.jar lib/commons-lang-2.1.jar +-----+ + +* Altering The Classpath: Using a Maven Repository-Style Classpath + + <(Since: 2.3, see below)> + + Occasionally, you may want to include a Maven repository-style directory structure in your + archive. If you wish to reference the dependency archives within those directories in your + manifest classpath, try using the <<<\<classpathLayoutType\>>>> element with a value of + <<<'repository'>>>, like this: + ++-----+ +<project> + ... + <build> + <plugins> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifest> + <addClasspath>true</addClasspath> + <classpathPrefix>lib/</classpathPrefix> + <classpathLayoutType>repository</classpathLayoutType> + + <!-- NOTE: Deprecated in version 2.4. Use 'classpathLayoutType' instead. + <classpathMavenRepositoryLayout>true</classpathMavenRepositoryLayout> + --> + </manifest> + </archive> + </configuration> + </plugin> + </plugins> + </build> + ... + <dependencies> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <version>2.1</version> + </dependency> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-utils</artifactId> + <version>1.1</version> + </dependency> + </dependencies> + ... +</project> ++-----+ + + <<NOTE:>> In version 2.3, this feature was available by setting the <<<\<classpathMavenRepositoryLayout\>>>> + element to the value <<<true>>>. This configuration option has been *deprecated* in version 2.4, + in favor of the more general <<<\<classpathLayoutType\>>>> element, where a value of <<<'repository'>>> + will render the same behavior. + + The manifest classpath produced using the above configuration would look like this: + ++-----+ +Class-Path: lib/org/codehaus/plexus/plexus-utils/1.1/plexus-utils-1.1.jar lib/commons-lang/commons-lang/2.1/commons-lang-2.1.jar ++-----+ + +* Altering The Classpath: Using a Custom Classpath Format + + <(Since: 2.4)> + + At times, you may have dependency archives in a custom format within your own archive, one that doesn't + conform to any of the above classpath layouts. If you wish to define a custom layout for dependency archives + within your archive's manifest classpath, try using the <<<\<classpathLayoutType\>>>> element with a value of + <<<'custom'>>>, along with the <<<\<customClasspathLayout\>>>> element, like this: + ++-----+ +<project> + ... + <build> + <plugins> + <plugin> + <artifactId>maven-war-plugin</artifactId> + <configuration> + <archive> + <manifest> + <addClasspath>true</addClasspath> + <classpathLayoutType>custom</classpathLayoutType> + <customClasspathLayout>WEB-INF/lib/${artifact.groupIdPath}/${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}</customClasspathLayout> + </manifest> + </archive> + </configuration> + </plugin> + </plugins> + </build> + ... + <dependencies> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <version>2.1</version> + </dependency> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-utils</artifactId> + <version>1.1</version> + </dependency> + </dependencies> + ... +</project> ++-----+ + + This classpath layout is a little more involved than the previous examples. + To understand how the value of the <<<\<customClasspathLayout\>>>> configuration + is interpreted, it's useful to understand the rules applied when resolving + expressions within the value: + + [[1]] If present, trim off the prefix 'artifact.' from the expression. + + [[2]] Attempt to resolve the expression as a reference to the Artifact using + reflection (eg. <<<'artifactId'>>> becomes a reference to the method + <<<'getArtifactId()'>>>). + + [[3]] Attempt to resolve the expression as a reference to the ArtifactHandler of + the current Artifact, again using reflection (eg. <<<'extension'>>> becomes a reference + to the method <<<'getExtension()'>>>). + + [[4]] Attempt to resolve the expression as a key in the special-case Properties instance, + which contains the following mappings: + + * <<<'dashClassifier'>>>: If the Artifact has a classifier, this will be + <<<'-${artifact.classifier}'>>>, otherwise this + is an empty string. + + * <<<'dashClassifier?'>>>: This is a synonym of <<<'dashClassifier'>>>. + + * <<<'groupIdPath'>>>: This is the equivalent of <<<'${artifact.groupId}'>>>, + with all <<<'.'>>> characters replaced by <<<'/'>>>. + + [] + + [] + + The manifest classpath produced using the above configuration would look like this: + ++-----+ +Class-Path: WEB-INF/lib/org/codehaus/plexus/plexus-utils-1.1.jar WEB-INF/lib/commons-lang/commons-lang-2.1.jar ++-----+ + +** Forcing the use of -SNAPSHOT in classpath archive versions + + Depending on how you construct your archive, you may have the ability to specify whether + snapshot dependency archives are included with the version suffix <<<'-SNAPSHOT'>>>, or + whether the unique timestamp and build-number for that archive is used. For instance, + the {{{http://maven.apache.org/plugins/maven-assembly-plugin}Assembly Plugin}} allows + you to make this decision in the <<<\<outputFileNameMapping\>>>> element of its + <<<\<dependencySet>>>> descriptor section. + + To force the use of <<<'-SNAPSHOT'>>> version naming, simply replace <<<'${artifact.version}'>>> + with <<<'${artifact.baseVersion}'>>> in the example above, so it looks like this: + ++-----+ +<customClasspathLayout>WEB-INF/lib/${artifact.groupIdPath}/${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</customClasspathLayout> ++-----+ Modified: maven/shared/trunk/maven-archiver/src/test/java/org/apache/maven/archiver/MavenArchiverTest.java URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-archiver/src/test/java/org/apache/maven/archiver/MavenArchiverTest.java?rev=697952&r1=697951&r2=697952&view=diff ============================================================================== --- maven/shared/trunk/maven-archiver/src/test/java/org/apache/maven/archiver/MavenArchiverTest.java (original) +++ maven/shared/trunk/maven-archiver/src/test/java/org/apache/maven/archiver/MavenArchiverTest.java Mon Sep 22 12:16:33 2008 @@ -20,6 +20,8 @@ */ import java.io.File; +import java.net.URI; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -188,8 +190,8 @@ Manifest manifest = archiver.getManifest( project, archiveConfiguration ); String classPath = manifest.getMainSection().getAttribute( "Class-Path" ).getValue(); assertTrue( "User specified Class-Path entry was not added to manifest", classPath.indexOf( "help/" ) != -1 ); - assertTrue( "Class-Path generated by addClasspath was not added to manifest", classPath.indexOf( tempFile - .getName() ) != -1 ); + assertTrue( "Class-Path generated by addClasspath was not added to manifest", + classPath.indexOf( tempFile.getName() ) != -1 ); } finally { @@ -236,7 +238,7 @@ archiver.createArchive( project, config ); assertTrue( jarFile.lastModified() > time ); } - + public void testNotGenerateImplementationVersionForMANIFESTMF() throws Exception { @@ -274,7 +276,7 @@ } } } - + public void testGenerateImplementationVersionForMANIFESTMF() throws Exception { @@ -316,8 +318,8 @@ jar.close(); } } - } - + } + public void testCarriageReturnInManifestEntry() throws Exception { @@ -341,7 +343,8 @@ config.setForced( true ); config.getManifest().setAddDefaultImplementationEntries( true ); config.addManifestEntry( "Description", project.getDescription() ); - //config.addManifestEntry( "EntryWithTab", " foo tab " + ( '\u0009' ) + ( '\u0009' ) + " bar tab" + ( '\u0009' ) ); + // config.addManifestEntry( "EntryWithTab", " foo tab " + ( '\u0009' ) + ( '\u0009' ) + " bar tab" + ( + // '\u0009' ) ); archiver.createArchive( project, config ); assertTrue( jarFile.exists() ); @@ -362,8 +365,8 @@ jar.close(); } } - } - + } + public void testManifestEntries() throws Exception { @@ -385,13 +388,13 @@ config.setForced( true ); config.getManifest().setAddDefaultImplementationEntries( true ); config.getManifest().setAddDefaultSpecificationEntries( true ); - + Map manifestEntries = new HashMap(); manifestEntries.put( "foo", "bar" ); manifestEntries.put( "first-name", "olivier" ); manifestEntries.put( "keyWithEmptyValue", null ); config.setManifestEntries( manifestEntries ); - + ManifestSection manifestSection = new ManifestSection(); manifestSection.setName( "UserSection" ); manifestSection.addManifestEntry( "key", "value" ); @@ -406,31 +409,35 @@ Attributes manifest = jar.getManifest().getMainAttributes(); assertEquals( "Apache Maven", manifest.get( new Attributes.Name( "Created-By" ) ) ); - assertEquals( "archiver test", manifest.get( Attributes.Name.SPECIFICATION_TITLE ));// "Specification-Title" ) ); - assertEquals( "0.1", manifest.get( Attributes.Name.SPECIFICATION_VERSION ));// "Specification-Version" ) ); - assertEquals( "Apache", manifest.get( Attributes.Name.SPECIFICATION_VENDOR ));// "Specification-Vendor" ) ); - - assertEquals( "archiver test", manifest.get( Attributes.Name.IMPLEMENTATION_TITLE ));// "Implementation-Title" ) ); - assertEquals( "0.1", manifest.get( Attributes.Name.IMPLEMENTATION_VERSION ));// "Implementation-Version" ) ); - assertEquals( "org.apache.dummy", manifest.get( Attributes.Name.IMPLEMENTATION_VENDOR_ID ));// "Implementation-Vendor-Id" ) ); - assertEquals( "Apache", manifest.get( Attributes.Name.IMPLEMENTATION_VENDOR ));// "Implementation-Vendor" ) ); - assertEquals( "org.apache.maven.Foo", manifest.get( Attributes.Name.MAIN_CLASS ));// "Main-Class" ) ); + assertEquals( "archiver test", manifest.get( Attributes.Name.SPECIFICATION_TITLE ) );// "Specification-Title" + // ) ); + assertEquals( "0.1", manifest.get( Attributes.Name.SPECIFICATION_VERSION ) );// "Specification-Version" ) ); + assertEquals( "Apache", manifest.get( Attributes.Name.SPECIFICATION_VENDOR ) );// "Specification-Vendor" ) + // ); + + assertEquals( "archiver test", manifest.get( Attributes.Name.IMPLEMENTATION_TITLE ) );// "Implementation-Title" + // ) ); + assertEquals( "0.1", manifest.get( Attributes.Name.IMPLEMENTATION_VERSION ) );// "Implementation-Version" ) + // ); + assertEquals( "org.apache.dummy", manifest.get( Attributes.Name.IMPLEMENTATION_VENDOR_ID ) );// "Implementation-Vendor-Id" + // ) ); + assertEquals( "Apache", manifest.get( Attributes.Name.IMPLEMENTATION_VENDOR ) );// "Implementation-Vendor" ) + // ); + assertEquals( "org.apache.maven.Foo", manifest.get( Attributes.Name.MAIN_CLASS ) );// "Main-Class" ) ); assertEquals( "bar", manifest.get( new Attributes.Name( "foo" ) ) ); assertEquals( "olivier", manifest.get( new Attributes.Name( "first-name" ) ) ); - - assertEquals( System.getProperty( "java.version"), manifest.get( new Attributes.Name( "Build-Jdk" ) ) ); - assertEquals( System.getProperty( "user.name"), manifest.get( new Attributes.Name( "Built-By" ) ) ); - + assertEquals( System.getProperty( "java.version" ), manifest.get( new Attributes.Name( "Build-Jdk" ) ) ); + assertEquals( System.getProperty( "user.name" ), manifest.get( new Attributes.Name( "Built-By" ) ) ); + assertTrue( StringUtils.isEmpty( manifest.getValue( new Attributes.Name( "keyWithEmptyValue" ) ) ) ); assertTrue( manifest.containsKey( new Attributes.Name( "keyWithEmptyValue" ) ) ); - - + manifest = jar.getManifest().getAttributes( "UserSection" ); - + assertEquals( "value", manifest.get( new Attributes.Name( "key" ) ) ); - + } finally { @@ -463,7 +470,7 @@ Manifest manifest = archiver.getManifest( project, config ); Manifest.Section section = manifest.getSection( "SectionOne" ); - assertNotNull( "The section is not present in the manifest as it should be.", section ); + assertNotNull( "The section is not present in the manifest as it should be.", section ); Manifest.Attribute attribute = section.getAttribute( "key" ); assertNotNull( "The attribute we are looking for is not present in the section.", attribute ); @@ -496,13 +503,13 @@ archiver.createArchive( project, config ); assertTrue( jarFile.exists() ); jar = new JarFile( jarFile ); - + String classPath = jar.getManifest().getMainAttributes().getValue( Attributes.Name.CLASS_PATH ); assertNotNull( classPath ); String[] classPathEntries = StringUtils.split( classPath, " " ); - assertEquals("dummy1-1.0.jar", classPathEntries[0]); - assertEquals("dummy2-1.5.jar", classPathEntries[1]); - assertEquals("dummy3-2.0.jar", classPathEntries[2]); + assertEquals( "dummy1-1.0.jar", classPathEntries[0] ); + assertEquals( "dummy2-1.5.jar", classPathEntries[1] ); + assertEquals( "dummy3-2.0.jar", classPathEntries[2] ); } finally { @@ -513,7 +520,7 @@ } } } - + public void testMavenRepoClassPathValue() throws Exception { @@ -537,19 +544,20 @@ config.getManifest().setAddDefaultSpecificationEntries( true ); config.getManifest().setMainClass( "org.apache.maven.Foo" ); config.getManifest().setAddClasspath( true ); - config.getManifest().setClasspathMavenRepositoryLayout( true ); + config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_REPOSITORY ); archiver.createArchive( project, config ); assertTrue( jarFile.exists() ); jar = new JarFile( jarFile ); Manifest manifest = archiver.getManifest( project, config ); - String[] classPathEntries = StringUtils.split( new String( manifest.getMainSection() - .getAttributeValue( "Class-Path" ).getBytes() ), " " ); + String[] classPathEntries = + StringUtils.split( + new String( manifest.getMainSection().getAttributeValue( "Class-Path" ).getBytes() ), + " " ); assertEquals( "org/apache/dummy/dummy1/1.0/dummy1-1.0.jar", classPathEntries[0] ); assertEquals( "org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar", classPathEntries[1] ); assertEquals( "org/apache/dummy/bar/dummy3/2.0/dummy3-2.0.jar", classPathEntries[2] ); - String classPath = jar.getManifest().getMainAttributes().getValue( Attributes.Name.CLASS_PATH ); assertNotNull( classPath ); classPathEntries = StringUtils.split( classPath, " " ); @@ -567,11 +575,67 @@ } } } - + + public void testCustomClassPathValue() + throws Exception + { + MavenProject project = getDummyProject(); + JarFile jar = null; + try + { + File jarFile = new File( "target/test/dummy.jar" ); + jarFile.delete(); + assertFalse( jarFile.exists() ); + JarArchiver jarArchiver = new JarArchiver(); + jarArchiver.setDestFile( jarFile ); + + MavenArchiver archiver = new MavenArchiver(); + archiver.setArchiver( jarArchiver ); + archiver.setOutputFile( jarArchiver.getDestFile() ); + + MavenArchiveConfiguration config = new MavenArchiveConfiguration(); + config.setForced( true ); + config.getManifest().setAddDefaultImplementationEntries( true ); + config.getManifest().setAddDefaultSpecificationEntries( true ); + config.getManifest().setMainClass( "org.apache.maven.Foo" ); + config.getManifest().setAddClasspath( true ); + config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM ); + config.getManifest().setCustomClasspathLayout( "${artifact.groupIdPath}/${artifact.artifactId}/${artifact.version}/TEST-${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}" ); + archiver.createArchive( project, config ); + assertTrue( jarFile.exists() ); + jar = new JarFile( jarFile ); + + Manifest manifest = archiver.getManifest( project, config ); + String[] classPathEntries = + StringUtils.split( + new String( manifest.getMainSection().getAttributeValue( "Class-Path" ).getBytes() ), + " " ); + assertEquals( "org/apache/dummy/dummy1/1.0/TEST-dummy1-1.0.jar", classPathEntries[0] ); + assertEquals( "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar", classPathEntries[1] ); + assertEquals( "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0.jar", classPathEntries[2] ); + + String classPath = jar.getManifest().getMainAttributes().getValue( Attributes.Name.CLASS_PATH ); + assertNotNull( classPath ); + classPathEntries = StringUtils.split( classPath, " " ); + assertEquals( "org/apache/dummy/dummy1/1.0/TEST-dummy1-1.0.jar", classPathEntries[0] ); + assertEquals( "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar", classPathEntries[1] ); + assertEquals( "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0.jar", classPathEntries[2] ); + + } + finally + { + // cleanup streams + if ( jar != null ) + { + jar.close(); + } + } + } + // ---------------------------------------- - // common methods for testing + // common methods for testing // ---------------------------------------- - + private MavenProject getDummyProject() { Model model = new Model(); @@ -585,7 +649,7 @@ project.setExtensionArtifacts( Collections.EMPTY_SET ); project.setRemoteArtifactRepositories( Collections.EMPTY_LIST ); project.setPluginArtifactRepositories( Collections.EMPTY_LIST ); - + File pomFile = new File( "src/test/resources/pom.xml" ); pomFile.setLastModified( System.currentTimeMillis() - 60000L ); project.setFile( pomFile ); @@ -603,8 +667,6 @@ artifact.setVersion( "0.1" ); artifact.setType( "jar" ); project.setArtifact( artifact ); - - ArtifactHandler artifactHandler = new ArtifactHandler() { @@ -621,7 +683,7 @@ public String getExtension() { - return null; + return "jar"; } public String getLanguage() @@ -643,24 +705,23 @@ { return false; } - + }; - - + Set artifacts = new TreeSet( new ArtifactComparator() ); - + MockArtifact artifact1 = new MockArtifact(); artifact1.setGroupId( "org.apache.dummy" ); artifact1.setArtifactId( "dummy1" ); artifact1.setVersion( "1.0" ); artifact1.setType( "jar" ); - artifact1.setScope( "runtime" ); - artifact1.setFile( new File( "target/test-classes", artifact1.getArtifactId() + "-" + artifact1.getVersion() + ".jar" ) ); - - artifact1.setArtifactHandler( artifactHandler); + artifact1.setScope( "runtime" ); + artifact1.setFile( getClasspathFile( artifact1.getArtifactId() + "-" + artifact1.getVersion() + + ".jar" ) ); - artifacts.add( artifact1 ); + artifact1.setArtifactHandler( artifactHandler ); + artifacts.add( artifact1 ); MockArtifact artifact2 = new MockArtifact(); artifact2.setGroupId( "org.apache.dummy.foo" ); @@ -668,24 +729,39 @@ artifact2.setVersion( "1.5" ); artifact2.setType( "jar" ); artifact2.setScope( "runtime" ); - artifact2.setFile( new File( "target/test-classes", artifact2.getArtifactId() + "-" + artifact2.getVersion() + ".jar" ) ); - - artifact2.setArtifactHandler( artifactHandler); - artifacts.add( artifact2 ); + artifact2.setFile( getClasspathFile( artifact2.getArtifactId() + "-" + artifact2.getVersion() + + ".jar" ) ); + artifact2.setArtifactHandler( artifactHandler ); + artifacts.add( artifact2 ); MockArtifact artifact3 = new MockArtifact(); artifact3.setGroupId( "org.apache.dummy.bar" ); artifact3.setArtifactId( "dummy3" ); artifact3.setVersion( "2.0" ); artifact3.setScope( "runtime" ); - artifact3.setType( "jar" ); - artifact3.setFile( new File( "target/test-classes", artifact3.getArtifactId() + "-" + artifact3.getVersion() + ".jar" ) ); - artifact3.setArtifactHandler( artifactHandler); + artifact3.setType( "jar" ); + artifact3.setFile( getClasspathFile( artifact3.getArtifactId() + "-" + artifact3.getVersion() + + ".jar" ) ); + artifact3.setArtifactHandler( artifactHandler ); artifacts.add( artifact3 ); - + project.setArtifacts( artifacts ); - + return project; } + + private File getClasspathFile( String file ) + { + URL resource = Thread.currentThread().getContextClassLoader().getResource( file ); + if ( resource == null ) + { + fail( "Cannot retrieve java.net.URL for file: " + file + " on the current test classpath." ); + } + + URI uri = new File( resource.getPath() ).toURI().normalize(); + File result = new File( uri.getPath().replaceAll( "%20", " " ) ); + + return result; + } }