Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,59 @@ The project was relocated from <https://github.com/sonatype/sisu-build-api>. Als

## Provided APIs

### Resources API

The Resources API provides a modern, Path-based interface for managing build resources. It separates resource management concerns from logging/messaging functionality and provides better control over file operations during the build process.

**Key Features:**
- Modern `java.nio.file.Path` instead of `java.io.File`
- Change detection with `hasDelta()` (best effort hint)
- Reliable input/output freshness checking with `isUptodate()`
- Optimized output streams that only update files when content changes
- Support for marking generated/derived files for IDE integration
- Convenient copy operation that respects up-to-date checks
- Relative path conversion via `getPath()`

**Example Usage:**

```java
@Inject
private Resources resources;

public void execute() {
// Convert relative paths to absolute paths
Path source = resources.getPath("src/main/java/Example.java");
Path target = resources.getPath("target/classes/Example.class");

// Smart copy - only copies when target is stale
resources.copy(source, target);

// Check if file has changed (best effort hint)
if (resources.hasDelta(source)) {
// Process the file
}

// Reliable check for input/output scenarios
if (!resources.isUptodate(target, source)) {
// Regenerate target from source
}

// Write to a file with change detection
Path generated = resources.getPath("target/generated/Proto.java");
try (OutputStream out = resources.newOutputStream(generated, true)) {
// Write content - file marked as derived for IDE warnings
out.write(content);
}

// Mark a file as generated/derived
resources.markDerived(generated);
}
```

**When to use `hasDelta()` vs `isUptodate()`:**
- Use `hasDelta()` as a hint for user-editable source files where you want to detect changes
- Use `isUptodate()` when there's a clear input/output relationship, as it handles cases where target files may be deleted or modified outside the build process

### Messages API

The Messages API provides a modern, flexible way to create and manage build messages/markers that inform users in an IDE about issues in their files. It uses a builder pattern for constructing messages in a more convenient and extensible way compared to the legacy BuildContext message methods.
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/org/codehaus/plexus/build/BuildContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ public interface BuildContext {
*
* @param relpath is path relative to build context basedir
* @return a boolean.
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#hasDelta(java.nio.file.Path)} instead
*/
@Deprecated
boolean hasDelta(String relpath);

/**
Expand All @@ -45,7 +47,9 @@ public interface BuildContext {
* @since 0.0.5
* @param file a {@link java.io.File} object.
* @return a boolean.
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#hasDelta(java.nio.file.Path)} instead
*/
@Deprecated
boolean hasDelta(File file);

/**
Expand All @@ -54,15 +58,19 @@ public interface BuildContext {
*
* @param relpaths paths relative to build context basedir
* @return a boolean.
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#hasDelta(java.nio.file.Path)} instead
*/
@Deprecated
boolean hasDelta(List<String> relpaths);

/**
* Indicates that the file or folder content has been modified during the build.
*
* @see #newFileOutputStream(File)
* @param file a {@link java.io.File} object.
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#refresh(java.nio.file.Path)} instead
*/
@Deprecated
void refresh(File file);

/**
Expand All @@ -78,7 +86,9 @@ public interface BuildContext {
* @param file a {@link java.io.File} object.
* @return a {@link java.io.OutputStream} object.
* @throws java.io.IOException if any.
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#newOutputStream(java.nio.file.Path)} instead
*/
@Deprecated
OutputStream newFileOutputStream(File file) throws IOException;

/**
Expand Down Expand Up @@ -236,6 +246,8 @@ public interface BuildContext {
* @param target a {@link java.io.File} object.
* @param source a {@link java.io.File} object.
* @return a boolean.
* @deprecated Use {@link org.codehaus.plexus.build.resources.Resources#isUptodate(java.nio.file.Path, java.nio.file.Path)} instead
*/
@Deprecated
boolean isUptodate(File target, File source);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
Copyright (c) 2008 Sonatype, Inc. All rights reserved.

This program is licensed to you under the Apache License Version 2.0,
and you may not use this file except in compliance with the Apache License Version 2.0.
You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.

Unless required by applicable law or agreed to in writing,
software distributed under the Apache License Version 2.0 is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package org.codehaus.plexus.build.resources;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

import org.apache.maven.plugin.LegacySupport;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.build.BuildContext;

/**
* Default implementation of the Resources interface.
* <p>
* This implementation delegates to the BuildContext for compatibility with existing
* build infrastructure. It provides a transition path from the File-based API to the
* modern Path-based API.
* </p>
*/
@Named("default")
@Singleton
public class DefaultResources implements Resources {

private final BuildContext buildContext;
private final LegacySupport legacySupport;

/**
* Creates a new DefaultResources instance.
*
* @param buildContext the BuildContext to which operations will be delegated
* @param legacySupport the LegacySupport to get the current Maven project
*/
@Inject
public DefaultResources(BuildContext buildContext, LegacySupport legacySupport) {
this.buildContext = buildContext;
this.legacySupport = legacySupport;
}

@Override
public boolean hasDelta(Path file) {
if (file == null) {
return false;
}
return buildContext.hasDelta(file.toFile());
}

@Override
public void refresh(Path file) {
if (file != null) {
buildContext.refresh(file.toFile());
}
}

@Override
public OutputStream newOutputStream(Path file) throws IOException {
if (file == null) {
throw new IllegalArgumentException("file cannot be null");
}
return buildContext.newFileOutputStream(file.toFile());
}

@Override
public OutputStream newOutputStream(Path file, boolean derived) throws IOException {
OutputStream outputStream = newOutputStream(file);
if (derived) {
// Mark the file as derived after creating the output stream.
// In the default implementation, markDerived is a no-op, so the timing doesn't matter.
// Custom implementations that track derived files should consider wrapping the stream
// to defer marking until successful close() to avoid marking files on failed writes.
markDerived(file);
}
return outputStream;
}

@Override
public boolean isUptodate(Path target, Path source) {
if (target == null || source == null) {
return false;
}
return buildContext.isUptodate(target.toFile(), source.toFile());
}

@Override
public Path getPath(String relpath) {
if (relpath == null) {
throw new IllegalArgumentException("relpath cannot be null");
}
// Get the basedir from the current Maven project via LegacySupport
MavenProject project =
legacySupport.getSession() != null ? legacySupport.getSession().getCurrentProject() : null;
if (project != null) {
File basedir = project.getBasedir();
if (basedir != null) {
return basedir.toPath().resolve(relpath);
}
}
// Fallback to current working directory if project is not available
return java.nio.file.Paths.get(relpath);
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should use LegacySupport here to get the current maven project and return the basedir then.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to use LegacySupport to get the current Maven project and resolve paths against the project basedir. Falls back to current working directory if project is not available. See commit 495f4ad.

}

@Override
public void markDerived(Path file) {
// No-op in the default implementation
// Custom implementations (e.g., IDE integrations) can override this
// to provide actual derived file tracking
}

@Override
public void copy(Path source, Path target) throws IOException {
if (source == null) {
throw new IllegalArgumentException("source cannot be null");
}
if (target == null) {
throw new IllegalArgumentException("target cannot be null");
}

// Only copy if the target is not up-to-date with the source
if (!isUptodate(target, source)) {
// Ensure parent directory exists
Path parentDir = target.getParent();
if (parentDir != null && !Files.exists(parentDir)) {
Files.createDirectories(parentDir);
}

// Copy using Files API and newOutputStream to ensure proper change detection
try (InputStream in = Files.newInputStream(source);
OutputStream out = newOutputStream(target)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
}
}
}
Loading