diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ExtensionComponent.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ExtensionComponent.java
index 1f07f569fb..8a4447d585 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ExtensionComponent.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ExtensionComponent.java
@@ -47,6 +47,8 @@ public class ExtensionComponent extends DefinedType {
private Stateful stateful;
+ private boolean additionalDetails;
+
@ApiModelProperty("The build metadata for this component")
public BuildInfo getBuildInfo() {
return buildInfo;
@@ -120,6 +122,7 @@ public class ExtensionComponent extends DefinedType {
this.explicitRestrictions = explicitRestrictions;
}
+ @ApiModelProperty("Indicates if the component stores state")
public Stateful getStateful() {
return stateful;
}
@@ -128,6 +131,15 @@ public class ExtensionComponent extends DefinedType {
this.stateful = stateful;
}
+ @ApiModelProperty("Indicates if the component has additional details documentation")
+ public boolean isAdditionalDetails() {
+ return additionalDetails;
+ }
+
+ public void setAdditionalDetails(boolean additionalDetails) {
+ this.additionalDetails = additionalDetails;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/nifi-assembly/pom.xml b/nifi-assembly/pom.xml
index 73929956ff..416fb2799a 100644
--- a/nifi-assembly/pom.xml
+++ b/nifi-assembly/pom.xml
@@ -32,16 +32,13 @@ language governing permissions and limitations under the License. -->
generate-resources
nar
- **/extension-manifest.xml
+ **/docs/**
false
${project.build.directory}/extension-manifests
true
true
true
true
-
-
-
diff --git a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/ExtensionManifestContainer.java b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/ExtensionManifestContainer.java
new file mode 100644
index 0000000000..16b91bf193
--- /dev/null
+++ b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/ExtensionManifestContainer.java
@@ -0,0 +1,48 @@
+/*
+ * 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.runtime.manifest;
+
+import org.apache.nifi.extension.manifest.ExtensionManifest;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class ExtensionManifestContainer {
+
+ private final ExtensionManifest manifest;
+ private final Map additionalDetails;
+
+ public ExtensionManifestContainer(final ExtensionManifest manifest) {
+ this(manifest, null);
+ }
+
+ public ExtensionManifestContainer(final ExtensionManifest manifest, final Map additionalDetails) {
+ this.manifest = Objects.requireNonNull(manifest);
+ this.additionalDetails = Collections.unmodifiableMap(additionalDetails == null
+ ? Collections.emptyMap() : new LinkedHashMap<>(additionalDetails));
+ }
+
+ public ExtensionManifest getManifest() {
+ return manifest;
+ }
+
+ public Map getAdditionalDetails() {
+ return additionalDetails;
+ }
+}
diff --git a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/ExtensionManifestProvider.java b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/ExtensionManifestProvider.java
index 4039ed0c4e..1e4a5ff734 100644
--- a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/ExtensionManifestProvider.java
+++ b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/ExtensionManifestProvider.java
@@ -16,8 +16,6 @@
*/
package org.apache.nifi.runtime.manifest;
-import org.apache.nifi.extension.manifest.ExtensionManifest;
-
import java.util.List;
/**
@@ -25,6 +23,6 @@ import java.util.List;
*/
public interface ExtensionManifestProvider {
- List getExtensionManifests();
+ List getExtensionManifests();
}
diff --git a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/RuntimeManifestBuilder.java b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/RuntimeManifestBuilder.java
index f78298816a..6c5ce1e8ba 100644
--- a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/RuntimeManifestBuilder.java
+++ b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/RuntimeManifestBuilder.java
@@ -20,7 +20,6 @@ import org.apache.nifi.c2.protocol.component.api.BuildInfo;
import org.apache.nifi.c2.protocol.component.api.Bundle;
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
import org.apache.nifi.c2.protocol.component.api.SchedulingDefaults;
-import org.apache.nifi.extension.manifest.ExtensionManifest;
/**
* Builder for creating a RuntimeManifest.
@@ -57,7 +56,7 @@ public interface RuntimeManifestBuilder {
* @param extensionManifest the extension manifest to add
* @return the builder
*/
- RuntimeManifestBuilder addBundle(ExtensionManifest extensionManifest);
+ RuntimeManifestBuilder addBundle(ExtensionManifestContainer extensionManifest);
/**
* Adds a Bundle for each of the given ExtensionManifests.
@@ -65,7 +64,7 @@ public interface RuntimeManifestBuilder {
* @param extensionManifests the extension manifests to add
* @return the builder
*/
- RuntimeManifestBuilder addBundles(Iterable extensionManifests);
+ RuntimeManifestBuilder addBundles(Iterable extensionManifests);
/**
* Adds the given Bundle.
diff --git a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/DirectoryExtensionManifestProvider.java b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/DirectoryExtensionManifestProvider.java
index 387c84b267..b482096954 100644
--- a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/DirectoryExtensionManifestProvider.java
+++ b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/DirectoryExtensionManifestProvider.java
@@ -16,8 +16,9 @@
*/
package org.apache.nifi.runtime.manifest.impl;
-import org.apache.nifi.extension.manifest.parser.ExtensionManifestParser;
import org.apache.nifi.extension.manifest.ExtensionManifest;
+import org.apache.nifi.extension.manifest.parser.ExtensionManifestParser;
+import org.apache.nifi.runtime.manifest.ExtensionManifestContainer;
import org.apache.nifi.runtime.manifest.ExtensionManifestProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,8 +27,12 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
/**
* ExtensionManifestProvider that loads extension manifests from a directory where the nifi-assembly-manifests
@@ -46,7 +51,7 @@ public class DirectoryExtensionManifestProvider implements ExtensionManifestProv
}
@Override
- public List getExtensionManifests() {
+ public List getExtensionManifests() {
if (!baseDir.exists()) {
throw new IllegalArgumentException("The specified manifest directory does not exist");
}
@@ -56,18 +61,22 @@ public class DirectoryExtensionManifestProvider implements ExtensionManifestProv
LOGGER.info("Loading extension manifests from: {}", baseDir.getAbsolutePath());
- final List extensionManifests = new ArrayList<>();
+ final List extensionManifests = new ArrayList<>();
for (final File manifestDir : baseDir.listFiles()) {
if (!manifestDir.isDirectory()) {
LOGGER.debug("Skipping [{}], not a directory...", manifestDir.getAbsolutePath());
continue;
}
- final File manifestFile = new File(manifestDir, "extension-manifest.xml");
+ final File manifestFile = new File(manifestDir, "META-INF/docs/extension-manifest.xml");
LOGGER.debug("Loading extension manifest file [{}]", manifestFile.getAbsolutePath());
final ExtensionManifest extensionManifest = loadExtensionManifest(manifestFile);
- extensionManifests.add(extensionManifest);
+ final Map additionalDetails = loadAdditionalDetails(manifestDir);
+
+ final ExtensionManifestContainer container = new ExtensionManifestContainer(extensionManifest, additionalDetails);
+ extensionManifests.add(container);
+
LOGGER.debug("Successfully loaded extension manifest for [{}-{}-{}]",
extensionManifest.getGroupId(), extensionManifest.getArtifactId(), extensionManifest.getVersion());
}
@@ -83,4 +92,40 @@ public class DirectoryExtensionManifestProvider implements ExtensionManifestProv
throw new RuntimeException("Unable to load extension manifest: " + manifestFile.getAbsolutePath(), ioException);
}
}
+
+ private Map loadAdditionalDetails(final File manifestDir) {
+ final Map additionalDetailsMap = new LinkedHashMap<>();
+
+ final File additionalDetailsDir = new File(manifestDir, "META-INF/docs/additional-details");
+ if (!additionalDetailsDir.exists()) {
+ LOGGER.debug("No additional-details directory found under [{}]", manifestDir.getAbsolutePath());
+ return additionalDetailsMap;
+ }
+
+ for (final File additionalDetailsTypeDir : additionalDetailsDir.listFiles()) {
+ if (!additionalDetailsTypeDir.isDirectory()) {
+ LOGGER.debug("Skipping [{}], not a directory...", additionalDetailsTypeDir.getAbsolutePath());
+ continue;
+ }
+
+ final File additionalDetailsFile = new File(additionalDetailsTypeDir, "additionalDetails.html");
+ if (!additionalDetailsFile.exists()) {
+ LOGGER.debug("No additionalDetails.html found under [{}]", additionalDetailsTypeDir.getAbsolutePath());
+ continue;
+ }
+
+ try {
+ final String typeName = additionalDetailsTypeDir.getName();
+ final byte[] additionalDetailsBytes = Files.readAllBytes(additionalDetailsFile.toPath());
+ LOGGER.debug("Added additionalDetails for {} from {}", typeName, additionalDetailsFile.getAbsolutePath());
+ additionalDetailsMap.put(typeName, new String(additionalDetailsBytes, StandardCharsets.UTF_8));
+ } catch (final IOException e) {
+ throw new RuntimeException("Unable to load additional details content for "
+ + additionalDetailsFile.getAbsolutePath() + " due to: " + e.getMessage(), e);
+ }
+ }
+
+ return additionalDetailsMap;
+ }
+
}
diff --git a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/RuntimeManifestGenerator.java b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/RuntimeManifestGenerator.java
index 334a4a11b3..bbbe1f9db1 100644
--- a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/RuntimeManifestGenerator.java
+++ b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/RuntimeManifestGenerator.java
@@ -21,8 +21,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.apache.nifi.c2.protocol.component.api.BuildInfo;
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
+import org.apache.nifi.extension.manifest.Extension;
+import org.apache.nifi.extension.manifest.ExtensionManifest;
import org.apache.nifi.extension.manifest.parser.ExtensionManifestParser;
import org.apache.nifi.extension.manifest.parser.jaxb.JAXBExtensionManifestParser;
+import org.apache.nifi.runtime.manifest.ExtensionManifestContainer;
import org.apache.nifi.runtime.manifest.ExtensionManifestProvider;
import org.apache.nifi.runtime.manifest.RuntimeManifestSerializer;
import org.slf4j.Logger;
@@ -34,8 +37,12 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.List;
+import java.util.Map;
import java.util.Properties;
/**
@@ -93,12 +100,14 @@ public class RuntimeManifestGenerator {
buildInfo.setTimestamp(buildTimestampMillis);
buildInfo.setCompiler(buildJdkVendor + " " + buildJdk);
+ final List extensionsManifests = extensionManifestProvider.getExtensionManifests();
+
final RuntimeManifest runtimeManifest = new StandardRuntimeManifestBuilder()
.identifier(runtimeManifestId)
.version(runtimeVersion)
.runtimeType("nifi")
.buildInfo(buildInfo)
- .addBundles(extensionManifestProvider.getExtensionManifests())
+ .addBundles(extensionsManifests)
.schedulingDefaults(SchedulingDefaultsFactory.getNifiSchedulingDefaults())
.build();
@@ -106,6 +115,30 @@ public class RuntimeManifestGenerator {
try (final OutputStream outputStream = new FileOutputStream(runtimeManifestFile)) {
runtimeManifestSerializer.write(runtimeManifest, outputStream);
}
+
+ final File docsDir = new File(runtimeManifestFile.getParent(), "docs");
+ docsDir.mkdirs();
+
+ for (final ExtensionManifestContainer manifestContainer : extensionsManifests) {
+ final ExtensionManifest extensionManifest = manifestContainer.getManifest();
+ final Map additionalDetailsMap = manifestContainer.getAdditionalDetails();
+
+ final File bundleDir = new File(docsDir, extensionManifest.getGroupId()
+ + "/" + extensionManifest.getArtifactId()
+ + "/" + extensionManifest.getVersion());
+
+ for (final Extension extension : extensionManifest.getExtensions()) {
+ final String extensionType = extension.getName();
+ final File extensionDir = new File(bundleDir, extensionType);
+
+ final String additionalDetails = additionalDetailsMap.get(extensionType);
+ if (additionalDetails != null) {
+ extensionDir.mkdirs();
+ final File additionalDetailsFile = new File(extensionDir, "additionalDetails.html");
+ Files.write(additionalDetailsFile.toPath(), additionalDetails.getBytes(StandardCharsets.UTF_8));
+ }
+ }
+ }
}
private ExtensionManifestProvider createExtensionManifestProvider() {
diff --git a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/StandardRuntimeManifestBuilder.java b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/StandardRuntimeManifestBuilder.java
index 6a56035dce..c5cf27d95c 100644
--- a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/StandardRuntimeManifestBuilder.java
+++ b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/StandardRuntimeManifestBuilder.java
@@ -52,6 +52,7 @@ import org.apache.nifi.extension.manifest.Restricted;
import org.apache.nifi.extension.manifest.Stateful;
import org.apache.nifi.logging.LogLevel;
import org.apache.nifi.runtime.manifest.ComponentManifestBuilder;
+import org.apache.nifi.runtime.manifest.ExtensionManifestContainer;
import org.apache.nifi.runtime.manifest.RuntimeManifestBuilder;
import org.apache.nifi.scheduling.SchedulingStrategy;
@@ -105,7 +106,12 @@ public class StandardRuntimeManifestBuilder implements RuntimeManifestBuilder {
}
@Override
- public RuntimeManifestBuilder addBundle(final ExtensionManifest extensionManifest) {
+ public RuntimeManifestBuilder addBundle(final ExtensionManifestContainer extensionManifestContainer) {
+ if (extensionManifestContainer == null) {
+ throw new IllegalArgumentException("Extension manifest container is required");
+ }
+
+ final ExtensionManifest extensionManifest = extensionManifestContainer.getManifest();
if (extensionManifest == null) {
throw new IllegalArgumentException("Extension manifest is required");
}
@@ -125,8 +131,12 @@ public class StandardRuntimeManifestBuilder implements RuntimeManifestBuilder {
bundle.setVersion(extensionManifest.getVersion());
if (extensionManifest.getExtensions() != null) {
+ final Map additionalDetailsMap = extensionManifestContainer.getAdditionalDetails();
final ComponentManifestBuilder componentManifestBuilder = new StandardComponentManifestBuilder();
- extensionManifest.getExtensions().forEach(extension -> addExtension(extensionManifest, extension, componentManifestBuilder));
+ extensionManifest.getExtensions().forEach(extension -> {
+ final String additionalDetails = additionalDetailsMap.get(extension.getName());
+ addExtension(extensionManifest, extension, additionalDetails, componentManifestBuilder);
+ });
bundle.setComponentManifest(componentManifestBuilder.build());
}
bundles.add(bundle);
@@ -135,7 +145,7 @@ public class StandardRuntimeManifestBuilder implements RuntimeManifestBuilder {
}
@Override
- public RuntimeManifestBuilder addBundles(final Iterable extensionManifests) {
+ public RuntimeManifestBuilder addBundles(final Iterable extensionManifests) {
extensionManifests.forEach(em -> addBundle(em));
return this;
}
@@ -167,30 +177,32 @@ public class StandardRuntimeManifestBuilder implements RuntimeManifestBuilder {
return runtimeManifest;
}
- private void addExtension(final ExtensionManifest extensionManifest, final Extension extension, final ComponentManifestBuilder componentManifestBuilder) {
+ private void addExtension(final ExtensionManifest extensionManifest, final Extension extension, final String additionalDetails,
+ final ComponentManifestBuilder componentManifestBuilder) {
if (extension == null) {
throw new IllegalArgumentException("Extension cannot be null");
}
switch(extension.getType()) {
case PROCESSOR:
- addProcessorDefinition(extensionManifest, extension, componentManifestBuilder);
+ addProcessorDefinition(extensionManifest, extension, additionalDetails, componentManifestBuilder);
break;
case CONTROLLER_SERVICE:
- addControllerServiceDefinition(extensionManifest, extension, componentManifestBuilder);
+ addControllerServiceDefinition(extensionManifest, extension, additionalDetails, componentManifestBuilder);
break;
case REPORTING_TASK:
- addReportingTaskDefinition(extensionManifest, extension, componentManifestBuilder);
+ addReportingTaskDefinition(extensionManifest, extension, additionalDetails, componentManifestBuilder);
break;
default:
throw new IllegalArgumentException("Unknown extension type: " + extension.getType());
}
}
- private void addProcessorDefinition(final ExtensionManifest extensionManifest, final Extension extension, final ComponentManifestBuilder componentManifestBuilder) {
+ private void addProcessorDefinition(final ExtensionManifest extensionManifest, final Extension extension, final String additionalDetails,
+ final ComponentManifestBuilder componentManifestBuilder) {
final ProcessorDefinition processorDefinition = new ProcessorDefinition();
populateDefinedType(extensionManifest, extension, processorDefinition);
- populateExtensionComponent(extensionManifest, extension, processorDefinition);
+ populateExtensionComponent(extensionManifest, extension, additionalDetails, processorDefinition);
populateConfigurableComponent(extension, processorDefinition);
// processor specific fields
@@ -279,18 +291,20 @@ public class StandardRuntimeManifestBuilder implements RuntimeManifestBuilder {
return componentRelationships;
}
- private void addControllerServiceDefinition(final ExtensionManifest extensionManifest, final Extension extension, final ComponentManifestBuilder componentManifestBuilder) {
+ private void addControllerServiceDefinition(final ExtensionManifest extensionManifest, final Extension extension, final String additionalDetails,
+ final ComponentManifestBuilder componentManifestBuilder) {
final ControllerServiceDefinition controllerServiceDefinition = new ControllerServiceDefinition();
populateDefinedType(extensionManifest, extension, controllerServiceDefinition);
- populateExtensionComponent(extensionManifest, extension, controllerServiceDefinition);
+ populateExtensionComponent(extensionManifest, extension, additionalDetails, controllerServiceDefinition);
populateConfigurableComponent(extension, controllerServiceDefinition);
componentManifestBuilder.addControllerService(controllerServiceDefinition);
}
- private void addReportingTaskDefinition(final ExtensionManifest extensionManifest, final Extension extension, final ComponentManifestBuilder componentManifestBuilder) {
+ private void addReportingTaskDefinition(final ExtensionManifest extensionManifest, final Extension extension, final String additionalDetails,
+ final ComponentManifestBuilder componentManifestBuilder) {
final ReportingTaskDefinition reportingTaskDefinition = new ReportingTaskDefinition();
populateDefinedType(extensionManifest, extension, reportingTaskDefinition);
- populateDefinedType(extensionManifest, extension, reportingTaskDefinition);
+ populateExtensionComponent(extensionManifest, extension, additionalDetails, reportingTaskDefinition);
populateConfigurableComponent(extension, reportingTaskDefinition);
final List schedulingStrategies = new ArrayList<>();
@@ -326,7 +340,8 @@ public class StandardRuntimeManifestBuilder implements RuntimeManifestBuilder {
definedType.setVersion(extensionManifest.getVersion());
}
- private void populateExtensionComponent(final ExtensionManifest extensionManifest, final Extension extension, final ExtensionComponent extensionComponent) {
+ private void populateExtensionComponent(final ExtensionManifest extensionManifest, final Extension extension, final String additionalDetails,
+ final ExtensionComponent extensionComponent) {
final org.apache.nifi.extension.manifest.BuildInfo buildInfo = extensionManifest.getBuildInfo();
if (buildInfo != null) {
final BuildInfo componentBuildInfo = new BuildInfo();
@@ -377,7 +392,10 @@ public class StandardRuntimeManifestBuilder implements RuntimeManifestBuilder {
);
extensionComponent.setStateful(componentStateful);
}
+ }
+ if (additionalDetails != null) {
+ extensionComponent.setAdditionalDetails(true);
}
}
diff --git a/nifi-manifest/nifi-runtime-manifest-test/src/test/java/org/apache/nifi/runtime/manifest/TestRuntimeManifest.java b/nifi-manifest/nifi-runtime-manifest-test/src/test/java/org/apache/nifi/runtime/manifest/TestRuntimeManifest.java
index 9f053b987b..714128c3c5 100644
--- a/nifi-manifest/nifi-runtime-manifest-test/src/test/java/org/apache/nifi/runtime/manifest/TestRuntimeManifest.java
+++ b/nifi-manifest/nifi-runtime-manifest-test/src/test/java/org/apache/nifi/runtime/manifest/TestRuntimeManifest.java
@@ -107,6 +107,7 @@ class TestRuntimeManifest {
assertFalse(listHdfsDefinition.getSupportsDynamicProperties());
assertFalse(listHdfsDefinition.getSupportsDynamicRelationships());
assertEquals(InputRequirement.Requirement.INPUT_FORBIDDEN, listHdfsDefinition.getInputRequirement());
+ assertTrue(listHdfsDefinition.isAdditionalDetails());
assertEquals("30 sec", listHdfsDefinition.getDefaultPenaltyDuration());
assertEquals("1 sec", listHdfsDefinition.getDefaultYieldDuration());
@@ -159,6 +160,7 @@ class TestRuntimeManifest {
"org.apache.nifi.processors.hadoop.FetchHDFS");
assertNotNull(fetchHdfsDefinition.isRestricted());
assertTrue(fetchHdfsDefinition.isRestricted());
+ assertFalse(fetchHdfsDefinition.isAdditionalDetails());
final Set restrictions = fetchHdfsDefinition.getExplicitRestrictions();
assertNotNull(restrictions);
@@ -171,6 +173,7 @@ class TestRuntimeManifest {
// Verify ConsumeKafka_2_6 definition which has properties with dependencies
final ProcessorDefinition consumeKafkaDefinition = getProcessorDefinition(bundles, "nifi-kafka-2-6-nar",
"org.apache.nifi.processors.kafka.pubsub.ConsumeKafka_2_6");
+ assertTrue(consumeKafkaDefinition.isAdditionalDetails());
final PropertyDescriptor maxUncommitProp = getPropertyDescriptor(consumeKafkaDefinition, "max-uncommit-offset-wait");
final List propertyDependencies = maxUncommitProp.getDependencies();
diff --git a/nifi-manifest/nifi-runtime-manifest/pom.xml b/nifi-manifest/nifi-runtime-manifest/pom.xml
index e36f9d29c3..87d69216d0 100644
--- a/nifi-manifest/nifi-runtime-manifest/pom.xml
+++ b/nifi-manifest/nifi-runtime-manifest/pom.xml
@@ -92,7 +92,6 @@
nifi-assembly
manifests
- **/extension-manifest.xml
true
${extension.manifest.unpack.dir}
true
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/manifest/StandardRuntimeManifestService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/manifest/StandardRuntimeManifestService.java
index 36fa80cb62..fe016be9c1 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/manifest/StandardRuntimeManifestService.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/manifest/StandardRuntimeManifestService.java
@@ -24,6 +24,7 @@ import org.apache.nifi.extension.manifest.ExtensionManifest;
import org.apache.nifi.extension.manifest.parser.ExtensionManifestParser;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.NarClassLoadersHolder;
+import org.apache.nifi.runtime.manifest.ExtensionManifestContainer;
import org.apache.nifi.runtime.manifest.RuntimeManifestBuilder;
import org.apache.nifi.runtime.manifest.impl.SchedulingDefaultsFactory;
import org.apache.nifi.runtime.manifest.impl.StandardRuntimeManifestBuilder;
@@ -32,11 +33,16 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
public class StandardRuntimeManifestService implements RuntimeManifestService {
@@ -90,12 +96,25 @@ public class StandardRuntimeManifestService implements RuntimeManifestService {
return manifestBuilder.build();
}
- private Optional getExtensionManifest(final Bundle bundle) {
+ private Optional getExtensionManifest(final Bundle bundle) {
final BundleDetails bundleDetails = bundle.getBundleDetails();
+ try {
+ final ExtensionManifest extensionManifest = loadExtensionManifest(bundleDetails);
+ final Map additionalDetails = loadAdditionalDetails(bundleDetails);
+
+ final ExtensionManifestContainer container = new ExtensionManifestContainer(extensionManifest, additionalDetails);
+ return Optional.of(container);
+ } catch (final IOException e) {
+ LOGGER.error("Unable to load extension manifest for bundle [{}]", bundleDetails.getCoordinate(), e);
+ return Optional.empty();
+ }
+ }
+
+ private ExtensionManifest loadExtensionManifest(final BundleDetails bundleDetails) throws IOException {
final File manifestFile = new File(bundleDetails.getWorkingDirectory(), "META-INF/docs/extension-manifest.xml");
if (!manifestFile.exists()) {
- LOGGER.warn("Unable to find extension manifest for [{}] at [{}]...", bundleDetails.getCoordinate(), manifestFile.getAbsolutePath());
- return Optional.empty();
+ throw new FileNotFoundException("Extension manifest files does not exist for "
+ + bundleDetails.getCoordinate() + " at " + manifestFile.getAbsolutePath());
}
try (final InputStream inputStream = new FileInputStream(manifestFile)) {
@@ -105,13 +124,45 @@ public class StandardRuntimeManifestService implements RuntimeManifestService {
extensionManifest.setGroupId(bundleDetails.getCoordinate().getGroup());
extensionManifest.setArtifactId(bundleDetails.getCoordinate().getId());
extensionManifest.setVersion(bundleDetails.getCoordinate().getVersion());
- return Optional.of(extensionManifest);
- } catch (final IOException e) {
- LOGGER.error("Unable to load extension manifest for bundle [{}]", bundleDetails.getCoordinate(), e);
- return Optional.empty();
+ return extensionManifest;
}
}
+ private Map loadAdditionalDetails(final BundleDetails bundleDetails) {
+ final Map additionalDetailsMap = new LinkedHashMap<>();
+
+ final File additionalDetailsDir = new File(bundleDetails.getWorkingDirectory(), "META-INF/docs/additional-details");
+ if (!additionalDetailsDir.exists()) {
+ LOGGER.debug("No additional-details directory found under [{}]", bundleDetails.getWorkingDirectory().getAbsolutePath());
+ return additionalDetailsMap;
+ }
+
+ for (final File additionalDetailsTypeDir : additionalDetailsDir.listFiles()) {
+ if (!additionalDetailsTypeDir.isDirectory()) {
+ LOGGER.debug("Skipping [{}], not a directory...", additionalDetailsTypeDir.getAbsolutePath());
+ continue;
+ }
+
+ final File additionalDetailsFile = new File(additionalDetailsTypeDir, "additionalDetails.html");
+ if (!additionalDetailsFile.exists()) {
+ LOGGER.debug("No additionalDetails.html found under [{}]", additionalDetailsTypeDir.getAbsolutePath());
+ continue;
+ }
+
+ try {
+ final String typeName = additionalDetailsTypeDir.getName();
+ final String additionalDetailsContent = Files.lines(additionalDetailsFile.toPath()).collect(Collectors.joining());
+ LOGGER.debug("Added additionalDetails for {} from {}", typeName, additionalDetailsFile.getAbsolutePath());
+ additionalDetailsMap.put(typeName, additionalDetailsContent);
+ } catch (final IOException e) {
+ throw new RuntimeException("Unable to load additional details content for "
+ + additionalDetailsFile.getAbsolutePath() + " due to: " + e.getMessage(), e);
+ }
+ }
+
+ return additionalDetailsMap;
+ }
+
// Visible for overriding from tests
Bundle getFrameworkBundle() {
return NarClassLoadersHolder.getInstance().getFrameworkBundle();