diff --git a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/pom.xml b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/pom.xml
index c1a8b0a82d..7ea4cc4281 100644
--- a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/pom.xml
+++ b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/pom.xml
@@ -141,6 +141,14 @@
src/test/resources/examples/basic-types.dat
src/test/resources/examples/composite.dat
src/test/resources/examples/tbcd-string.dat
+ src/test/resources/test_hugging_comment.asn
+ src/test/resources/test_version_bracket.asn
+ src/test/resources/test_constraints.asn
+ src/test/resources/test_complex_for_preprocessing.asn
+ src/test/resources/preprocessed_test_hugging_comment.asn
+ src/test/resources/preprocessed_test_version_bracket.asn
+ src/test/resources/preprocessed_test_constraints.asn
+ src/test/resources/preprocessed_test_complex_for_preprocessing.asn
diff --git a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/JASN1Reader.java b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/JASN1Reader.java
index c2bbd99761..ba40f45b78 100644
--- a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/JASN1Reader.java
+++ b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/JASN1Reader.java
@@ -29,6 +29,7 @@ import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.annotation.lifecycle.OnDisabled;
import org.apache.nifi.annotation.lifecycle.OnEnabled;
import org.apache.nifi.components.AbstractConfigurableComponent;
+import org.apache.nifi.components.AllowableValue;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.components.ValidationContext;
@@ -36,12 +37,10 @@ import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.controller.ConfigurationContext;
import org.apache.nifi.controller.ControllerServiceInitializationContext;
import org.apache.nifi.expression.ExpressionLanguageScope;
+import org.apache.nifi.jasn1.preprocess.AsnPreprocessorEngine;
import org.apache.nifi.logging.ComponentLog;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.processor.util.StandardValidators;
-import org.apache.nifi.reporting.InitializationException;
-import org.apache.nifi.schema.access.SchemaNotFoundException;
-import org.apache.nifi.serialization.MalformedRecordException;
import org.apache.nifi.serialization.RecordReader;
import org.apache.nifi.serialization.RecordReaderFactory;
import org.apache.nifi.util.file.classloader.ClassLoaderUtils;
@@ -134,16 +133,51 @@ public class JASN1Reader extends AbstractConfigurableComponent implements Record
.required(false)
.build();
+ private static final AllowableValue DEFAULT = new AllowableValue(
+ "DEFAULT",
+ "Default",
+ "No additional preprocessing should occur, use original schema."
+ );
+
+ private static final AllowableValue ADDITIONAL_PREPROCESSING = new AllowableValue(
+ "ADDITIONAL_PREPROCESSING",
+ "Additional Preprocessing",
+ "Perform additional preprocessing, resulting in potentially modified schema. (See additional details for more information.)"
+ );
+
+ private static final PropertyDescriptor SCHEMA_PREPARATION_STRATEGY = new PropertyDescriptor.Builder()
+ .name("Schema Preparation Strategy")
+ .description("When set, NiFi will do additional preprocessing steps that creates modified versions of the provided ASN files," +
+ " removing unsupported features in a way that makes them less strict but otherwise should still be compatible with incoming data." +
+ " The original files will remain intact and new ones will be created with the same names in the directory defined in the 'Additional Preprocessing Output Directory' property." +
+ " For more information about these additional preprocessing steps please see Additional Details - Additional Preprocessing.")
+ .allowableValues(DEFAULT, ADDITIONAL_PREPROCESSING)
+ .required(true)
+ .defaultValue(DEFAULT.getValue())
+ .build();
+
+ private static final PropertyDescriptor SCHEMA_PREPARATION_DIRECTORY = new PropertyDescriptor.Builder()
+ .name("Schema Preparation Directory")
+ .description("When the processor is configured to do additional preprocessing, new modified schema files will be created in this directory." +
+ " For more information about additional preprocessing please see description of the 'Do Additional Preprocessing' property or Additional Details - Additional Preprocessing.")
+ .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
+ .addValidator(StandardValidators.createDirectoryExistsValidator(true, false))
+ .dependsOn(SCHEMA_PREPARATION_STRATEGY, ADDITIONAL_PREPROCESSING)
+ .required(true)
+ .build();
+
private final List propertyDescriptors = Arrays.asList(
ROOT_MODEL_NAME,
ROOT_CLASS_NAME,
- ASN_FILES
+ ASN_FILES,
+ SCHEMA_PREPARATION_STRATEGY,
+ SCHEMA_PREPARATION_DIRECTORY
);
private String identifier;
ComponentLog logger;
- private RecordSchemaProvider schemaProvider = new RecordSchemaProvider();
+ private final RecordSchemaProvider schemaProvider = new RecordSchemaProvider();
volatile Path asnOutDir;
private volatile PropertyValue rootModelNameProperty;
@@ -159,7 +193,7 @@ public class JASN1Reader extends AbstractConfigurableComponent implements Record
}
@Override
- public void initialize(ControllerServiceInitializationContext context) throws InitializationException {
+ public void initialize(ControllerServiceInitializationContext context) {
identifier = context.getIdentifier();
logger = context.getLogger();
}
@@ -175,7 +209,7 @@ public class JASN1Reader extends AbstractConfigurableComponent implements Record
results.add(new ValidationResult.Builder()
.subject(ROOT_MODEL_NAME.getName())
.valid(false)
- .explanation("Onle one of '" + ROOT_MODEL_NAME.getDisplayName() + "' or '" + ROOT_CLASS_NAME.getDisplayName() + "' should be set!")
+ .explanation("Only one of '" + ROOT_MODEL_NAME.getDisplayName() + "' or '" + ROOT_CLASS_NAME.getDisplayName() + "' should be set!")
.build());
}
@@ -191,12 +225,25 @@ public class JASN1Reader extends AbstractConfigurableComponent implements Record
}
@OnEnabled
- public void onEnabled(final ConfigurationContext context) throws InitializationException {
+ public void onEnabled(final ConfigurationContext context) {
if (context.getProperty(ASN_FILES) != null && context.getProperty(ASN_FILES).isSet()) {
- String[] asnFilesPaths = Arrays.stream(context.getProperty(ASN_FILES).evaluateAttributeExpressions().getValue().split(","))
- .map(String::trim)
- .toArray(String[]::new);
+ String asnFilesString = context.getProperty(ASN_FILES).evaluateAttributeExpressions().getValue();
+ if (ADDITIONAL_PREPROCESSING.getValue().equals(context.getProperty(SCHEMA_PREPARATION_STRATEGY).getValue())) {
+ final AsnPreprocessorEngine asnPreprocessorEngine = new AsnPreprocessorEngine();
+
+ final String preprocessOutputDirectory = context.getProperty(SCHEMA_PREPARATION_DIRECTORY).evaluateAttributeExpressions().getValue();
+
+ asnFilesString = asnPreprocessorEngine.preprocess(
+ logger,
+ asnFilesString,
+ preprocessOutputDirectory
+ );
+ }
+
+ final String[] asnFilesPaths = Arrays.stream(asnFilesString.split(","))
+ .map(String::trim)
+ .toArray(String[]::new);
compileAsnToClass(asnFilesPaths);
}
@@ -211,7 +258,7 @@ public class JASN1Reader extends AbstractConfigurableComponent implements Record
customClassLoader = this.getClass().getClassLoader();
}
} catch (final Exception ex) {
- logger.error("Couldn't create classloader for compiled classes.", ex);
+ logger.error("Could not create ClassLoader for compiled ASN.1 classes", ex);
}
rootModelNameProperty = context.getProperty(ROOT_MODEL_NAME);
@@ -223,18 +270,10 @@ public class JASN1Reader extends AbstractConfigurableComponent implements Record
private void compileAsnToClass(String... asnFilePaths) {
try {
asnOutDir = Files.createTempDirectory(getIdentifier() + "_asn_");
-
- logger.info("ASN files will be compiled to '" + asnOutDir + "'");
} catch (IOException e) {
- throw new ProcessException("Couldn't create temporary directory for compiled asn files.", e);
+ throw new ProcessException("Could not create temporary directory for compiled ASN.1 files", e);
}
- List asnCompilerArguments = new ArrayList<>();
- asnCompilerArguments.add("-f");
- asnCompilerArguments.addAll(Arrays.asList(asnFilePaths));
- asnCompilerArguments.add("-o");
- asnCompilerArguments.add(asnOutDir.toString());
-
HashMap modulesByName = new HashMap<>();
Exception parseException = null;
@@ -295,12 +334,12 @@ public class JASN1Reader extends AbstractConfigurableComponent implements Record
Boolean success = task.call();
if (!success) {
- Set errorMessages = new LinkedHashSet();
+ Set errorMessages = new LinkedHashSet<>();
diagnosticListener.getDiagnostics().stream().map(d -> d.getMessage(Locale.getDefault())).forEach(errorMessages::add);
errorMessages.forEach(logger::error);
- throw new ProcessException("Java compilation failed");
+ throw new ProcessException("ASN.1 Java compilation failed");
}
}
@@ -333,7 +372,7 @@ public class JASN1Reader extends AbstractConfigurableComponent implements Record
InputStream in,
long inputLength,
ComponentLog logger
- ) throws MalformedRecordException, IOException, SchemaNotFoundException {
+ ) {
final String rootClassName;
if (rootModelNameProperty != null && rootModelNameProperty.isSet()) {
rootClassName = guessRootClassName(rootModelNameProperty.evaluateAttributeExpressions(variables).getValue());
@@ -366,13 +405,13 @@ public class JASN1Reader extends AbstractConfigurableComponent implements Record
}
};
+ AsnModel model = new AsnModel();
+ parser.module_definitions(model);
+
if (parseError.get()) {
throw new ProcessException("ASN.1 parsing failed");
}
- AsnModel model = new AsnModel();
- parser.module_definitions(model);
-
return model;
}
@@ -390,7 +429,7 @@ public class JASN1Reader extends AbstractConfigurableComponent implements Record
return rootClassNameBuilder.toString();
} catch (Exception e) {
- throw new ProcessException("Couldn't infer root model name from '" + rootModelName + "'", e);
+ throw new ProcessException("Could not infer root model name from '" + rootModelName + "'", e);
}
}
}
diff --git a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/AsnPreprocessor.java b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/AsnPreprocessor.java
new file mode 100644
index 0000000000..d58a5b4f02
--- /dev/null
+++ b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/AsnPreprocessor.java
@@ -0,0 +1,23 @@
+/*
+ * 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.jasn1.preprocess;
+
+import java.util.List;
+
+public interface AsnPreprocessor {
+ List preprocessAsn(List lines);
+}
diff --git a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/AsnPreprocessorEngine.java b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/AsnPreprocessorEngine.java
new file mode 100644
index 0000000000..7a3d82e465
--- /dev/null
+++ b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/AsnPreprocessorEngine.java
@@ -0,0 +1,105 @@
+/*
+ * 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.jasn1.preprocess;
+
+import org.apache.nifi.jasn1.preprocess.preprocessors.ConstraintAsnPreprocessor;
+import org.apache.nifi.jasn1.preprocess.preprocessors.HuggingCommentAsnPreprocessor;
+import org.apache.nifi.jasn1.preprocess.preprocessors.VersionBracketAsnPreprocessor;
+import org.apache.nifi.logging.ComponentLog;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+public class AsnPreprocessorEngine {
+ public static final String COMMA = "\\s*,\\s*";
+
+ private static final List PREPROCESSORS = Arrays.asList(
+ new HuggingCommentAsnPreprocessor(),
+ new VersionBracketAsnPreprocessor(),
+ new ConstraintAsnPreprocessor()
+ );
+
+ public String preprocess(
+ ComponentLog componentLog,
+ String asnFilesString,
+ String outputDirectory
+ ) {
+ final String[] inputFiles = asnFilesString.split(COMMA);
+
+ final StringJoiner preprocessedInputFiles = new StringJoiner(",");
+
+ for (String inputFile : inputFiles) {
+ final Path inputFilePath = Paths.get(inputFile);
+ final Path fileName = inputFilePath.getFileName();
+
+ final List lines = readAsnLines(componentLog, inputFile, inputFilePath);
+
+ final List preprocessedLines = preprocessAsn(lines);
+
+ final String preprocessedAsn = preprocessedLines
+ .stream()
+ .collect(Collectors.joining(System.lineSeparator()));
+
+ final Path preprocessedAsnPath = Paths.get(outputDirectory, fileName.toString());
+ preprocessedInputFiles.add(preprocessedAsnPath.toString());
+
+ writePreprocessedAsn(componentLog, preprocessedAsn, preprocessedAsnPath);
+ }
+
+ return preprocessedInputFiles.toString();
+ }
+
+ List preprocessAsn(List lines) {
+ List preprocessedAsn = lines;
+
+ for (AsnPreprocessor preprocessor : getPreprocessors()) {
+ preprocessedAsn = preprocessor.preprocessAsn(preprocessedAsn);
+ }
+
+ return preprocessedAsn;
+ }
+
+ List readAsnLines(ComponentLog componentLog, String inputFile, Path inputFilePath) {
+ List lines;
+ try {
+ lines = Files.readAllLines(inputFilePath);
+ } catch (IOException e) {
+ throw new UncheckedIOException(String.format("Read ASN.1 Schema failed [%s]", inputFile), e);
+ }
+ return lines;
+ }
+
+ void writePreprocessedAsn(ComponentLog componentLog, String preprocessedAsn, Path preprocessedAsnPath) {
+ try {
+ Files.write(preprocessedAsnPath, preprocessedAsn.getBytes(StandardCharsets.UTF_8));
+ } catch (IOException e) {
+ throw new UncheckedIOException(String.format("Write ASN.1 Schema failed [%s]", preprocessedAsnPath), e);
+ }
+ }
+
+ List getPreprocessors() {
+ return PREPROCESSORS;
+ }
+}
diff --git a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/preprocessors/ConstraintAsnPreprocessor.java b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/preprocessors/ConstraintAsnPreprocessor.java
new file mode 100644
index 0000000000..1d3fd8eb5c
--- /dev/null
+++ b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/preprocessors/ConstraintAsnPreprocessor.java
@@ -0,0 +1,91 @@
+/*
+ * 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.jasn1.preprocess.preprocessors;
+
+import org.apache.nifi.jasn1.preprocess.AsnPreprocessor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ConstraintAsnPreprocessor implements AsnPreprocessor {
+ public static final String OPEN_BRACKET = "(";
+ public static final String CLOSE_BRACKET = ")";
+
+ public static final Pattern ALLOWED = Pattern.compile("^(\\d+\\))(.*)");
+
+ @Override
+ public List preprocessAsn(List lines) {
+ final List preprocessedLines = new ArrayList<>();
+
+ final AtomicInteger unclosedCounter = new AtomicInteger(0);
+ lines.forEach(line -> {
+ final StringBuilder preprocessedLine = new StringBuilder();
+
+ String contentToProcess = line;
+
+ while (contentToProcess.contains(OPEN_BRACKET) || contentToProcess.contains(CLOSE_BRACKET)) {
+ if (contentToProcess.matches("^\\s*--.*$")) {
+ break;
+ }
+
+ final int openBracketIndex = contentToProcess.indexOf(OPEN_BRACKET);
+ final int closeBracketIndex = contentToProcess.indexOf(CLOSE_BRACKET);
+
+ if (openBracketIndex != -1 && (openBracketIndex < closeBracketIndex) || closeBracketIndex == -1) {
+ final String contentBeforeOpenBracket = contentToProcess.substring(0, openBracketIndex);
+ final String contentAfterOpenBracket = contentToProcess.substring(openBracketIndex + 1);
+
+ if (unclosedCounter.get() < 1) {
+ if (!contentBeforeOpenBracket.isEmpty()) {
+ preprocessedLine.append(contentBeforeOpenBracket + " ");
+ // Adding a space " " because (...) blocks can serve as separators so removing them might
+ // join together parts that should stay separated
+ }
+
+ final Matcher supportedMatcher = ALLOWED.matcher(contentAfterOpenBracket);
+ if (supportedMatcher.matches()) {
+ preprocessedLine.append(OPEN_BRACKET + supportedMatcher.group(1));
+ contentToProcess = supportedMatcher.group(2);
+ continue;
+ }
+ }
+
+ unclosedCounter.incrementAndGet();
+
+ contentToProcess = contentAfterOpenBracket;
+ } else if (closeBracketIndex != -1) {
+ unclosedCounter.decrementAndGet();
+
+ contentToProcess = contentToProcess.substring(closeBracketIndex + 1);
+ }
+ }
+
+ if (unclosedCounter.get() < 1) {
+ if (!contentToProcess.isEmpty()) {
+ preprocessedLine.append(contentToProcess);
+ }
+ }
+
+ preprocessedLines.add(preprocessedLine.toString());
+ });
+
+ return preprocessedLines;
+ }
+}
diff --git a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/preprocessors/HuggingCommentAsnPreprocessor.java b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/preprocessors/HuggingCommentAsnPreprocessor.java
new file mode 100644
index 0000000000..8667f46e30
--- /dev/null
+++ b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/preprocessors/HuggingCommentAsnPreprocessor.java
@@ -0,0 +1,49 @@
+/*
+ * 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.jasn1.preprocess.preprocessors;
+
+import org.apache.nifi.jasn1.preprocess.AsnPreprocessor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class HuggingCommentAsnPreprocessor implements AsnPreprocessor {
+ public static final Pattern HUGGING_COMMENT_PATTERN = Pattern.compile("^(.*[^\\s])(--.*)$");
+
+ @Override
+ public List preprocessAsn(List lines) {
+ final List preprocessedLines = new ArrayList<>();
+
+ lines.forEach(line -> {
+ final StringBuilder preprocessedLine = new StringBuilder();
+
+ final Matcher huggingCommentMather = HUGGING_COMMENT_PATTERN.matcher(line);
+ if (huggingCommentMather.matches()) {
+ preprocessedLine.append(huggingCommentMather.group(1))
+ .append(" ")
+ .append(huggingCommentMather.group(2));
+ preprocessedLines.add(preprocessedLine.toString());
+ } else {
+ preprocessedLines.add(line);
+ }
+ });
+
+ return preprocessedLines;
+ }
+}
diff --git a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/preprocessors/VersionBracketAsnPreprocessor.java b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/preprocessors/VersionBracketAsnPreprocessor.java
new file mode 100644
index 0000000000..ca976de535
--- /dev/null
+++ b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/preprocess/preprocessors/VersionBracketAsnPreprocessor.java
@@ -0,0 +1,89 @@
+/*
+ * 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.jasn1.preprocess.preprocessors;
+
+import org.apache.nifi.jasn1.preprocess.AsnPreprocessor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Pattern;
+
+public class VersionBracketAsnPreprocessor implements AsnPreprocessor {
+ public static final String OPEN_VERSION_BRACKET = "[[";
+ public static final String CLOSE_VERSION_BRACKET = "]]";
+
+ public static final Pattern ONLY_WHITESPACES = Pattern.compile("^\\s*$");
+ public static final Pattern ONLY_COMMENT = Pattern.compile("^\\s*--.*$");
+ public static final Pattern IS_OPTIONAL_ALREADY = Pattern.compile(".*OPTIONAL\\s*,?\\s*$");
+
+ public static final String TRAILING_COMMA_WITH_POTENTIAL_WHITESPACES = "(\\s*)(,?)(\\s*)$";
+ public static final String ADD_OPTIONAL = " OPTIONAL$1$2$3";
+
+ @Override
+ public List preprocessAsn(List lines) {
+ final List preprocessedLines = new ArrayList<>();
+
+ final AtomicBoolean inVersionBracket = new AtomicBoolean(false);
+ lines.forEach(line -> {
+ final StringBuilder preprocessedLine = new StringBuilder();
+ String contentToProcess = line;
+
+ final int versionBracketStart = contentToProcess.indexOf(OPEN_VERSION_BRACKET);
+ if (versionBracketStart > -1) {
+ inVersionBracket.set(true);
+
+ final String contentBeforeVersionBracket = line.substring(0, versionBracketStart);
+ if (!contentBeforeVersionBracket.isEmpty()) {
+ preprocessedLine.append(contentBeforeVersionBracket);
+ }
+
+ contentToProcess = contentToProcess.substring(versionBracketStart + 2);
+ }
+
+ final int versionBracketEnd = contentToProcess.indexOf(CLOSE_VERSION_BRACKET);
+ String contentAfterVersionBracket = null;
+ if (versionBracketEnd > -1) {
+ contentAfterVersionBracket = contentToProcess.substring(versionBracketEnd + 2);
+ contentToProcess = contentToProcess.substring(0, versionBracketEnd);
+ }
+
+ if (inVersionBracket.get()
+ && !ONLY_WHITESPACES.matcher(contentToProcess).matches()
+ && !ONLY_COMMENT.matcher(contentToProcess).matches()
+ && !IS_OPTIONAL_ALREADY.matcher(contentToProcess).matches()
+ ) {
+ contentToProcess = contentToProcess.replaceFirst(TRAILING_COMMA_WITH_POTENTIAL_WHITESPACES, ADD_OPTIONAL);
+ }
+
+ if (!contentToProcess.isEmpty()) {
+ preprocessedLine.append(contentToProcess);
+ }
+
+ if (contentAfterVersionBracket != null) {
+ if (!contentAfterVersionBracket.isEmpty()) {
+ preprocessedLine.append(contentAfterVersionBracket);
+ }
+ inVersionBracket.set(false);
+ }
+
+ preprocessedLines.add(preprocessedLine.toString());
+ });
+
+ return preprocessedLines;
+ }
+}
diff --git a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/resources/docs/org.apache.nifi.jasn1.JASN1Reader/additionalDetails.html b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/resources/docs/org.apache.nifi.jasn1.JASN1Reader/additionalDetails.html
index 31edf6d64e..51ca11fdd9 100644
--- a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/resources/docs/org.apache.nifi.jasn1.JASN1Reader/additionalDetails.html
+++ b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/resources/docs/org.apache.nifi.jasn1.JASN1Reader/additionalDetails.html
@@ -122,5 +122,62 @@
+
+ Additional Preprocessing
+
+
+ NiFi doesn't support every feature that the ASN standard allows. To alleviate problems when encountering ASN files with unsupported features,
+ NiFi can do additional preprocessing steps that creates modified versions of the provided ASN files,
+ removing unsupported features in a way that makes them less strict but otherwise should still be compatible with incoming data.
+ This feature can be switched on via the 'Do Additional Preprocessing' property.
+ The original files will remain intact and new ones will be created with the same names in a directory set in the 'Additional Preprocessing Output Directory' property.
+ Please note that this is a best-effort attempt. It is also strongly recommended to compare the resulting ASN files to the originals and make sure they are still appropriate.
+
+
+ The following modification are applied:
+
+ -
+ Constraints - Advanced constraints are not recognized as valid ASN elements by NiFi. This step will try to remove all types of constraints.
+
E.g.
+ field [3] INTEGER(SIZE(1..8,...,10|12|20)) OPTIONAL
+ will be changed to
+ field [3] INTEGER OPTIONAL
+
+ -
+ Version brackets - NiFi will try to remove all version brackets and leave all defined fields as OPTIONAL.
+
E.g.
+
+MyType ::= SEQUENCE {
+ integerField1 INTEGER,
+ integerField2 INTEGER,
+ ..., -- comment1
+[[ -- comment2
+ integerField3 INTEGER,
+ integerField4 INTEGER,
+ integerField5 INTEGER ]]
+}
+
+ will be changed to
+
+MyType ::= SEQUENCE {
+ integerField1 INTEGER,
+ integerField2 INTEGER,
+ ..., -- comment1
+ -- comment2
+ integerField3 INTEGER OPTIONAL,
+ integerField4 INTEGER OPTIONAL,
+ integerField5 INTEGER OPTIONAL
+}
+
+
+ -
+ "Hugging" comments - This is not really an ASN feature but a potential error. The double dash comment indicator "--" should be separated from ASN elements.
+
E.g.
+ field [0] INTEGER(1..8)--comment
+ will be changed to
+ field [0] INTEGER(1..8) --comment
+
+
+