Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MEAR-267] - Fixed detection if JAR module is included into classpath of particular EAR module manifest #19

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@
<javaVersion>7</javaVersion>
<surefire.version>2.22.2</surefire.version>
<project.build.outputTimestamp>2020-09-26T20:10:30Z</project.build.outputTimestamp>
<mavenWarPluginVersion>2.1.1</mavenWarPluginVersion>
<mavenCompilerPluginVersion>2.5.1</mavenCompilerPluginVersion>
<mavenEjbPluginVersion>2.3</mavenEjbPluginVersion>
<invoker.skip>false</invoker.skip>
<invoker.install.skip>${invoker.skip}</invoker.install.skip>
<invoker.it.skip>${invoker.skip}</invoker.it.skip>
Expand Down Expand Up @@ -280,9 +283,9 @@
<goal>package</goal>
</goals>
<extraArtifacts>
<extraArtifact>org.apache.maven.plugins:maven-war-plugin:2.1.1:jar</extraArtifact>
<extraArtifact>org.apache.maven.plugins:maven-compiler-plugin:2.5.1:jar</extraArtifact>
<extraArtifact>org.apache.maven.plugins:maven-ejb-plugin:2.3:jar</extraArtifact>
<extraArtifact>org.apache.maven.plugins:maven-war-plugin:${mavenWarPluginVersion}:jar</extraArtifact>
<extraArtifact>org.apache.maven.plugins:maven-compiler-plugin:${mavenCompilerPluginVersion}:jar</extraArtifact>
<extraArtifact>org.apache.maven.plugins:maven-ejb-plugin:${mavenEjbPluginVersion}:jar</extraArtifact>
</extraArtifacts>
<skipInstallation>${invoker.install.skip}</skipInstallation>
<skipInvocation>${invoker.it.skip}</skipInvocation>
Expand Down
43 changes: 33 additions & 10 deletions src/main/java/org/apache/maven/plugins/ear/EarMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ private void copyModules( final JavaEEVersion javaEEVersion,
}
unpack( sourceFile, destinationFile, outdatedResources );

if ( skinnyWars && module.changeManifestClasspath() )
if ( module.changeManifestClasspath() && ( skinnyWars || module.getLibDir() == null ) )
{
changeManifestClasspath( module, destinationFile, javaEEVersion );
}
Expand All @@ -461,7 +461,7 @@ private void copyModules( final JavaEEVersion javaEEVersion,
getLog().info( "Copying artifact [" + module + "] to [" + module.getUri() + "]" );
FileUtils.copyFile( sourceFile, destinationFile );

if ( skinnyWars && module.changeManifestClasspath() )
if ( module.changeManifestClasspath() && ( skinnyWars || module.getLibDir() == null ) )
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks that this change introduces non backward compatible behavior - modification of Class-Path entry of manifest - for SAR and HAR modules. Refer to pull request #24 which is going to fix that (with false default value of skinnyModules option).

{
changeManifestClasspath( module, destinationFile, javaEEVersion );
}
Expand Down Expand Up @@ -806,8 +806,8 @@ private void changeManifestClasspath( EarModule module, File original, JavaEEVer
// We use the original name, cause in case of outputFileNameMapping
// we could not not delete it and it will end up in the resulting EAR and the WAR
// will not be cleaned up.
File artifact = new File( new File( workDirectory, module.getLibDir() ),
module.getArtifact().getFile().getName() );
final File workLibDir = new File( workDirectory, module.getLibDir() );
File artifact = new File( workLibDir, module.getArtifact().getFile().getName() );

// MEAR-217
// If WAR contains files with timestamps, but EAR strips them away (useBaseVersion=true)
Expand All @@ -816,16 +816,15 @@ private void changeManifestClasspath( EarModule module, File original, JavaEEVer
if ( !artifact.exists() )
{
getLog().debug( "module does not exist with original file name." );
artifact = new File( new File( workDirectory, module.getLibDir() ), jm.getBundleFileName() );
artifact = new File( workLibDir, jm.getBundleFileName() );
getLog().debug( "Artifact with mapping:" + artifact.getAbsolutePath() );
}

if ( !artifact.exists() )
{
getLog().debug( "Artifact with mapping does not exist." );
artifact = new File( new File( workDirectory, module.getLibDir() ),
jm.getArtifact().getFile().getName() );
getLog().debug( "Artifact with orignal file name:" + artifact.getAbsolutePath() );
artifact = new File( workLibDir, jm.getArtifact().getFile().getName() );
getLog().debug( "Artifact with original file name:" + artifact.getAbsolutePath() );
}

if ( artifact.exists() )
Expand All @@ -845,9 +844,10 @@ private void changeManifestClasspath( EarModule module, File original, JavaEEVer
if ( o instanceof JarModule )
{
JarModule jm = (JarModule) o;
if ( classPathElements.contains( jm.getBundleFileName() ) )
final int moduleClassPathIndex = findModuleInClassPathElements( classPathElements, jm );
if ( moduleClassPathIndex != -1 )
{
classPathElements.set( classPathElements.indexOf( jm.getBundleFileName() ), jm.getUri() );
classPathElements.set( moduleClassPathIndex, jm.getUri() );
}
else
{
Expand Down Expand Up @@ -946,4 +946,27 @@ private void deleteOutdatedResources( final Collection<String> outdatedResources
}
}
}

/**
* Searches for the given JAR module in the list of classpath elements. If JAR module is found among specified
* classpath elements then returns index of first matching element. Returns -1 otherwise.
*
* @param classPathElements classpath elements to search among
* @param module module to find among classpath elements defined by {@code classPathElements}
* @return -1 if {@code module} was not found in {@code classPathElements} or index of item of
* {@code classPathElements} which matches {@code module}
*/
private int findModuleInClassPathElements( final List<String> classPathElements, final JarModule module )
mabrarov marked this conversation as resolved.
Show resolved Hide resolved
{
if ( classPathElements.isEmpty() )
{
return -1;
}
final int moduleClassPathIndex = classPathElements.indexOf( module.getBundleFileName() );
if ( moduleClassPathIndex != -1 )
{
return moduleClassPathIndex;
}
return classPathElements.indexOf( module.getArtifact().getFile().getName() );
}
}
167 changes: 141 additions & 26 deletions src/test/java/org/apache/maven/plugins/ear/it/AbstractEarPluginIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
Expand All @@ -39,6 +44,7 @@
import org.apache.maven.plugins.ear.util.ResourceEntityResolver;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLAssert;
import org.junit.Assert;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

Expand Down Expand Up @@ -68,14 +74,17 @@ public abstract class AbstractEarPluginIT
* @param projectName the name of the project
* @param properties extra properties to be used by the embedder
* @param expectNoError true/false
* @param cleanBeforeExecute call clean plugin before execution
* @return the base directory of the project
*/
protected File executeMojo( final String projectName, final Properties properties, boolean expectNoError ) throws VerificationException, IOException
protected File executeMojo( final String projectName, final Properties properties, boolean expectNoError,
boolean cleanBeforeExecute ) throws VerificationException, IOException
{
System.out.println( " Building: " + projectName );

File testDir = getTestDir( projectName );
Verifier verifier = new Verifier( testDir.getAbsolutePath() );
verifier.setAutoclean( cleanBeforeExecute );
// Let's add alternate settings.xml setting so that the latest dependencies are used
String localRepo = System.getProperty( "localRepositoryPath" );
verifier.setLocalRepo( localRepo );
Expand Down Expand Up @@ -119,61 +128,75 @@ protected File executeMojo( final String projectName, final Properties propertie
protected File executeMojo( final String projectName, final Properties properties )
throws VerificationException, IOException
{
return executeMojo( projectName, properties, true );
return executeMojo( projectName, properties, true, true );
}

/**
* Executes the specified projects and asserts the given artifacts. Assert the deployment descriptors are valid
*
* Executes the specified projects and asserts the given artifacts. Asserts the deployment descriptors are valid.
* Asserts Class-Path entry of manifest of EAR modules.
*
* @param projectName the project to test
* @param earModuleName the name of 1st level EAR module in multi-module project or null if project is single-module
* @param expectedArtifacts the list of artifacts to be found in the EAR archive
* @param artifactsDirectory whether the artifact is an exploded artifactsDirectory or not
* @param artifactsToValidateManifest the list of EAR archive artifacts to validate Class-Path entry of artifact
* manifest or {@code null} if there is no need to validate Class-Path entry
* @param artifactsToValidateManifestDirectory whether the artifact from {@code artifactsToValidateManifest} list is
* an exploded or not, can be {@code null} if
* {@code artifactsToValidateManifest} is {@code null}
* @param expectedClassPathElements the list of elements of Class-Path entry of manifest, rows should match
* artifacts passed in {@code artifactsToValidateManifest} parameter;
* can be {@code null} if {@code artifactsToValidateManifest} is {@code null}
* @param cleanBeforeExecute call clean plugin before execution
* @return the base directory of the project
*/
protected File doTestProject( final String projectName, final String[] expectedArtifacts,
final boolean[] artifactsDirectory )
protected File doTestProject( final String projectName, final String earModuleName,
final String[] expectedArtifacts, boolean[] artifactsDirectory,
final String[] artifactsToValidateManifest,
boolean[] artifactsToValidateManifestDirectory,
final String[][] expectedClassPathElements,
final boolean cleanBeforeExecute )
throws VerificationException, IOException
{
final File baseDir = executeMojo( projectName, new Properties() );
assertEarArchive( baseDir, projectName );
assertEarDirectory( baseDir, projectName );

assertArchiveContent( baseDir, projectName, expectedArtifacts, artifactsDirectory );

assertDeploymentDescriptors( baseDir, projectName );

return baseDir;
final File baseDir = executeMojo( projectName, new Properties(), true, cleanBeforeExecute );

final File earModuleDir = getEarModuleDirectory( baseDir, earModuleName );
assertEarArchive( earModuleDir, projectName );
assertEarDirectory( earModuleDir, projectName );
assertArchiveContent( earModuleDir, projectName, expectedArtifacts, artifactsDirectory );
assertDeploymentDescriptors( earModuleDir, projectName );
assertClassPathElements( earModuleDir, projectName, artifactsToValidateManifest,
artifactsToValidateManifestDirectory, expectedClassPathElements );

return baseDir;
}

/**
* Executes the specified projects and asserts the given artifacts as artifacts (non directory)
*
* Executes the specified projects and asserts the given artifacts. Assert the deployment descriptors are valid.
*
* @param projectName the project to test
* @param expectedArtifacts the list of artifacts to be found in the EAR archive
* @param testDeploymentDescriptors whether we should test deployment descriptors
* @param artifactsDirectory whether the artifact is an exploded artifactsDirectory or not
* @return the base directory of the project
*/
private File doTestProject( final String projectName, final String[] expectedArtifacts,
boolean testDeploymentDescriptors )
protected File doTestProject( final String projectName, final String[] expectedArtifacts,
final boolean[] artifactsDirectory )
throws VerificationException, IOException
{
return doTestProject( projectName, expectedArtifacts, new boolean[expectedArtifacts.length] );
return doTestProject( projectName, null, expectedArtifacts, artifactsDirectory, null, null, null, true );
}

/**
* Executes the specified projects and asserts the given artifacts as artifacts (non directory). Assert the
* deployment descriptors are valid
* Executes the specified projects and asserts the given artifacts as artifacts (non directory)
*
* @param projectName the project to test
* @param expectedArtifacts the list of artifacts to be found in the EAR archive
* @return the base directory of the project
* @throws Exception Mojo exception in case of an error.
*/
protected File doTestProject( final String projectName, final String[] expectedArtifacts )
throws Exception
throws VerificationException, IOException
{
return doTestProject( projectName, expectedArtifacts, true );
return doTestProject( projectName, expectedArtifacts, new boolean[expectedArtifacts.length] );
}

protected void assertEarArchive( final File baseDir, final String projectName )
Expand All @@ -186,6 +209,11 @@ protected void assertEarDirectory( final File baseDir, final String projectName
assertTrue( "EAR archive directory does not exist", getEarDirectory( baseDir, projectName ).exists() );
}

protected File getEarModuleDirectory( final File baseDir, final String earModuleName)
{
return earModuleName == null ? baseDir : new File( baseDir, earModuleName );
}

protected File getTargetDirectory( final File basedir )
{
return new File( basedir, "target" );
Expand Down Expand Up @@ -384,4 +412,91 @@ public boolean accept( File dir, String name )
}
} );
}

/**
* Asserts that given EAR archive artifacts have expected elements in artifact manifest Class-Path entry.
*
* @param baseDir the directory of the tested project
* @param projectName the name of the project
* @param artifacts the list of EAR archive artifacts to validate Class-Path entry of artifact manifest or
* {@code null} if there is no need to validate Class-Path entry
* @param artifactsDirectory whether the artifact from {@code artifacts} list is an exploded or not,
* can be {@code null} if {@code artifacts} is {@code null}
* @param expectedClassPathElements the list of expected elements of Class-Path entry of manifest, rows should match
* artifacts passed in {@code artifacts} parameter; can be {@code null}
* if {@code artifacts} is {@code null}
* @throws IOException exception in case of an failure during reading of artifact manifest.
*/
protected void assertClassPathElements( final File baseDir, String projectName, String[] artifacts,
boolean[] artifactsDirectory, String[][] expectedClassPathElements )
throws IOException
{
if ( artifacts == null )
{
return;
}

assertNotNull( "artifactsDirectory should be provided if artifacts is provided",
artifactsDirectory );
assertTrue( "Size of artifactsDirectory should match size of artifacts parameter",
artifacts.length <= artifactsDirectory.length );
assertNotNull( "expectedClassPathElements should be provided if artifacts is provided",
expectedClassPathElements );
assertTrue( "Rows of expectedClassPathElements parameter should match items of artifacts parameter",
artifacts.length <= expectedClassPathElements.length );

final File earFile = getEarArchive( baseDir, projectName );
for ( int i = 0; i != artifacts.length; ++i )
{
final String moduleArtifact = artifacts[i];
Assert.assertArrayEquals( "Wrong elements of Class-Path entry of module [" + moduleArtifact + "] manifest",
expectedClassPathElements[i],
getClassPathElements( earFile, moduleArtifact, artifactsDirectory[i] ) );
}
}

/**
* Retrieves elements of Class-Path entry of manifest of given EAR module.
*
* @param earFile the EAR file to investigate
* @param artifact the name of artifact in EAR archive representing EAR module
* @return elements of Class-Path entry of manifest of EAR module which is represented by
* {@code artifact} artifact in {@code earFile} file
*/
protected String[] getClassPathElements( final File earFile, final String artifact, final boolean directory )
throws IOException
{
final String classPath;
try ( JarFile earJarFile = new JarFile( earFile ) )
{
final ZipEntry moduleEntry = earJarFile.getEntry( artifact );
assertNotNull( "Artifact [" + artifact + "] should exist in EAR", moduleEntry );
if (directory)
{
final String manifestEntryName = artifact + "/META-INF/MANIFEST.MF";
final ZipEntry manifestEntry = earJarFile.getEntry( manifestEntryName );
assertNotNull( manifestEntryName + " manifest file should exist in EAR", manifestEntry );
try ( InputStream manifestInputStream = earJarFile.getInputStream( manifestEntry ) )
{
final Manifest manifest = new Manifest(manifestInputStream);
classPath = manifest.getMainAttributes().getValue( "Class-Path" );
}
}
else
{
try ( InputStream moduleInputStream = earJarFile.getInputStream( moduleEntry );
JarInputStream moduleJarInputStream = new JarInputStream( moduleInputStream ) )
{
final Manifest manifest = moduleJarInputStream.getManifest();
assertNotNull( "Artifact [" + artifact + "] of EAR should have manifest", manifest );
classPath = manifest.getMainAttributes().getValue( "Class-Path" );
}
}
}
if ( classPath == null )
{
return new String[0];
}
return classPath.split( " " );
}
}
Loading