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

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


The following commit(s) were added to refs/heads/main by this push:
     new b1e7701  NIFI-6128 UnpackContent: Store unpacked file data
b1e7701 is described below

commit b1e77019accec8cb32c41d34838f2225299f72b4
Author: Tamás Bunth <[email protected]>
AuthorDate: Tue Jun 30 15:47:20 2020 +0200

    NIFI-6128 UnpackContent: Store unpacked file data
    
    Tar format allows us to archive files with their original permission,
    owner, group name and last modification time.
    
    When unpacking with Tar unpacker, these information are stored in new
    attributes with names: "file.inner.*". This way, it preserves backward
    compatibility when using parallel with GetFile processor (which stores
    information in "file.*").
    
    NIFI-6128 Tar unpackContent: assert date of last modification of content is 
a valid date format.
    
    NIFI-6128 UnpackContent: use original attributes
    
    In case of tar format:
    - Use "file.*" attributes instead of "file.inner.*" (which eventually
      lead to overwrite if tar had been fetched with GetFile)
    - Store file permission in "rwx" format, instead of integer
      representation.
    - Also replace SimpleDateFormat with DateTimeFormatter.
    
    Replace generic error with IllegalArgumentException
    Also refactor: move permission string to top of file as static constant.
    
    Update test
    
    Remove 'file systems' text
    
    Unpackcontent: Fill file.creationTime attribute
    which always holds the very same value as file.lastModifiedTime.
    
    This closes #4370.
    
    Signed-off-by: Peter Turcsanyi <[email protected]>
---
 .../nifi/processors/standard/UnpackContent.java    | 28 +++++++++++++-
 .../nifi/processors/standard/util/FileInfo.java    | 15 ++++++++
 .../processors/standard/TestUnpackContent.java     | 19 +++++++++
 .../processors/standard/util/TestFileInfo.java     | 45 ++++++++++++++++++++++
 4 files changed, 106 insertions(+), 1 deletion(-)

diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/UnpackContent.java
 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/UnpackContent.java
index 32e9ff8..f802a5d 100644
--- 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/UnpackContent.java
+++ 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/UnpackContent.java
@@ -23,6 +23,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.file.Path;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -64,6 +66,7 @@ import org.apache.nifi.processor.exception.ProcessException;
 import org.apache.nifi.processor.io.InputStreamCallback;
 import org.apache.nifi.processor.io.OutputStreamCallback;
 import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.processors.standard.util.FileInfo;
 import org.apache.nifi.stream.io.StreamUtils;
 import org.apache.nifi.util.FlowFileUnpackager;
 import org.apache.nifi.util.FlowFileUnpackagerV1;
@@ -92,7 +95,12 @@ import org.apache.nifi.util.FlowFileUnpackagerV3;
             + "parent FlowFile"),
     @WritesAttribute(attribute = "fragment.count", description = "The number 
of unpacked FlowFiles generated from the parent FlowFile"),
     @WritesAttribute(attribute = "segment.original.filename ", description = 
"The filename of the parent FlowFile. Extensions of .tar, .zip or .pkg are 
removed because "
-            + "the MergeContent processor automatically adds those extensions 
if it is used to rebuild the original FlowFile")})
+            + "the MergeContent processor automatically adds those extensions 
if it is used to rebuild the original FlowFile"),
+    @WritesAttribute(attribute = "file.lastModifiedTime", description = "The 
date and time that the unpacked file was last modified (tar only)."),
+    @WritesAttribute(attribute = "file.creationTime", description = "The date 
and time that the file was created. This attribute holds always the same value 
as file.lastModifiedTime (tar only)."),
+    @WritesAttribute(attribute = "file.owner", description = "The owner of the 
unpacked file (tar only)"),
+    @WritesAttribute(attribute = "file.group", description = "The group owner 
of the unpacked file (tar only)"),
+    @WritesAttribute(attribute = "file.permissions", description = "The 
read/write/execute permissions of the unpacked file (tar only)")})
 @SeeAlso(MergeContent.class)
 public class UnpackContent extends AbstractProcessor {
     // attribute keys
@@ -110,6 +118,15 @@ public class UnpackContent extends AbstractProcessor {
 
     public static final String OCTET_STREAM = "application/octet-stream";
 
+    public static final String FILE_LAST_MODIFIED_TIME_ATTRIBUTE = 
"file.lastModifiedTime";
+    public static final String FILE_CREATION_TIME_ATTRIBUTE = 
"file.creationTime";
+    public static final String FILE_OWNER_ATTRIBUTE = "file.owner";
+    public static final String FILE_GROUP_ATTRIBUTE = "file.group";
+    public static final String FILE_PERMISSIONS_ATTRIBUTE = "file.permissions";
+
+    public static final String FILE_MODIFIED_DATE_ATTR_FORMAT = 
"yyyy-MM-dd'T'HH:mm:ssZ";
+    public static final DateTimeFormatter DATE_TIME_FORMATTER = 
DateTimeFormatter.ofPattern(FILE_MODIFIED_DATE_ATTR_FORMAT).withZone(ZoneId.systemDefault());
+
     public static final PropertyDescriptor PACKAGING_FORMAT = new 
PropertyDescriptor.Builder()
             .name("Packaging Format")
             .description("The Packaging Format used to create the file")
@@ -298,6 +315,7 @@ public class UnpackContent extends AbstractProcessor {
         public void unpack(final ProcessSession session, final FlowFile 
source, final List<FlowFile> unpacked) {
             final String fragmentId = UUID.randomUUID().toString();
             session.read(source, new InputStreamCallback() {
+
                 @Override
                 public void process(final InputStream in) throws IOException {
                     int fragmentCount = 0;
@@ -321,6 +339,14 @@ public class UnpackContent extends AbstractProcessor {
                                 
attributes.put(CoreAttributes.ABSOLUTE_PATH.key(), absPathString);
                                 attributes.put(CoreAttributes.MIME_TYPE.key(), 
OCTET_STREAM);
 
+                                attributes.put(FILE_PERMISSIONS_ATTRIBUTE, 
FileInfo.permissionToString(tarEntry.getMode()));
+                                attributes.put(FILE_OWNER_ATTRIBUTE, 
String.valueOf(tarEntry.getUserName()));
+                                attributes.put(FILE_GROUP_ATTRIBUTE, 
String.valueOf(tarEntry.getGroupName()));
+
+                                final String timeAsString = 
DATE_TIME_FORMATTER.format(tarEntry.getModTime().toInstant());
+                                
attributes.put(FILE_LAST_MODIFIED_TIME_ATTRIBUTE, timeAsString);
+                                attributes.put(FILE_CREATION_TIME_ATTRIBUTE, 
timeAsString);
+
                                 attributes.put(FRAGMENT_ID, fragmentId);
                                 attributes.put(FRAGMENT_INDEX, 
String.valueOf(++fragmentCount));
 
diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/FileInfo.java
 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/FileInfo.java
index 9b33de1..763ad07 100644
--- 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/FileInfo.java
+++ 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/FileInfo.java
@@ -44,6 +44,8 @@ public class FileInfo implements Comparable<FileInfo>, 
Serializable, ListableEnt
     private static final String OWNER = "owner";
     private static final String GROUP = "group";
 
+    private static final char[] PERMISSION_MODIFIER_CHARS = 
"xwrxwrxwr".toCharArray();
+
     static {
         final List<RecordField> recordFields = new ArrayList<>();
         recordFields.add(new RecordField(FILENAME, 
RecordFieldType.STRING.getDataType(), false));
@@ -217,6 +219,19 @@ public class FileInfo implements Comparable<FileInfo>, 
Serializable, ListableEnt
         }
     }
 
+    public static String permissionToString(int fileModeOctal) {
+        if (fileModeOctal > 0777 || fileModeOctal < 00) {
+            throw new IllegalArgumentException("Invalid permission numerals");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        for (char p : PERMISSION_MODIFIER_CHARS) {
+            sb.append((fileModeOctal & 1) == 1 ? p : '-');
+            fileModeOctal >>= 1;
+        }
+        return sb.reverse().toString();
+    }
+
     @Override
     public String getName() {
         return getFileName();
diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestUnpackContent.java
 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestUnpackContent.java
index 980d293..42cc244 100644
--- 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestUnpackContent.java
+++ 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestUnpackContent.java
@@ -20,11 +20,14 @@ import static 
org.apache.nifi.processors.standard.SplitContent.FRAGMENT_COUNT;
 import static org.apache.nifi.processors.standard.SplitContent.FRAGMENT_ID;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -70,10 +73,26 @@ public class TestUnpackContent {
         autoUnpackRunner.assertTransferCount(UnpackContent.REL_FAILURE, 0);
 
         final List<MockFlowFile> unpacked = 
unpackRunner.getFlowFilesForRelationship(UnpackContent.REL_SUCCESS);
+
         for (final MockFlowFile flowFile : unpacked) {
             final String filename = 
flowFile.getAttribute(CoreAttributes.FILENAME.key());
             final String folder = 
flowFile.getAttribute(CoreAttributes.PATH.key());
             final Path path = dataPath.resolve(folder).resolve(filename);
+            assertEquals("rw-r--r--", 
flowFile.getAttribute("file.permissions"));
+            assertEquals("jmcarey", flowFile.getAttribute("file.owner"));
+            assertEquals("mkpasswd", flowFile.getAttribute("file.group"));
+            String modifiedTimeAsString = 
flowFile.getAttribute("file.lastModifiedTime");
+            try {
+                
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ").parse(modifiedTimeAsString);
+            } catch (DateTimeParseException e) {
+                fail();
+            }
+            String creationTimeAsString = 
flowFile.getAttribute("file.creationTime");
+            try {
+                
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ").parse(creationTimeAsString);
+            } catch (DateTimeParseException e) {
+                fail();
+            }
             assertTrue(Files.exists(path));
 
             flowFile.assertContentEquals(path.toFile());
diff --git 
a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/util/TestFileInfo.java
 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/util/TestFileInfo.java
new file mode 100644
index 0000000..1ff9bd3
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/util/TestFileInfo.java
@@ -0,0 +1,45 @@
+/*
+ * 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.nifi.processors.standard.util;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class TestFileInfo {
+
+    @Test
+    public void testPermissionModeToString() {
+        String rwxPerm = FileInfo.permissionToString(0567);
+        assertEquals("r-xrw-rwx", rwxPerm);
+
+        rwxPerm = FileInfo.permissionToString(03);
+        assertEquals("-------wx", rwxPerm);
+
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPermissionModeToStringInvalidFourDigits() {
+        FileInfo.permissionToString(01000);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPermissionModeToStringInvalidNegative() {
+        FileInfo.permissionToString(-1);
+    }
+
+}

Reply via email to