This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push: new eed7b11 CAMEL-13680: camel-file - From file to file with readLock=fileLock dont work on windows eed7b11 is described below commit eed7b1113ec1b72731b051f69ea3d269bf5fb1c5 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Tue Jun 25 09:42:19 2019 +0200 CAMEL-13680: camel-file - From file to file with readLock=fileLock dont work on windows --- .../camel/component/file/FileOperations.java | 24 ++++++++-- .../camel/component/file/GenericFileHelper.java | 36 ++++++++++++++ .../FileLockExclusiveReadLockStrategy.java | 29 +++++------ .../src/main/java/org/apache/camel/Exchange.java | 1 + .../file/FileExclusiveReadLockCopyTest.java | 56 ++++++++++++++++++++++ 5 files changed, 126 insertions(+), 20 deletions(-) diff --git a/components/camel-file/src/main/java/org/apache/camel/component/file/FileOperations.java b/components/camel-file/src/main/java/org/apache/camel/component/file/FileOperations.java index ba21975..18e0bec 100644 --- a/components/camel-file/src/main/java/org/apache/camel/component/file/FileOperations.java +++ b/components/camel-file/src/main/java/org/apache/camel/component/file/FileOperations.java @@ -17,12 +17,14 @@ package org.apache.camel.component.file; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.Writer; import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.nio.channels.SeekableByteChannel; import java.nio.charset.Charset; import java.nio.file.Files; @@ -44,6 +46,8 @@ import org.apache.camel.util.StringHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.file.GenericFileHelper.asExclusiveReadLockKey; + /** * File operations for {@link java.io.File}. */ @@ -300,7 +304,7 @@ public class FileOperations implements GenericFileOperations<File> { } } else if (source != null && source.exists()) { // no there is no local work file so use file to file copy if the source exists - writeFileByFile(source, file); + writeFileByFile(source, file, exchange); // try to keep last modified timestamp if configured to do so keepLastModified(exchange, file); // set permissions if the chmod option was set @@ -375,12 +379,24 @@ public class FileOperations implements GenericFileOperations<File> { } private boolean writeFileByLocalWorkPath(File source, File file) throws IOException { - LOG.trace("Using local work file being renamed from: {} to: {}", source, file); + LOG.trace("writeFileByFile using local work file being renamed from: {} to: {}", source, file); return FileUtil.renameFile(source, file, endpoint.isCopyAndDeleteOnRenameFail()); } - private void writeFileByFile(File source, File target) throws IOException { - Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING); + private void writeFileByFile(File source, File target, Exchange exchange) throws IOException { + // in case we are using file locks as read-locks then we need to use file channels for copying to support this + String path = source.getAbsolutePath(); + FileChannel channel = exchange.getProperty(asExclusiveReadLockKey(path, Exchange.FILE_LOCK_CHANNEL_FILE), FileChannel.class); + if (channel != null) { + try (FileChannel out = new FileOutputStream(target).getChannel()) { + LOG.trace("writeFileByFile using FileChannel: {} -> {}", source, target); + channel.transferTo(0, channel.size(), out); + } + } else { + // use regular file copy + LOG.trace("writeFileByFile using Files.copy: {} -> {}", source, target); + Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING); + } } private void writeFileByStream(InputStream in, File target) throws IOException { diff --git a/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileHelper.java b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileHelper.java new file mode 100644 index 0000000..9519d87 --- /dev/null +++ b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileHelper.java @@ -0,0 +1,36 @@ +/* + * 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; + +public final class GenericFileHelper { + + private GenericFileHelper() { + } + + public static String asExclusiveReadLockKey(GenericFile file, String key) { + // use the copy from absolute path as that was the original path of the file when the lock was acquired + // for example if the file consumer uses preMove then the file is moved and therefore has another name + // that would no longer match + String path = file.getCopyFromAbsoluteFilePath() != null ? file.getCopyFromAbsoluteFilePath() : file.getAbsoluteFilePath(); + return asExclusiveReadLockKey(path, key); + } + + public static String asExclusiveReadLockKey(String path, String key) { + return path + "-" + key; + } + +} diff --git a/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileLockExclusiveReadLockStrategy.java b/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileLockExclusiveReadLockStrategy.java index 42a3dd6..0c7cbd5 100644 --- a/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileLockExclusiveReadLockStrategy.java +++ b/components/camel-file/src/main/java/org/apache/camel/component/file/strategy/FileLockExclusiveReadLockStrategy.java @@ -34,6 +34,8 @@ import org.apache.camel.util.StopWatch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.file.GenericFileHelper.asExclusiveReadLockKey; + /** * Acquires exclusive read lock to the given file. Will wait until the lock is granted. * After granting the read lock it is released, we just want to make sure that when we start @@ -133,8 +135,9 @@ public class FileLockExclusiveReadLockStrategy extends MarkerFileExclusiveReadLo } // store read-lock state - exchange.setProperty(asReadLockKey(file, Exchange.FILE_LOCK_EXCLUSIVE_LOCK), lock); - exchange.setProperty(asReadLockKey(file, Exchange.FILE_LOCK_RANDOM_ACCESS_FILE), randomAccessFile); + exchange.setProperty(asExclusiveReadLockKey(file, Exchange.FILE_LOCK_EXCLUSIVE_LOCK), lock); + exchange.setProperty(asExclusiveReadLockKey(file, Exchange.FILE_LOCK_RANDOM_ACCESS_FILE), randomAccessFile); + exchange.setProperty(asExclusiveReadLockKey(file, Exchange.FILE_LOCK_CHANNEL_FILE), channel); // we grabbed the lock return true; @@ -146,16 +149,18 @@ public class FileLockExclusiveReadLockStrategy extends MarkerFileExclusiveReadLo // must call super super.doReleaseExclusiveReadLock(operations, file, exchange); - FileLock lock = exchange.getProperty(asReadLockKey(file, Exchange.FILE_LOCK_EXCLUSIVE_LOCK), FileLock.class); - RandomAccessFile rac = exchange.getProperty(asReadLockKey(file, Exchange.FILE_LOCK_EXCLUSIVE_LOCK), RandomAccessFile.class); + FileLock lock = exchange.getProperty(asExclusiveReadLockKey(file, Exchange.FILE_LOCK_EXCLUSIVE_LOCK), FileLock.class); + RandomAccessFile rac = exchange.getProperty(asExclusiveReadLockKey(file, Exchange.FILE_LOCK_EXCLUSIVE_LOCK), RandomAccessFile.class); + Channel channel = exchange.getProperty(asExclusiveReadLockKey(file, Exchange.FILE_LOCK_CHANNEL_FILE), FileChannel.class); String target = file.getFileName(); if (lock != null) { - Channel channel = lock.acquiredBy(); - try { - lock.release(); + channel = lock.acquiredBy() != null ? lock.acquiredBy() : channel; + try (FileLock fileLock = lock) { + // use try-with-resource to auto-close lock + fileLock.release(); } finally { - // close channel as well + // close channel and rac as well IOHelper.close(channel, "while releasing exclusive read lock for file: " + target, LOG); IOHelper.close(rac, "while releasing exclusive read lock for file: " + target, LOG); } @@ -192,12 +197,4 @@ public class FileLockExclusiveReadLockStrategy extends MarkerFileExclusiveReadLo this.readLockLoggingLevel = readLockLoggingLevel; } - private static String asReadLockKey(GenericFile file, String key) { - // use the copy from absolute path as that was the original path of the file when the lock was acquired - // for example if the file consumer uses preMove then the file is moved and therefore has another name - // that would no longer match - String path = file.getCopyFromAbsoluteFilePath() != null ? file.getCopyFromAbsoluteFilePath() : file.getAbsoluteFilePath(); - return path + "-" + key; - } - } 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 ad38c87..a61748d 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 @@ -137,6 +137,7 @@ public interface Exchange { String FILE_LOCK_FILE_NAME = "CamelFileLockFileName"; String FILE_LOCK_EXCLUSIVE_LOCK = "CamelFileLockExclusiveLock"; String FILE_LOCK_RANDOM_ACCESS_FILE = "CamelFileLockRandomAccessFile"; + String FILE_LOCK_CHANNEL_FILE = "CamelFileLockChannelFile"; String FILTER_MATCHED = "CamelFilterMatched"; String FILTER_NON_XML_CHARS = "CamelFilterNonXmlChars"; diff --git a/core/camel-core/src/test/java/org/apache/camel/component/file/FileExclusiveReadLockCopyTest.java b/core/camel-core/src/test/java/org/apache/camel/component/file/FileExclusiveReadLockCopyTest.java new file mode 100644 index 0000000..5323c81 --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/component/file/FileExclusiveReadLockCopyTest.java @@ -0,0 +1,56 @@ +/* + * 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; + +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.junit.Before; +import org.junit.Test; + +public class FileExclusiveReadLockCopyTest extends ContextTestSupport { + + private String fileUrl = "file://target/data/exclusiveread?readLock=fileLock&initialDelay=0&delay=10"; + + @Override + @Before + public void setUp() throws Exception { + deleteDirectory("target/data/exclusiveread"); + super.setUp(); + } + + @Test + public void testCopy() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMessageCount(1); + mock.expectedFileExists("target/data/exclusiveread/out/hello.txt", "Hello World"); + + template.sendBodyAndHeader(fileUrl, "Hello World", Exchange.FILE_NAME, "hello.txt"); + + mock.assertIsSatisfied(); + } + + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + public void configure() throws Exception { + from(fileUrl).to("file://target/data/exclusiveread/out").to("mock:result"); + } + }; + } + +} \ No newline at end of file