Loose EAR Manifest Class-Path Issue Fix#1073
Loose EAR Manifest Class-Path Issue Fix#1073sajeerzeji wants to merge 6 commits intoOpenLiberty:mainfrom
Conversation
…ifest, Adds that dependency's class and resource directories to the module archive element in the loose XML
venmanyarun
left a comment
There was a problem hiding this comment.
as discussed, add tests
| if (jarName && jarName.endsWith(".jar")) { | ||
| String depName = jarName.replace(".jar", "") | ||
|
|
||
| def depProj = proj.rootProject.findProject(":${depName}") |
There was a problem hiding this comment.
Are we guaranteed that the dependency project name is the jarName without .jar? I wouldn't think so. We will be missing dependencies if they don't match right?
There was a problem hiding this comment.
I did a research on this and found below cases, we can do fallback matching for these cases
- Custom archiveBaseName - Projects using archiveBaseName = 'custom-name' produce JARs that don't match the project name
- Versioned JARs - Projects with archiveVersion = '1.0' produce JARs like ProjectName-1.0.jar instead of ProjectName.jar
- Classified JARs - Projects using archiveClassifier = 'client' produce JARs like ProjectName-client.jar for different variants
- Combined customizations - Projects combining base name, version, and classifier produce complex names like custom-2.0-prod.jar
Additionally
- Completely custom archiveFileName - Projects using archiveFileName = 'xyz-database-layer.jar' have no relationship between project name and JAR name
- Dynamic JAR names - Projects computing JAR names at runtime using expressions like "${env}-${project.name}.jar" may not be resolvable at configuration time
- Nested projects with custom naming - Multi-level projects (:parent:child) using custom base names like archiveBaseName = 'parent-child-combined' require path-based matching
| //The location of the resource directory should be the same as proj.getProjectDir()/build/resources. | ||
| //If the manifest file exists, it is copied to proj.getProjectDir()/build/resources/tmp/META-INF. If it does not exist, one is created there. | ||
| addManifestFileWithParent(moduleArchive, f, proj.sourceSets.main.getOutput().getResourcesDir().getParentFile().getCanonicalPath()) | ||
| // Prefer the jar task's generated manifest (build/tmp/jar/MANIFEST.MF) which has |
There was a problem hiding this comment.
Do we need to do something similar for Liberty Maven plugin?
There was a problem hiding this comment.
@sajeerzeji Please check by creating similar project in Liberty Maven plugin and try deploying the EAR to see issue exists in LMP
There was a problem hiding this comment.
Sure, will do this and will update here
| } | ||
| // If no .MF file was found in the jar source set (e.g. no static MANIFEST.MF in resources), | ||
| // still add the jar task's generated manifest if it exists. | ||
| if (!manifestAdded) { |
There was a problem hiding this comment.
Are we sure we should always do this? We don't even know if any files were added above for this module. Is this ok to do for war and rar?
…' into GH766-Multi_module_issue_with_loose_app
|
Hi @cherylking Previous approach was to use manifest Class Path entries to determine dependencies, requiring users to manually declare Class-Path in their build.gradle files. This approach was reverted after cross-checking with Maven plugin implementation. Added
Now the plugin automatically detects project dependencies from Gradle configurations, for each EJB/JAR module, it finds all project dependencies, adds dependency class directories as |
src/main/groovy/io/openliberty/tools/gradle/utils/LooseEarApplication.groovy
Show resolved
Hide resolved
src/test/groovy/io/openliberty/tools/gradle/TestMultiModuleLooseEarEjbDependency.groovy
Outdated
Show resolved
Hide resolved
| @@ -0,0 +1,8 @@ | |||
| rootProject.name = 'ejb-dependency' | |||
| include ':lib-jar' | |||
There was a problem hiding this comment.
here I can see we have direct dependency with both lib-jar and ejb-jar
we should have tests that have
- Multi-level transitive dependencies (A depends on B depends on C)
- Mixed dependency types (project + external JARs)
|
@sajeerzeji |
Fixes #766
Issue
The application failed to start in loose EAR mode with,
In packaged EAR mode, JAR files are physically present in the EAR, and Liberty's classloader uses the manifest Class-Path entries to locate dependency JARs within the EAR.
In loose EAR mode (looseApplication = true), there are no physical JAR files. Instead, the plugin generates an XML descriptor (GarageSaleLibertyEAR.ear.xml) that maps source directories. Liberty's classloader needs to know which class directories belong to each module.
The Problem
compileOnlyorimplementationdependenciesPrevious Approach (Reverted)
The previous fix attempted to use manifest Class-Path entries to determine dependencies, requiring users to manually declare Class-Path in their build.gradle files. This approach was reverted after cross-checking with Maven plugin implementation.
Current Approach
Added
addDependencyClassDirectories()method that,compileClasspathandruntimeClasspathfor project dependenciesHow it works
<dir>elements in the loose XMLUsage Requirements
No special configuration needed. The plugin automatically handles dependencies declared in build.gradle.
Backward Compatibility
looseApplication = false) continues to work unchangedTesting
Added a new test
TestMultiModuleLooseEarEjbDependencythat verifiesBefore

After
