This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 1f35d963cd79 CAMEL-24484: camel-file - readLockRemoveOnCommit=false 
must not remove a pre-existing idempotent entry
1f35d963cd79 is described below

commit 1f35d963cd79366d6b102cf1cadb90ce50c711d3
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Aug 26 18:11:08 2026 +0200

    CAMEL-24484: camel-file - readLockRemoveOnCommit=false must not remove a 
pre-existing idempotent entry
    
    Fixes a regression where readLockRemoveOnCommit=false (and 
readLockRemoveOnRollback=false)
    was silently ignored: a previously committed idempotent-repository entry 
got removed the
    next time a file with the same idempotent key was polled.
    
    Two places assumed "file did not start processing" implies "we added this 
idempotent entry
    ourselves, so it's safe to remove" - false whenever the entry pre-existed. 
Fixed by tracking
    lock ownership explicitly via a new Exchange.FILE_LOCK_IDEMPOTENT_ACQUIRED 
property, and by
    guarding GenericFileConsumer's "not started" cleanup with isIdempotent().
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
    
    Closes #25726
---
 .../camel/component/file/GenericFileConsumer.java  |   6 +-
 ...dempotentChangedRepositoryReadLockStrategy.java |  25 ++++-
 ...IdempotentRenameRepositoryReadLockStrategy.java |  25 ++++-
 .../FileIdempotentRepositoryReadLockStrategy.java  |  24 ++++-
 .../org/apache/camel/ExchangeConstantProvider.java |   3 +-
 .../src/main/java/org/apache/camel/Exchange.java   |   1 +
 ...dempotentChangedReadLockRemoveOnCommitTest.java |  82 ++++++++++++++++
 ...mpotentRepositoryReadLockStrategyAbortTest.java | 105 +++++++++++++++++++++
 8 files changed, 262 insertions(+), 9 deletions(-)

diff --git 
a/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileConsumer.java
 
b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileConsumer.java
index fe72a23124b1..f42276871293 100644
--- 
a/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileConsumer.java
+++ 
b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileConsumer.java
@@ -284,7 +284,8 @@ public abstract class GenericFileConsumer<T> extends 
ScheduledBatchPollingConsum
             String key = file.getAbsoluteFilePath();
             endpoint.getInProgressRepository().remove(key);
             // if we added eager to idempotent then we need to remove this
-            if (endpoint.isIdempotentEager() && 
endpoint.getIdempotentRepository() != null) {
+            if (Boolean.TRUE.equals(endpoint.isIdempotent()) && 
endpoint.isIdempotentEager()
+                    && endpoint.getIdempotentRepository() != null) {
                 removeExcessiveIdempotentFile(file, null);
             }
             releaseExchange(exchange, true);
@@ -311,7 +312,8 @@ public abstract class GenericFileConsumer<T> extends 
ScheduledBatchPollingConsum
         for (GenericFile file : files) {
             String key = file.getAbsoluteFilePath();
             endpoint.getInProgressRepository().remove(key);
-            if (endpoint.isIdempotentEager() && 
endpoint.getIdempotentRepository() != null) {
+            if (Boolean.TRUE.equals(endpoint.isIdempotent()) && 
endpoint.isIdempotentEager()
+                    && endpoint.getIdempotentRepository() != null) {
                 removeExcessiveIdempotentFile(file, null);
             }
         }
diff --git 
a/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentChangedRepositoryReadLockStrategy.java
 
b/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentChangedRepositoryReadLockStrategy.java
index 945277336efc..ce5e6c8c355a 100644
--- 
a/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentChangedRepositoryReadLockStrategy.java
+++ 
b/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentChangedRepositoryReadLockStrategy.java
@@ -108,6 +108,10 @@ public class 
FileIdempotentChangedRepositoryReadLockStrategy extends ServiceSupp
                 idempotentRepository.remove(exchange, key);
             }
         }
+        // remember whether we own the key end-to-end (idempotent add + 
changed check both
+        // succeeded), so releaseExclusiveReadLockOnAbort knows whether it is 
safe to remove it if
+        // begin() later fails for an unrelated reason
+        exchange.setProperty(asReadLockKey(file, 
Exchange.FILE_LOCK_IDEMPOTENT_ACQUIRED), answer);
         return answer;
     }
 
@@ -115,8 +119,16 @@ public class 
FileIdempotentChangedRepositoryReadLockStrategy extends ServiceSupp
     public void releaseExclusiveReadLockOnAbort(
             GenericFileOperations<File> operations, GenericFile<File> file, 
Exchange exchange)
             throws Exception {
-        String key = asKey(exchange, file);
-        idempotentRepository.remove(exchange, key);
+        // only remove the key if we own it end-to-end. If we never owned it 
(pre-existing key) or
+        // the changed-check failed (already cleaned up above in 
acquireExclusiveReadLock) there is
+        // nothing to do here. If we do own it, acquireExclusiveReadLock 
succeeded and something
+        // later in begin() must have failed, so the key is ours to clean up
+        boolean acquired
+                = exchange.getProperty(asReadLockKey(file, 
Exchange.FILE_LOCK_IDEMPOTENT_ACQUIRED), false, Boolean.class);
+        if (acquired) {
+            String key = asKey(exchange, file);
+            idempotentRepository.remove(exchange, key);
+        }
         changed.releaseExclusiveReadLockOnAbort(operations, file, exchange);
     }
 
@@ -328,6 +340,15 @@ public class 
FileIdempotentChangedRepositoryReadLockStrategy extends ServiceSupp
         return key;
     }
 
+    private static String asReadLockKey(GenericFile<File> file, String key) {
+        // use the copy from absolute path as that was the original path of the
+        // file when the lock was acquired; e.g. if the file consumer uses 
preMove
+        // then the file is moved and would otherwise no longer match
+        String path = file.getCopyFromAbsoluteFilePath() != null
+                ? file.getCopyFromAbsoluteFilePath() : 
file.getAbsoluteFilePath();
+        return path + "-" + key;
+    }
+
     @Override
     protected void doStart() throws Exception {
         ObjectHelper.notNull(camelContext, "camelContext", this);
diff --git 
a/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentRenameRepositoryReadLockStrategy.java
 
b/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentRenameRepositoryReadLockStrategy.java
index 72ca3c2a866a..0f205dc20bf8 100644
--- 
a/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentRenameRepositoryReadLockStrategy.java
+++ 
b/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentRenameRepositoryReadLockStrategy.java
@@ -100,6 +100,10 @@ public class 
FileIdempotentRenameRepositoryReadLockStrategy extends ServiceSuppo
                 idempotentRepository.remove(exchange, key);
             }
         }
+        // remember whether we own the key end-to-end (idempotent add + rename 
check both
+        // succeeded), so releaseExclusiveReadLockOnAbort knows whether it is 
safe to remove it if
+        // begin() later fails for an unrelated reason
+        exchange.setProperty(asReadLockKey(file, 
Exchange.FILE_LOCK_IDEMPOTENT_ACQUIRED), answer);
         return answer;
     }
 
@@ -107,8 +111,16 @@ public class 
FileIdempotentRenameRepositoryReadLockStrategy extends ServiceSuppo
     public void releaseExclusiveReadLockOnAbort(
             GenericFileOperations<File> operations, GenericFile<File> file, 
Exchange exchange)
             throws Exception {
-        String key = asKey(exchange, file);
-        idempotentRepository.remove(exchange, key);
+        // only remove the key if we own it end-to-end. If we never owned it 
(pre-existing key) or
+        // the rename-check failed (already cleaned up above in 
acquireExclusiveReadLock) there is
+        // nothing to do here. If we do own it, acquireExclusiveReadLock 
succeeded and something
+        // later in begin() must have failed, so the key is ours to clean up
+        boolean acquired
+                = exchange.getProperty(asReadLockKey(file, 
Exchange.FILE_LOCK_IDEMPOTENT_ACQUIRED), false, Boolean.class);
+        if (acquired) {
+            String key = asKey(exchange, file);
+            idempotentRepository.remove(exchange, key);
+        }
         rename.releaseExclusiveReadLockOnAbort(operations, file, exchange);
     }
 
@@ -242,6 +254,15 @@ public class 
FileIdempotentRenameRepositoryReadLockStrategy extends ServiceSuppo
         return key;
     }
 
+    private static String asReadLockKey(GenericFile<File> file, String key) {
+        // use the copy from absolute path as that was the original path of the
+        // file when the lock was acquired; e.g. if the file consumer uses 
preMove
+        // then the file is moved and would otherwise no longer match
+        String path = file.getCopyFromAbsoluteFilePath() != null
+                ? file.getCopyFromAbsoluteFilePath() : 
file.getAbsoluteFilePath();
+        return path + "-" + key;
+    }
+
     @Override
     protected void doStart() throws Exception {
         ObjectHelper.notNull(camelContext, "camelContext", this);
diff --git 
a/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentRepositoryReadLockStrategy.java
 
b/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentRepositoryReadLockStrategy.java
index 94c124619af2..1883051b947d 100644
--- 
a/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentRepositoryReadLockStrategy.java
+++ 
b/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileIdempotentRepositoryReadLockStrategy.java
@@ -88,6 +88,9 @@ public class FileIdempotentRepositoryReadLockStrategy extends 
ServiceSupport
             // another node is processing the file so skip
             CamelLogger.log(LOG, readLockLoggingLevel, "Cannot acquire read 
lock. Will skip the file: " + file);
         }
+        // remember whether we added the key ourselves, so 
releaseExclusiveReadLockOnAbort knows
+        // whether it is safe to remove it if begin() later fails for an 
unrelated reason
+        exchange.setProperty(asReadLockKey(file, 
Exchange.FILE_LOCK_IDEMPOTENT_ACQUIRED), answer);
         return answer;
     }
 
@@ -95,8 +98,16 @@ public class FileIdempotentRepositoryReadLockStrategy 
extends ServiceSupport
     public void releaseExclusiveReadLockOnAbort(
             GenericFileOperations<File> operations, GenericFile<File> file, 
Exchange exchange)
             throws Exception {
-        String key = asKey(exchange, file);
-        idempotentRepository.remove(exchange, key);
+        // only remove the key if we added it during acquireExclusiveReadLock. 
If the key already
+        // existed (owned by a previous run or another node) it must not be 
touched here. If we did
+        // add it, acquireExclusiveReadLock succeeded and something later in 
begin() must have
+        // failed, so the key is ours to clean up
+        boolean acquired
+                = exchange.getProperty(asReadLockKey(file, 
Exchange.FILE_LOCK_IDEMPOTENT_ACQUIRED), false, Boolean.class);
+        if (acquired) {
+            String key = asKey(exchange, file);
+            idempotentRepository.remove(exchange, key);
+        }
     }
 
     @Override
@@ -290,6 +301,15 @@ public class FileIdempotentRepositoryReadLockStrategy 
extends ServiceSupport
         return key;
     }
 
+    private static String asReadLockKey(GenericFile<File> file, String key) {
+        // use the copy from absolute path as that was the original path of the
+        // file when the lock was acquired; e.g. if the file consumer uses 
preMove
+        // then the file is moved and would otherwise no longer match
+        String path = file.getCopyFromAbsoluteFilePath() != null
+                ? file.getCopyFromAbsoluteFilePath() : 
file.getAbsoluteFilePath();
+        return path + "-" + key;
+    }
+
     @Override
     protected void doStart() throws Exception {
         ObjectHelper.notNull(camelContext, "camelContext", this);
diff --git 
a/core/camel-api/src/generated/java/org/apache/camel/ExchangeConstantProvider.java
 
b/core/camel-api/src/generated/java/org/apache/camel/ExchangeConstantProvider.java
index ec7e00bb731b..9b93e7bb3081 100644
--- 
a/core/camel-api/src/generated/java/org/apache/camel/ExchangeConstantProvider.java
+++ 
b/core/camel-api/src/generated/java/org/apache/camel/ExchangeConstantProvider.java
@@ -17,7 +17,7 @@ public class ExchangeConstantProvider {
 
     private static final Map<String, String> MAP;
     static {
-        Map<String, String> map = new HashMap<>(155);
+        Map<String, String> map = new HashMap<>(156);
         map.put("ACTIVITY_SPAN_TAGS", "CamelActivitySpanTags");
         map.put("AGGREGATED_COLLECTION_GUARD", 
"CamelAggregatedCollectionGuard");
         map.put("AGGREGATED_COMPLETED_BY", "CamelAggregatedCompletedBy");
@@ -73,6 +73,7 @@ public class ExchangeConstantProvider {
         map.put("FILE_LOCK_EXCLUSIVE_LOCK", "CamelFileLockExclusiveLock");
         map.put("FILE_LOCK_FILE_ACQUIRED", "CamelFileLockFileAcquired");
         map.put("FILE_LOCK_FILE_NAME", "CamelFileLockFileName");
+        map.put("FILE_LOCK_IDEMPOTENT_ACQUIRED", 
"CamelFileLockIdempotentAcquired");
         map.put("FILE_LOCK_RANDOM_ACCESS_FILE", 
"CamelFileLockRandomAccessFile");
         map.put("FILE_NAME", "CamelFileName");
         map.put("FILE_NAME_CONSUMED", "CamelFileNameConsumed");
diff --git a/core/camel-api/src/main/java/org/apache/camel/Exchange.java 
b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
index d2b64260536d..74457a08e0e8 100644
--- a/core/camel-api/src/main/java/org/apache/camel/Exchange.java
+++ b/core/camel-api/src/main/java/org/apache/camel/Exchange.java
@@ -156,6 +156,7 @@ public interface Exchange extends VariableAware {
     String FILE_LENGTH = "CamelFileLength";
     String FILE_LOCK_FILE_ACQUIRED = "CamelFileLockFileAcquired";
     String FILE_LOCK_FILE_NAME = "CamelFileLockFileName";
+    String FILE_LOCK_IDEMPOTENT_ACQUIRED = "CamelFileLockIdempotentAcquired";
     String FILE_LOCK_EXCLUSIVE_LOCK = "CamelFileLockExclusiveLock";
     String FILE_LOCK_RANDOM_ACCESS_FILE = "CamelFileLockRandomAccessFile";
     String FILE_LOCK_CHANNEL_FILE = "CamelFileLockChannelFile";
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/component/file/strategy/FileIdempotentChangedReadLockRemoveOnCommitTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/component/file/strategy/FileIdempotentChangedReadLockRemoveOnCommitTest.java
new file mode 100644
index 000000000000..1d35ca58c1cf
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/component/file/strategy/FileIdempotentChangedReadLockRemoveOnCommitTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file.strategy;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spi.Registry;
+import 
org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+
+class FileIdempotentChangedReadLockRemoveOnCommitTest extends 
ContextTestSupport {
+
+    final MemoryIdempotentRepository myRepo = new MemoryIdempotentRepository();
+
+    @Override
+    protected Registry createCamelRegistry() throws Exception {
+        Registry jndi = super.createCamelRegistry();
+        jndi.bind("myRepo", myRepo);
+        return jndi;
+    }
+
+    @Test
+    void testExistingEntryNotRemovedOnCommitFalse() throws Exception {
+        assertThat(myRepo.getCacheSize()).isZero();
+
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(1);
+
+        // drop hello.txt -> processed once, moved to .camel, entry retained
+        template.sendBodyAndHeader(fileUri(), "Hello World", 
Exchange.FILE_NAME, "hello.txt");
+        mock.assertIsSatisfied();
+
+        await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> 
assertThat(myRepo.contains("hello.txt")).isTrue());
+
+        // re-drop a file with the same name -> must be skipped as a duplicate
+        // and the existing entry must be retained because 
readLockRemoveOnCommit=false
+        mock.reset();
+        mock.expectedMessageCount(0);
+        template.sendBodyAndHeader(fileUri(), "Hello World Again", 
Exchange.FILE_NAME, "hello.txt");
+
+        // give the consumer several poll cycles to (wrongly) process/remove it
+        mock.assertIsSatisfied(2000);
+
+        assertThat(myRepo.contains("hello.txt"))
+                .as("existing idempotent entry must be retained when 
readLockRemoveOnCommit=false")
+                .isTrue();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                
from(fileUri("?initialDelay=0&delay=50&readLockCheckInterval=50"
+                             + 
"&readLock=idempotent-changed&idempotentRepository=#myRepo"
+                             + 
"&idempotentKey=${file:onlyname}&readLockRemoveOnCommit=false"))
+                        .to("mock:result");
+            }
+        };
+    }
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/component/file/strategy/FileIdempotentRepositoryReadLockStrategyAbortTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/component/file/strategy/FileIdempotentRepositoryReadLockStrategyAbortTest.java
new file mode 100644
index 000000000000..adeb287c4ada
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/component/file/strategy/FileIdempotentRepositoryReadLockStrategyAbortTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file.strategy;
+
+import java.io.File;
+import java.nio.file.Files;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.Exchange;
+import org.apache.camel.component.file.FileEndpoint;
+import org.apache.camel.component.file.GenericFile;
+import org.apache.camel.support.DefaultExchange;
+import 
org.apache.camel.support.processor.idempotent.MemoryIdempotentRepository;
+import org.apache.camel.support.service.ServiceHelper;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Unit tests releaseExclusiveReadLockOnAbort directly: it must only remove 
the idempotent key when this strategy itself
+ * added it during acquireExclusiveReadLock. abort() can also be reached after 
a successful acquire, e.g. when a preMove
+ * rename fails afterwards, so a pre-existing key (owned by a previous run or 
another node) must never be touched, while
+ * a key we own must be cleaned up.
+ */
+class FileIdempotentRepositoryReadLockStrategyAbortTest extends 
ContextTestSupport {
+
+    private FileIdempotentRepositoryReadLockStrategy 
newStrategy(MemoryIdempotentRepository repo) throws Exception {
+        ServiceHelper.startService(repo);
+
+        FileEndpoint endpoint = context.getEndpoint(fileUri(), 
FileEndpoint.class);
+
+        FileIdempotentRepositoryReadLockStrategy strategy = new 
FileIdempotentRepositoryReadLockStrategy();
+        strategy.setCamelContext(context);
+        strategy.setIdempotentRepository(repo);
+        strategy.prepareOnStartup(null, endpoint);
+        ServiceHelper.startService(strategy);
+        return strategy;
+    }
+
+    private GenericFile<File> newGenericFile(String name) throws Exception {
+        File file = testFile(name).toFile();
+        Files.createDirectories(file.getParentFile().toPath());
+        Files.writeString(file.toPath(), "Hello World");
+
+        GenericFile<File> genericFile = new GenericFile<>();
+        genericFile.setFile(file);
+        genericFile.setAbsoluteFilePath(file.getAbsolutePath());
+        return genericFile;
+    }
+
+    @Test
+    void testAbortDoesNotRemovePreExistingKey() throws Exception {
+        MemoryIdempotentRepository repo = new MemoryIdempotentRepository();
+        FileIdempotentRepositoryReadLockStrategy strategy = newStrategy(repo);
+
+        GenericFile<File> genericFile = newGenericFile("hello.txt");
+        Exchange exchange = new DefaultExchange(context);
+
+        // simulate the key already being owned by a previous, 
already-committed run
+        repo.add(exchange, genericFile.getAbsoluteFilePath());
+
+        boolean acquired = strategy.acquireExclusiveReadLock(null, 
genericFile, exchange);
+        assertThat(acquired).as("must not acquire a key that already 
exists").isFalse();
+
+        strategy.releaseExclusiveReadLockOnAbort(null, genericFile, exchange);
+
+        assertThat(repo.contains(genericFile.getAbsoluteFilePath()))
+                .as("pre-existing key must be retained since we never owned 
it")
+                .isTrue();
+    }
+
+    @Test
+    void testAbortRemovesKeyWeAcquired() throws Exception {
+        MemoryIdempotentRepository repo = new MemoryIdempotentRepository();
+        FileIdempotentRepositoryReadLockStrategy strategy = newStrategy(repo);
+
+        GenericFile<File> genericFile = newGenericFile("hello2.txt");
+        Exchange exchange = new DefaultExchange(context);
+
+        boolean acquired = strategy.acquireExclusiveReadLock(null, 
genericFile, exchange);
+        assertThat(acquired).as("must acquire a key that does not yet 
exist").isTrue();
+
+        // simulate begin() throwing afterwards for an unrelated reason (e.g. 
a preMove rename
+        // failing), which still routes into releaseExclusiveReadLockOnAbort
+        strategy.releaseExclusiveReadLockOnAbort(null, genericFile, exchange);
+
+        assertThat(repo.contains(genericFile.getAbsoluteFilePath()))
+                .as("key we acquired ourselves must be removed on abort")
+                .isFalse();
+    }
+}

Reply via email to