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

mattyb149 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 334c9db9c3 NIFI-10915 Add additionalDetails content to runtime 
manifest - Update manifests assembly to include the additionaDetails.html files 
- Update C2 ExtensionDefinition to add a new field for additional details 
content - Update manifest generation to read the additional details and pass 
through to C2 runtime manifest - Switch to boolean to indicate presence of 
additional details, instead of including content
334c9db9c3 is described below

commit 334c9db9c3c468af2cdcfd247f5af8fdc48d8947
Author: Bryan Bende <[email protected]>
AuthorDate: Mon Dec 5 17:26:12 2022 -0500

    NIFI-10915 Add additionalDetails content to runtime manifest
    - Update manifests assembly to include the additionaDetails.html files
    - Update C2 ExtensionDefinition to add a new field for additional details 
content
    - Update manifest generation to read the additional details and pass 
through to C2 runtime manifest
    - Switch to boolean to indicate presence of additional details, instead of 
including content
    
    Add additionalDetails files to nifi-runtime-manifest artifact, organized by 
group/artifact/version/type
    
    Signed-off-by: Matthew Burgess <[email protected]>
    
    This closes #6767
---
 .../protocol/component/api/ExtensionComponent.java | 12 ++++
 nifi-assembly/pom.xml                              |  5 +-
 ...ovider.java => ExtensionManifestContainer.java} | 30 ++++++++--
 .../manifest/ExtensionManifestProvider.java        |  4 +-
 .../runtime/manifest/RuntimeManifestBuilder.java   |  5 +-
 .../impl/DirectoryExtensionManifestProvider.java   | 55 ++++++++++++++++--
 .../manifest/impl/RuntimeManifestGenerator.java    | 35 +++++++++++-
 .../impl/StandardRuntimeManifestBuilder.java       | 46 ++++++++++-----
 .../nifi/runtime/manifest/TestRuntimeManifest.java |  3 +
 nifi-manifest/nifi-runtime-manifest/pom.xml        |  1 -
 .../manifest/StandardRuntimeManifestService.java   | 65 +++++++++++++++++++---
 11 files changed, 217 insertions(+), 44 deletions(-)

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. -->
                         <phase>generate-resources</phase>
                         <configuration>
                             <includeTypes>nar</includeTypes>
-                            <includes>**/extension-manifest.xml</includes>
+                            <includes>**/docs/**</includes>
                             <excludeTransitive>false</excludeTransitive>
                             
<outputDirectory>${project.build.directory}/extension-manifests</outputDirectory>
                             
<useSubDirectoryPerArtifact>true</useSubDirectoryPerArtifact>
                             <stripClassifier>true</stripClassifier>
                             <stripVersion>true</stripVersion>
                             <silent>true</silent>
-                            <fileMappers>
-                                
<org.codehaus.plexus.components.io.filemappers.FlattenFileMapper />
-                            </fileMappers>
                         </configuration>
                     </execution>
                 </executions>
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/ExtensionManifestContainer.java
similarity index 50%
copy from 
nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/ExtensionManifestProvider.java
copy to 
nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/ExtensionManifestContainer.java
index 4039ed0c4e..16b91bf193 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/ExtensionManifestContainer.java
@@ -18,13 +18,31 @@ package org.apache.nifi.runtime.manifest;
 
 import org.apache.nifi.extension.manifest.ExtensionManifest;
 
-import java.util.List;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
 
-/**
- * Provides a list of extension manifests.
- */
-public interface ExtensionManifestProvider {
+public class ExtensionManifestContainer {
+
+    private final ExtensionManifest manifest;
+    private final Map<String, String> additionalDetails;
+
+    public ExtensionManifestContainer(final ExtensionManifest manifest) {
+        this(manifest, null);
+    }
+
+    public ExtensionManifestContainer(final ExtensionManifest manifest, final 
Map<String, String> additionalDetails) {
+        this.manifest = Objects.requireNonNull(manifest);
+        this.additionalDetails = Collections.unmodifiableMap(additionalDetails 
== null
+                ? Collections.emptyMap() : new 
LinkedHashMap<>(additionalDetails));
+    }
 
-    List<ExtensionManifest> getExtensionManifests();
+    public ExtensionManifest getManifest() {
+        return manifest;
+    }
 
+    public Map<String, String> 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<ExtensionManifest> getExtensionManifests();
+    List<ExtensionManifestContainer> 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<ExtensionManifest> 
extensionManifests);
+    RuntimeManifestBuilder addBundles(Iterable<ExtensionManifestContainer> 
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<ExtensionManifest> getExtensionManifests() {
+    public List<ExtensionManifestContainer> 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<ExtensionManifest> extensionManifests = new ArrayList<>();
+        final List<ExtensionManifestContainer> 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<String, String> 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<String, String> loadAdditionalDetails(final File manifestDir) {
+        final Map<String, String> 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<ExtensionManifestContainer> 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<String, String> 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<String, String> 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<ExtensionManifest> 
extensionManifests) {
+    public RuntimeManifestBuilder addBundles(final 
Iterable<ExtensionManifestContainer> 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<String> 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<Restriction> 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<PropertyDependency> 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 @@
                         <configuration>
                             
<includeArtifactIds>nifi-assembly</includeArtifactIds>
                             <includeClassifiers>manifests</includeClassifiers>
-                            <includes>**/extension-manifest.xml</includes>
                             <excludeTransitive>true</excludeTransitive>
                             
<outputDirectory>${extension.manifest.unpack.dir}</outputDirectory>
                             <silent>true</silent>
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<ExtensionManifest> getExtensionManifest(final Bundle 
bundle) {
+    private Optional<ExtensionManifestContainer> getExtensionManifest(final 
Bundle bundle) {
         final BundleDetails bundleDetails = bundle.getBundleDetails();
+        try {
+            final ExtensionManifest extensionManifest = 
loadExtensionManifest(bundleDetails);
+            final Map<String, String> 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<String, String> loadAdditionalDetails(final BundleDetails 
bundleDetails) {
+        final Map<String, String> 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();


Reply via email to