@Prefix¶
Narrows a listing or walk to only S3 keys that begin with the given prefix.
Declaration¶
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Prefix {
String value();
}
Attributes¶
| Attribute | Type | Description |
|---|---|---|
value |
String |
The prefix to filter by, relative to the current proxy's position |
Description¶
The prefix value is relative — it is appended to the current proxy's position
in the key hierarchy. The prefix is sent server-side in the ListObjects
request, so keys that do not match are never returned by AWS and never consume
bandwidth or processing.
@Prefix works on both immediate listing methods and @Recursive methods.
Examples¶
Consider a Maven repository stored in S3:
repository/
org/apache/maven/maven-core/3.9.6/maven-core-3.9.6.jar
org/apache/maven/maven-core/3.9.6/maven-core-3.9.6.pom
org/apache/tomcat/tomcat/10.1.18/tomcat-10.1.18.jar
com/google/guava/guava/33.0/guava-33.0.jar
junit/junit/4.13.2/junit-4.13.2.jar
Flat listing¶
On a method without @Recursive, the prefix narrows an immediate listing:
public interface Repository extends S3.Dir {
@Prefix("org/apache")
Stream<S3File> apacheArtifacts();
}
Recursive listing¶
Combined with @Recursive, the prefix limits which keys are returned,
reducing both the bandwidth and the volume of results:
public interface Repository extends S3.Dir {
// Recursively list only keys under the org/ namespace
// — com/ and junit/ are never fetched from AWS
@Recursive
@Prefix("org/")
Stream<S3File> orgArtifacts();
}
Without @Prefix, the recursive listing returns every key in the
directory. With @Prefix("org/"), only keys under org/ are returned
by AWS. In a large repository with thousands of groupIds, this can
significantly reduce the data transferred.
Partial prefixes¶
The prefix does not need to end with a delimiter — it can be any key prefix, including a partial name. This is useful for matching a subset of entries within a single directory:
public interface Tomcat extends S3.Dir {
// Only version directories starting with "10."
// Matches 10.1.18/, 10.1.19/, etc.
// Skips 9.0.85/, 11.0.0/, etc.
@Prefix("10.")
Stream<S3File> version10();
}
S3File tomcatDir = bucket.getFile("org/apache/tomcat/tomcat");
Tomcat tomcat = tomcatDir.as(Tomcat.class);
tomcat.version10().forEach(v -> System.out.println(v.getName()));
Because the prefix is applied server-side, AWS returns only the matching keys. A directory with hundreds of version entries can be efficiently narrowed to just the relevant subset.
Input Validation on Single-Arg Methods¶
When @Prefix is placed on a single-arg proxy method, it validates that
the input name starts with the prefix. If not, an IllegalArgumentException
is thrown:
public interface Logs extends S3.Dir {
@Prefix("app-")
Stream<S3File> appLogs(); // server-side prefix filter
@Prefix("app-")
S3File appLog(String name); // validates name starts with "app-"
}
logs.appLog("app-2025-03-01.log"); // OK
logs.appLog("system-2025-03-01.log"); // throws IllegalArgumentException
Note that @Prefix is method-only (@Target(ElementType.METHOD)), so it
cannot be placed on a type. For listing methods, the prefix is applied
server-side in the ListObjects request. For single-arg methods, it is
checked client-side against the input name.
See Also¶
- @Recursive — recursive listing
- @Suffix — client-side suffix filtering
- @Match — client-side regex filtering
- @Filter — client-side filtering with arbitrary predicates
- Filtering — server-side vs client-side filtering