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

exceptionfactory 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 5e2a10c328 NIFI-11971: Ensure that if we do not write any bytes to a 
file after calling ProcessSession.write() that we set content claim's length to 
0 when closing OutputStream; otherwise it remains -1, which causes issues, 
since the length will later be added to the offset to determine the position in 
the ste stream
5e2a10c328 is described below

commit 5e2a10c32834973ba2837c3bdf5963aab2aa92bc
Author: Mark Payne <[email protected]>
AuthorDate: Sun Aug 20 16:26:51 2023 -0400

    NIFI-11971: Ensure that if we do not write any bytes to a file after 
calling ProcessSession.write() that we set content claim's length to 0 when 
closing OutputStream; otherwise it remains -1, which causes issues, since the 
length will later be added to the offset to determine the position in the ste 
stream
    
    This closes #7629
    
    Signed-off-by: David Handermann <[email protected]>
---
 .../claim/StandardContentClaimWriteCache.java      |   5 ++
 .../nifi/processors/tests/system/IngestFile.java   |  22 ++++-
 .../processors/tests/system/UnzipFlowFile.java     |  94 +++++++++++++++++++++
 .../services/org.apache.nifi.processor.Processor   |   3 +-
 .../tests/system/repositories/ContentAccessIT.java |  37 ++++++++
 .../test/resources/empty-and-small-text-files.zip  | Bin 0 -> 316 bytes
 6 files changed, 157 insertions(+), 4 deletions(-)

diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/claim/StandardContentClaimWriteCache.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/claim/StandardContentClaimWriteCache.java
index 1a728071c2..8d78953297 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/claim/StandardContentClaimWriteCache.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/claim/StandardContentClaimWriteCache.java
@@ -149,6 +149,11 @@ public class StandardContentClaimWriteCache implements 
ContentClaimWriteCache {
                 }
                 closed = true;
 
+                if (scc.getLength() < 0) {
+                    // If claim was not written to, set length to 0
+                    scc.setLength(0L);
+                }
+
                 queue.offer(claim);
             }
         };
diff --git 
a/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/processors/tests/system/IngestFile.java
 
b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/processors/tests/system/IngestFile.java
index 4637987216..844aed1788 100644
--- 
a/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/processors/tests/system/IngestFile.java
+++ 
b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/processors/tests/system/IngestFile.java
@@ -56,6 +56,13 @@ public class IngestFile extends AbstractProcessor {
         .allowableValues(COMMIT_ASYNC, COMMIT_SYNCHRONOUS)
         .defaultValue(COMMIT_ASYNC)
         .build();
+    static final PropertyDescriptor DELETE_FILE = new 
PropertyDescriptor.Builder()
+        .name("Delete File")
+        .description("Whether or not the file should be deleted after 
successfully ingesting")
+        .allowableValues("true", "false")
+        .defaultValue("true")
+        .required(true)
+        .build();
 
     static final Relationship REL_SUCCESS = new Relationship.Builder()
         .name("success")
@@ -63,7 +70,7 @@ public class IngestFile extends AbstractProcessor {
 
     @Override
     protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
-        return Arrays.asList(FILENAME, COMMIT_MODE);
+        return Arrays.asList(FILENAME, COMMIT_MODE, DELETE_FILE);
     }
 
     @Override
@@ -81,12 +88,21 @@ public class IngestFile extends AbstractProcessor {
         session.transfer(flowFile, REL_SUCCESS);
         session.getProvenanceReporter().receive(flowFile, 
file.toURI().toString());
 
+        final boolean deleteFile = 
context.getProperty(DELETE_FILE).asBoolean();
+
         final String commitMode = context.getProperty(COMMIT_MODE).getValue();
         if (COMMIT_SYNCHRONOUS.equalsIgnoreCase(commitMode)) {
             session.commit();
-            cleanup(file);
+
+            if (deleteFile) {
+                cleanup(file);
+            }
         } else {
-            session.commitAsync(() -> cleanup(file));
+            session.commitAsync(() -> {
+                if (deleteFile) {
+                    cleanup(file);
+                }
+            });
         }
     }
 
diff --git 
a/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/processors/tests/system/UnzipFlowFile.java
 
b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/processors/tests/system/UnzipFlowFile.java
new file mode 100644
index 0000000000..b522c9b97a
--- /dev/null
+++ 
b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/processors/tests/system/UnzipFlowFile.java
@@ -0,0 +1,94 @@
+/*
+ * 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.tests.system;
+
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.stream.io.StreamUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class UnzipFlowFile extends AbstractProcessor {
+
+    private final Relationship REL_UNZIPPED = new Relationship.Builder()
+        .name("unzipped")
+        .build();
+
+    private final Relationship REL_ORIGINAL = new Relationship.Builder()
+        .name("original")
+        .autoTerminateDefault(true)
+        .build();
+
+    private final Relationship REL_FAILURE = new Relationship.Builder()
+        .name("failure")
+        .build();
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return new HashSet<>(Arrays.asList(REL_UNZIPPED, REL_ORIGINAL, 
REL_FAILURE));
+    }
+
+    @Override
+    public void onTrigger(final ProcessContext context, final ProcessSession 
session) throws ProcessException {
+        FlowFile flowFile = session.get();
+        if (flowFile == null) {
+            return;
+        }
+
+        final List<FlowFile> created = new ArrayList<>();
+        try (final InputStream in = session.read(flowFile);
+             final ZipInputStream zipInputStream = new ZipInputStream(in)) {
+
+            while (true) {
+                final ZipEntry zipEntry = zipInputStream.getNextEntry();
+                if (zipEntry == null) {
+                    break;
+                }
+
+                final String filename = zipEntry.getName();
+                FlowFile outFile = session.create(flowFile);
+                outFile = session.putAttribute(outFile, "filename", filename);
+                created.add(outFile);
+
+                session.write(outFile, out -> {
+                    StreamUtils.copy(zipInputStream, out);
+                });
+            }
+
+        } catch (final IOException e) {
+            getLogger().error("Failed to unzip {}", flowFile, e);
+            session.transfer(flowFile, REL_FAILURE);
+            session.remove(created);
+            return;
+        }
+
+        session.transfer(created, REL_UNZIPPED);
+        session.transfer(flowFile, REL_ORIGINAL);
+    }
+}
diff --git 
a/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
 
b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
index 1f278c32ac..781dcceb8e 100644
--- 
a/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
+++ 
b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor
@@ -40,13 +40,14 @@ 
org.apache.nifi.processors.tests.system.SensitiveDynamicPropertiesProcessor
 org.apache.nifi.processors.tests.system.SetAttribute
 org.apache.nifi.processors.tests.system.Sleep
 org.apache.nifi.processors.tests.system.SplitByLine
+org.apache.nifi.processors.tests.system.SplitTextByLine
 org.apache.nifi.processors.tests.system.TerminateFlowFile
 org.apache.nifi.processors.tests.system.TransferBatch
 org.apache.nifi.processors.tests.system.ThrowProcessException
 org.apache.nifi.processors.tests.system.UpdateContent
+org.apache.nifi.processors.tests.system.UnzipFlowFile
 org.apache.nifi.processors.tests.system.ValidateFileExists
 org.apache.nifi.processors.tests.system.VerifyContents
 org.apache.nifi.processors.tests.system.WriteFlowFileCountToFile
 org.apache.nifi.processors.tests.system.WriteLifecycleEvents
 org.apache.nifi.processors.tests.system.WriteToFile
-org.apache.nifi.processors.tests.system.SplitTextByLine
diff --git 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/repositories/ContentAccessIT.java
 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/repositories/ContentAccessIT.java
index b6ee614eb8..1ae0788243 100644
--- 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/repositories/ContentAccessIT.java
+++ 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/repositories/ContentAccessIT.java
@@ -20,9 +20,11 @@ package org.apache.nifi.tests.system.repositories;
 import org.apache.nifi.tests.system.NiFiSystemIT;
 import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
 import org.apache.nifi.web.api.entity.ConnectionEntity;
+import org.apache.nifi.web.api.entity.FlowFileEntity;
 import org.apache.nifi.web.api.entity.ProcessorEntity;
 import org.junit.jupiter.api.Test;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
@@ -30,6 +32,7 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
@@ -40,6 +43,40 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
  */
 public class ContentAccessIT extends NiFiSystemIT {
 
+    @Test
+    public void testWriteZeroBytesToFlowFileThenWriteBytes() throws 
NiFiClientException, IOException, InterruptedException {
+        final ProcessorEntity ingestFile = 
getClientUtil().createProcessor("IngestFile");
+        final ProcessorEntity unzipFile = 
getClientUtil().createProcessor("UnzipFlowFile");
+        final ProcessorEntity terminate = 
getClientUtil().createProcessor("TerminateFlowFile");
+
+        getClientUtil().createConnection(ingestFile, unzipFile, "success");
+        final ConnectionEntity unzipped = 
getClientUtil().createConnection(unzipFile, terminate, "unzipped");
+
+        getClientUtil().setAutoTerminatedRelationships(unzipFile, "failure");
+
+        final Map<String, String> ingestProperties = new HashMap<>();
+        ingestProperties.put("Filename", new 
File("src/test/resources/empty-and-small-text-files.zip").getAbsolutePath());
+        ingestProperties.put("Delete File", "false");
+        getClientUtil().updateProcessorProperties(ingestFile, 
ingestProperties);
+        getClientUtil().updateProcessorSchedulingPeriod(ingestFile, "10 mins");
+        getClientUtil().waitForValidProcessor(ingestFile.getId());
+
+        getClientUtil().startProcessor(ingestFile);
+        getClientUtil().startProcessor(unzipFile);
+
+        waitForQueueCount(unzipped.getId(), 2);
+
+        final FlowFileEntity flowFileEntity1 = 
getClientUtil().getQueueFlowFile(unzipped.getId(), 0);
+        assertEquals("a.txt", 
flowFileEntity1.getFlowFile().getAttributes().get("filename"));
+        assertEquals(0, flowFileEntity1.getFlowFile().getSize());
+
+        final FlowFileEntity flowFileEntity2 = 
getClientUtil().getQueueFlowFile(unzipped.getId(), 1);
+        assertEquals("b.txt", 
flowFileEntity2.getFlowFile().getAttributes().get("filename"));
+
+        final String content2 = 
getClientUtil().getFlowFileContentAsUtf8(unzipped.getId(), 1);
+        assertEquals("Hello there! b.txt", content2);
+    }
+
     @Test
     public void 
testCorrectContentReadWhenMultipleFlowFilesInClaimWithBatchAndWrite() throws 
NiFiClientException, IOException, InterruptedException {
         testCorrectContentReadWhenMultipleFlowFilesInClaim(true, false);
diff --git 
a/nifi-system-tests/nifi-system-test-suite/src/test/resources/empty-and-small-text-files.zip
 
b/nifi-system-tests/nifi-system-test-suite/src/test/resources/empty-and-small-text-files.zip
new file mode 100644
index 0000000000..cce7330ccf
Binary files /dev/null and 
b/nifi-system-tests/nifi-system-test-suite/src/test/resources/empty-and-small-text-files.zip
 differ

Reply via email to