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 nar
. The
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. As mentioned in the Developing a ControllerService section, 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:
Controller Service NAR Layout
root ├── my-controller-service-api │ ├── pom.xml │ └── src │ └── main │ └── java │ └── org │ └── my │ └── services │ └── MyService.java │ ├── my-controller-service-api-nar │ └── pom.xml (1) │ │ │ ├── my-controller-service-impl │ ├── pom.xml (2) │ └── src │ ├── main │ │ ├── java │ │ │ └── org │ │ │ └── my │ │ │ └── services │ │ │ └── MyServiceImpl.java │ │ └── resources │ │ └── META-INF │ │ └── services │ │ └── org.apache.nifi.controller.ControllerService │ └── test │ └── java │ └── org │ └── my │ └── services │ └── TestMyServiceImpl.java │ │ ├── my-controller-service-nar │ └── pom.xml (3) │ │ └── other-processor-nar └── pom.xml (3)
1 |
This POM file has a type of |
2 |
This POM file is of type |
3 |
This POM file has a type of |
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.