CAMEL-9610: Add chmodDirectory to allow setting file permissions on directories created by file producer.
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/9784d030 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/9784d030 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/9784d030 Branch: refs/heads/master Commit: 9784d030100e74a909c2f5fa6d2801b3dfc2a57a Parents: 5be46e4 Author: Claus Ibsen <davscl...@apache.org> Authored: Thu Feb 18 18:42:43 2016 +0100 Committer: Claus Ibsen <davscl...@apache.org> Committed: Thu Feb 18 19:57:39 2016 +0100 ---------------------------------------------------------------------- .../camel/component/file/FileOperations.java | 54 +++++++-- .../component/file/GenericFileEndpoint.java | 63 ++++++++++ .../FileProducerDirectoryChmodOptionTest.java | 117 +++++++++++++++++++ 3 files changed, 226 insertions(+), 8 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/9784d030/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java b/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java index 412d81a..c44c141 100644 --- a/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java +++ b/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java @@ -89,13 +89,47 @@ public class FileOperations implements GenericFileOperations<File> { return file.exists(); } + protected boolean buildDirectory(File dir, Set<PosixFilePermission> permissions) { + if (dir.exists()) { + return true; + } + + if (permissions == null || permissions.isEmpty()) { + return dir.mkdirs(); + } + + // create directory one part of a time and set permissions + try { + String[] parts = dir.getPath().split("\\" + File.separatorChar); + File base = new File("."); + for (String part : parts) { + File subDir = new File(base, part); + if (!subDir.exists()) { + if (subDir.mkdir()) { + if (LOG.isTraceEnabled()) { + LOG.trace("Setting chmod: {} on directory: {} ", PosixFilePermissions.toString(permissions), subDir); + } + Files.setPosixFilePermissions(subDir.toPath(), permissions); + } else { + return false; + } + } + base = new File(base, subDir.getName()); + } + } catch (IOException e) { + throw new GenericFileOperationFailedException("Error setting chmod on directory: " + dir, e); + } + + return true; + } + public boolean buildDirectory(String directory, boolean absolute) throws GenericFileOperationFailedException { ObjectHelper.notNull(endpoint, "endpoint"); // always create endpoint defined directory if (endpoint.isAutoCreate() && !endpoint.getFile().exists()) { LOG.trace("Building starting directory: {}", endpoint.getFile()); - endpoint.getFile().mkdirs(); + buildDirectory(endpoint.getFile(), endpoint.getDirectoryPermissions()); } if (ObjectHelper.isEmpty(directory)) { @@ -131,10 +165,8 @@ public class FileOperations implements GenericFileOperations<File> { // the directory already exists return true; } else { - if (LOG.isTraceEnabled()) { - LOG.trace("Building directory: {}", path); - } - return path.mkdirs(); + LOG.trace("Building directory: {}", path); + return buildDirectory(path, endpoint.getDirectoryPermissions()); } } } @@ -246,8 +278,10 @@ public class FileOperations implements GenericFileOperations<File> { if (ObjectHelper.isNotEmpty(endpoint.getChmod())) { Set<PosixFilePermission> permissions = endpoint.getPermissions(); if (!permissions.isEmpty()) { + if (LOG.isTraceEnabled()) { + LOG.trace("Setting chmod: {} on file: {} ", PosixFilePermissions.toString(permissions), file); + } Files.setPosixFilePermissions(file.toPath(), permissions); - LOG.trace("Setting chmod: {} on file: {} ", PosixFilePermissions.toString(permissions), file); } } // clear header as we have renamed the file @@ -265,8 +299,10 @@ public class FileOperations implements GenericFileOperations<File> { if (ObjectHelper.isNotEmpty(endpoint.getChmod())) { Set<PosixFilePermission> permissions = endpoint.getPermissions(); if (!permissions.isEmpty()) { + if (LOG.isTraceEnabled()) { + LOG.trace("Setting chmod: {} on file: {} ", PosixFilePermissions.toString(permissions), file); + } Files.setPosixFilePermissions(file.toPath(), permissions); - LOG.trace("Setting chmod: {} on file: {} ", PosixFilePermissions.toString(permissions), file); } } return true; @@ -296,8 +332,10 @@ public class FileOperations implements GenericFileOperations<File> { if (ObjectHelper.isNotEmpty(endpoint.getChmod())) { Set<PosixFilePermission> permissions = endpoint.getPermissions(); if (!permissions.isEmpty()) { + if (LOG.isTraceEnabled()) { + LOG.trace("Setting chmod: {} on file: {} ", PosixFilePermissions.toString(permissions), file); + } Files.setPosixFilePermissions(file.toPath(), permissions); - LOG.trace("Setting chmod: {} on file: {} ", PosixFilePermissions.toString(permissions), file); } } http://git-wip-us.apache.org/repos/asf/camel/blob/9784d030/camel-core/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java b/camel-core/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java index 0e20247..da6f235 100644 --- a/camel-core/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java +++ b/camel-core/src/main/java/org/apache/camel/component/file/GenericFileEndpoint.java @@ -96,6 +96,8 @@ public abstract class GenericFileEndpoint<T> extends ScheduledPollEndpoint imple protected boolean allowNullBody; @UriParam(label = "producer,advanced") protected String chmod; + @UriParam(label = "producer,advanced") + protected String chmodDirectory; // consumer options @@ -391,6 +393,67 @@ public abstract class GenericFileEndpoint<T> extends ScheduledPollEndpoint imple } } + public Set<PosixFilePermission> getDirectoryPermissions() { + Set<PosixFilePermission> permissions = new HashSet<PosixFilePermission>(); + if (ObjectHelper.isEmpty(chmodDirectory)) { + return permissions; + } + + String chmodString = chmodDirectory.substring(chmodDirectory.length() - 3); // if 4 digits chop off leading one + + Integer ownerValue = Integer.parseInt(chmodString.substring(0, 1)); + Integer groupValue = Integer.parseInt(chmodString.substring(1, 2)); + Integer othersValue = Integer.parseInt(chmodString.substring(2, 3)); + + if ((ownerValue & CHMOD_WRITE_MASK) > 0) { + permissions.add(PosixFilePermission.OWNER_WRITE); + } + if ((ownerValue & CHMOD_READ_MASK) > 0) { + permissions.add(PosixFilePermission.OWNER_READ); + } + if ((ownerValue & CHMOD_EXECUTE_MASK) > 0) { + permissions.add(PosixFilePermission.OWNER_EXECUTE); + } + + if ((groupValue & CHMOD_WRITE_MASK) > 0) { + permissions.add(PosixFilePermission.GROUP_WRITE); + } + if ((groupValue & CHMOD_READ_MASK) > 0) { + permissions.add(PosixFilePermission.GROUP_READ); + } + if ((groupValue & CHMOD_EXECUTE_MASK) > 0) { + permissions.add(PosixFilePermission.GROUP_EXECUTE); + } + + if ((othersValue & CHMOD_WRITE_MASK) > 0) { + permissions.add(PosixFilePermission.OTHERS_WRITE); + } + if ((othersValue & CHMOD_READ_MASK) > 0) { + permissions.add(PosixFilePermission.OTHERS_READ); + } + if ((othersValue & CHMOD_EXECUTE_MASK) > 0) { + permissions.add(PosixFilePermission.OTHERS_EXECUTE); + } + + return permissions; + } + + public String getChmodDirectory() { + return chmodDirectory; + } + + /** + * Specify the directory permissions used when the producer creates missing directories, the chmod value must be between 000 and 777; + * If there is a leading digit like in 0755 we will ignore it. + */ + public void setChmodDirectory(String chmodDirectory) throws Exception { + if (ObjectHelper.isNotEmpty(chmodDirectory) && chmodPermissionsAreValid(chmodDirectory)) { + this.chmodDirectory = chmodDirectory.trim(); + } else { + throw new IllegalArgumentException("chmodDirectory option [" + chmodDirectory + "] is not valid"); + } + } + public boolean isNoop() { return noop; } http://git-wip-us.apache.org/repos/asf/camel/blob/9784d030/camel-core/src/test/java/org/apache/camel/component/file/FileProducerDirectoryChmodOptionTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/component/file/FileProducerDirectoryChmodOptionTest.java b/camel-core/src/test/java/org/apache/camel/component/file/FileProducerDirectoryChmodOptionTest.java new file mode 100644 index 0000000..932b767 --- /dev/null +++ b/camel-core/src/test/java/org/apache/camel/component/file/FileProducerDirectoryChmodOptionTest.java @@ -0,0 +1,117 @@ +/** + * 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 java.io.File; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.Date; +import java.util.Set; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; + +public class FileProducerDirectoryChmodOptionTest extends ContextTestSupport { + public static final String TEST_DIRECTORY = "target/chmoddir/foo/"; + + @Override + protected void setUp() throws Exception { + deleteDirectory(TEST_DIRECTORY); + super.setUp(); + } + + private boolean canTest() { + // can not run on windows + return !isPlatform("windows"); + } + + public void testWriteValidNoDir() throws Exception { + if (!canTest()) { + return; + } + + runChmodCheck("NoDir", null, "rwxr-xr-x"); + } + + public void testWriteValidChmod0755() throws Exception { + if (!canTest()) { + return; + } + + runChmodCheck("0755", "rwxrwxrwx", "rwxr-xr-x"); + } + + public void testWriteValidChmod666() throws Exception { + if (!canTest()) { + return; + } + + runChmodCheck("666", "rwxrwxrwx", "rw-rw-rw-"); + } + + private void runChmodCheck(String routeSuffix, String expectedDirectoryPermissions, String expectedPermissions) throws Exception { + MockEndpoint mock = getMockEndpoint("mock:chmod" + routeSuffix); + mock.expectedMessageCount(1); + String testFileName = "chmod" + routeSuffix + ".txt"; + String fullTestFileName = TEST_DIRECTORY + testFileName; + String testFileContent = "Writing file with chmod " + routeSuffix + " option at " + new Date(); + mock.expectedFileExists(fullTestFileName, testFileContent); + + template.sendBodyAndHeader("direct:write" + routeSuffix, testFileContent, Exchange.FILE_NAME, testFileName); + + if (expectedDirectoryPermissions != null) { + File d = new File(TEST_DIRECTORY); + Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(d.toPath(), LinkOption.NOFOLLOW_LINKS); + assertEquals(expectedDirectoryPermissions, PosixFilePermissions.toString(permissions)); + assertEquals(expectedDirectoryPermissions.replace("-", "").length(), permissions.size()); + } + + if (expectedPermissions != null) { + File f = new File(fullTestFileName); + Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(f.toPath(), LinkOption.NOFOLLOW_LINKS); + assertEquals(expectedPermissions, PosixFilePermissions.toString(permissions)); + assertEquals(expectedPermissions.replace("-", "").length(), permissions.size()); + } + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + // Valid chmod values + from("direct:write666") + .to("file://" + TEST_DIRECTORY + "?chmodDirectory=777&chmod=666") + .to("mock:chmod666"); + + from("direct:write0755") + .to("file://" + TEST_DIRECTORY + "?chmodDirectory=777&chmod=0755") + .to("mock:chmod0755"); + + from("direct:writeNoDir") + .to("file://" + TEST_DIRECTORY + "?chmod=0755") + .to("mock:chmodNoDir"); + + } + }; + } +}