NiFi Archives (NARs)
When software from many different organizations is all hosted within the same environment, Java ClassLoaders quickly become a concern. If multiple components have a dependency on the same library but each depends on a different version, many problems arise, typically resulting in unexpected behavior or
NoClassDefFoundError errors occurring. In order to prevent these issues from becoming problematic, NiFi introduces the notion of a NiFi Archive, or NAR.
A NAR allows several components and their dependencies to be packaged together into a single package. The NAR package is then provided ClassLoader isolation from other NAR packages. Developers should always deploy their NiFi components as NAR packages.
To achieve this, a developer creates a new Maven Artifact, which we refer to as the NAR artifact. The packaging is set to
dependencies section of the POM is then created so that the NAR has a dependency on all NiFi Components that are to be included within the NAR.
In order to use a packaging of
nar, we must use the
nifi-nar-maven-plugin module. This is included by adding the following snippet to the NAR's pom.xml:
<build> <plugins> <plugin> <groupId>org.apache.nifi</groupId> <artifactId>nifi-nar-maven-plugin</artifactId> <version>1.1.0</version> <extensions>true</extensions> </plugin> </plugins> </build>
In the Apache NiFi codebase, this exists in the NiFi root POM from which all other NiFi artifacts (with the exception of the nifi-nar-maven-plugin itself) inherit, so that we do not need to include this in any of our other POM files.
The NAR is able to have one dependency that is of type
nar. If more than one dependency is specified that is of type
nar, then the nifi-nar-maven-plugin will error. If NAR A adds a dependency on NAR B, this will not result in NAR B packaging all of the components of NAR A. Rather, this will add a
Nar-Dependency-Id element to the
MANIFEST.MF file of NAR A. This will result in setting the ClassLoader of NAR B as the Parent ClassLoader of NAR A. In this case, we refer to NAR B as the Parent of NAR A.
This linkage of Parent ClassLoaders is the mechanism that NiFi uses in order to enable
Controller Services to be shared across all NARs.
A Controller Service must be separated into an interface that extends
ControllerService and an implementation that implements that
interface. Controller Services can be referenced from any Processor, regardless of which
NAR it is in, as long as both the Controller Service Implementation and the Processor
share the same definition of the Controller Service interface.
In order to share this same definition, both the Processor's NAR and the Controller Service Implementation's NAR must have as a Parent the Controller Service definition's NAR. An example hierarchy may look like this:
While these may seem very complex at first, after creating such a hierarchy once or twice, it becomes far less complicated. Note here that the
my-controller-service-api-nar has a dependency on
nifi-standard-services-api-nar. This is done so that any NAR that has a dependency on
my-controller-service-api-nar will also be able to access all of the Controller Services that are provided by the
nifi-standard-services-api-nar, such as the SSLContextService. In this same vane, it is not necessary to create a different "service-api" NAR for each service. Instead, it often makes sense to have a single "service-api" NAR that encapsulates the API's for many different Controller Services, as is done by the
nifi-standard-services-api-nar. Generally, the API will not include extensive dependencies, and as a result, ClassLoader isolation may be less important, so lumping together many API artifacts into the same NAR is often acceptable.