From 4522f2c82b580014f2c358a94d192a60a62c52b8 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Mon, 8 Jun 2026 21:12:05 +0100 Subject: [PATCH 01/15] Fix flaky tests and optimise resend process --- Server/pom.xml | 2 +- .../main/java/org/openas2/util/AS2Util.java | 85 ++++++++++--------- .../java/org/openas2/app/BaseServerSetup.java | 6 +- .../org/openas2/app/CertificatesTest.java | 6 +- .../org/openas2/app/FilenameParsingTest.java | 2 +- .../message/DynamicContentTypeTest.java | 2 +- .../receiver/ContentDispositionTest.java | 2 +- pom.xml | 2 +- 8 files changed, 57 insertions(+), 50 deletions(-) diff --git a/Server/pom.xml b/Server/pom.xml index 86c05f72..271f416c 100644 --- a/Server/pom.xml +++ b/Server/pom.xml @@ -7,7 +7,7 @@ net.sf.openas2 OpenAS2 - 4.8.2 + 4.8.3 ../pom.xml diff --git a/Server/src/main/java/org/openas2/util/AS2Util.java b/Server/src/main/java/org/openas2/util/AS2Util.java index d92eadc5..47ae7e7d 100644 --- a/Server/src/main/java/org/openas2/util/AS2Util.java +++ b/Server/src/main/java/org/openas2/util/AS2Util.java @@ -369,7 +369,7 @@ public static boolean resend(Session session, Class sourceClass, String how, // Going to try again so increment the try count retries++; } - // Keep a popinter to the passed in msg object in case it is overwritten in this method so that the setting + // Keep a popinter to the passed in msg object in case it is overwritten in this method so that setting // the resend flag to avoid file cleanup is not lost when this method exits and the original initiating // method of this cycle queries the msg object to check if it is ok to call file cleanup Message passed_in_msg = msg; @@ -726,7 +726,7 @@ public static void getMetaData(AS2Message msg, File inFile) throws OpenAS2Except } - public static void cleanupFiles(Message msg, boolean isError) { + public synchronized static void cleanupFiles(Message msg, boolean isError) { Logger logger = LoggerFactory.getLogger(AS2Util.class); if (msg.isFileCleanupCompleted()) { if (logger.isTraceEnabled()) { @@ -760,14 +760,17 @@ public static void cleanupFiles(Message msg, boolean isError) { String pendingFileName = msg.getAttribute(FileAttribute.MA_PENDINGFILE); if (pendingFileName != null) { File fPendingFile = new File(pendingFileName); - try { - IOUtil.deleteFile(new File(pendingFileName + ".object")); - if (logger.isTraceEnabled()) { - logger.trace("The RETRY message object file deleted: " + pendingFileName + ".object" + msg.getLogMsgID()); + File msgObjFile = new File(pendingFileName + ".object"); + if (msgObjFile.exists()) { + try { + IOUtil.deleteFile(msgObjFile); + if (logger.isTraceEnabled()) { + logger.trace("The RETRY message object file deleted: " + pendingFileName + ".object" + msg.getLogMsgID()); + } + } catch (Exception e) { + msg.setLogMsg("The RETRY message object file NOT deleted: " + org.openas2.util.Logging.getExceptionMsg(e)); + logger.warn(msg.getLogMsg(), e); } - } catch (Exception e) { - msg.setLogMsg("The RETRY message object file NOT deleted: " + org.openas2.util.Logging.getExceptionMsg(e)); - logger.warn(msg.getLogMsg(), e); } if (logger.isTraceEnabled()) { logger.trace("Cleaning up pending file : " + fPendingFile.getName() + " ::: From pending folder : " + fPendingFile.getParent() + msg.getLogMsgID()); @@ -775,40 +778,42 @@ public static void cleanupFiles(Message msg, boolean isError) { try { // Move file to error or sent directory if the error or sent saving functionality is enabled boolean isMoved = false; - String tgtDir = null; - String targetFilenameUnparsed = ""; - if (isError) { - tgtDir = msg.getAttribute(FileAttribute.MA_ERROR_DIR); - targetFilenameUnparsed = msg.getAttribute(FileAttribute.MA_ERROR_FILENAME); - } else { - // If the Sent Directory option is set, move the transmitted file to the sent - // directory - tgtDir = msg.getAttribute(FileAttribute.MA_SENT_DIR); - targetFilenameUnparsed = msg.getAttribute(FileAttribute.MA_SENT_FILENAME); - } - if (tgtDir != null && tgtDir.length() > 0) { - File tgtFile = null; - try { - String tgtFileName = fPendingFile.getName(); - if (targetFilenameUnparsed != null && targetFilenameUnparsed.length() > 0) { - CompositeParameters parser = new CompositeParameters(false).add("date", new DateParameters()).add("msg", new MessageParameters(msg)).add("rand", new RandomParameters()); - tgtFileName = ParameterParser.parse(targetFilenameUnparsed, parser); - } - tgtFileName = IOUtil.cleanFilename(tgtFileName); - tgtFile = new File(tgtDir + "/" + tgtFileName); - tgtFile = IOUtil.moveFile(fPendingFile, tgtFile, false); - isMoved = true; - - if (logger.isDebugEnabled()) { - logger.debug("Pending MDN MSG FILE file " + fPendingFile.getAbsolutePath() + " moved to " + tgtFile.getAbsolutePath() + msg.getLogMsgID()); + if (fPendingFile.exists()) { + String tgtDir = null; + String targetFilenameUnparsed = ""; + if (isError) { + tgtDir = msg.getAttribute(FileAttribute.MA_ERROR_DIR); + targetFilenameUnparsed = msg.getAttribute(FileAttribute.MA_ERROR_FILENAME); + } else { + // If the Sent Directory option is set, move the transmitted file to the sent + // directory + tgtDir = msg.getAttribute(FileAttribute.MA_SENT_DIR); + targetFilenameUnparsed = msg.getAttribute(FileAttribute.MA_SENT_FILENAME); + } + if (tgtDir != null && tgtDir.length() > 0) { + File tgtFile = null; + try { + String tgtFileName = fPendingFile.getName(); + if (targetFilenameUnparsed != null && targetFilenameUnparsed.length() > 0) { + CompositeParameters parser = new CompositeParameters(false).add("date", new DateParameters()).add("msg", new MessageParameters(msg)).add("rand", new RandomParameters()); + tgtFileName = ParameterParser.parse(targetFilenameUnparsed, parser); + } + tgtFileName = IOUtil.cleanFilename(tgtFileName); + tgtFile = new File(tgtDir + "/" + tgtFileName); + tgtFile = IOUtil.moveFile(fPendingFile, tgtFile, false); + logger.info("PENDING FILE " + pendingFileName + " AFTER MOVE STILL EXISTS? " + fPendingFile.exists()); + isMoved = true; + + if (logger.isDebugEnabled()) { + logger.debug("Pending MDN MSG FILE file " + fPendingFile.getAbsolutePath() + " moved to " + tgtFile.getAbsolutePath() + msg.getLogMsgID()); + } + + } catch (IOException iose) { + msg.setLogMsg("Error moving file to " + tgtDir + " : " + org.openas2.util.Logging.getExceptionMsg(iose)); + logger.error(msg.getLogMsg(), iose); } - - } catch (IOException iose) { - msg.setLogMsg("Error moving file to " + tgtDir + " : " + org.openas2.util.Logging.getExceptionMsg(iose)); - logger.error(msg.getLogMsg(), iose); } } - if (!isMoved) { // Could not find somewhere to move it to so delete it if it still exists if (fPendingFile.exists()) { diff --git a/Server/src/test/java/org/openas2/app/BaseServerSetup.java b/Server/src/test/java/org/openas2/app/BaseServerSetup.java index cb284336..dbd28f57 100644 --- a/Server/src/test/java/org/openas2/app/BaseServerSetup.java +++ b/Server/src/test/java/org/openas2/app/BaseServerSetup.java @@ -60,9 +60,11 @@ public void setStartActiveModules(boolean startActiveModules) { this.startActiveModules = startActiveModules; } - public void createFileSystemResources() throws Exception { - tmpDir = Files.createTempDirectory("testResources").toFile(); + public void createFileSystemResources(String srcClassName) throws Exception { + tmpDir = Files.createTempDirectory(srcClassName).toFile(); openAS2PropertiesFile = new File(tmpDir, "test.openas2.properties"); + // Just in case there is an existing file... + if (openAS2PropertiesFile.exists()) openAS2PropertiesFile.delete(); } public void setup() throws Exception { diff --git a/Server/src/test/java/org/openas2/app/CertificatesTest.java b/Server/src/test/java/org/openas2/app/CertificatesTest.java index 04cd8f80..000b5d0b 100644 --- a/Server/src/test/java/org/openas2/app/CertificatesTest.java +++ b/Server/src/test/java/org/openas2/app/CertificatesTest.java @@ -78,7 +78,7 @@ public X509CertificateFactory genSelfSignedCert( @BeforeAll public void setUp() throws Exception { - super.createFileSystemResources(); + super.createFileSystemResources(this.getClass().getName()); String tmpDirAbsolutePath = tmpDir.getAbsolutePath(); sslCertsFile = Files.createFile(Paths.get(tmpDirAbsolutePath, "ssl_certs.p12")).toFile(); String tgtHostName = "test.openas2.org"; @@ -99,7 +99,7 @@ public void setUp() throws Exception { this.trustFx.setKeyStore(AS2Util.getCryptoHelper().getKeyStore()); this.trustFx.load(); - customPropsFile = Files.createFile(Paths.get(tmpDirAbsolutePath, "openas2.properties")).toFile(); + customPropsFile = Files.createFile(Paths.get(tmpDirAbsolutePath, this.getClass().getName() + ".properties")).toFile(); System.setProperty(Properties.OPENAS2_PROPERTIES_FILE_PROP, customPropsFile.getAbsolutePath()); FileOutputStream fos = new FileOutputStream(customPropsFile); // Switch to forward slash to avoid backslash being dropped when creating property string when on Windows @@ -164,4 +164,4 @@ public void a2_shouldConnect() throws Exception { } assertTrue(200 == resp.getStatusCode(), "Check default is mapping off."); } -} \ No newline at end of file +} diff --git a/Server/src/test/java/org/openas2/app/FilenameParsingTest.java b/Server/src/test/java/org/openas2/app/FilenameParsingTest.java index aee29b03..1d2b4e45 100644 --- a/Server/src/test/java/org/openas2/app/FilenameParsingTest.java +++ b/Server/src/test/java/org/openas2/app/FilenameParsingTest.java @@ -31,7 +31,7 @@ public class FilenameParsingTest extends BaseServerSetup { @BeforeAll public void setup() throws Exception { - super.createFileSystemResources(); + super.createFileSystemResources(this.getClass().getName()); super.setup(); try { simpleTestMsg = new AS2Message(); diff --git a/Server/src/test/java/org/openas2/message/DynamicContentTypeTest.java b/Server/src/test/java/org/openas2/message/DynamicContentTypeTest.java index d27fc506..72bffb8e 100644 --- a/Server/src/test/java/org/openas2/message/DynamicContentTypeTest.java +++ b/Server/src/test/java/org/openas2/message/DynamicContentTypeTest.java @@ -45,7 +45,7 @@ public class DynamicContentTypeTest extends BaseServerSetup { @BeforeAll public void setUp() throws Exception { - super.createFileSystemResources(); + super.createFileSystemResources(this.getClass().getName()); // Set up the system level mappings systemContentTypesMappingFile = new File(tmpDir, "content_type_map.properties"); systemMappedContentTypes.put(xmlFileExtension, "application/xml"); diff --git a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java index b4ec48f3..4743407b 100644 --- a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java +++ b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java @@ -24,7 +24,7 @@ public class ContentDispositionTest extends BaseServerSetup { @BeforeAll public void setUp() throws Exception { - super.createFileSystemResources(); + super.createFileSystemResources(this.getClass().getName()); super.setup(); this.poller = session.getPartnershipPoller(simpleTestMsg.getPartnership().getName()); } diff --git a/pom.xml b/pom.xml index 0df8d340..0130c0df 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 net.sf.openas2 OpenAS2 - 4.8.2 + 4.8.3 OpenAS2 pom From 662df813930cc8825f7b4203e230479297beb519 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Mon, 8 Jun 2026 23:14:46 +0100 Subject: [PATCH 02/15] Debugging for failing test --- .../openas2/processor/receiver/ContentDispositionTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java index 4743407b..8fa29d43 100644 --- a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java +++ b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java @@ -16,6 +16,9 @@ import org.openas2.app.BaseServerSetup; import org.openas2.message.FileAttribute; import org.openas2.partner.Partnership; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + @TestInstance(Lifecycle.PER_CLASS) @TestMethodOrder(MethodOrderer.MethodName.class) @@ -26,6 +29,10 @@ public class ContentDispositionTest extends BaseServerSetup { public void setUp() throws Exception { super.createFileSystemResources(this.getClass().getName()); super.setup(); + + Logger logger = LoggerFactory.getLogger(ContentDispositionTest.class); + logger.info("ContentDispositionTest:: POLLER COUNT: " + session.getDirectoryPollers().size()); + logger.info("ContentDispositionTest:: PARTNERSHIP NAME: " + simpleTestMsg.getPartnership().getName()); this.poller = session.getPartnershipPoller(simpleTestMsg.getPartnership().getName()); } From 99b7d7bae8a835bea3fae9e63748c6fb03f72bed Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Mon, 8 Jun 2026 23:26:07 +0100 Subject: [PATCH 03/15] Debugging for failing test --- .../org/openas2/processor/receiver/ContentDispositionTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java index 8fa29d43..bc3da6a1 100644 --- a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java +++ b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java @@ -31,9 +31,10 @@ public void setUp() throws Exception { super.setup(); Logger logger = LoggerFactory.getLogger(ContentDispositionTest.class); - logger.info("ContentDispositionTest:: POLLER COUNT: " + session.getDirectoryPollers().size()); + logger.info("ContentDispositionTest:: POLLER COUNT: " + session.getPolledDirectories().size()); logger.info("ContentDispositionTest:: PARTNERSHIP NAME: " + simpleTestMsg.getPartnership().getName()); this.poller = session.getPartnershipPoller(simpleTestMsg.getPartnership().getName()); + logger.info("ContentDispositionTest:: POLLER BY NAME: " + this.poller); } @AfterAll From 9a47af61c102ccd602cfaf9de08aee80d364c778 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Mon, 8 Jun 2026 23:40:07 +0100 Subject: [PATCH 04/15] Debugging for failing test --- .../openas2/processor/receiver/ContentDispositionTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java index bc3da6a1..e4c1fff8 100644 --- a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java +++ b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java @@ -33,6 +33,10 @@ public void setUp() throws Exception { Logger logger = LoggerFactory.getLogger(ContentDispositionTest.class); logger.info("ContentDispositionTest:: POLLER COUNT: " + session.getPolledDirectories().size()); logger.info("ContentDispositionTest:: PARTNERSHIP NAME: " + simpleTestMsg.getPartnership().getName()); + for (Map.Entry> entry : polledDirectories.entrySet()) { + Map meta = entry.getValue(); + logger.info("ContentDispositionTest:: ACTIVE POLLER PARTNERSHIP NAME: " + meta.get("partnershipName"); + } this.poller = session.getPartnershipPoller(simpleTestMsg.getPartnership().getName()); logger.info("ContentDispositionTest:: POLLER BY NAME: " + this.poller); } From ae13610aa384efeb62216ac45416fdec972d87f4 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Mon, 8 Jun 2026 23:43:57 +0100 Subject: [PATCH 05/15] Debugging for failing test --- .../org/openas2/processor/receiver/ContentDispositionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java index e4c1fff8..d2220e85 100644 --- a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java +++ b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java @@ -35,7 +35,7 @@ public void setUp() throws Exception { logger.info("ContentDispositionTest:: PARTNERSHIP NAME: " + simpleTestMsg.getPartnership().getName()); for (Map.Entry> entry : polledDirectories.entrySet()) { Map meta = entry.getValue(); - logger.info("ContentDispositionTest:: ACTIVE POLLER PARTNERSHIP NAME: " + meta.get("partnershipName"); + logger.info("ContentDispositionTest:: ACTIVE POLLER PARTNERSHIP NAME: " + meta.get("partnershipName")); } this.poller = session.getPartnershipPoller(simpleTestMsg.getPartnership().getName()); logger.info("ContentDispositionTest:: POLLER BY NAME: " + this.poller); From 2c6d143db72617023a71200b03f7af082b8b36cc Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Mon, 8 Jun 2026 23:48:18 +0100 Subject: [PATCH 06/15] Debugging for failing test --- .../org/openas2/processor/receiver/ContentDispositionTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java index d2220e85..146cb264 100644 --- a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java +++ b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java @@ -16,6 +16,7 @@ import org.openas2.app.BaseServerSetup; import org.openas2.message.FileAttribute; import org.openas2.partner.Partnership; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 0e375b3b92b900203369b499994e8736efc60d45 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Mon, 8 Jun 2026 23:52:26 +0100 Subject: [PATCH 07/15] Debugging for failing test --- .../org/openas2/processor/receiver/ContentDispositionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java index 146cb264..55984a80 100644 --- a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java +++ b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java @@ -34,7 +34,7 @@ public void setUp() throws Exception { Logger logger = LoggerFactory.getLogger(ContentDispositionTest.class); logger.info("ContentDispositionTest:: POLLER COUNT: " + session.getPolledDirectories().size()); logger.info("ContentDispositionTest:: PARTNERSHIP NAME: " + simpleTestMsg.getPartnership().getName()); - for (Map.Entry> entry : polledDirectories.entrySet()) { + for (Map.Entry> entry : session.getPolledDirectories().entrySet()) { Map meta = entry.getValue(); logger.info("ContentDispositionTest:: ACTIVE POLLER PARTNERSHIP NAME: " + meta.get("partnershipName")); } From 3efcec4005d659a6b3446b622fe4620e63417bb3 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Tue, 9 Jun 2026 00:44:18 +0100 Subject: [PATCH 08/15] Debugging for failing test --- .../java/org/openas2/partner/XMLPartnershipFactory.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java index 1f0c926d..bbf1ba8a 100644 --- a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java +++ b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java @@ -105,8 +105,10 @@ void loadPartnershipsFile() throws OpenAS2Exception { Document document = parser.parse(inputStream); setPartnershipsXml(document); } catch (Exception e) { - throw new WrappedException(e); - } + Path filePath = Path.of(getFilename()); + String content = Files.readString(filePath); + Logger logger = LoggerFactory.getLogger("TESTING"); + logger.info("PARTNERSHIPS FILE: " + content); } void refreshConfig() throws OpenAS2Exception { From 8a0d56f034d803cef992e413421f5f72507dc0dd Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Tue, 9 Jun 2026 00:55:30 +0100 Subject: [PATCH 09/15] Debugging for failing test --- .../main/java/org/openas2/partner/XMLPartnershipFactory.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java index bbf1ba8a..a029b0a0 100644 --- a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java +++ b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java @@ -105,9 +105,10 @@ void loadPartnershipsFile() throws OpenAS2Exception { Document document = parser.parse(inputStream); setPartnershipsXml(document); } catch (Exception e) { + throw new WrappedException(e); + } Path filePath = Path.of(getFilename()); String content = Files.readString(filePath); - Logger logger = LoggerFactory.getLogger("TESTING"); logger.info("PARTNERSHIPS FILE: " + content); } From d4af214c608ba881f1ecb6c6313bf02e64e35a67 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Tue, 9 Jun 2026 01:03:24 +0100 Subject: [PATCH 10/15] Debugging for failing test --- .../org/openas2/partner/XMLPartnershipFactory.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java index a029b0a0..1b853e0b 100644 --- a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java +++ b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java @@ -34,6 +34,8 @@ import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.HashMap; @@ -107,9 +109,12 @@ void loadPartnershipsFile() throws OpenAS2Exception { } catch (Exception e) { throw new WrappedException(e); } - Path filePath = Path.of(getFilename()); - String content = Files.readString(filePath); - logger.info("PARTNERSHIPS FILE: " + content); + try { + Path filePath = Path.of(getFilename()); + String content = Files.readString(filePath); + logger.info("PARTNERSHIPS FILE: " + content); + } catch (IOException e) { + } } void refreshConfig() throws OpenAS2Exception { From 570c6885717bbd35df91849b757260e7fb0de452 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Tue, 9 Jun 2026 13:56:09 +0100 Subject: [PATCH 11/15] Fix flaky tests and optimise resend process --- .../partner/XMLPartnershipFactory.java | 8 ---- .../java/org/openas2/app/BaseServerSetup.java | 38 ++++++++++++++++--- .../org/openas2/app/CertificatesTest.java | 13 +++---- .../openas2/app/ParallelProcessingTest.java | 8 ++-- .../java/org/openas2/app/RestApiTest.java | 28 +++++++------- .../message/DynamicContentTypeTest.java | 4 +- .../receiver/ContentDispositionTest.java | 11 ------ 7 files changed, 57 insertions(+), 53 deletions(-) diff --git a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java index 1b853e0b..1f0c926d 100644 --- a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java +++ b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java @@ -34,8 +34,6 @@ import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.HashMap; @@ -109,12 +107,6 @@ void loadPartnershipsFile() throws OpenAS2Exception { } catch (Exception e) { throw new WrappedException(e); } - try { - Path filePath = Path.of(getFilename()); - String content = Files.readString(filePath); - logger.info("PARTNERSHIPS FILE: " + content); - } catch (IOException e) { - } } void refreshConfig() throws OpenAS2Exception { diff --git a/Server/src/test/java/org/openas2/app/BaseServerSetup.java b/Server/src/test/java/org/openas2/app/BaseServerSetup.java index dbd28f57..07df165a 100644 --- a/Server/src/test/java/org/openas2/app/BaseServerSetup.java +++ b/Server/src/test/java/org/openas2/app/BaseServerSetup.java @@ -1,8 +1,13 @@ package org.openas2.app; import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -19,6 +24,9 @@ public class BaseServerSetup { + static String resourcePathPrefix = Paths.get("src","test","resources").toAbsolutePath().toString(); + static Path srcConfigDirPath = Paths.get(resourcePathPrefix + File.separator + "config"); + static String myCompanyOid = "MyCompany_OID"; static String myPartnerOid = "PartnerA_OID"; private boolean startActiveModules = false; @@ -27,7 +35,7 @@ public class BaseServerSetup { protected static Message simpleTestMsg; @TempDir - public static File tmpDir; + public static File configDir; public File openAS2PropertiesFile; public void refresh() throws Exception { @@ -51,7 +59,7 @@ public void addSendPayloadStuffToMsg(String fileName, Message msg) throws Except msg.setHeader("AS2-To", msg.getPartnership().getReceiverID(Partnership.PID_AS2)); msg.setHeader("AS2-From", msg.getPartnership().getSenderID(Partnership.PID_AS2)); msg.updateMessageID(); - File testFile = new File(tmpDir, fileName); + File testFile = new File(configDir, fileName); FileUtils.writeStringToFile(testFile, "Show me the money!", Charset.forName("UTF-8")); frm.buildMessageData(msg, testFile, null); } @@ -60,11 +68,14 @@ public void setStartActiveModules(boolean startActiveModules) { this.startActiveModules = startActiveModules; } - public void createFileSystemResources(String srcClassName) throws Exception { - tmpDir = Files.createTempDirectory(srcClassName).toFile(); - openAS2PropertiesFile = new File(tmpDir, "test.openas2.properties"); + public void createFileSystemResources(String configDirName) throws Exception { + Path destConfigDirPath = Files.createTempDirectory(configDirName); + configDir = destConfigDirPath.toFile(); + openAS2PropertiesFile = new File(configDir, "test.openas2.properties"); // Just in case there is an existing file... if (openAS2PropertiesFile.exists()) openAS2PropertiesFile.delete(); + // Copy standard config resources to this folder + this.copyConfig(destConfigDirPath.toString()); } public void setup() throws Exception { @@ -73,7 +84,7 @@ public void setup() throws Exception { if (openAS2PropertiesFile.exists()) { System.setProperty(Properties.OPENAS2_PROPERTIES_FILE_PROP, openAS2PropertiesFile.getAbsolutePath()); } - session = new XMLSession(TestResource.getResource("config")); + session = new XMLSession(configDir.getAbsolutePath() + "/config.xml"); simpleTestMsg = getSimpleTestMsg(); if (startActiveModules) { session.start(); @@ -85,6 +96,21 @@ public void setup() throws Exception { } } + /** + * Copy standard config files from {@link #resourcePathPrefix} to the target config folder + * @throws IOException + * + */ + public void copyConfig(String destDir) throws IOException { + if (!srcConfigDirPath.toFile().exists()) { + throw new FileNotFoundException("The source config was not found here: " + srcConfigDirPath.getFileName()); + } + for (String f : srcConfigDirPath.toFile().list()) { + Files.copy(Paths.get(srcConfigDirPath.toString(), f), Paths.get(destDir, f), StandardCopyOption.REPLACE_EXISTING); + } + } + + @AfterAll public void tearDown() throws Exception { session.stop(); diff --git a/Server/src/test/java/org/openas2/app/CertificatesTest.java b/Server/src/test/java/org/openas2/app/CertificatesTest.java index 000b5d0b..daff7360 100644 --- a/Server/src/test/java/org/openas2/app/CertificatesTest.java +++ b/Server/src/test/java/org/openas2/app/CertificatesTest.java @@ -44,7 +44,6 @@ public class CertificatesTest extends BaseServerSetup { private String alias = null; private static final String receiverPort = "10443"; private static final String url = "https://localhost:" + receiverPort + "/"; - private File customPropsFile = null; private File sslCertsFile = null; // The private key and certificate for the HTTPS private String sslTrustCertsFilePath = null; // The public key for the SSL private key private X509CertificateFactory trustFx = null; // The trust certificates @@ -79,8 +78,8 @@ public X509CertificateFactory genSelfSignedCert( @BeforeAll public void setUp() throws Exception { super.createFileSystemResources(this.getClass().getName()); - String tmpDirAbsolutePath = tmpDir.getAbsolutePath(); - sslCertsFile = Files.createFile(Paths.get(tmpDirAbsolutePath, "ssl_certs.p12")).toFile(); + String configDirAbsolutePath = configDir.getAbsolutePath(); + sslCertsFile = Files.createFile(Paths.get(configDirAbsolutePath, "ssl_certs.p12")).toFile(); String tgtHostName = "test.openas2.org"; this.alias = tgtHostName; // Create the SSL file for the server to use @@ -88,7 +87,7 @@ public void setUp() throws Exception { String sslCertsFilePath = sslCertsFile.getAbsolutePath().replace("\\", "/"); this.certFx = genSelfSignedCert(alias, sslCertsFilePath, "RSA", "SHA256", 2048, tgtHostName); // Create the trust store with the public key so the certificate returned from the server is trusted - File sslTrustCertsFile = Files.createFile(Paths.get(tmpDirAbsolutePath, "ssl_trust_certs.p12")).toFile(); + File sslTrustCertsFile = Files.createFile(Paths.get(configDirAbsolutePath, "ssl_trust_certs.p12")).toFile(); // Switch to forward slash to avoid backslash being dropped when creating property string when on Windows sslTrustCertsFilePath = sslTrustCertsFile.getAbsolutePath().replace("\\", "/"); String trustAlias = "trust-" + tgtHostName; @@ -99,9 +98,7 @@ public void setUp() throws Exception { this.trustFx.setKeyStore(AS2Util.getCryptoHelper().getKeyStore()); this.trustFx.load(); - customPropsFile = Files.createFile(Paths.get(tmpDirAbsolutePath, this.getClass().getName() + ".properties")).toFile(); - System.setProperty(Properties.OPENAS2_PROPERTIES_FILE_PROP, customPropsFile.getAbsolutePath()); - FileOutputStream fos = new FileOutputStream(customPropsFile); + FileOutputStream fos = new FileOutputStream(openAS2PropertiesFile); // Switch to forward slash to avoid backslash being dropped when creating property string when on Windows fos.write(("ssl_keystore=" + sslCertsFilePath + "\n").getBytes()); fos.write(("ssl_keystore_password=" + new String(password) + "\n").getBytes()); @@ -149,7 +146,7 @@ public void a1_shouldFailSSLConnect() throws Exception { @Test public void a2_shouldConnect() throws Exception { - FileOutputStream fos = new FileOutputStream(customPropsFile, true); + FileOutputStream fos = new FileOutputStream(openAS2PropertiesFile, true); fos.write(("ssl_trust_keystore.enabled=true\n").getBytes()); fos.write(("ssl_trust_keystore=" + sslTrustCertsFilePath + "\n").getBytes()); fos.write(("ssl_trust_keystore_password=" + new String(password) + "\n").getBytes()); diff --git a/Server/src/test/java/org/openas2/app/ParallelProcessingTest.java b/Server/src/test/java/org/openas2/app/ParallelProcessingTest.java index a14f0285..ea685b11 100644 --- a/Server/src/test/java/org/openas2/app/ParallelProcessingTest.java +++ b/Server/src/test/java/org/openas2/app/ParallelProcessingTest.java @@ -116,10 +116,10 @@ public static void tearDown() throws Exception { // NOTE: For debugging "missing" files it is best to comment this out for (int i = 0; i < dataFolders.length; i++) { try { - FileUtils.deleteDirectory(new File(dataFolders[i])); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + File dir = new File(dataFolders[i]); + for (File f: dir.listFiles()) f.delete(); + dir.delete(); + } catch (Exception e) { } } } diff --git a/Server/src/test/java/org/openas2/app/RestApiTest.java b/Server/src/test/java/org/openas2/app/RestApiTest.java index 78fa5c49..f02f3132 100644 --- a/Server/src/test/java/org/openas2/app/RestApiTest.java +++ b/Server/src/test/java/org/openas2/app/RestApiTest.java @@ -23,8 +23,11 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.io.TempDir; import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +import org.openas2.app.BaseServerSetup; import org.openas2.TestResource; import org.openas2.TestUtils; import org.openas2.cmd.processor.restapi.AuthenticationRequestFilter; @@ -44,15 +47,14 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +@TestInstance(Lifecycle.PER_CLASS) @ExtendWith(MockitoExtension.class) @TestMethodOrder(MethodOrderer.MethodName.class) -public class RestApiTest { +public class RestApiTest extends BaseServerSetup { // private static File openAS2AHome; private static OpenAS2Server serverInstance; private static String TEST_PARTNER_NAME = "partnerX"; private static String TEST_PARTNERSHIP_NAME = TEST_PARTNER_NAME + "-partnerA"; - @TempDir - private static Path scratchpad; private static CloseableHttpClient httpclient; private static String restHostAddr = "http://127.0.0.1:8080"; private static String baseUrl = restHostAddr + "/api/"; @@ -60,24 +62,23 @@ public class RestApiTest { private static String authPwd = "admin"; @BeforeAll - public static void start_A_Server() throws Exception { + public void start_A_Server() throws Exception { // Set up some override properties so we can use the standard config in tests - // to make sure the release package is fully tested - scratchpad = Files.createTempDirectory("tempResources"); - File customPropsFile = Files.createFile(Paths.get(scratchpad.toString(), "openas2.properties")).toFile(); - System.setProperty(Properties.OPENAS2_PROPERTIES_FILE_PROP, customPropsFile.getAbsolutePath()); - FileOutputStream fos = new FileOutputStream(customPropsFile); + super.createFileSystemResources(this.getClass().getName()); + String configDirAbsolutePath = configDir.getAbsolutePath(); + + FileOutputStream fos = new FileOutputStream(openAS2PropertiesFile); fos.write("restapi.command.processor.enabled=true\n".getBytes()); fos.write(("restapi.command.processor.baseuri=" + restHostAddr + "\n").getBytes()); fos.write(("restapi.command.processor.userid=" + authUser + "\n").getBytes()); fos.write(("restapi.command.processor.password=" + authPwd + "\n").getBytes()); fos.close(); - + System.setProperty(Properties.OPENAS2_PROPERTIES_FILE_PROP, openAS2PropertiesFile.getAbsolutePath()); try { //System.setProperty(OPENAS2_LOG_LEVEL", "TRACE"); //executorService = Executors.newFixedThreadPool(20); - RestApiTest.serverInstance = new OpenAS2Server.Builder().run(TestResource.getResource("config")); + RestApiTest.serverInstance = new OpenAS2Server.Builder().run(configDirAbsolutePath + "/config.xml"); } catch (Throwable e) { // aid for debugging JUnit tests System.err.println("ERROR occurred: " + ExceptionUtils.getStackTrace(e)); @@ -92,11 +93,10 @@ public static void start_B_Client() throws Exception { } @AfterAll - public static void tearDown() throws Exception { + public void tearDown() throws Exception { serverInstance.shutdown(); // executorService.shutdown(); System.clearProperty("openas2.properties.file"); - TestUtils.deleteDirectory(scratchpad.toFile()); httpclient.close(); } diff --git a/Server/src/test/java/org/openas2/message/DynamicContentTypeTest.java b/Server/src/test/java/org/openas2/message/DynamicContentTypeTest.java index 72bffb8e..98932672 100644 --- a/Server/src/test/java/org/openas2/message/DynamicContentTypeTest.java +++ b/Server/src/test/java/org/openas2/message/DynamicContentTypeTest.java @@ -47,7 +47,7 @@ public class DynamicContentTypeTest extends BaseServerSetup { public void setUp() throws Exception { super.createFileSystemResources(this.getClass().getName()); // Set up the system level mappings - systemContentTypesMappingFile = new File(tmpDir, "content_type_map.properties"); + systemContentTypesMappingFile = new File(configDir, "content_type_map.properties"); systemMappedContentTypes.put(xmlFileExtension, "application/xml"); systemMappedContentTypes.put(ediFileExtension, "application/edifact"); systemMappedContentTypes.put("txt", "text/plain"); @@ -57,7 +57,7 @@ public void setUp() throws Exception { } writer.close(); // Set up the partnership override mappings - partnershipContentTypesMappingFile = new File(tmpDir, "override_content_type_map.properties"); + partnershipContentTypesMappingFile = new File(configDir, "override_content_type_map.properties"); partnershipMappedContentTypes.put(xmlFileExtension, "application/xml-custom"); BufferedWriter writer2 = new BufferedWriter(new FileWriter(partnershipContentTypesMappingFile)); for (Map.Entry entry : partnershipMappedContentTypes.entrySet()) { diff --git a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java index 55984a80..5fecb43d 100644 --- a/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java +++ b/Server/src/test/java/org/openas2/processor/receiver/ContentDispositionTest.java @@ -17,8 +17,6 @@ import org.openas2.message.FileAttribute; import org.openas2.partner.Partnership; import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @TestInstance(Lifecycle.PER_CLASS) @@ -30,16 +28,7 @@ public class ContentDispositionTest extends BaseServerSetup { public void setUp() throws Exception { super.createFileSystemResources(this.getClass().getName()); super.setup(); - - Logger logger = LoggerFactory.getLogger(ContentDispositionTest.class); - logger.info("ContentDispositionTest:: POLLER COUNT: " + session.getPolledDirectories().size()); - logger.info("ContentDispositionTest:: PARTNERSHIP NAME: " + simpleTestMsg.getPartnership().getName()); - for (Map.Entry> entry : session.getPolledDirectories().entrySet()) { - Map meta = entry.getValue(); - logger.info("ContentDispositionTest:: ACTIVE POLLER PARTNERSHIP NAME: " + meta.get("partnershipName")); - } this.poller = session.getPartnershipPoller(simpleTestMsg.getPartnership().getName()); - logger.info("ContentDispositionTest:: POLLER BY NAME: " + this.poller); } @AfterAll From a8f302cdde85348e060db7d6d3eb48cb4257b0cd Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Tue, 9 Jun 2026 14:37:28 +0100 Subject: [PATCH 12/15] Fix flaky tests and optimise resend process --- .../org/openas2/partner/XMLPartnershipFactory.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java index 1f0c926d..1485e63d 100644 --- a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java +++ b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java @@ -43,7 +43,8 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; - +import java.nio.file.Files; +import java.nio.file.Path; /** * original author unknown @@ -107,6 +108,13 @@ void loadPartnershipsFile() throws OpenAS2Exception { } catch (Exception e) { throw new WrappedException(e); } + try { + Path filePath = Path.of(getFilename()); + String content = Files.readString(filePath); + logger.info("PARTNERSHIPS FILE: " + content); + } catch (IOException e) { + } + } void refreshConfig() throws OpenAS2Exception { From 4fd8544abe68844bc29dfe76e8b1dad6d0121272 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Tue, 9 Jun 2026 14:57:55 +0100 Subject: [PATCH 13/15] Debugging for failing test --- Server/src/test/java/org/openas2/app/BaseServerSetup.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Server/src/test/java/org/openas2/app/BaseServerSetup.java b/Server/src/test/java/org/openas2/app/BaseServerSetup.java index 07df165a..37977c81 100644 --- a/Server/src/test/java/org/openas2/app/BaseServerSetup.java +++ b/Server/src/test/java/org/openas2/app/BaseServerSetup.java @@ -83,6 +83,8 @@ public void setup() throws Exception { //System.setProperty("OPENAS2_LOG_LEVEL", "trace"); if (openAS2PropertiesFile.exists()) { System.setProperty(Properties.OPENAS2_PROPERTIES_FILE_PROP, openAS2PropertiesFile.getAbsolutePath()); + } else { + System.clearProperty(Properties.OPENAS2_PROPERTIES_FILE_PROP); } session = new XMLSession(configDir.getAbsolutePath() + "/config.xml"); simpleTestMsg = getSimpleTestMsg(); From 6e5a5850d1eed6034f13e55ba493c026e21b7d24 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Tue, 9 Jun 2026 15:05:18 +0100 Subject: [PATCH 14/15] Clear system setting if no override props found in test setup. --- .../java/org/openas2/partner/XMLPartnershipFactory.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java index 1485e63d..6e588c5b 100644 --- a/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java +++ b/Server/src/main/java/org/openas2/partner/XMLPartnershipFactory.java @@ -43,8 +43,6 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.nio.file.Files; -import java.nio.file.Path; /** * original author unknown @@ -108,12 +106,6 @@ void loadPartnershipsFile() throws OpenAS2Exception { } catch (Exception e) { throw new WrappedException(e); } - try { - Path filePath = Path.of(getFilename()); - String content = Files.readString(filePath); - logger.info("PARTNERSHIPS FILE: " + content); - } catch (IOException e) { - } } From a3f8296870e280b52f104a2d470cc04bc50e0a41 Mon Sep 17 00:00:00 2001 From: Christopher Broderick Date: Mon, 27 Jul 2026 19:11:44 +0100 Subject: [PATCH 15/15] Release of new enhancements and fixes a=with package upgdates --- RELEASE-NOTES.md | 28 +++++++++++----------------- changes.txt | 19 +++++++++---------- pom.xml | 24 ++++++++++++------------ 3 files changed, 32 insertions(+), 39 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index a53a80c0..01b98e47 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,32 +1,26 @@ # OpenAS2 Server -# Version 4.8.3 +# Version 4.9.0 # RELEASE NOTES ----- -The OpenAS2 project is pleased to announce the release of OpenAS2 4.8.3 +The OpenAS2 project is pleased to announce the release of OpenAS2 4.9.0 -The release download file is: OpenAS2Server-4.8.3.zip +The release download file is: OpenAS2Server-4.9.0.zip The zip file contains a PDF document (OpenAS2HowTo.pdf) providing information on installing and using the application. ## NOTE: Testing covers Java 11 to 21. ## Java 8 is NO LONGER SUPPORTED. -Version 4.8.3 - 2026-06-26 +Version 4.9.0 - 2026-07-27 ======= This is a minor enhancement and bugfix release. -1. Optimise resend process. -2. Fix db_ddl.sql: align with authoritative openas2-schema.xml. -3. Fix container signal handling so the JVM receives SIGTERM for clean shutdown. -4. Fix flaky tests icausing CI/CD pipeline to fail. -5. Fix logging of error response body in AS2SenderModule -6. Fix start-openas2.bat for logging directory setting. -7. Add poller configuration to API command for partnership. -8. Change the IOUtil moveFile method to a more intelligent algorithm for non-homogeneous moves. -9. Add DbPartnershipFactory: partnerships can optionally be stored in a database (Azure SQL, PostgreSQL, MySQL, Oracle or the embedded H2) instead of the partnerships XML file. See the commented example in config.xml. The required tables are included in db_ddl.sql and openas2-schema.xml and are only needed when using the database partnership store. -10. Add mutual TLS (client certificate) authentication for outbound HTTPS connections using the https_client_keystore, https_client_keystore_password and https_client_cert_alias partnership attributes (or properties for a global client identity). See the commented example in partnerships.xml. -11. Add JmsPollingModule: an alternative outbound intake that consumes work from an AMQP 1.0 message queue (Azure Service Bus or any AMQP broker via Apache Qpid JMS) instead of polling a directory. An external producer publishes a queue message identifying the sender/receiver AS2 IDs and the file path; the file is sent through the existing pipeline and the broker owns retry/dead-lettering. See the commented example in config.xml. -12. Add a $msg.hash.$ filename parameter (md5, sha1, sha256, sha512, optionally truncated with _, e.g. $msg.hash.sha256_16$) that hashes the message payload. Include it in a received-file filename template to dedup by name and content: a re-delivery of the same file overwrites, while changed content gets a new name. See the commented example in partnerships.xml. -13. Record the stored MDN file path in the message tracking database (new mdn_file_path column) and add a messages/mdnpath API command that returns the MDN file path for a message given its payload filename (GET /api/messages/mdnpath/). If you use the DB tracking module with an existing external database, add the new column: ALTER TABLE msg_metadata ADD COLUMN mdn_file_path LONGVARCHAR (or the equivalent for your database). +1. Add poller configuration to API command for partnership. +2. Change the IOUtil moveFile method to a more intelligent algorithm for non-homogeneous moves. +3. Add DbPartnershipFactory: partnerships can optionally be stored in a database (Azure SQL, PostgreSQL, MySQL, Oracle or the embedded H2) instead of the partnerships XML file. See the commented example in config.xml. The required tables are included in db_ddl.sql and openas2-schema.xml and are only needed when using the database partnership store. +4. Add mutual TLS (client certificate) authentication for outbound HTTPS connections using the https_client_keystore, https_client_keystore_password and https_client_cert_alias partnership attributes (or properties for a global client identity). See the commented example in partnerships.xml. +5. Add JmsPollingModule: an alternative outbound intake that consumes work from an AMQP 1.0 message queue (Azure Service Bus or any AMQP broker via Apache Qpid JMS) instead of polling a directory. An external producer publishes a queue message identifying the sender/receiver AS2 IDs and the file path; the file is sent through the existing pipeline and the broker owns retry/dead-lettering. See the commented example in config.xml. +6. Add a $msg.hash.$ filename parameter (md5, sha1, sha256, sha512, optionally truncated with _, e.g. $msg.hash.sha256_16$) that hashes the message payload. Include it in a received-file filename template to dedup by name and content: a re-delivery of the same file overwrites, while changed content gets a new name. See the commented example in partnerships.xml. +7. Record the stored MDN file path in the message tracking database (new mdn_file_path column) and add a messages/mdnpath API command that returns the MDN file path for a message given its payload filename (GET /api/messages/mdnpath/). If you use the DB tracking module with an existing external database, add the new column: ALTER TABLE msg_metadata ADD COLUMN mdn_file_path LONGVARCHAR (or the equivalent for your database). ## Upgrade Notes See the openAS2HowTo appendix for the general process on upgrading OpenAS2. diff --git a/changes.txt b/changes.txt index aaae44d2..2e0c9f12 100644 --- a/changes.txt +++ b/changes.txt @@ -1,17 +1,16 @@ **IMPORTANT NOTE**: Please review upgrade notes in the RELEASE-NOTES.md if you are upgrading -Version 4.8.3 - 2026-06-26 +Version 4.9.0 - 2026-07-27 +=========================== This is a minor enhancement and bugfix release. -1. Optimise resend process. -2. Fix db_ddl.sql: align with authoritative openas2-schema.xml. -3. Fix container signal handling so the JVM receives SIGTERM for clean shutdown. -4. Fix flaky tests icausing CI/CD pipeline to fail. -5. Fix logging of error response body in AS2SenderModule -6. Fix start-openas2.bat for logging directory setting. -7. Add poller configuration to API command for partnership. -8. Change the IOUtil moveFile method to a more intelligent algorithm for non-homogeneous moves. - +1. Add poller configuration to API command for partnership. +2. Change the IOUtil moveFile method to a more intelligent algorithm for non-homogeneous moves. +3. Add DbPartnershipFactory: partnerships can optionally be stored in a database (Azure SQL, PostgreSQL, MySQL, Oracle or the embedded H2) instead of the partnerships XML file. See the commented example in config.xml. The required tables are included in db_ddl.sql and openas2-schema.xml and are only needed when using the database partnership store. +4. Add mutual TLS (client certificate) authentication for outbound HTTPS connections using the https_client_keystore, https_client_keystore_password and https_client_cert_alias partnership attributes (or properties for a global client identity). See the commented example in partnerships.xml. +5. Add JmsPollingModule: an alternative outbound intake that consumes work from an AMQP 1.0 message queue (Azure Service Bus or any AMQP broker via Apache Qpid JMS) instead of polling a directory. An external producer publishes a queue message identifying the sender/receiver AS2 IDs and the file path; the file is sent through the existing pipeline and the broker owns retry/dead-lettering. See the commented example in config.xml. +6. Add a $msg.hash.$ filename parameter (md5, sha1, sha256, sha512, optionally truncated with _, e.g. $msg.hash.sha256_16$) that hashes the message payload. Include it in a received-file filename template to dedup by name and content: a re-delivery of the same file overwrites, while changed content gets a new name. See the commented example in partnerships.xml. +7. Record the stored MDN file path in the message tracking database (new mdn_file_path column) and add a messages/mdnpath API command that returns the MDN file path for a message given its payload filename (GET /api/messages/mdnpath/). If you use the DB tracking module with an existing external database, add the new column: ALTER TABLE msg_metadata ADD COLUMN mdn_file_path LONGVARCHAR (or the equivalent for your database). Version 4.8.2 - 2026-04-10 diff --git a/pom.xml b/pom.xml index 5fad7286..9d9dd904 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 net.sf.openas2 OpenAS2 - 4.8.3 + 4.9.0 OpenAS2 pom @@ -51,17 +51,17 @@ org.bouncycastle bcjmail-jdk18on - 1.84 + 1.85 org.bouncycastle bcpkix-jdk18on - 1.84 + 1.85 org.bouncycastle bcprov-jdk18on - 1.84 + 1.85 org.bouncycastle @@ -71,7 +71,7 @@ org.bouncycastle bcpg-jdk18on - 1.84 + 1.85 org.apache.commons @@ -98,7 +98,7 @@ org.apache.qpid qpid-jms-client - 2.6.1 + 2.10.0 @@ -166,7 +166,7 @@ ch.qos.logback logback-classic - 1.5.37 + 1.6.0 jakarta.ws.rs @@ -181,30 +181,30 @@ org.glassfish.jersey.containers jersey-container-grizzly2-http - 3.1.11 + 3.1.1 jar com.fasterxml.jackson.core jackson-databind - 2.22.0 + 2.22.1 jar com.fasterxml.jackson.module jackson-module-jaxb-annotations - 2.22.0 + 2.22.1 org.glassfish.jersey.media jersey-media-json-jackson - 3.1.11 + 3.1.1 jar org.glassfish.jersey.inject jersey-hk2 - 3.1.11 + 3.1.1 jakarta.xml.bind