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

mosermw 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 4d0696afb7 NIFI-14343 Replaced IdentifyMimeType property descriptors 
Config File and Config Body with one property descriptor using 
identifiesExternalResource.
4d0696afb7 is described below

commit 4d0696afb76d66b873bb33a54d4798530d978781
Author: dan-s1 <[email protected]>
AuthorDate: Mon Mar 10 21:28:33 2025 +0000

    NIFI-14343 Replaced IdentifyMimeType property descriptors Config File and 
Config Body with one property descriptor using identifiesExternalResource.
    
    Signed-off-by: Mike Moser <[email protected]>
    
    Closes #9793
---
 .../nifi/processors/standard/IdentifyMimeType.java | 118 ++++++++++-----------
 .../processors/standard/TestIdentifyMimeType.java  |  34 +++---
 2 files changed, 75 insertions(+), 77 deletions(-)

diff --git 
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/IdentifyMimeType.java
 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/IdentifyMimeType.java
index b6efd68a07..da465ee144 100644
--- 
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/IdentifyMimeType.java
+++ 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/IdentifyMimeType.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.processors.standard;
 
+import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.nifi.annotation.behavior.InputRequirement;
 import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
 import org.apache.nifi.annotation.behavior.SideEffectFree;
@@ -29,8 +30,9 @@ import org.apache.nifi.components.AllowableValue;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.ValidationContext;
 import org.apache.nifi.components.ValidationResult;
-import org.apache.nifi.components.Validator;
-import org.apache.nifi.expression.ExpressionLanguageScope;
+import org.apache.nifi.components.resource.ResourceCardinality;
+import org.apache.nifi.components.resource.ResourceType;
+import org.apache.nifi.context.PropertyContext;
 import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.flowfile.attributes.CoreAttributes;
 import org.apache.nifi.logging.ComponentLog;
@@ -54,8 +56,6 @@ import org.apache.tika.mime.MimeTypeException;
 import org.apache.tika.mime.MimeTypes;
 import org.apache.tika.mime.MimeTypesFactory;
 
-import java.io.ByteArrayInputStream;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.Charset;
@@ -90,8 +90,8 @@ import java.util.Set;
 )
 public class IdentifyMimeType extends AbstractProcessor {
     static final AllowableValue PRESET = new AllowableValue("Preset", 
"Preset", "Use default NiFi MIME Types.");
-    static final AllowableValue REPLACE = new AllowableValue("Replace", 
"Replace", "Use config MIME Types only.");
-    static final AllowableValue MERGE = new AllowableValue("Merge", "Merge", 
"Use config together with default NiFi MIME Types.");
+    static final AllowableValue REPLACE = new AllowableValue("Replace", 
"Replace", "Use custom MIME types configuration only.");
+    static final AllowableValue MERGE = new AllowableValue("Merge", "Merge", 
"Use custom MIME types configuration together with default NiFi MIME types.");
 
     public static final PropertyDescriptor USE_FILENAME_IN_DETECTION = new 
PropertyDescriptor.Builder()
             .displayName("Use Filename In Detection")
@@ -111,31 +111,19 @@ public class IdentifyMimeType extends AbstractProcessor {
             .defaultValue(PRESET.getValue())
             .build();
 
-    public static final PropertyDescriptor MIME_CONFIG_FILE = new 
PropertyDescriptor.Builder()
-            .displayName("Config File")
-            .name("config-file")
-            .required(false)
-            .description("Path to MIME type config file. Only one of Config 
File or Config Body may be used.")
-            .addValidator(new StandardValidators.FileExistsValidator(true))
-            .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
-            .dependsOn(CONFIG_STRATEGY, REPLACE, MERGE)
-            .build();
-
-    public static final PropertyDescriptor MIME_CONFIG_BODY = new 
PropertyDescriptor.Builder()
-            .displayName("Config Body")
-            .name("config-body")
-            .required(false)
-            .description("Body of MIME type config file. Only one of Config 
File or Config Body may be used.")
-            .addValidator(Validator.VALID)
-            .expressionLanguageSupported(ExpressionLanguageScope.NONE)
+    public static final PropertyDescriptor CUSTOM_MIME_CONFIGURATION = new 
PropertyDescriptor.Builder()
+            .name("Custom MIME Configuration")
+            .description("A URL or file path to a custom Tika Mime type 
configuration or the actual content of a custom Tika Mime type configuration.")
+            .required(true)
+            .identifiesExternalResource(ResourceCardinality.SINGLE, 
ResourceType.FILE, ResourceType.URL, ResourceType.TEXT)
+            .addValidator(StandardValidators.NON_BLANK_VALIDATOR)
             .dependsOn(CONFIG_STRATEGY, REPLACE, MERGE)
             .build();
 
     private static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = 
List.of(
         USE_FILENAME_IN_DETECTION,
         CONFIG_STRATEGY,
-        MIME_CONFIG_BODY,
-        MIME_CONFIG_FILE
+        CUSTOM_MIME_CONFIGURATION
     );
 
     public static final Relationship REL_SUCCESS = new Relationship.Builder()
@@ -147,6 +135,8 @@ public class IdentifyMimeType extends AbstractProcessor {
         REL_SUCCESS
     );
 
+    private static final String CUSTOM_MIME_TYPES_FILENAME = 
"custom-mimetypes.xml";
+    private static final String DEFAULT_MIME_TYPES_PATH = 
"org/apache/tika/mime/tika-mimetypes.xml";
     private final TikaConfig config;
     private Detector detector;
     private EncodingDetector encodingDetector;
@@ -158,11 +148,23 @@ public class IdentifyMimeType extends AbstractProcessor {
 
     @Override
     public void migrateProperties(PropertyConfiguration config) {
+        final String configFileProperty = "config-file";
+        final String configBodyProperty = "config-body";
+
         if (!config.hasProperty(CONFIG_STRATEGY)) {
-            if (config.isPropertySet(MIME_CONFIG_FILE) || 
config.isPropertySet(MIME_CONFIG_BODY)) {
+            if (config.isPropertySet(configFileProperty) || 
config.isPropertySet(configBodyProperty)) {
                 config.setProperty(CONFIG_STRATEGY, REPLACE.getValue());
             }
         }
+
+        if (config.isPropertySet(configFileProperty)) {
+            config.setProperty(CUSTOM_MIME_CONFIGURATION.getName(), 
config.getRawPropertyValue(configFileProperty).orElse(""));
+        } else if (config.isPropertySet(configBodyProperty)) {
+            config.setProperty(CUSTOM_MIME_CONFIGURATION.getName(), 
config.getRawPropertyValue(configBodyProperty).orElse(""));
+        }
+
+        config.removeProperty(configFileProperty);
+        config.removeProperty(configBodyProperty);
     }
 
     @OnScheduled
@@ -173,33 +175,18 @@ public class IdentifyMimeType extends AbstractProcessor {
             this.detector = config.getDetector();
             this.mimeTypes = config.getMimeRepository();
         } else {
-            setCustomMimeTypes(configStrategy, context);
+            try {
+                this.detector = createCustomMimeTypes(configStrategy, context);
+                this.mimeTypes = (MimeTypes) this.detector;
+            } catch (Exception e) {
+                context.yield();
+                throw new ProcessException("Failed to load configuration", e);
+            }
         }
 
         this.encodingDetector = config.getEncodingDetector();
     }
 
-    private void setCustomMimeTypes(String configStrategy, ProcessContext 
context) {
-        String configBody = context.getProperty(MIME_CONFIG_BODY).getValue();
-        String configFile = 
context.getProperty(MIME_CONFIG_FILE).evaluateAttributeExpressions().getValue();
-
-        try (final InputStream customInputStream = configBody != null ? new 
ByteArrayInputStream(configBody.getBytes()) : new FileInputStream(configFile)) {
-            if (configStrategy.equals(REPLACE.getValue())) {
-                this.detector = MimeTypesFactory.create(customInputStream);
-            } else {
-                try (final InputStream nifiInputStream = 
getClass().getClassLoader().getResourceAsStream("custom-mimetypes.xml");
-                     final InputStream tikaInputStream = 
MimeTypes.class.getClassLoader().getResourceAsStream("org/apache/tika/mime/tika-mimetypes.xml"))
 {
-                    this.detector = MimeTypesFactory.create(customInputStream, 
nifiInputStream, tikaInputStream);
-                }
-            }
-            this.mimeTypes = (MimeTypes) this.detector;
-        } catch (Exception e) {
-            context.yield();
-            String configSource = configBody != null ? "body" : "file";
-            throw new ProcessException("Failed to load config " + 
configSource, e);
-        }
-    }
-
     @Override
     public Set<Relationship> getRelationships() {
         return RELATIONSHIPS;
@@ -277,29 +264,36 @@ public class IdentifyMimeType extends AbstractProcessor {
             return null;
         }
     }
-
     @Override
     protected Collection<ValidationResult> customValidate(ValidationContext 
validationContext) {
         Set<ValidationResult> results = new HashSet<>();
-        String body = 
validationContext.getProperty(MIME_CONFIG_BODY).getValue();
-        String file = 
validationContext.getProperty(MIME_CONFIG_FILE).getValue();
-        if 
(!validationContext.getProperty(CONFIG_STRATEGY).getValue().equals(PRESET.getValue()))
 {
-            if (body != null && file != null) {
-                results.add(new ValidationResult.Builder()
-                        .subject(MIME_CONFIG_FILE.getDisplayName())
-                        .input(file)
-                        .valid(false)
-                        .explanation("Either [Config Body] or [Config File] 
can be specified, but not both properties.")
-                        .build());
-            } else if (body == null && file == null) {
+        String configStrategy = 
validationContext.getProperty(CONFIG_STRATEGY).getValue();
+
+        if (!configStrategy.equals(PRESET.getValue())) {
+            try {
+                createCustomMimeTypes(configStrategy, validationContext);
+            } catch (Exception e) {
                 results.add(new ValidationResult.Builder()
-                        .subject(MIME_CONFIG_FILE.getDisplayName())
+                        .subject(CUSTOM_MIME_CONFIGURATION.getDisplayName())
+                        
.input(validationContext.getProperty(CUSTOM_MIME_CONFIGURATION).getValue())
                         .valid(false)
-                        .explanation("Either [Config Body] or [Config File] 
must be specified")
+                        .explanation("Invalid configuration " + 
ExceptionUtils.getRootCauseMessage(e))
                         .build());
             }
         }
         return results;
     }
 
+    private MimeTypes createCustomMimeTypes(String configStrategy, 
PropertyContext context) throws MimeTypeException, IOException {
+        try (final InputStream customInputStream = 
context.getProperty(CUSTOM_MIME_CONFIGURATION).asResource().read()) {
+            if (configStrategy.equals(REPLACE.getValue())) {
+                return MimeTypesFactory.create(customInputStream);
+            } else {
+                try (final InputStream nifiInputStream = 
getClass().getClassLoader().getResourceAsStream(CUSTOM_MIME_TYPES_FILENAME);
+                     final InputStream tikaInputStream = 
MimeTypes.class.getClassLoader().getResourceAsStream(DEFAULT_MIME_TYPES_PATH)) {
+                    return MimeTypesFactory.create(customInputStream, 
nifiInputStream, tikaInputStream);
+                }
+            }
+        }
+    }
 }
diff --git 
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestIdentifyMimeType.java
 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestIdentifyMimeType.java
index 69c38b60e1..ffcc5a996a 100644
--- 
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestIdentifyMimeType.java
+++ 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestIdentifyMimeType.java
@@ -19,6 +19,7 @@ package org.apache.nifi.processors.standard;
 import org.apache.nifi.flowfile.attributes.CoreAttributes;
 import org.apache.nifi.flowfile.attributes.StandardFlowFileMediaType;
 import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.PropertyMigrationResult;
 import org.apache.nifi.util.TestRunner;
 import org.apache.nifi.util.TestRunners;
 import org.junit.jupiter.api.BeforeEach;
@@ -33,11 +34,11 @@ import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Stream;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 class TestIdentifyMimeType {
 
@@ -138,7 +139,7 @@ class TestIdentifyMimeType {
     @MethodSource("replaceWithConfigBodyData")
     void testReplaceWithConfigBody(File file, Map<String, String> 
expectedMimeTypes, Map<String, String> expectedExtensions) throws IOException {
         runner.setProperty(IdentifyMimeType.CONFIG_STRATEGY, 
IdentifyMimeType.REPLACE);
-        runner.setProperty(IdentifyMimeType.MIME_CONFIG_BODY, CONFIG_BODY);
+        runner.setProperty(IdentifyMimeType.CUSTOM_MIME_CONFIGURATION, 
CONFIG_BODY);
         runner.enqueue(file.toPath());
 
         runner.run();
@@ -213,7 +214,7 @@ class TestIdentifyMimeType {
     @MethodSource("replaceWithConfigFileData")
     void testReplaceWithConfigFile(File file, Map<String, String> 
expectedMimeTypes, Map<String, String> expectedExtensions) throws IOException {
         runner.setProperty(IdentifyMimeType.CONFIG_STRATEGY, 
IdentifyMimeType.REPLACE);
-        runner.setProperty(IdentifyMimeType.MIME_CONFIG_FILE, CONFIG_FILE);
+        runner.setProperty(IdentifyMimeType.CUSTOM_MIME_CONFIGURATION, 
CONFIG_FILE);
         runner.enqueue(file.toPath());
 
         runner.run();
@@ -287,7 +288,7 @@ class TestIdentifyMimeType {
     @MethodSource("mergeWithConfigBodyData")
     void testMergeWithConfigBody(File file, Map<String, String> 
expectedMimeTypes, Map<String, String> expectedExtensions) throws IOException {
         runner.setProperty(IdentifyMimeType.CONFIG_STRATEGY, 
IdentifyMimeType.MERGE);
-        runner.setProperty(IdentifyMimeType.MIME_CONFIG_BODY, CONFIG_BODY);
+        runner.setProperty(IdentifyMimeType.CUSTOM_MIME_CONFIGURATION, 
CONFIG_BODY);
         runner.enqueue(file.toPath());
 
         runner.run();
@@ -325,7 +326,7 @@ class TestIdentifyMimeType {
     @MethodSource("mergeWithConfigFileData")
     void testMergeWithConfigFile(File file, Map<String, String> 
expectedMimeTypes, Map<String, String> expectedExtensions) throws IOException {
         runner.setProperty(IdentifyMimeType.CONFIG_STRATEGY, 
IdentifyMimeType.MERGE);
-        runner.setProperty(IdentifyMimeType.MIME_CONFIG_FILE, CONFIG_FILE);
+        runner.setProperty(IdentifyMimeType.CUSTOM_MIME_CONFIGURATION, 
CONFIG_FILE);
         runner.enqueue(file.toPath());
 
         runner.run();
@@ -361,23 +362,26 @@ class TestIdentifyMimeType {
     }
 
     @Test
-    void testOnlyOneCustomMimeConfigSpecified() {
+    void testNoReplaceCustomMimeConfigurationSpecified() {
         runner.setProperty(IdentifyMimeType.CONFIG_STRATEGY, 
IdentifyMimeType.REPLACE);
-        runner.setProperty(IdentifyMimeType.MIME_CONFIG_FILE, CONFIG_FILE);
 
-        String configBody = "foo";
-        runner.setProperty(IdentifyMimeType.MIME_CONFIG_BODY, configBody);
-
-        runner.setThreadCount(1);
-        assertThrows(AssertionError.class, () -> runner.run());
+        runner.assertNotValid();
     }
 
     @Test
-    void testNoCustomMimeConfigSpecified() {
+    void testInvalidCustomConfigurationReplace() {
         runner.setProperty(IdentifyMimeType.CONFIG_STRATEGY, 
IdentifyMimeType.REPLACE);
+        runner.setProperty(IdentifyMimeType.CUSTOM_MIME_CONFIGURATION, 
"gibberish");
+
+        runner.assertNotValid();
+    }
+
+    @Test
+    void testMigration() {
+        final PropertyMigrationResult propertyMigrationResult = 
runner.migrateProperties();
+        final Set<String> expectedPropertiesRemoved = Set.of("config-file", 
"config-body");
 
-        runner.setThreadCount(1);
-        assertThrows(AssertionError.class, () -> runner.run());
+        assertEquals(expectedPropertiesRemoved, 
propertyMigrationResult.getPropertiesRemoved());
     }
 
     private static Map<String, String> getCommonExpectedMimeTypes() {

Reply via email to