14.4 Advanced File System Patterns

Master advanced file system operations including locking, memory-mapped files, custom file systems, and specialized patterns.

File Locking

Advisory File Locking:

import java.nio.channels.*;
import java.nio.file.*;
import java.io.IOException;

/**
 * File locking for coordinating access
 */
public class FileLockExample {

    /**
     * Exclusive lock (write lock)
     */
    public static void exclusiveLock(Path file) throws IOException {
        try (FileChannel channel = FileChannel.open(file,
                StandardOpenOption.READ,
                StandardOpenOption.WRITE,
                StandardOpenOption.CREATE)) {

            // Acquire exclusive lock (blocks until available)
            FileLock lock = channel.lock();

            try {
                System.out.println("Exclusive lock acquired");

                // Perform exclusive operations
                // Write to file, etc.

                Thread.sleep(5000); // Simulate work

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.release();
                System.out.println("Lock released");
            }
        }
    }

    /**
     * Shared lock (read lock)
     */
    public static void sharedLock(Path file) throws IOException {
        try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ)) {

            // Acquire shared lock
            FileLock lock = channel.lock(0, Long.MAX_VALUE, true); // shared=true

            try {
                System.out.println("Shared lock acquired");

                // Read file
                String content = Files.readString(file);
                System.out.println("Content: " + content);

            } finally {
                lock.release();
            }
        }
    }

    /**
     * Try lock (non-blocking)
     */
    public static void tryLock(Path file) throws IOException {
        try (FileChannel channel = FileChannel.open(file,
                StandardOpenOption.READ,
                StandardOpenOption.WRITE,
                StandardOpenOption.CREATE)) {

            // Try to acquire lock (returns immediately)
            FileLock lock = channel.tryLock();

            if (lock == null) {
                System.out.println("Could not acquire lock - file locked by another process");
                return;
            }

            try {
                System.out.println("Lock acquired");
                // Do work
            } finally {
                lock.release();
            }
        }
    }

    /**
     * Partial file locking
     */
    public static void partialLock(Path file) throws IOException {
        try (FileChannel channel = FileChannel.open(file,
                StandardOpenOption.READ,
                StandardOpenOption.WRITE)) {

            // Lock only first 1024 bytes
            FileLock lock = channel.lock(0, 1024, false);

            try {
                System.out.println("Locked bytes 0-1024");
                // Modify locked region
            } finally {
                lock.release();
            }
        }
    }
}

Lock-Based Safe Write:

public class SafeFileWriter {

    /**
     * Write with exclusive lock
     */
    public static void safeWrite(Path file, String content) throws IOException {
        try (FileChannel channel = FileChannel.open(file,
                StandardOpenOption.CREATE,
                StandardOpenOption.WRITE,
                StandardOpenOption.TRUNCATE_EXISTING)) {

            // Acquire exclusive lock
            try (FileLock lock = channel.lock()) {
                // Write content
                channel.write(java.nio.ByteBuffer.wrap(content.getBytes()));

                // Force to disk
                channel.force(true);
            }
        }
    }

    /**
     * Read with shared lock
     */
    public static String safeRead(Path file) throws IOException {
        try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ)) {

            try (FileLock lock = channel.lock(0, Long.MAX_VALUE, true)) {
                return Files.readString(file);
            }
        }
    }
}

Memory-Mapped Files

Basic Memory Mapping:

import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * Memory-mapped file operations
 */
public class MemoryMappedFile {

    /**
     * Read using memory mapping
     */
    public static void readMapped(Path file) throws IOException {
        try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ)) {

            long size = channel.size();

            // Map entire file to memory
            MappedByteBuffer buffer = channel.map(
                FileChannel.MapMode.READ_ONLY,
                0,
                size
            );

            // Read from buffer
            byte[] data = new byte[(int) size];
            buffer.get(data);

            System.out.println("Read " + data.length + " bytes");
        }
    }

    /**
     * Write using memory mapping
     */
    public static void writeMapped(Path file, byte[] data) throws IOException {
        try (FileChannel channel = FileChannel.open(file,
                StandardOpenOption.CREATE,
                StandardOpenOption.READ,
                StandardOpenOption.WRITE)) {

            // Map file to memory
            MappedByteBuffer buffer = channel.map(
                FileChannel.MapMode.READ_WRITE,
                0,
                data.length
            );

            // Write to buffer
            buffer.put(data);

            // Force to disk
            buffer.force();
        }
    }

    /**
     * Large file processing with memory mapping
     */
    public static long processLargeFile(Path file) throws IOException {
        try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ)) {

            long size = channel.size();
            long chunkSize = 1024 * 1024 * 100; // 100 MB chunks
            long position = 0;
            long checksum = 0;

            while (position < size) {
                long remaining = size - position;
                long mapSize = Math.min(remaining, chunkSize);

                // Map chunk
                MappedByteBuffer buffer = channel.map(
                    FileChannel.MapMode.READ_ONLY,
                    position,
                    mapSize
                );

                // Process chunk
                for (int i = 0; i < mapSize; i++) {
                    checksum += buffer.get() & 0xFF;
                }

                position += mapSize;
            }

            return checksum;
        }
    }
}

Shared Memory Between Processes:

/**
 * Shared memory region for IPC
 */
public class SharedMemoryRegion {
    private final Path file;
    private final int size;

    public SharedMemoryRegion(Path file, int size) throws IOException {
        this.file = file;
        this.size = size;

        // Create file if doesn't exist
        if (!Files.exists(file)) {
            try (FileChannel channel = FileChannel.open(file,
                    StandardOpenOption.CREATE,
                    StandardOpenOption.READ,
                    StandardOpenOption.WRITE)) {

                // Allocate space
                channel.write(java.nio.ByteBuffer.allocate(size));
            }
        }
    }

    /**
     * Writer process
     */
    public void write(String message) throws IOException {
        try (FileChannel channel = FileChannel.open(file,
                StandardOpenOption.READ,
                StandardOpenOption.WRITE)) {

            MappedByteBuffer buffer = channel.map(
                FileChannel.MapMode.READ_WRITE,
                0,
                size
            );

            byte[] bytes = message.getBytes();
            buffer.putInt(bytes.length);
            buffer.put(bytes);
            buffer.force();
        }
    }

    /**
     * Reader process
     */
    public String read() throws IOException {
        try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ)) {

            MappedByteBuffer buffer = channel.map(
                FileChannel.MapMode.READ_ONLY,
                0,
                size
            );

            int length = buffer.getInt();
            byte[] bytes = new byte[length];
            buffer.get(bytes);

            return new String(bytes);
        }
    }
}

Temporary Files and Directories

Creating Temporary Files:

// Create temp file in system temp directory
Path tempFile = Files.createTempFile("upload-", ".tmp");
System.out.println("Temp file: " + tempFile);

// Create temp file in specific directory
Path tempDir = Path.of("/var/tmp");
Path tempFile2 = Files.createTempFile(tempDir, "data-", ".bin");

// With file attributes
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------");
FileAttribute<Set<PosixFilePermission>> attr = 
    PosixFilePermissions.asFileAttribute(perms);

Path secureTempFile = Files.createTempFile("secret-", ".dat", attr);

// Delete on exit (not recommended for long-running apps)
tempFile.toFile().deleteOnExit();

// Better: use try-with-resources pattern
try (var ignored = tempFile) {
    // Use temp file
    Files.writeString(tempFile, "temporary data");

    // Process file

} finally {
    // Clean up
    Files.deleteIfExists(tempFile);
}

Creating Temporary Directories:

// Create temp directory
Path tempDir = Files.createTempDirectory("work-");
System.out.println("Temp directory: " + tempDir);

// Create in specific location
Path tempDir2 = Files.createTempDirectory(
    Path.of("/var/tmp"),
    "cache-"
);

// With attributes
Path secureTempDir = Files.createTempDirectory("secure-", attr);

// Use and clean up
try {
    // Work in temp directory
    Path workFile = tempDir.resolve("data.txt");
    Files.writeString(workFile, "content");

    // Process files

} finally {
    // Delete entire directory tree
    deleteDirectory(tempDir);
}

private static void deleteDirectory(Path directory) throws IOException {
    if (!Files.exists(directory)) return;

    Files.walk(directory)
        .sorted(Comparator.reverseOrder())
        .forEach(path -> {
            try {
                Files.delete(path);
            } catch (IOException e) {
                System.err.println("Failed to delete: " + path);
            }
        });
}

Temporary File Manager:

/**
 * Manage temporary files with automatic cleanup
 */
public class TempFileManager implements AutoCloseable {
    private final Set<Path> tempFiles = ConcurrentHashMap.newKeySet();
    private final Path baseDir;

    public TempFileManager() throws IOException {
        this.baseDir = Files.createTempDirectory("app-");
        System.out.println("Temp directory: " + baseDir);
    }

    /**
     * Create tracked temp file
     */
    public Path createTempFile(String prefix, String suffix) throws IOException {
        Path tempFile = Files.createTempFile(baseDir, prefix, suffix);
        tempFiles.add(tempFile);
        return tempFile;
    }

    /**
     * Create tracked temp directory
     */
    public Path createTempDirectory(String prefix) throws IOException {
        Path tempDir = Files.createTempDirectory(baseDir, prefix);
        tempFiles.add(tempDir);
        return tempDir;
    }

    /**
     * Clean up specific file
     */
    public void cleanup(Path path) {
        try {
            if (Files.isDirectory(path)) {
                deleteDirectory(path);
            } else {
                Files.deleteIfExists(path);
            }
            tempFiles.remove(path);
        } catch (IOException e) {
            System.err.println("Cleanup failed: " + path);
        }
    }

    @Override
    public void close() {
        // Clean up all tracked files
        tempFiles.forEach(this::cleanup);

        // Remove base directory
        try {
            Files.deleteIfExists(baseDir);
        } catch (IOException e) {
            System.err.println("Failed to delete base dir: " + baseDir);
        }
    }
}

// Usage
try (TempFileManager manager = new TempFileManager()) {
    Path temp1 = manager.createTempFile("data-", ".txt");
    Path temp2 = manager.createTempDirectory("work-");

    // Use temp files
    Files.writeString(temp1, "data");

    // Automatic cleanup on close
}

Creating and Managing Symlinks:

// Create symbolic link
Path target = Path.of("/var/data/original.txt");
Path link = Path.of("/tmp/link.txt");

Files.createSymbolicLink(link, target);

// Read link target
Path linkTarget = Files.readSymbolicLink(link);
System.out.println("Link points to: " + linkTarget);

// Check if path is symbolic link
boolean isSymlink = Files.isSymbolicLink(link);

// Follow vs don't follow links
BasicFileAttributes attrs1 = Files.readAttributes(link, 
    BasicFileAttributes.class, 
    LinkOption.NOFOLLOW_LINKS); // Read link itself

BasicFileAttributes attrs2 = Files.readAttributes(link,
    BasicFileAttributes.class); // Read target

// Copy symlink (not target)
Path linkCopy = Path.of("/tmp/link-copy.txt");
Files.copy(link, linkCopy, LinkOption.NOFOLLOW_LINKS);

// Resolve real path (follows symlinks)
Path realPath = link.toRealPath();
System.out.println("Real path: " + realPath);

Hard Links:

// Create hard link
Path original = Path.of("/data/file.txt");
Path hardLink = Path.of("/backup/file-link.txt");

Files.createLink(hardLink, original);

// Both paths refer to same inode
// Deleting one doesn't affect the other

Custom File Systems

ZIP File System:

import java.net.URI;
import java.nio.file.*;
import java.util.Map;

/**
 * Work with ZIP files as file systems
 */
public class ZipFileSystemExample {

    /**
     * Create new ZIP file
     */
    public static void createZip(Path zipPath, Map<String, String> files) 
            throws IOException {

        Map<String, String> env = Map.of("create", "true");
        URI uri = URI.create("jar:" + zipPath.toUri());

        try (FileSystem zipFs = FileSystems.newFileSystem(uri, env)) {
            // Add files to ZIP
            for (Map.Entry<String, String> entry : files.entrySet()) {
                Path pathInZip = zipFs.getPath(entry.getKey());

                // Create parent directories
                Path parent = pathInZip.getParent();
                if (parent != null) {
                    Files.createDirectories(parent);
                }

                // Write file
                Files.writeString(pathInZip, entry.getValue());
            }
        }
    }

    /**
     * Read from ZIP file
     */
    public static void readZip(Path zipPath) throws IOException {
        URI uri = URI.create("jar:" + zipPath.toUri());

        try (FileSystem zipFs = FileSystems.newFileSystem(uri, Map.of())) {
            Path root = zipFs.getPath("/");

            // Walk ZIP contents
            Files.walk(root).forEach(path -> {
                try {
                    if (Files.isRegularFile(path)) {
                        String content = Files.readString(path);
                        System.out.println("File: " + path);
                        System.out.println("Content: " + content);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    /**
     * Update file in ZIP
     */
    public static void updateZip(Path zipPath, String fileName, String newContent)
            throws IOException {

        URI uri = URI.create("jar:" + zipPath.toUri());

        try (FileSystem zipFs = FileSystems.newFileSystem(uri, Map.of())) {
            Path fileInZip = zipFs.getPath(fileName);
            Files.writeString(fileInZip, newContent);
        }
    }

    /**
     * Extract ZIP contents
     */
    public static void extractZip(Path zipPath, Path destDir) throws IOException {
        URI uri = URI.create("jar:" + zipPath.toUri());

        try (FileSystem zipFs = FileSystems.newFileSystem(uri, Map.of())) {
            Path root = zipFs.getPath("/");

            Files.walk(root).forEach(source -> {
                try {
                    if (Files.isRegularFile(source)) {
                        Path dest = destDir.resolve(root.relativize(source).toString());

                        // Create parent directories
                        Files.createDirectories(dest.getParent());

                        // Copy file
                        Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    // Example usage
    public static void main(String[] args) throws IOException {
        Path zipPath = Path.of("archive.zip");

        // Create ZIP
        Map<String, String> files = Map.of(
            "readme.txt", "This is a readme",
            "data/config.json", "{\"version\": \"1.0\"}",
            "data/users.csv", "id,name\n1,Alice\n2,Bob"
        );
        createZip(zipPath, files);

        // Read ZIP
        readZip(zipPath);

        // Update file in ZIP
        updateZip(zipPath, "readme.txt", "Updated readme content");

        // Extract ZIP
        extractZip(zipPath, Path.of("extracted"));
    }
}

JAR File System:

/**
 * Access JAR files as file systems
 */
public class JarFileSystemExample {

    public static void listJarContents(Path jarPath) throws IOException {
        URI uri = URI.create("jar:" + jarPath.toUri());

        try (FileSystem jarFs = FileSystems.newFileSystem(uri, Map.of())) {
            Path root = jarFs.getPath("/");

            Files.walk(root)
                .filter(Files::isRegularFile)
                .forEach(System.out::println);
        }
    }

    public static void readClassFile(Path jarPath, String className) 
            throws IOException {

        URI uri = URI.create("jar:" + jarPath.toUri());

        try (FileSystem jarFs = FileSystems.newFileSystem(uri, Map.of())) {
            String classPath = className.replace('.', '/') + ".class";
            Path classFile = jarFs.getPath(classPath);

            if (Files.exists(classFile)) {
                byte[] bytes = Files.readAllBytes(classFile);
                System.out.println("Class size: " + bytes.length + " bytes");
            }
        }
    }
}

File Stores and Space Information

File Store Information:

/**
 * Get file system space information
 */
public class FileStoreInfo {

    public static void printFileStoreInfo(Path path) throws IOException {
        FileStore store = Files.getFileStore(path);

        System.out.println("Name: " + store.name());
        System.out.println("Type: " + store.type());
        System.out.println("Read-only: " + store.isReadOnly());

        long total = store.getTotalSpace();
        long usable = store.getUsableSpace();
        long unallocated = store.getUnallocatedSpace();

        System.out.println("Total space: " + formatSize(total));
        System.out.println("Usable space: " + formatSize(usable));
        System.out.println("Unallocated space: " + formatSize(unallocated));
        System.out.println("Used: " + formatSize(total - unallocated));

        // Usage percentage
        double usedPercent = ((total - unallocated) * 100.0) / total;
        System.out.printf("Usage: %.2f%%%n", usedPercent);
    }

    public static void printAllFileStores() {
        for (FileStore store : FileSystems.getDefault().getFileStores()) {
            try {
                System.out.println("\n" + store.name());
                System.out.println("  Type: " + store.type());
                System.out.println("  Total: " + formatSize(store.getTotalSpace()));
                System.out.println("  Available: " + formatSize(store.getUsableSpace()));
            } catch (IOException e) {
                System.err.println("  Error: " + e.getMessage());
            }
        }
    }

    private static String formatSize(long bytes) {
        if (bytes < 1024) return bytes + " B";
        int exp = (int) (Math.log(bytes) / Math.log(1024));
        String pre = "KMGTPE".charAt(exp - 1) + "";
        return String.format("%.2f %sB", bytes / Math.pow(1024, exp), pre);
    }

    /**
     * Check if enough space available
     */
    public static boolean hasEnoughSpace(Path path, long requiredBytes) 
            throws IOException {
        FileStore store = Files.getFileStore(path);
        return store.getUsableSpace() >= requiredBytes;
    }
}

Real-World Example: Document Archive System

import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.time.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.zip.*;

/**
 * Complete document archive system with compression, 
 * indexing, and retrieval
 */
public class DocumentArchiveSystem implements AutoCloseable {
    private final Path archiveDir;
    private final Path indexFile;
    private final Map<String, DocumentMetadata> index = new ConcurrentHashMap<>();

    public static class DocumentMetadata {
        public final String id;
        public final String originalName;
        public final Path archivePath;
        public final long size;
        public final Instant archivedAt;
        public final Map<String, String> tags;

        public DocumentMetadata(String id, String originalName, Path archivePath,
                               long size, Instant archivedAt, Map<String, String> tags) {
            this.id = id;
            this.originalName = originalName;
            this.archivePath = archivePath;
            this.size = size;
            this.archivedAt = archivedAt;
            this.tags = tags;
        }
    }

    public DocumentArchiveSystem(Path archiveDir) throws IOException {
        this.archiveDir = archiveDir;
        this.indexFile = archiveDir.resolve("index.dat");

        Files.createDirectories(archiveDir);
        loadIndex();
    }

    /**
     * Archive a document
     */
    public String archiveDocument(Path document, Map<String, String> tags) 
            throws IOException {

        // Generate unique ID
        String id = UUID.randomUUID().toString();

        // Create archive path
        String yearMonth = LocalDate.now().toString().substring(0, 7);
        Path archiveSubdir = archiveDir.resolve(yearMonth);
        Files.createDirectories(archiveSubdir);

        Path archivePath = archiveSubdir.resolve(id + ".gz");

        // Compress and archive
        compressFile(document, archivePath);

        // Create metadata
        DocumentMetadata metadata = new DocumentMetadata(
            id,
            document.getFileName().toString(),
            archivePath,
            Files.size(archivePath),
            Instant.now(),
            new HashMap<>(tags)
        );

        // Update index
        index.put(id, metadata);
        saveIndex();

        System.out.println("Archived: " + document.getFileName() + " -> " + id);
        return id;
    }

    /**
     * Retrieve document
     */
    public void retrieveDocument(String id, Path destination) throws IOException {
        DocumentMetadata metadata = index.get(id);
        if (metadata == null) {
            throw new IllegalArgumentException("Document not found: " + id);
        }

        decompressFile(metadata.archivePath, destination);
        System.out.println("Retrieved: " + id + " -> " + destination);
    }

    /**
     * Search by tags
     */
    public List<DocumentMetadata> search(Map<String, String> searchTags) {
        return index.values().stream()
            .filter(doc -> matchesTags(doc.tags, searchTags))
            .collect(Collectors.toList());
    }

    private boolean matchesTags(Map<String, String> docTags, 
                                Map<String, String> searchTags) {
        return searchTags.entrySet().stream()
            .allMatch(entry -> entry.getValue().equals(docTags.get(entry.getKey())));
    }

    /**
     * List all documents
     */
    public List<DocumentMetadata> listAll() {
        return new ArrayList<>(index.values());
    }

    /**
     * Delete archived document
     */
    public boolean deleteDocument(String id) throws IOException {
        DocumentMetadata metadata = index.remove(id);
        if (metadata == null) {
            return false;
        }

        Files.deleteIfExists(metadata.archivePath);
        saveIndex();

        System.out.println("Deleted: " + id);
        return true;
    }

    /**
     * Get statistics
     */
    public void printStatistics() throws IOException {
        long totalSize = index.values().stream()
            .mapToLong(doc -> doc.size)
            .sum();

        System.out.println("Total documents: " + index.size());
        System.out.println("Total size: " + formatSize(totalSize));

        // Space usage
        FileStore store = Files.getFileStore(archiveDir);
        System.out.println("Archive location: " + store.name());
        System.out.println("Available space: " + formatSize(store.getUsableSpace()));
    }

    /**
     * Compress file
     */
    private void compressFile(Path source, Path destination) throws IOException {
        try (InputStream in = Files.newInputStream(source);
             OutputStream out = Files.newOutputStream(destination,
                 StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
             GZIPOutputStream gzip = new GZIPOutputStream(out)) {

            byte[] buffer = new byte[8192];
            int len;
            while ((len = in.read(buffer)) != -1) {
                gzip.write(buffer, 0, len);
            }
        }
    }

    /**
     * Decompress file
     */
    private void decompressFile(Path source, Path destination) throws IOException {
        try (InputStream in = Files.newInputStream(source);
             GZIPInputStream gzip = new GZIPInputStream(in);
             OutputStream out = Files.newOutputStream(destination,
                 StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {

            byte[] buffer = new byte[8192];
            int len;
            while ((len = gzip.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
        }
    }

    /**
     * Load index from disk
     */
    private void loadIndex() throws IOException {
        if (!Files.exists(indexFile)) {
            return;
        }

        try (ObjectInputStream ois = new ObjectInputStream(
                Files.newInputStream(indexFile))) {
            @SuppressWarnings("unchecked")
            Map<String, DocumentMetadata> loaded = 
                (Map<String, DocumentMetadata>) ois.readObject();
            index.putAll(loaded);
        } catch (ClassNotFoundException e) {
            throw new IOException("Failed to load index", e);
        }
    }

    /**
     * Save index to disk
     */
    private void saveIndex() throws IOException {
        Path tempFile = Files.createTempFile(archiveDir, "index-", ".tmp");

        try (ObjectOutputStream oos = new ObjectOutputStream(
                Files.newOutputStream(tempFile))) {
            oos.writeObject(new HashMap<>(index));
        }

        Files.move(tempFile, indexFile, 
            StandardCopyOption.ATOMIC_MOVE,
            StandardCopyOption.REPLACE_EXISTING);
    }

    private static String formatSize(long bytes) {
        if (bytes < 1024) return bytes + " B";
        int exp = (int) (Math.log(bytes) / Math.log(1024));
        return String.format("%.2f %sB", 
            bytes / Math.pow(1024, exp), 
            "KMGTPE".charAt(exp - 1));
    }

    @Override
    public void close() throws IOException {
        saveIndex();
    }

    // Example usage
    public static void main(String[] args) throws IOException {
        try (DocumentArchiveSystem archive = 
                new DocumentArchiveSystem(Path.of("/var/archive"))) {

            // Archive documents
            String id1 = archive.archiveDocument(
                Path.of("invoice.pdf"),
                Map.of("type", "invoice", "year", "2024", "customer", "ACME")
            );

            String id2 = archive.archiveDocument(
                Path.of("contract.pdf"),
                Map.of("type", "contract", "year", "2024", "customer", "ACME")
            );

            // Search
            List<DocumentMetadata> results = archive.search(
                Map.of("customer", "ACME", "year", "2024")
            );
            System.out.println("Found " + results.size() + " documents");

            // Retrieve
            archive.retrieveDocument(id1, Path.of("retrieved-invoice.pdf"));

            // Statistics
            archive.printStatistics();
        }
    }
}

Best Practices

1. Use Appropriate Tools:

// Memory-mapped for large file processing
// File locking for concurrent access
// Temp files for intermediate results

2. Clean Up Resources:

// Always clean up temp files
try (TempFileManager manager = new TempFileManager()) {
    // Use temp files
} // Automatic cleanup

3. Handle Platform Differences:

// Check file system capabilities
boolean supportsPosix = FileSystems.getDefault()
    .supportedFileAttributeViews()
    .contains("posix");

4. Use Atomic Operations:

// Atomic file replacement
Files.move(temp, target, StandardCopyOption.ATOMIC_MOVE);

5. Monitor Resource Usage:

// Check available space before operations
if (!hasEnoughSpace(path, requiredSize)) {
    throw new IOException("Insufficient disk space");
}

These advanced patterns enable sophisticated file system operations for production Java applications.