Common base implementations for the ShrinkWrap project enabling declarative assembly of Java archives (JAR, WAR, EAR, RAR) in code
—
Path and filter utilities provide comprehensive support for archive path manipulation, content filtering, and selective archive operations.
Core implementation of the ArchivePath interface for representing archive entry paths.
public class BasicPath implements ArchivePath, Comparable<ArchivePath>Constructors:
public BasicPath() // Root path "/"
public BasicPath(String context) // Path from string
public BasicPath(ArchivePath basePath, String context) // Path from base + contextKey Methods:
public String get() // Get full path string
public ArchivePath getParent() // Get parent path
public Set<ArchivePath> getChildPaths() // Get child paths
public boolean isRoot() // Check if root path
public int compareTo(ArchivePath other) // Path comparisonUsage Examples:
// Create paths
ArchivePath root = new BasicPath(); // "/"
ArchivePath webInf = new BasicPath("/WEB-INF"); // "/WEB-INF"
ArchivePath classes = new BasicPath(webInf, "classes"); // "/WEB-INF/classes"
// Path operations
String pathString = classes.get(); // "/WEB-INF/classes"
ArchivePath parent = classes.getParent(); // "/WEB-INF"
boolean isRoot = root.isRoot(); // trueUtility class for path manipulation and normalization.
public class PathUtilKey Methods:
public static String normalize(String path)
public static boolean isValid(String path)
public static String getParent(String path)
public static String getName(String path)
public static String getExtension(String path)Usage:
// Path normalization
String normalized = PathUtil.normalize("//META-INF/../classes/./MyClass.class");
// Result: "/classes/MyClass.class"
// Path validation
boolean valid = PathUtil.isValid("/META-INF/MANIFEST.MF"); // true
// Path components
String parent = PathUtil.getParent("/WEB-INF/classes/MyClass.class"); // "/WEB-INF/classes"
String name = PathUtil.getName("/WEB-INF/classes/MyClass.class"); // "MyClass.class"
String ext = PathUtil.getExtension("/WEB-INF/classes/MyClass.class"); // "class"Filter that includes all archive paths without restriction.
public class IncludeAllPaths implements Filter<ArchivePath>Methods:
public boolean include(ArchivePath object) // Always returns trueFilter that includes only specified paths.
public class IncludePaths implements Filter<ArchivePath>Constructors:
public IncludePaths(String... paths)
public IncludePaths(ArchivePath... paths)Usage:
Filter<ArchivePath> filter = new IncludePaths(
"/META-INF/MANIFEST.MF",
"/WEB-INF/web.xml"
);Filter that excludes specified paths.
public class ExcludePaths implements Filter<ArchivePath>Constructors:
public ExcludePaths(String... paths)
public ExcludePaths(ArchivePath... paths)Usage:
Filter<ArchivePath> filter = new ExcludePaths(
"/temp",
"/logs",
"/backup"
);Filter using regular expressions for path inclusion.
public class IncludeRegExpPaths implements Filter<ArchivePath>Constructors:
public IncludeRegExpPaths(String... expressions)
public IncludeRegExpPaths(Pattern... patterns)Usage:
// Include only Java class files and XML files
Filter<ArchivePath> filter = new IncludeRegExpPaths(
".*\\.class$",
".*\\.xml$"
);
// Include files in specific directories
Filter<ArchivePath> filter = new IncludeRegExpPaths(
"/WEB-INF/.*",
"/META-INF/.*"
);Filter using regular expressions for path exclusion.
public class ExcludeRegExpPaths implements Filter<ArchivePath>Constructors:
public ExcludeRegExpPaths(String... expressions)
public ExcludeRegExpPaths(Pattern... patterns)Usage:
// Exclude test files and temporary files
Filter<ArchivePath> filter = new ExcludeRegExpPaths(
".*Test\\.class$",
".*\\.tmp$",
".*\\.bak$"
);
// Exclude specific directories
Filter<ArchivePath> filter = new ExcludeRegExpPaths(
"/test/.*",
"/temp/.*"
);Filter that includes all classes without restriction.
public class IncludeAllClasses implements Filter<Class<?>>Methods:
public boolean include(Class<?> clazz) // Always returns trueFilters can be combined for complex filtering logic:
// Combine filters with logical operations
Filter<ArchivePath> webResources = new IncludeRegExpPaths("/web/.*");
Filter<ArchivePath> noTemp = new ExcludeRegExpPaths(".*\\.tmp$");
// Composite filter (include web resources but exclude temp files)
Filter<ArchivePath> composite = Filters.and(webResources, noTemp);
// Use in archive operations
archive.merge(sourceArchive, composite);Safe path traversal with validation:
public class PathTraversal {
public static void walkPaths(Archive<?> archive, PathVisitor visitor) {
for (ArchivePath path : archive.getContent().keySet()) {
if (PathUtil.isValid(path.get())) {
visitor.visit(path, archive.get(path));
}
}
}
}
// Usage
PathTraversal.walkPaths(archive, (path, node) -> {
System.out.println("Found: " + path.get());
if (node.getAsset() != null) {
System.out.println(" Size: " + node.getAsset().getSize());
}
});Advanced pattern matching for complex path selection:
// Utility for complex path matching
public class PathMatcher {
public static boolean matches(ArchivePath path, String pattern) {
return path.get().matches(pattern);
}
public static List<ArchivePath> findMatching(Archive<?> archive, String pattern) {
return archive.getContent().keySet().stream()
.filter(path -> matches(path, pattern))
.collect(Collectors.toList());
}
}
// Find all JSP files in web application
List<ArchivePath> jspFiles = PathMatcher.findMatching(warArchive, ".*\\.jsp$");
// Find all configuration files
List<ArchivePath> configFiles = PathMatcher.findMatching(archive,
".*(config|properties|xml)$");Utilities for analyzing archive contents:
public class ArchiveAnalyzer {
public static Map<String, Integer> getFileTypeCounts(Archive<?> archive) {
Map<String, Integer> counts = new HashMap<>();
for (ArchivePath path : archive.getContent().keySet()) {
String extension = PathUtil.getExtension(path.get());
counts.merge(extension, 1, Integer::sum);
}
return counts;
}
public static long getTotalSize(Archive<?> archive) {
return archive.getContent().values().stream()
.filter(node -> node.getAsset() != null)
.mapToLong(node -> node.getAsset().getSize())
.sum();
}
}
// Usage
Map<String, Integer> types = ArchiveAnalyzer.getFileTypeCounts(archive);
long totalSize = ArchiveAnalyzer.getTotalSize(archive);Comprehensive path validation utilities:
public class PathValidator {
public static boolean isValidArchivePath(String path) {
return PathUtil.isValid(path) &&
!path.contains("..") && // No parent directory traversal
!path.startsWith("../") && // No relative parent access
path.length() <= 260; // Path length limit
}
public static void validateArchivePaths(Archive<?> archive) {
for (ArchivePath path : archive.getContent().keySet()) {
if (!isValidArchivePath(path.get())) {
throw new IllegalArchivePathException(
"Invalid path: " + path.get());
}
}
}
}Optimized filter implementations with caching:
public class CachedRegExpFilter implements Filter<ArchivePath> {
private final Pattern pattern;
private final Map<String, Boolean> cache = new ConcurrentHashMap<>();
public CachedRegExpFilter(String regex) {
this.pattern = Pattern.compile(regex);
}
@Override
public boolean include(ArchivePath path) {
return cache.computeIfAbsent(path.get(),
pathString -> pattern.matcher(pathString).matches());
}
}Efficient batch operations on paths:
// Batch path creation
List<ArchivePath> createPaths(List<String> pathStrings) {
return pathStrings.parallelStream()
.map(BasicPath::new)
.collect(Collectors.toList());
}
// Batch filtering
List<ArchivePath> filterPaths(Collection<ArchivePath> paths, Filter<ArchivePath> filter) {
return paths.parallelStream()
.filter(filter::include)
.collect(Collectors.toList());
}Install with Tessl CLI
npx tessl i tessl/maven-org-jboss-shrinkwrap--shrinkwrap-impl-base