Skip to content

Commit 546f986

Browse files
bbendegresockj
authored andcommitted
NIFI-9775 Create RuntimeManifestService
Signed-off-by: Joe Gresock <jgresock@gmail.com> This closes #5849.
1 parent 92202a5 commit 546f986

File tree

8 files changed

+388
-66
lines changed

8 files changed

+388
-66
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.nifi.manifest;
18+
19+
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
20+
21+
/**
22+
* Produces a RuntimeManifest for the current NiFi instance.
23+
*/
24+
public interface RuntimeManifestService {
25+
26+
/**
27+
* @return the RuntimeManifest
28+
*/
29+
RuntimeManifest getManifest();
30+
31+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.nifi.manifest;
18+
19+
import org.apache.nifi.bundle.Bundle;
20+
import org.apache.nifi.bundle.BundleDetails;
21+
import org.apache.nifi.c2.protocol.component.api.BuildInfo;
22+
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
23+
import org.apache.nifi.extension.manifest.ExtensionManifest;
24+
import org.apache.nifi.extension.manifest.parser.ExtensionManifestParser;
25+
import org.apache.nifi.nar.ExtensionManager;
26+
import org.apache.nifi.nar.NarClassLoadersHolder;
27+
import org.apache.nifi.runtime.manifest.RuntimeManifestBuilder;
28+
import org.apache.nifi.runtime.manifest.impl.SchedulingDefaultsFactory;
29+
import org.apache.nifi.runtime.manifest.impl.StandardRuntimeManifestBuilder;
30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
32+
33+
import java.io.File;
34+
import java.io.FileInputStream;
35+
import java.io.IOException;
36+
import java.io.InputStream;
37+
import java.util.Date;
38+
import java.util.Optional;
39+
import java.util.Set;
40+
41+
public class StandardRuntimeManifestService implements RuntimeManifestService {
42+
43+
private static final Logger LOGGER = LoggerFactory.getLogger(StandardRuntimeManifestService.class);
44+
45+
private static final String RUNTIME_MANIFEST_IDENTIFIER = "nifi";
46+
private static final String RUNTIME_TYPE = "nifi";
47+
48+
private final ExtensionManager extensionManager;
49+
private final ExtensionManifestParser extensionManifestParser;
50+
51+
public StandardRuntimeManifestService(final ExtensionManager extensionManager, final ExtensionManifestParser extensionManifestParser) {
52+
this.extensionManager = extensionManager;
53+
this.extensionManifestParser = extensionManifestParser;
54+
}
55+
56+
@Override
57+
public RuntimeManifest getManifest() {
58+
final Set<Bundle> allBundles = extensionManager.getAllBundles();
59+
60+
final Bundle frameworkBundle = getFrameworkBundle();
61+
final BundleDetails frameworkDetails = frameworkBundle.getBundleDetails();
62+
final Date frameworkBuildDate = frameworkDetails.getBuildTimestampDate();
63+
64+
final BuildInfo buildInfo = new BuildInfo();
65+
buildInfo.setVersion(frameworkDetails.getCoordinate().getVersion());
66+
buildInfo.setRevision(frameworkDetails.getBuildRevision());
67+
buildInfo.setCompiler(frameworkDetails.getBuildJdk());
68+
buildInfo.setTimestamp(frameworkBuildDate == null ? null : frameworkBuildDate.getTime());
69+
70+
final RuntimeManifestBuilder manifestBuilder = new StandardRuntimeManifestBuilder()
71+
.identifier(RUNTIME_MANIFEST_IDENTIFIER)
72+
.runtimeType(RUNTIME_TYPE)
73+
.version(buildInfo.getVersion())
74+
.schedulingDefaults(SchedulingDefaultsFactory.getNifiSchedulingDefaults())
75+
.buildInfo(buildInfo);
76+
77+
for (final Bundle bundle : allBundles) {
78+
getExtensionManifest(bundle).ifPresent(em -> manifestBuilder.addBundle(em));
79+
}
80+
81+
return manifestBuilder.build();
82+
}
83+
84+
private Optional<ExtensionManifest> getExtensionManifest(final Bundle bundle) {
85+
final BundleDetails bundleDetails = bundle.getBundleDetails();
86+
final File manifestFile = new File(bundleDetails.getWorkingDirectory(), "META-INF/docs/extension-manifest.xml");
87+
if (!manifestFile.exists()) {
88+
LOGGER.warn("Unable to find extension manifest for [{}] at [{}]...", bundleDetails.getCoordinate(), manifestFile.getAbsolutePath());
89+
return Optional.empty();
90+
}
91+
92+
try (final InputStream inputStream = new FileInputStream(manifestFile)) {
93+
final ExtensionManifest extensionManifest = extensionManifestParser.parse(inputStream);
94+
// Newer NARs will have these fields populated in extension-manifest.xml, but older NARs will not, so we can
95+
// set the values from the BundleCoordinate which already has the group, artifact id, and version
96+
extensionManifest.setGroupId(bundleDetails.getCoordinate().getGroup());
97+
extensionManifest.setArtifactId(bundleDetails.getCoordinate().getId());
98+
extensionManifest.setVersion(bundleDetails.getCoordinate().getVersion());
99+
return Optional.of(extensionManifest);
100+
} catch (final IOException e) {
101+
LOGGER.error("Unable to load extension manifest for bundle [{}]", bundleDetails.getCoordinate(), e);
102+
return Optional.empty();
103+
}
104+
}
105+
106+
// Visible for overriding from tests
107+
Bundle getFrameworkBundle() {
108+
return NarClassLoadersHolder.getInstance().getFrameworkBundle();
109+
}
110+
111+
}

nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/resources/nifi-context.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@
6767
<property name="authorizer" ref="authorizer" />
6868
</bean>
6969

70+
<!-- extension manifest parser -->
71+
<bean id="extensionManifestParser" class="org.apache.nifi.extension.manifest.parser.jaxb.JAXBExtensionManifestParser" />
72+
73+
<!-- runtime manifest generator -->
74+
<bean id="runtimeManifestService" class="org.apache.nifi.manifest.StandardRuntimeManifestService" >
75+
<constructor-arg ref="extensionManager" />
76+
<constructor-arg ref="extensionManifestParser" />
77+
</bean>
78+
7079
<!-- bulletin repository -->
7180
<bean id="bulletinRepository" class="org.apache.nifi.events.VolatileBulletinRepository" />
7281

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.nifi.manifest;
18+
19+
import org.apache.nifi.bundle.Bundle;
20+
import org.apache.nifi.bundle.BundleCoordinate;
21+
import org.apache.nifi.bundle.BundleDetails;
22+
import org.apache.nifi.c2.protocol.component.api.ComponentManifest;
23+
import org.apache.nifi.c2.protocol.component.api.ControllerServiceDefinition;
24+
import org.apache.nifi.c2.protocol.component.api.ProcessorDefinition;
25+
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
26+
import org.apache.nifi.extension.manifest.parser.ExtensionManifestParser;
27+
import org.apache.nifi.extension.manifest.parser.jaxb.JAXBExtensionManifestParser;
28+
import org.apache.nifi.nar.ExtensionManager;
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.Test;
31+
32+
import java.io.File;
33+
import java.util.Arrays;
34+
import java.util.HashSet;
35+
import java.util.List;
36+
37+
import static org.junit.jupiter.api.Assertions.assertEquals;
38+
import static org.junit.jupiter.api.Assertions.assertNotNull;
39+
import static org.mockito.Mockito.mock;
40+
import static org.mockito.Mockito.when;
41+
42+
public class StandardRuntimeManifestServiceTest {
43+
44+
private Bundle frameworkBundle;
45+
private Bundle testComponentsBundle;
46+
private ExtensionManager extensionManager;
47+
private ExtensionManifestParser extensionManifestParser;
48+
private RuntimeManifestService runtimeManifestService;
49+
50+
@BeforeEach
51+
public void setup() {
52+
final BundleDetails frameworkBundleDetails = new BundleDetails.Builder()
53+
.coordinate(new BundleCoordinate("org.apache.nifi", "nifi-framework-nar", "1.16.0"))
54+
.buildJdk("1.8.0")
55+
.buildRevision("revision1")
56+
.buildTimestamp("2022-03-07T08:54:46Z")
57+
.workingDir(new File("src/test/resources/TestRuntimeManifest/nifi-framework-nar"))
58+
.build();
59+
60+
frameworkBundle = new Bundle(frameworkBundleDetails, this.getClass().getClassLoader());
61+
62+
final BundleDetails testComponentsBundleDetails = new BundleDetails.Builder()
63+
.coordinate(new BundleCoordinate("org.apache.nifi", "nifi-test-components-nar", "1.16.0"))
64+
.buildJdk("1.8.0")
65+
.buildRevision("revision1")
66+
.buildTimestamp("2022-03-07T08:54:46Z")
67+
.workingDir(new File("src/test/resources/TestRuntimeManifest/nifi-test-components-nar"))
68+
.build();
69+
70+
testComponentsBundle = new Bundle(testComponentsBundleDetails, this.getClass().getClassLoader());
71+
72+
extensionManager = mock(ExtensionManager.class);
73+
extensionManifestParser = new JAXBExtensionManifestParser();
74+
runtimeManifestService = new TestableRuntimeManifestService(extensionManager, extensionManifestParser, frameworkBundle);
75+
}
76+
77+
@Test
78+
public void testGetRuntimeManifest() {
79+
when(extensionManager.getAllBundles()).thenReturn(new HashSet<>(Arrays.asList(testComponentsBundle)));
80+
81+
final RuntimeManifest runtimeManifest = runtimeManifestService.getManifest();
82+
assertNotNull(runtimeManifest);
83+
assertEquals(frameworkBundle.getBundleDetails().getCoordinate().getVersion(), runtimeManifest.getVersion());
84+
85+
assertEquals(frameworkBundle.getBundleDetails().getCoordinate().getVersion(), runtimeManifest.getBuildInfo().getVersion());
86+
assertEquals(frameworkBundle.getBundleDetails().getBuildRevision(), runtimeManifest.getBuildInfo().getRevision());
87+
assertEquals(frameworkBundle.getBundleDetails().getBuildJdk(), runtimeManifest.getBuildInfo().getCompiler());
88+
89+
final List<org.apache.nifi.c2.protocol.component.api.Bundle> bundles = runtimeManifest.getBundles();
90+
assertNotNull(bundles);
91+
assertEquals(1, bundles.size());
92+
93+
final org.apache.nifi.c2.protocol.component.api.Bundle testComponentsManifestBundle = bundles.get(0);
94+
assertEquals(testComponentsBundle.getBundleDetails().getCoordinate().getGroup(), testComponentsManifestBundle.getGroup());
95+
assertEquals(testComponentsBundle.getBundleDetails().getCoordinate().getId(), testComponentsManifestBundle.getArtifact());
96+
assertEquals(testComponentsBundle.getBundleDetails().getCoordinate().getVersion(), testComponentsManifestBundle.getVersion());
97+
98+
final ComponentManifest testComponentsManifest = testComponentsManifestBundle.getComponentManifest();
99+
assertNotNull(testComponentsManifest);
100+
101+
final List<ProcessorDefinition> processorDefinitions = testComponentsManifest.getProcessors();
102+
assertEquals(3, processorDefinitions.size());
103+
104+
final List<ControllerServiceDefinition> controllerServiceDefinitions = testComponentsManifest.getControllerServices();
105+
assertEquals(1, controllerServiceDefinitions.size());
106+
}
107+
108+
/**
109+
* Override getFrameworkBundle to provide a mocked Bundle.
110+
*/
111+
private static class TestableRuntimeManifestService extends StandardRuntimeManifestService {
112+
113+
private Bundle frameworkBundle;
114+
115+
public TestableRuntimeManifestService(final ExtensionManager extensionManager, final ExtensionManifestParser extensionManifestParser, final Bundle frameworkBundle) {
116+
super(extensionManager, extensionManifestParser);
117+
this.frameworkBundle = frameworkBundle;
118+
}
119+
120+
@Override
121+
Bundle getFrameworkBundle() {
122+
return frameworkBundle;
123+
}
124+
}
125+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
-->
15+
<extensionManifest>
16+
<systemApiVersion>1.8.0</systemApiVersion>
17+
<extensions>
18+
</extensions>
19+
</extensionManifest>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
-->
15+
<extensionManifest>
16+
<systemApiVersion>1.8.0</systemApiVersion>
17+
<extensions>
18+
<extension>
19+
<name>org.apache.nifi.processors.TestProcessor1</name>
20+
<type>PROCESSOR</type>
21+
<description>Test processor 1.</description>
22+
<tags>
23+
<tag>test</tag>
24+
<tag>processor</tag>
25+
</tags>
26+
<triggerSerially>true</triggerSerially>
27+
<triggerWhenEmpty>true</triggerWhenEmpty>
28+
<triggerWhenAnyDestinationAvailable>true</triggerWhenAnyDestinationAvailable>
29+
<primaryNodeOnly>true</primaryNodeOnly>
30+
<supportsBatching>true</supportsBatching>
31+
<eventDriven>true</eventDriven>
32+
<sideEffectFree>true</sideEffectFree>
33+
<defaultSettings>
34+
<yieldDuration>10 secs</yieldDuration>
35+
<penaltyDuration>20 secs</penaltyDuration>
36+
<bulletinLevel>DEBUG</bulletinLevel>
37+
</defaultSettings>
38+
<defaultSchedule>
39+
<strategy>CRON_DRIVEN</strategy>
40+
<period>* 1 * * *</period>
41+
<concurrentTasks>5</concurrentTasks>
42+
</defaultSchedule>
43+
</extension>
44+
<extension>
45+
<name>org.apache.nifi.processors.TestProcessor2</name>
46+
<type>PROCESSOR</type>
47+
<description>Test processor 2.</description>
48+
<tags>
49+
<tag>test</tag>
50+
<tag>processor</tag>
51+
</tags>
52+
<restricted>
53+
<restrictions>
54+
<restriction>
55+
<requiredPermission>write filesystem</requiredPermission>
56+
<explanation>Test explanation.</explanation>
57+
</restriction>
58+
</restrictions>
59+
</restricted>
60+
</extension>
61+
<extension>
62+
<name>org.apache.nifi.processors.TestProcessor3</name>
63+
<type>PROCESSOR</type>
64+
<description/>
65+
<tags>
66+
</tags>
67+
</extension>
68+
<extension>
69+
<name>org.apache.nifi.service.TestServiceImpl</name>
70+
<type>CONTROLLER_SERVICE</type>
71+
<deprecationNotice/>
72+
<description>Test service.</description>
73+
<tags>
74+
<tag>test</tag>
75+
<tag>service</tag>
76+
</tags>
77+
<providedServiceAPIs>
78+
<providedServiceAPI>
79+
<className>org.apache.nifi.service.TestService</className>
80+
<groupId>org.apache.nifi</groupId>
81+
<artifactId>nifi-test-service-api-nar</artifactId>
82+
<version>1.0.0</version>
83+
</providedServiceAPI>
84+
</providedServiceAPIs>
85+
</extension>
86+
</extensions>
87+
</extensionManifest>

0 commit comments

Comments
 (0)