Neuer FTP Connector mit Atomic Write

This commit is contained in:
Ralph Soika 2025-11-17 12:36:21 +01:00
parent 1897377eb3
commit 7b421e0a37
2 changed files with 31 additions and 18 deletions

View file

@ -76,7 +76,7 @@ public class CargosoftExportAdapter implements SignalAdapter {
@Override
public ItemCollection execute(ItemCollection document, ItemCollection event) throws AdapterException {
logger.info("......starting export...");
logger.info("├── 📤 Cargosoft export...");
try {
// read the cargosoft export options
@ -84,14 +84,14 @@ public class CargosoftExportAdapter implements SignalAdapter {
if (evalItemCollection == null) {
throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), CONFIG_ERROR,
"missign cargosoft configuration in model event - please check model configuration");
"missing cargosoft configuration in model event - please check model configuration");
}
String reportID = evalItemCollection.getItemValueString("report");
ItemCollection report = reportService.findReport(reportID);
if (report == null) {
throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), CONFIG_ERROR,
"missign cargosoft report '" + reportID + "' - please check model configuration");
"missing cargosoft report '" + reportID + "' - please check model configuration");
}
List<ItemCollection> sourceData = new ArrayList<ItemCollection>();
@ -106,7 +106,6 @@ public class CargosoftExportAdapter implements SignalAdapter {
// transfer file via FTP...
if (ftpServer.isPresent()) {
logger.info("ftp transfer...");
ftpConnector.put(exportFile);
}
@ -115,7 +114,7 @@ public class CargosoftExportAdapter implements SignalAdapter {
} catch (PluginException | IOException | jakarta.xml.bind.JAXBException
| javax.xml.transform.TransformerException e) {
logger.severe("cargosoft export failed: " + e.getMessage());
logger.severe("├── ⚠️ Cargosoft export failed: " + e.getMessage());
document.setItemValue("cargosoft.error", e.getMessage());
document.event(EVENT_FAILURE);
}

View file

@ -82,10 +82,12 @@ public class FTPConnector {
Optional<String> ftpPassword;
/**
* This method transfers a snapshot to a ftp server.
* This method transfers a file to a FTP server using atomic upload.
* The file is first uploaded with a temporary name and then renamed
* to avoid race conditions with the receiver.
*
* @param fileData object
* @throws PluginException
* @param fileData object containing the file to upload
* @throws PluginException if the upload fails
*/
public void put(FileData fileData) throws PluginException {
@ -95,6 +97,8 @@ public class FTPConnector {
}
String fileName = fileData.getName();
// Create temporary filename to avoid race conditions
String tempFileName = fileName + ".part";
// Compute file path
String ftpWorkingPath = ftpPath.get();
@ -108,7 +112,8 @@ public class FTPConnector {
FTPClient ftpClient = null;
try {
logger.finest("......put " + fileName + " to FTP server: " + ftpServer + "...");
logger.info("├── 🔜 uploading " + fileName + " to FTP server: " + ftpServer + " ...");
logger.info("│ ├── working directory=" + ftpWorkingPath);
ftpClient = new FTPSClient("TLS", false);
ftpClient.setBufferSize(8192);
ftpClient.connect(ftpServer.get(), ftpPort.get().intValue());
@ -120,38 +125,47 @@ public class FTPConnector {
ftpClient.setFileType(FTP.ASCII_FILE_TYPE);
ftpClient.setControlEncoding("UTF-8");
// verify directories
// Verify directories
if (!ftpClient.changeWorkingDirectory(ftpWorkingPath)) {
throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
"FTP file transfer failed: missing working directory '" + ftpWorkingPath + "' : "
+ ftpClient.getReplyString());
}
// upload file to FTP server.
// Upload file to FTP server with temporary name
writer = new ByteArrayInputStream(fileData.getContent());
if (!ftpClient.storeFile(fileName, writer)) {
if (!ftpClient.storeFile(tempFileName, writer)) {
throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
"FTP file transfer failed: unable to write '" + ftpWorkingPath + fileName + "' : "
"FTP file transfer failed: unable to write '" + ftpWorkingPath + tempFileName + "' : "
+ ftpClient.getReplyString());
}
logger.finest("...." + ftpWorkingPath + fileName + " transfered successfull to " + ftpServer);
// Rename to final name - this is an atomic operation
logger.info("│ ├── rename '" + tempFileName + "' to '" + fileName + " ...");
if (!ftpClient.rename(tempFileName, fileName)) {
throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
"FTP file transfer failed: unable to rename '" + tempFileName + "' to '" + fileName + "' : "
+ ftpClient.getReplyString());
}
logger.info("│ └── ✓ ftp transfer completed.");
} catch (IOException e) {
throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
"FTP file transfer failed: " + e.getMessage(), e);
} finally {
// do logout....
// Cleanup
try {
if (writer != null) {
writer.close();
}
ftpClient.logout();
ftpClient.disconnect();
if (ftpClient != null && ftpClient.isConnected()) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException e) {
throw new PluginException(CargosoftExportAdapter.class.getSimpleName(), FTP_ERROR,
"FTP file transfer failed: " + e.getMessage(), e);
"FTP file transfer failed during cleanup: " + e.getMessage(), e);
}
}
}