gnodet commented on issue #11381:
URL: https://github.com/apache/maven/issues/11381#issuecomment-3502272376
## Implementation Explanation: Resource targetPath Fix
This fix addresses the regression in Maven 4 where resource `targetPath` was
being resolved relative to the project base directory instead of the output
directory, breaking compatibility with Maven 3.x behavior.
### Problem Analysis
In Maven 3.x, when a resource has a relative `targetPath`, it's resolved
relative to the output directory (`target/classes` or `target/test-classes`).
However, in Maven 4, the `DefaultSourceRoot` constructor that takes a
`Resource` parameter was incorrectly resolving the targetPath as:
```java
nonBlank(resource.getTargetPath()).map(baseDir::resolve).orElse(null)
```
This resolved the targetPath relative to the base directory, not the output
directory.
### Solution Design
The fix was implemented with careful consideration for both Maven 4 API
conformance and Maven 3 compatibility:
#### 1. **API-First Approach**
The solution leverages Maven 4's new `SourceRoot` API which represents a
more structured approach to handling source and resource directories. The
`SourceRoot.targetPath()` method returns an `Optional<Path>` that should
contain a path relative to the output directory.
#### 2. **Backward Compatibility Strategy**
To maintain compatibility with existing Maven 3 code that uses the
`Resource` API:
- **Preserved existing constructor**: The original `DefaultSourceRoot(Path
baseDir, ProjectScope scope, Resource resource)` constructor was kept unchanged
to avoid breaking existing code
- **Correct targetPath handling**: The constructor now properly stores the
targetPath as a relative path (using `Path.of()`) instead of resolving it
against the base directory
- **ConnectedResource wrapper**: This class bridges between the Maven 4
`SourceRoot` API and the Maven 3 `Resource` API, ensuring that when Maven 3
code calls `Resource.getTargetPath()`, it receives the correct relative path
#### 3. **Implementation Details**
**DefaultSourceRoot Constructor (lines 157-171)**:
```java
public DefaultSourceRoot(final Path baseDir, ProjectScope scope, Resource
resource) {
this(
scope,
Language.RESOURCES,
null,
null,
baseDir.resolve(nonBlank(resource.getDirectory())
.orElseThrow(() -> new IllegalArgumentException("Source
declaration without directory value."))),
resource.getIncludes(),
resource.getExcludes(),
Boolean.parseBoolean(resource.getFiltering()),
nonBlank(resource.getTargetPath()).map(Path::of).orElse(null),
// Key fix: Path.of() instead of baseDir.resolve()
true);
}
```
**ConnectedResource Bridge (lines 52-60)**:
```java
private static String computeRelativeTargetPath(SourceRoot sourceRoot,
ProjectScope scope, MavenProject project) {
return sourceRoot.targetPath().map(Path::toString).orElse(null);
}
```
### Maven 4 API Conformance
#### 1. **Consistent Path Semantics**
The fix ensures that `SourceRoot.targetPath()` always returns a relative
path, which is the expected behavior in Maven 4's API design. This provides a
clear contract: targetPath is always relative to the output directory.
#### 2. **Type Safety**
By using `Path.of()` instead of string manipulation, the implementation
leverages Java's type-safe path handling, reducing the risk of path-related
bugs.
#### 3. **Optional Usage**
The implementation properly uses `Optional<Path>` for targetPath, following
Maven 4's API patterns for nullable values.
### Compatibility Preservation
#### 1. **Maven 3 Resource API**
Existing Maven 3 code that uses `Resource.getTargetPath()` continues to work
unchanged. The `ConnectedResource` class ensures that the returned string
represents a path relative to the output directory, maintaining the expected
Maven 3 behavior.
#### 2. **Plugin Compatibility**
Maven plugins that rely on the Resource API (like the
maven-resources-plugin) continue to work without modification, as they receive
the correct relative paths through the `ConnectedResource` wrapper.
#### 3. **Build Tool Integration**
IDEs and other build tools that inspect Maven project models will see
consistent behavior between Maven 3 and Maven 4.
### Testing Strategy
The fix includes comprehensive testing at multiple levels:
#### 1. **Unit Tests** (`DefaultSourceRootTest.java`)
- Tests for Resource constructor with various targetPath scenarios
- Verification that relative paths are preserved correctly
- Edge case handling (null, empty targetPath)
#### 2. **Integration Test** (`MavenITgh11381ResourceTargetPathTest.java`)
- End-to-end verification that resources are copied to the correct location
- Validates that `target/classes/target-dir/` is used instead of
`target-dir/`
- Ensures the fix works in real Maven builds
### Why This Approach?
This implementation was chosen because it:
1. **Minimizes Breaking Changes**: Existing code continues to work without
modification
2. **Follows Maven 4 Patterns**: Uses the new SourceRoot API as the primary
abstraction
3. **Maintains Clear Separation**: The SourceRoot API handles the "truth"
while ConnectedResource provides compatibility
4. **Enables Future Evolution**: The SourceRoot API can be extended without
affecting the Resource compatibility layer
5. **Provides Type Safety**: Uses Path objects internally while maintaining
string compatibility externally
This solution successfully bridges the gap between Maven 3's
Resource-centric approach and Maven 4's SourceRoot-centric design, ensuring
that both APIs work correctly while maintaining the expected behavior for
resource targetPath resolution.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]