diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 814f61c9..d6eccfda 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,19 +1,19 @@ # OpenAS2 Server -# Version 4.6.2 +# Version 4.6.3 # RELEASE NOTES ----- -The OpenAS2 project is pleased to announce the release of OpenAS2 4.6.2 +The OpenAS2 project is pleased to announce the release of OpenAS2 4.6.3 -The release download file is: OpenAS2Server-4.6.2.zip +The release download file is: OpenAS2Server-4.6.3.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.6.2 - 2025-09-27 +Version 4.6.3 - 2025-09-29 This is a bugfix release. -1. Enhanced the poller algorithm to avoid race conditions under very high volume file processing. +1. Avoid concurrent processing error that occurs after files after sent as part of the cleanup process. ##Upgrade Notes diff --git a/Server/pom.xml b/Server/pom.xml index e9bec853..264b39c9 100644 --- a/Server/pom.xml +++ b/Server/pom.xml @@ -7,7 +7,7 @@ net.sf.openas2 OpenAS2 - 4.6.2 + 4.6.3 ../pom.xml diff --git a/Server/src/main/java/org/openas2/processor/receiver/DirectoryPollingModule.java b/Server/src/main/java/org/openas2/processor/receiver/DirectoryPollingModule.java index c388ca71..d1339979 100644 --- a/Server/src/main/java/org/openas2/processor/receiver/DirectoryPollingModule.java +++ b/Server/src/main/java/org/openas2/processor/receiver/DirectoryPollingModule.java @@ -38,6 +38,8 @@ public abstract class DirectoryPollingModule extends PollingModule { private Map trackedFiles = new HashMap(); // Files that have been passed to a processor - key is the absolute file path and value is the processing start timestamp private Map processingFiles = new HashMap(); + // Files that have been processed in thread mode and need removing from the tracked files maps by the main thread + Collection threadProcessedFiles = Collections.synchronizedCollection(new ArrayList()); private String errorDir = null; private String sentDir = null; @@ -217,24 +219,37 @@ protected void processSingleFile(File file, String fileEntryKey) { //forceStop(e1); return; } - } finally { - // Remove trackedFiles entry first to avoid race condition in parallel processing - trackedFiles.remove(fileEntryKey); - processingFiles.remove(fileEntryKey); - } + } } - protected void triggerFileProcessing(File file, String fileEntryKey) { - // Add the in processing flag on the file now otherwise in threaded mode there is a race condition - processingFiles.put(fileEntryKey, System.currentTimeMillis()); - if (processFilesAsThreads) { - processFileInThread(file, fileEntryKey); - } else { - processSingleFile(file, fileEntryKey); + private void processFileInThread(File file, String fileEntryKey) { + if (logger.isDebugEnabled()) { + logger.debug("Parallel processing mode handling file: " + file.getName()); } + executorService.execute(new Runnable() { + @Override + public void run() { + try { + processSingleFile(file, fileEntryKey); + } finally { + // Add to list for removal from tracking maps when updateTracking is called + threadProcessedFiles.add(fileEntryKey); + } + } + }); } private void updateTracking() { + // first remove processed files if we are running in parallel processing mode to avoid concurrency issues + if (processFilesAsThreads) { + synchronized (threadProcessedFiles) { + threadProcessedFiles.forEach((fileEntryKey) -> { + trackedFiles.remove(fileEntryKey); + processingFiles.remove(fileEntryKey); + }); + threadProcessedFiles.clear(); + } + } // Use an iterator to be able to remove entries whilst iterating over the map. Iterator> iter = trackedFiles.entrySet().iterator(); while (iter.hasNext()) { @@ -264,24 +279,24 @@ private void updateTracking() { trackedFiles.put(fileEntryKey, Long.valueOf(newLength)); } else { // no change in file length so process the file - triggerFileProcessing(file, fileEntryKey); + // Add the processing flag on the file now otherwise in threaded mode there is a race condition + processingFiles.put(fileEntryKey, System.currentTimeMillis()); + if (processFilesAsThreads) { + processFileInThread(file, fileEntryKey); + } else { + try { + processSingleFile(file, fileEntryKey); + } finally { + iter.remove(); + processingFiles.remove(fileEntryKey); + } + } + } } } } - private void processFileInThread(File file, String fileEntryKey) { - if (logger.isDebugEnabled()) { - logger.debug("Parallel processing mode handling file: " + file.getName()); - } - executorService.execute(new Runnable() { - @Override - public void run() { - processSingleFile(file, fileEntryKey); - } - }); - } - protected abstract Message createMessage(); protected void processFile(File file) throws OpenAS2Exception { diff --git a/changes.txt b/changes.txt index 14fa40d7..18b252f5 100644 --- a/changes.txt +++ b/changes.txt @@ -1,5 +1,10 @@ **IMPORTANT NOTE**: Please review upgrade notes in the RELEASE-NOTES.md if you are upgrading +Version 4.6.3 - 2025-09-29 + +This is a bugfix release. +1. Avoid concurrent processing error that occurs after files after sent as part of the cleanup process. + Version 4.6.2 - 2025-09-27 This is a bugfix release. diff --git a/pom.xml b/pom.xml index 0ead45c5..4d2909b8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 net.sf.openas2 OpenAS2 - 4.6.2 + 4.6.3 OpenAS2 pom