Die Files API macht Dateioperationen lesbar, muss aber konsequent mit Limits, atomaren Moves und Fehlerkontext genutzt werden.
BP-026 - Atomar schreiben für Abrechnungsbeleg
Ausgangspunkt-Code
import java.nio.file.*;
import java.util.List;
final class Abrechnungsbeleg026Bad {
void publish(Path target, List<String> rows) throws Exception {
Files.write(target, rows); // Leser sehen eventuell halbe Datei
}
}
Ziel-Code
import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
final class Abrechnungsbeleg026AtomicWriter {
void publish(Path target, List<String> rows) throws Exception {
Files.createDirectories(target.toAbsolutePath().getParent());
Path tmp = Files.createTempFile(target.getParent(), target.getFileName().toString(), ".tmp");
try (BufferedWriter writer = Files.newBufferedWriter(tmp, StandardCharsets.UTF_8)) {
for (String row : rows) {
writer.write(row);
writer.newLine();
}
}
Files.move(tmp, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
}
}
BP-027 - Staging-Verzeichnis für Steuerdatei
Ausgangspunkt-Code
import java.io.InputStream;
import java.nio.file.*;
final class Steuerdatei027Bad {
Path store(InputStream input, Path target) throws Exception {
Files.copy(input, target, StandardCopyOption.REPLACE_EXISTING);
return target;
}
}
Ziel-Code
import java.io.InputStream;
import java.nio.file.*;
final class Steuerdatei027Staging {
Path store(InputStream input, Path target) throws Exception {
Files.createDirectories(target.getParent());
Path staged = Files.createTempFile(target.getParent(), "stage-", ".part");
try {
Files.copy(input, staged, StandardCopyOption.REPLACE_EXISTING);
Files.move(staged, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
return target;
} catch (Exception ex) {
Files.deleteIfExists(staged);
throw ex;
}
}
}
BP-028 - Dateibaum für Legacy-Migration kontrolliert laufen
Ausgangspunkt-Code
import java.nio.file.*;
final class LegacyMigration028Bad {
long count(Path root) throws Exception {
return Files.walk(root).count(); // Stream wird nicht geschlossen
}
}
Ziel-Code
import java.nio.file.*;
import java.util.stream.Stream;
final class LegacyMigration028TreeScan {
long countRegularFiles(Path root, int maxDepth) throws Exception {
try (Stream<Path> paths = Files.walk(root, maxDepth)) {
return paths.filter(Files::isRegularFile).count();
}
}
}
BP-029 - Sichere Pfadauflösung für Kubernetes-Config
Ausgangspunkt-Code
import java.nio.file.*;
final class KubernetesConfig029Bad {
byte[] read(String userName) throws Exception {
Path file = Path.of("/srv/app/data/" + userName);
return Files.readAllBytes(file);
}
}
Ziel-Code
import java.io.IOException;
import java.nio.file.*;
final class KubernetesConfig029PathGuard {
private final Path root;
KubernetesConfig029PathGuard(Path root) throws IOException {
this.root = root.toRealPath();
}
byte[] readAllowed(String userName) throws IOException {
Path candidate = root.resolve(userName).normalize();
if (!candidate.startsWith(root)) {
throw new SecurityException("Pfad verlässt Root: " + userName);
}
Path real = candidate.toRealPath(LinkOption.NOFOLLOW_LINKS);
if (!real.startsWith(root)) {
throw new SecurityException("Symlink verlässt Root: " + userName);
}
return Files.readAllBytes(real);
}
}
BP-030 - Große OpenShift-Secret-Export-Dateien streamen und begrenzen
Ausgangspunkt-Code
import java.nio.file.*;
final class OpenShiftSecretExport030Bad {
byte[] load(Path file) throws Exception {
return Files.readAllBytes(file);
}
}
Ziel-Code
import java.io.InputStream;
import java.nio.file.*;
final class OpenShiftSecretExport030StreamingReader {
long countBytes(Path file, long maxBytes) throws Exception {
byte[] buffer = new byte[64 * 1024];
long total = 0;
try (InputStream in = Files.newInputStream(file)) {
int read;
while ((read = in.read(buffer)) != -1) {
total += read;
if (total > maxBytes) throw new IllegalStateException("Datei zu groß");
}
}
return total;
}
}
BP-031 - Explizites Charset bei Fehlerprotokoll-Textdateien
Ausgangspunkt-Code
import java.nio.file.*;
import java.util.List;
final class Fehlerprotokoll031Bad {
List<String> read(Path file) throws Exception {
return Files.readAllLines(file); // Plattform-Default kann abweichen
}
}
Ziel-Code
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
final class Fehlerprotokoll031Utf8Reader {
List<String> readUtf8(Path file) throws Exception {
return Files.readAllLines(file, StandardCharsets.UTF_8);
}
}
BP-032 - Ressourcen bei Mandanten-Backup zuverlässig schließen
Ausgangspunkt-Code
import java.io.*;
import java.nio.file.*;
final class MandantenBackup032Bad {
String firstLine(Path file) throws Exception {
BufferedReader reader = Files.newBufferedReader(file);
return reader.readLine(); // Reader bleibt bei Fehlern offen
}
}
Ziel-Code
import java.io.BufferedReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
final class MandantenBackup032ResourceSafe {
String firstLine(Path file) throws Exception {
try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
String line = reader.readLine();
return line == null ? "" : line;
}
}
}
BP-033 - Gepufferte Verarbeitung für Schnittstellenprotokoll
Ausgangspunkt-Code
import java.io.*;
import java.nio.file.*;
final class Schnittstellenprotokoll033Bad {
long sum(Path file) throws Exception {
long total = 0;
try (InputStream in = Files.newInputStream(file)) {
int b;
while ((b = in.read()) != -1) total += b;
}
return total;
}
}
Ziel-Code
import java.io.*;
import java.nio.file.*;
final class Schnittstellenprotokoll033Buffered {
long sum(Path file) throws Exception {
byte[] buffer = new byte[32 * 1024];
long total = 0;
try (InputStream in = new BufferedInputStream(Files.newInputStream(file))) {
int read;
while ((read = in.read(buffer)) != -1) {
for (int i = 0; i < read; i++) total += buffer[i] & 0xff;
}
}
return total;
}
}
BP-034 - FileChannel-Transfer für Job-Checkpoint
Ausgangspunkt-Code
import java.nio.file.*;
final class JobCheckpoint034Bad {
void copy(Path source, Path target) throws Exception {
Files.write(target, Files.readAllBytes(source));
}
}
Ziel-Code
import java.nio.channels.FileChannel;
import java.nio.file.*;
final class JobCheckpoint034ChannelCopy {
long copy(Path source, Path target) throws Exception {
try (FileChannel in = FileChannel.open(source, StandardOpenOption.READ);
FileChannel out = FileChannel.open(target, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
long pos = 0;
while (pos < in.size()) {
pos += in.transferTo(pos, in.size() - pos, out);
}
return pos;
}
}
}
BP-035 - Atomar schreiben für Event-Replay
Ausgangspunkt-Code
import java.nio.file.*;
import java.util.List;
final class EventReplay035Bad {
void publish(Path target, List<String> rows) throws Exception {
Files.write(target, rows); // Leser sehen eventuell halbe Datei
}
}
Ziel-Code
import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
final class EventReplay035AtomicWriter {
void publish(Path target, List<String> rows) throws Exception {
Files.createDirectories(target.toAbsolutePath().getParent());
Path tmp = Files.createTempFile(target.getParent(), target.getFileName().toString(), ".tmp");
try (BufferedWriter writer = Files.newBufferedWriter(tmp, StandardCharsets.UTF_8)) {
for (String row : rows) {
writer.write(row);
writer.newLine();
}
}
Files.move(tmp, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
}
}
BP-036 - Staging-Verzeichnis für Data-Lake-Export
Ausgangspunkt-Code
import java.io.InputStream;
import java.nio.file.*;
final class DataLakeExport036Bad {
Path store(InputStream input, Path target) throws Exception {
Files.copy(input, target, StandardCopyOption.REPLACE_EXISTING);
return target;
}
}
Ziel-Code
import java.io.InputStream;
import java.nio.file.*;
final class DataLakeExport036Staging {
Path store(InputStream input, Path target) throws Exception {
Files.createDirectories(target.getParent());
Path staged = Files.createTempFile(target.getParent(), "stage-", ".part");
try {
Files.copy(input, staged, StandardCopyOption.REPLACE_EXISTING);
Files.move(staged, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
return target;
} catch (Exception ex) {
Files.deleteIfExists(staged);
throw ex;
}
}
}
BP-037 - Dateibaum für Kassenabschluss kontrolliert laufen
Ausgangspunkt-Code
import java.nio.file.*;
final class Kassenabschluss037Bad {
long count(Path root) throws Exception {
return Files.walk(root).count(); // Stream wird nicht geschlossen
}
}
Ziel-Code
import java.nio.file.*;
import java.util.stream.Stream;
final class Kassenabschluss037TreeScan {
long countRegularFiles(Path root, int maxDepth) throws Exception {
try (Stream<Path> paths = Files.walk(root, maxDepth)) {
return paths.filter(Files::isRegularFile).count();
}
}
}