You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by jg...@apache.org on 2022/03/10 12:52:27 UTC

[nifi] branch main updated: NIFI-9775 Create RuntimeManifestService

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

jgresock 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 546f986  NIFI-9775 Create RuntimeManifestService
546f986 is described below

commit 546f986603458062d9676b8511f8e566d0748f26
Author: Bryan Bende <bb...@gmail.com>
AuthorDate: Tue Mar 8 11:13:44 2022 -0500

    NIFI-9775 Create RuntimeManifestService
    
    Signed-off-by: Joe Gresock <jg...@gmail.com>
    
    This closes #5849.
---
 .../nifi/manifest/RuntimeManifestService.java      |  31 +++++
 .../manifest/StandardRuntimeManifestService.java   | 111 ++++++++++++++++++
 .../src/main/resources/nifi-context.xml            |   9 ++
 .../StandardRuntimeManifestServiceTest.java        | 125 +++++++++++++++++++++
 .../META-INF/docs/extension-manifest.xml           |  19 ++++
 .../META-INF/docs/extension-manifest.xml           |  87 ++++++++++++++
 .../nifi/web/controller/ControllerFacade.java      |  69 +-----------
 .../src/main/resources/nifi-web-api-context.xml    |   3 +-
 8 files changed, 388 insertions(+), 66 deletions(-)

diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/manifest/RuntimeManifestService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/manifest/RuntimeManifestService.java
new file mode 100644
index 0000000..7f02125
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/manifest/RuntimeManifestService.java
@@ -0,0 +1,31 @@
+/*
+ * 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.manifest;
+
+import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
+
+/**
+ * Produces a RuntimeManifest for the current NiFi instance.
+ */
+public interface RuntimeManifestService {
+
+    /**
+     * @return the RuntimeManifest
+     */
+    RuntimeManifest getManifest();
+
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..74473a2
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/manifest/StandardRuntimeManifestService.java
@@ -0,0 +1,111 @@
+/*
+ * 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.manifest;
+
+import org.apache.nifi.bundle.Bundle;
+import org.apache.nifi.bundle.BundleDetails;
+import org.apache.nifi.c2.protocol.component.api.BuildInfo;
+import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
+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.RuntimeManifestBuilder;
+import org.apache.nifi.runtime.manifest.impl.SchedulingDefaultsFactory;
+import org.apache.nifi.runtime.manifest.impl.StandardRuntimeManifestBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.Optional;
+import java.util.Set;
+
+public class StandardRuntimeManifestService implements RuntimeManifestService {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(StandardRuntimeManifestService.class);
+
+    private static final String RUNTIME_MANIFEST_IDENTIFIER = "nifi";
+    private static final String RUNTIME_TYPE = "nifi";
+
+    private final ExtensionManager extensionManager;
+    private final ExtensionManifestParser extensionManifestParser;
+
+    public StandardRuntimeManifestService(final ExtensionManager extensionManager, final ExtensionManifestParser extensionManifestParser) {
+        this.extensionManager = extensionManager;
+        this.extensionManifestParser = extensionManifestParser;
+    }
+
+    @Override
+    public RuntimeManifest getManifest() {
+        final Set<Bundle> allBundles = extensionManager.getAllBundles();
+
+        final Bundle frameworkBundle = getFrameworkBundle();
+        final BundleDetails frameworkDetails = frameworkBundle.getBundleDetails();
+        final Date frameworkBuildDate = frameworkDetails.getBuildTimestampDate();
+
+        final BuildInfo buildInfo = new BuildInfo();
+        buildInfo.setVersion(frameworkDetails.getCoordinate().getVersion());
+        buildInfo.setRevision(frameworkDetails.getBuildRevision());
+        buildInfo.setCompiler(frameworkDetails.getBuildJdk());
+        buildInfo.setTimestamp(frameworkBuildDate == null ? null : frameworkBuildDate.getTime());
+
+        final RuntimeManifestBuilder manifestBuilder = new StandardRuntimeManifestBuilder()
+                .identifier(RUNTIME_MANIFEST_IDENTIFIER)
+                .runtimeType(RUNTIME_TYPE)
+                .version(buildInfo.getVersion())
+                .schedulingDefaults(SchedulingDefaultsFactory.getNifiSchedulingDefaults())
+                .buildInfo(buildInfo);
+
+        for (final Bundle bundle : allBundles) {
+            getExtensionManifest(bundle).ifPresent(em -> manifestBuilder.addBundle(em));
+        }
+
+        return manifestBuilder.build();
+    }
+
+    private Optional<ExtensionManifest> getExtensionManifest(final Bundle bundle) {
+        final BundleDetails bundleDetails = bundle.getBundleDetails();
+        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();
+        }
+
+        try (final InputStream inputStream = new FileInputStream(manifestFile)) {
+            final ExtensionManifest extensionManifest = extensionManifestParser.parse(inputStream);
+            // Newer NARs will have these fields populated in extension-manifest.xml, but older NARs will not, so we can
+            // set the values from the BundleCoordinate which already has the group, artifact id, and version
+            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();
+        }
+    }
+
+    // Visible for overriding from tests
+    Bundle getFrameworkBundle() {
+        return NarClassLoadersHolder.getInstance().getFrameworkBundle();
+    }
+
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/nifi-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/nifi-context.xml
index 040329b..fe8058e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/nifi-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/nifi-context.xml
@@ -67,6 +67,15 @@
         <property name="authorizer" ref="authorizer" />
     </bean>
 
+    <!-- extension manifest parser -->
+    <bean id="extensionManifestParser" class="org.apache.nifi.extension.manifest.parser.jaxb.JAXBExtensionManifestParser" />
+
+    <!-- runtime manifest generator -->
+    <bean id="runtimeManifestService" class="org.apache.nifi.manifest.StandardRuntimeManifestService" >
+        <constructor-arg ref="extensionManager" />
+        <constructor-arg ref="extensionManifestParser" />
+    </bean>
+
     <!-- bulletin repository -->
     <bean id="bulletinRepository" class="org.apache.nifi.events.VolatileBulletinRepository" />
 
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/manifest/StandardRuntimeManifestServiceTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/manifest/StandardRuntimeManifestServiceTest.java
new file mode 100644
index 0000000..d644f64
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/manifest/StandardRuntimeManifestServiceTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.manifest;
+
+import org.apache.nifi.bundle.Bundle;
+import org.apache.nifi.bundle.BundleCoordinate;
+import org.apache.nifi.bundle.BundleDetails;
+import org.apache.nifi.c2.protocol.component.api.ComponentManifest;
+import org.apache.nifi.c2.protocol.component.api.ControllerServiceDefinition;
+import org.apache.nifi.c2.protocol.component.api.ProcessorDefinition;
+import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
+import org.apache.nifi.extension.manifest.parser.ExtensionManifestParser;
+import org.apache.nifi.extension.manifest.parser.jaxb.JAXBExtensionManifestParser;
+import org.apache.nifi.nar.ExtensionManager;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class StandardRuntimeManifestServiceTest {
+
+    private Bundle frameworkBundle;
+    private Bundle testComponentsBundle;
+    private ExtensionManager extensionManager;
+    private ExtensionManifestParser extensionManifestParser;
+    private RuntimeManifestService runtimeManifestService;
+
+    @BeforeEach
+    public void setup() {
+        final BundleDetails frameworkBundleDetails = new BundleDetails.Builder()
+                .coordinate(new BundleCoordinate("org.apache.nifi", "nifi-framework-nar", "1.16.0"))
+                .buildJdk("1.8.0")
+                .buildRevision("revision1")
+                .buildTimestamp("2022-03-07T08:54:46Z")
+                .workingDir(new File("src/test/resources/TestRuntimeManifest/nifi-framework-nar"))
+                .build();
+
+        frameworkBundle = new Bundle(frameworkBundleDetails, this.getClass().getClassLoader());
+
+        final BundleDetails testComponentsBundleDetails = new BundleDetails.Builder()
+                .coordinate(new BundleCoordinate("org.apache.nifi", "nifi-test-components-nar", "1.16.0"))
+                .buildJdk("1.8.0")
+                .buildRevision("revision1")
+                .buildTimestamp("2022-03-07T08:54:46Z")
+                .workingDir(new File("src/test/resources/TestRuntimeManifest/nifi-test-components-nar"))
+                .build();
+
+        testComponentsBundle = new Bundle(testComponentsBundleDetails, this.getClass().getClassLoader());
+
+        extensionManager = mock(ExtensionManager.class);
+        extensionManifestParser = new JAXBExtensionManifestParser();
+        runtimeManifestService = new TestableRuntimeManifestService(extensionManager, extensionManifestParser, frameworkBundle);
+    }
+
+    @Test
+    public void testGetRuntimeManifest() {
+        when(extensionManager.getAllBundles()).thenReturn(new HashSet<>(Arrays.asList(testComponentsBundle)));
+
+        final RuntimeManifest runtimeManifest = runtimeManifestService.getManifest();
+        assertNotNull(runtimeManifest);
+        assertEquals(frameworkBundle.getBundleDetails().getCoordinate().getVersion(), runtimeManifest.getVersion());
+
+        assertEquals(frameworkBundle.getBundleDetails().getCoordinate().getVersion(), runtimeManifest.getBuildInfo().getVersion());
+        assertEquals(frameworkBundle.getBundleDetails().getBuildRevision(), runtimeManifest.getBuildInfo().getRevision());
+        assertEquals(frameworkBundle.getBundleDetails().getBuildJdk(), runtimeManifest.getBuildInfo().getCompiler());
+
+        final List<org.apache.nifi.c2.protocol.component.api.Bundle> bundles = runtimeManifest.getBundles();
+        assertNotNull(bundles);
+        assertEquals(1, bundles.size());
+
+        final org.apache.nifi.c2.protocol.component.api.Bundle testComponentsManifestBundle = bundles.get(0);
+        assertEquals(testComponentsBundle.getBundleDetails().getCoordinate().getGroup(), testComponentsManifestBundle.getGroup());
+        assertEquals(testComponentsBundle.getBundleDetails().getCoordinate().getId(), testComponentsManifestBundle.getArtifact());
+        assertEquals(testComponentsBundle.getBundleDetails().getCoordinate().getVersion(), testComponentsManifestBundle.getVersion());
+
+        final ComponentManifest testComponentsManifest = testComponentsManifestBundle.getComponentManifest();
+        assertNotNull(testComponentsManifest);
+
+        final List<ProcessorDefinition> processorDefinitions = testComponentsManifest.getProcessors();
+        assertEquals(3, processorDefinitions.size());
+
+        final List<ControllerServiceDefinition> controllerServiceDefinitions = testComponentsManifest.getControllerServices();
+        assertEquals(1, controllerServiceDefinitions.size());
+    }
+
+    /**
+     * Override getFrameworkBundle to provide a mocked Bundle.
+     */
+    private static class TestableRuntimeManifestService extends StandardRuntimeManifestService {
+
+        private Bundle frameworkBundle;
+
+        public TestableRuntimeManifestService(final ExtensionManager extensionManager, final ExtensionManifestParser extensionManifestParser, final Bundle frameworkBundle) {
+            super(extensionManager, extensionManifestParser);
+            this.frameworkBundle = frameworkBundle;
+        }
+
+        @Override
+        Bundle getFrameworkBundle() {
+            return frameworkBundle;
+        }
+    }
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/TestRuntimeManifest/nifi-framework-nar/META-INF/docs/extension-manifest.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/TestRuntimeManifest/nifi-framework-nar/META-INF/docs/extension-manifest.xml
new file mode 100644
index 0000000..a67c2d2
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/TestRuntimeManifest/nifi-framework-nar/META-INF/docs/extension-manifest.xml
@@ -0,0 +1,19 @@
+<!--
+  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.
+-->
+<extensionManifest>
+    <systemApiVersion>1.8.0</systemApiVersion>
+    <extensions>
+    </extensions>
+</extensionManifest>
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/TestRuntimeManifest/nifi-test-components-nar/META-INF/docs/extension-manifest.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/TestRuntimeManifest/nifi-test-components-nar/META-INF/docs/extension-manifest.xml
new file mode 100644
index 0000000..d2d99a0
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/TestRuntimeManifest/nifi-test-components-nar/META-INF/docs/extension-manifest.xml
@@ -0,0 +1,87 @@
+<!--
+  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.
+-->
+<extensionManifest>
+    <systemApiVersion>1.8.0</systemApiVersion>
+    <extensions>
+        <extension>
+            <name>org.apache.nifi.processors.TestProcessor1</name>
+            <type>PROCESSOR</type>
+            <description>Test processor 1.</description>
+            <tags>
+                <tag>test</tag>
+                <tag>processor</tag>
+            </tags>
+            <triggerSerially>true</triggerSerially>
+            <triggerWhenEmpty>true</triggerWhenEmpty>
+            <triggerWhenAnyDestinationAvailable>true</triggerWhenAnyDestinationAvailable>
+            <primaryNodeOnly>true</primaryNodeOnly>
+            <supportsBatching>true</supportsBatching>
+            <eventDriven>true</eventDriven>
+            <sideEffectFree>true</sideEffectFree>
+            <defaultSettings>
+                <yieldDuration>10 secs</yieldDuration>
+                <penaltyDuration>20 secs</penaltyDuration>
+                <bulletinLevel>DEBUG</bulletinLevel>
+            </defaultSettings>
+            <defaultSchedule>
+                <strategy>CRON_DRIVEN</strategy>
+                <period>* 1 * * *</period>
+                <concurrentTasks>5</concurrentTasks>
+            </defaultSchedule>
+        </extension>
+        <extension>
+            <name>org.apache.nifi.processors.TestProcessor2</name>
+            <type>PROCESSOR</type>
+            <description>Test processor 2.</description>
+            <tags>
+                <tag>test</tag>
+                <tag>processor</tag>
+            </tags>
+            <restricted>
+                <restrictions>
+                    <restriction>
+                        <requiredPermission>write filesystem</requiredPermission>
+                        <explanation>Test explanation.</explanation>
+                    </restriction>
+                </restrictions>
+            </restricted>
+        </extension>
+        <extension>
+            <name>org.apache.nifi.processors.TestProcessor3</name>
+            <type>PROCESSOR</type>
+            <description/>
+            <tags>
+            </tags>
+        </extension>
+        <extension>
+            <name>org.apache.nifi.service.TestServiceImpl</name>
+            <type>CONTROLLER_SERVICE</type>
+            <deprecationNotice/>
+            <description>Test service.</description>
+            <tags>
+                <tag>test</tag>
+                <tag>service</tag>
+            </tags>
+            <providedServiceAPIs>
+                <providedServiceAPI>
+                    <className>org.apache.nifi.service.TestService</className>
+                    <groupId>org.apache.nifi</groupId>
+                    <artifactId>nifi-test-service-api-nar</artifactId>
+                    <version>1.0.0</version>
+                </providedServiceAPI>
+            </providedServiceAPIs>
+        </extension>
+    </extensions>
+</extensionManifest>
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
index 4a34aa6..a762d8a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
@@ -31,8 +31,6 @@ import org.apache.nifi.authorization.user.NiFiUser;
 import org.apache.nifi.authorization.user.NiFiUserUtils;
 import org.apache.nifi.bundle.Bundle;
 import org.apache.nifi.bundle.BundleCoordinate;
-import org.apache.nifi.bundle.BundleDetails;
-import org.apache.nifi.c2.protocol.component.api.BuildInfo;
 import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
 import org.apache.nifi.cluster.protocol.NodeIdentifier;
 import org.apache.nifi.components.ConfigurableComponent;
@@ -64,17 +62,15 @@ import org.apache.nifi.controller.status.analytics.StatusAnalytics;
 import org.apache.nifi.controller.status.analytics.StatusAnalyticsEngine;
 import org.apache.nifi.controller.status.history.StatusHistoryRepository;
 import org.apache.nifi.diagnostics.SystemDiagnostics;
-import org.apache.nifi.extension.manifest.ExtensionManifest;
-import org.apache.nifi.extension.manifest.parser.ExtensionManifestParser;
 import org.apache.nifi.flow.VersionedProcessGroup;
 import org.apache.nifi.flowfile.FlowFilePrioritizer;
 import org.apache.nifi.flowfile.attributes.CoreAttributes;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.ProcessGroupCounts;
 import org.apache.nifi.groups.RemoteProcessGroup;
+import org.apache.nifi.manifest.RuntimeManifestService;
 import org.apache.nifi.nar.ExtensionDefinition;
 import org.apache.nifi.nar.ExtensionManager;
-import org.apache.nifi.nar.NarClassLoadersHolder;
 import org.apache.nifi.processor.Processor;
 import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.provenance.ProvenanceEventRecord;
@@ -91,9 +87,6 @@ import org.apache.nifi.remote.PublicPort;
 import org.apache.nifi.remote.RemoteGroupPort;
 import org.apache.nifi.reporting.BulletinRepository;
 import org.apache.nifi.reporting.ReportingTask;
-import org.apache.nifi.runtime.manifest.RuntimeManifestBuilder;
-import org.apache.nifi.runtime.manifest.impl.SchedulingDefaultsFactory;
-import org.apache.nifi.runtime.manifest.impl.StandardRuntimeManifestBuilder;
 import org.apache.nifi.services.FlowService;
 import org.apache.nifi.util.BundleUtils;
 import org.apache.nifi.util.FormatUtils;
@@ -126,8 +119,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.WebApplicationException;
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.text.Collator;
@@ -141,7 +132,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.TimeZone;
@@ -155,9 +145,6 @@ public class ControllerFacade implements Authorizable {
 
     private static final Logger logger = LoggerFactory.getLogger(ControllerFacade.class);
 
-    private static final String RUNTIME_MANIFEST_IDENTIFIER = "nifi";
-    private static final String RUNTIME_TYPE = "nifi";
-
     // nifi components
     private FlowController flowController;
     private FlowService flowService;
@@ -168,7 +155,7 @@ public class ControllerFacade implements Authorizable {
     private DtoFactory dtoFactory;
     private SearchQueryParser searchQueryParser;
     private ControllerSearchService controllerSearchService;
-    private ExtensionManifestParser extensionManifestParser;
+    private RuntimeManifestService runtimeManifestService;
 
     private ProcessGroup getRootGroup() {
         return flowController.getFlowManager().getRootGroup();
@@ -587,53 +574,7 @@ public class ControllerFacade implements Authorizable {
      * @return the runtime manifest
      */
     public RuntimeManifest getRuntimeManifest() {
-        final ExtensionManager extensionManager = getExtensionManager();
-        final Set<Bundle> allBundles = extensionManager.getAllBundles();
-
-        final Bundle frameworkBundle = NarClassLoadersHolder.getInstance().getFrameworkBundle();
-        final BundleDetails frameworkDetails = frameworkBundle.getBundleDetails();
-        final Date frameworkBuildDate = frameworkDetails.getBuildTimestampDate();
-
-        final BuildInfo buildInfo = new BuildInfo();
-        buildInfo.setVersion(frameworkDetails.getCoordinate().getVersion());
-        buildInfo.setRevision(frameworkDetails.getBuildRevision());
-        buildInfo.setCompiler(frameworkDetails.getBuildJdk());
-        buildInfo.setTimestamp(frameworkBuildDate == null ? null : frameworkBuildDate.getTime());
-
-        final RuntimeManifestBuilder manifestBuilder = new StandardRuntimeManifestBuilder()
-                .identifier(RUNTIME_MANIFEST_IDENTIFIER)
-                .runtimeType(RUNTIME_TYPE)
-                .version(buildInfo.getVersion())
-                .schedulingDefaults(SchedulingDefaultsFactory.getNifiSchedulingDefaults())
-                .buildInfo(buildInfo);
-
-        for (final Bundle bundle : allBundles) {
-            getExtensionManifest(bundle).ifPresent(em -> manifestBuilder.addBundle(em));
-        }
-
-        return manifestBuilder.build();
-    }
-
-    private Optional<ExtensionManifest> getExtensionManifest(final Bundle bundle) {
-        final BundleDetails bundleDetails = bundle.getBundleDetails();
-        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();
-        }
-
-        try (final InputStream inputStream = new FileInputStream(manifestFile)) {
-            final ExtensionManifest extensionManifest = extensionManifestParser.parse(inputStream);
-            // Newer NARs will have these fields populated in extension-manifest.xml, but older NARs will not, so we can
-            // set the values from the BundleCoordinate which already has the group, artifact id, and version
-            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 runtimeManifestService.getManifest();
     }
 
     /**
@@ -1760,7 +1701,7 @@ public class ControllerFacade implements Authorizable {
         this.controllerSearchService = controllerSearchService;
     }
 
-    public void setExtensionManifestParser(ExtensionManifestParser extensionManifestParser) {
-        this.extensionManifestParser = extensionManifestParser;
+    public void setRuntimeManifestService(RuntimeManifestService runtimeManifestService) {
+        this.runtimeManifestService = runtimeManifestService;
     }
 }
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index 6ba3479..7b06418 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -290,7 +290,6 @@
         <property name="matcherForLabel" ref="matcherForLabel" />
         <property name="matcherForControllerServiceNode" ref="matcherForControllerServiceNode" />
     </bean>
-    <bean id="extensionManifestParser" class="org.apache.nifi.extension.manifest.parser.jaxb.JAXBExtensionManifestParser" />
     <bean id="controllerFacade" class="org.apache.nifi.web.controller.ControllerFacade">
         <property name="flowController" ref="flowController"/>
         <property name="flowService" ref="flowService"/>
@@ -299,7 +298,7 @@
         <property name="dtoFactory" ref="dtoFactory"/>
         <property name="searchQueryParser" ref="searchQueryParser"/>
         <property name="controllerSearchService" ref="controllerSearchService"/>
-        <property name="extensionManifestParser" ref="extensionManifestParser" />
+        <property name="runtimeManifestService" ref="runtimeManifestService" />
     </bean>
     <bean id="authorizableLookup" class="org.apache.nifi.authorization.StandardAuthorizableLookup">
         <property name="controllerFacade" ref="controllerFacade"/>