OCI / ORDS Implementierung hinzugefügt

This commit is contained in:
2026-04-22 08:55:54 +02:00
parent 599912ef94
commit cbcc6922a4
10 changed files with 258 additions and 36 deletions

View File

@@ -1,32 +1,121 @@
package de.galabau.dateieingang.oci;
import com.oracle.bmc.auth.SimpleAuthenticationDetailsProvider;
import com.oracle.bmc.objectstorage.ObjectStorage;
import com.oracle.bmc.objectstorage.ObjectStorageClient;
import com.oracle.bmc.objectstorage.requests.PutObjectRequest;
import de.galabau.dateieingang.config.OciConfig;
import de.galabau.dateieingang.exception.OciException;
import de.galabau.dateieingang.model.FileEntry;
import de.galabau.dateieingang.model.ProcessingContext;
import de.galabau.dateieingang.model.ProcessingStatus;
import io.quarkus.logging.Log;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
/**
* Lädt die entpackten Dateien und den Marker in OCI Object Storage hoch.
*
* <p><b>Stub:</b> OCI-Upload ist noch nicht implementiert.
* Der Upload wird übersprungen und der Status auf {@link ProcessingStatus#MARKER_UPLOADED} gesetzt,
* damit der Rest der Pipeline (SFTP-Rename, ORDS-Notify) getestet werden kann.
* Authentifizierung via OCI HTTP Signature V1 (entspricht APEX Web Credential vom Typ OCI).
*/
@ApplicationScoped
public class OciUploadService {
@Inject
OciConfig config;
private ObjectStorage client;
@PostConstruct
void init() {
SimpleAuthenticationDetailsProvider auth = SimpleAuthenticationDetailsProvider.builder()
.tenantId(config.tenancyId())
.userId(config.userId())
.fingerprint(config.fingerprint())
.region(com.oracle.bmc.Region.fromRegionId(config.region()))
.privateKeySupplier(() -> {
try {
return Files.newInputStream(Path.of(config.privateKeyPath()));
} catch (IOException e) {
throw new RuntimeException("OCI Private Key nicht lesbar: "
+ config.privateKeyPath(), e);
}
})
.build();
this.client = ObjectStorageClient.builder().build(auth);
}
/**
* Lädt alle Dateien aus {@code context.extractedFiles} sowie den Marker in OCI hoch.
* Dateien mit {@code isMarker = true} werden übersprungen — der Marker wird separat
* am Ende hochgeladen, um sicherzustellen dass er erst nach allen Dateien erscheint.
*
* @param context enthält die Liste der hochzuladenden Dateien und den Ziel-Prefix
* @throws OciException bei persistenten OCI-Fehlern (4xx) nach Retry-Erschöpfung
* @throws OciException bei Verbindungs- oder Upload-Fehlern
*/
public void upload(ProcessingContext context) throws OciException {
// TODO: OCI-Upload implementieren (OCI SDK, SimpleAuthenticationDetailsProvider)
Log.infof("[STUB] OCI-Upload übersprungen für '%s' (%d Dateien) — wird später implementiert",
context.zipNameWithoutExt, context.extractedFiles.size());
context.status = ProcessingStatus.PARTIALLY_UPLOADED;
List<FileEntry> files = context.extractedFiles.stream()
.filter(e -> !e.isMarker)
.toList();
for (FileEntry entry : files) {
String key = buildKey(context.zipNameWithoutExt, entry.relativePath);
entry.ociKey = key;
putFile(key, context.localExtractDir.resolve(entry.relativePath), entry.fileSize);
Log.debugf("Datei hochgeladen: %s (%d Bytes)", key, entry.fileSize);
}
String markerKey = buildKey(context.zipNameWithoutExt, "_READY_FOR_DB_PROCESSING_");
putEmpty(markerKey);
context.markerUploaded = true;
context.status = ProcessingStatus.MARKER_UPLOADED;
Log.infof("OCI-Upload abgeschlossen: %d Datei(en) + Marker in '%s'",
files.size(), buildPrefix(context.zipNameWithoutExt));
}
private String buildPrefix(String zipNameWithoutExt) {
return config.tenantPrefix() + config.incomingPrefix() + zipNameWithoutExt + "/";
}
private String buildKey(String zipNameWithoutExt, String relativePath) {
return buildPrefix(zipNameWithoutExt) + relativePath;
}
private void putFile(String key, Path localFile, long fileSize) throws OciException {
try (InputStream is = Files.newInputStream(localFile)) {
client.putObject(PutObjectRequest.builder()
.namespaceName(config.namespace())
.bucketName(config.bucket())
.objectName(key)
.putObjectBody(is)
.contentLength(fileSize)
.build());
} catch (Exception e) {
throw new OciException("OCI-Upload fehlgeschlagen für '" + key + "'", e);
}
}
private void putEmpty(String key) throws OciException {
try (InputStream is = InputStream.nullInputStream()) {
client.putObject(PutObjectRequest.builder()
.namespaceName(config.namespace())
.bucketName(config.bucket())
.objectName(key)
.putObjectBody(is)
.contentLength(0L)
.build());
} catch (Exception e) {
throw new OciException("OCI-Upload Marker fehlgeschlagen für '" + key + "'", e);
}
}
}