NIFI-8528 Migrate NiFi Registry into NiFi codebase (#5065)

NIFI-8528 Migrate NiFi Registry fully codebase into NiFi as a module. No changes except certain dependency scopes to preserve the NiFi Registry original by overriding the new parent (nifi).
- Version adjustments. Removed distinct checkstye rules form nifi-registry. (Using nifi's instead.)
- Made some tests Windows-compatible.
- Consolidated LICENSE, NOTICE and README.md.
- Fixed CryptoKeyLoaderGroovyTest.groovy.
- Disable frontend-maven-plugin on Windows.
- Skipping all goals of the frontend-maven-plugin on Windows.
- Registry integration tests not to run in github jobs (same as the original settings). Skip all registry tests (build and run) on Windows.
- Removed Husky from registry.
This commit is contained in:
tpalfy 2021-05-26 15:34:55 +02:00 committed by GitHub
parent 103aae64cb
commit dfa683af0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1113 changed files with 161397 additions and 17 deletions

View File

@ -93,7 +93,7 @@ jobs:
env:
MAVEN_OPTS: -Xmx2g -XX:ReservedCodeCacheSize=1g -XX:+UseG1GC -Dorg.slf4j.simpleLogger.defaultLogLevel=WARN -Duser.language=en -Duser.country=AU -Duser.region=AU -Duser.timezone=Australia/Melbourne -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false
run: |
mvn -V -T 0.7C package verify -B -Pcontrib-check,include-grpc -Ddir-only -ntp -ff -pl -nifi-assembly,-nifi-toolkit/nifi-toolkit-assembly,-nifi-system-tests -nsu
mvn -V -T 0.7C package verify -B -Pcontrib-check,include-grpc,nifi-registry-no-integration-tests -Ddir-only -ntp -ff -pl -nifi-assembly,-nifi-toolkit/nifi-toolkit-assembly,-nifi-system-tests -nsu
- name: Upload artifact
uses: actions/upload-artifact@v2
if: always()
@ -176,7 +176,7 @@ jobs:
env:
MAVEN_OPTS: -Xmx2g -XX:ReservedCodeCacheSize=1g -XX:+UseG1GC -Dorg.slf4j.simpleLogger.defaultLogLevel=WARN -Duser.language=ja -Duser.country=JP -Duser.region=JP -Duser.timezone=Asia/Tokyo -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false
run: |
mvn -V -T 0.7C package verify -B -Pcontrib-check,include-grpc -Ddir-only -ntp -ff -pl -nifi-assembly,-nifi-toolkit/nifi-toolkit-assembly,-nifi-system-tests -nsu
mvn -V -T 0.7C package verify -B -Pcontrib-check,include-grpc,nifi-registry-no-integration-tests -Ddir-only -ntp -ff -pl -nifi-assembly,-nifi-toolkit/nifi-toolkit-assembly,-nifi-system-tests -nsu
- name: Upload artifact
uses: actions/upload-artifact@v2
if: always()
@ -250,7 +250,7 @@ jobs:
env:
MAVEN_OPTS: -Xmx2g -Dorg.slf4j.simpleLogger.defaultLogLevel=WARN -Duser.language=fr -Duser.country=FR -Duser.region=FR -Duser.timezone=Europe/Paris -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false
run: |
mvn -V -T 0.7C package -B -Ddir-only -ntp -ff -pl -nifi-assembly -pl -nifi-system-tests -nsu
mvn -V -T 0.7C package -B -Pnifi-registry-no-integration-tests -Ddir-only -ntp -ff -pl -nifi-assembly -pl -nifi-system-tests -nsu
- name: Upload artifact
uses: actions/upload-artifact@v2
if: ${{ false }}

22
LICENSE
View File

@ -364,3 +364,25 @@ This product bundles source from 'AbstractingTheJavaConsole'. The source is avai
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
This product bundles karma-test-shim.js and systemjs-angular-loader.js from 'Angular Quickstart' which is available under an MIT license.
Copyright (c) 2010-2016 Google, Inc. https://angularjs.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -28,6 +28,7 @@ reliable system to process and distribute data.
- [Requirements](#requirements)
- [Getting Started](#getting-started)
- [MiNiFi subproject](#minifi-subproject)
- [Registry subproject](#registry-subproject)
- [Getting Help](#getting-help)
- [Documentation](#documentation)
- [License](#license)
@ -183,6 +184,84 @@ To build:
docker run -d -v YOUR_CONFIG.YML:/opt/minifi/minifi-${minifi.version}/conf/config.yml apacheminifi:${minifi.version}
```
## Registry subproject
Registry—a subproject of Apache NiFi—is a complementary application that provides a central location for storage and management of shared resources across one or more instances of NiFi and/or MiNiFi.
### Getting Registry Started
1) Build nifi
See [Gettin Started](#getting-started) for NiFi
or
Build only the Registry subproject:
cd nifi/nifi-registry
mvn clean install
If you wish to enable style and license checks, specify the contrib-check profile:
mvn clean install -Pcontrib-check
3) Start Registry
cd nifi-registry/nifi-registry-assembly/target/nifi-registry-<VERSION>-bin/nifi-registry-<VERSION>/
./bin/nifi-registry.sh start
Note that the application web server can take a while to load before it is accessible.
4) Accessing the application web UI
With the default settings, the application UI will be available at [http://localhost:18080/nifi-registry](http://localhost:18080/nifi-registry)
5) Accessing the application REST API
If you wish to test against the application REST API, you can access the REST API directly. With the default settings, the base URL of the REST API will be at `http://localhost:18080/nifi-registry-api`. A UI for testing the REST API will be available at [http://localhost:18080/nifi-registry-api/swagger/ui.html](http://localhost:18080/nifi-registry-api/swagger/ui.html)
6) Accessing the application logs
Logs will be available in `logs/nifi-registry-app.log`
### Database Testing
In order to ensure that NiFi Registry works correctly against different relational databases,
the existing integration tests can be run against different databases by leveraging the [Testcontainers framework](https://www.testcontainers.org/).
Spring profiles are used to control the DataSource factory that will be made available to the Spring application context.
DataSource factories are provided that use the Testcontainers framework to start a Docker container for a given database and create a corresponding DataSource.
If no profile is specified then an H2 DataSource will be used by default and no Docker containers are required.
Assuming Docker is running on the system where the build is running, then the following commands can be run:
| Target Database | Build Command |
| --------------- | ------------- |
| All supported | `mvn verify -Ptest-all-dbs` |
| H2 (default) | `mvn verify` |
| PostgreSQL 9.x | `mvn verify -Dspring.profiles.active=postgres` |
| PostgreSQL 10.x | `mvn verify -Dspring.profiles.active=postgres-10` |
| MySQL 5.6 | `mvn verify -Pcontrib-check -Dspring.profiles.active=mysql-56` |
| MySQL 5.7 | `mvn verify -Pcontrib-check -Dspring.profiles.active=mysql-57` |
| MySQL 8 | `mvn verify -Pcontrib-check -Dspring.profiles.active=mysql-8` |
When one of the Testcontainer profiles is activated, the test output should show logs that indicate a container has been started, such as the following:
2019-05-15 16:14:45.078 INFO 66091 --- [ main] 🐳 [mysql:5.7] : Creating container for image: mysql:5.7
2019-05-15 16:14:45.145 INFO 66091 --- [ main] o.t.utility.RegistryAuthLocator : Credentials not found for host (index.docker.io) when using credential helper/store (docker-credential-osxkeychain)
2019-05-15 16:14:45.646 INFO 66091 --- [ main] 🐳 [mysql:5.7] : Starting container with ID: ca85c8c5a1990d2a898fad04c5897ddcdb3a9405e695cc11259f50f2ebe67c5f
2019-05-15 16:14:46.437 INFO 66091 --- [ main] 🐳 [mysql:5.7] : Container mysql:5.7 is starting: ca85c8c5a1990d2a898fad04c5897ddcdb3a9405e695cc11259f50f2ebe67c5f
2019-05-15 16:14:46.479 INFO 66091 --- [ main] 🐳 [mysql:5.7] : Waiting for database connection to become available at jdbc:mysql://localhost:33051/test?useSSL=false&allowPublicKeyRetrieval=true using query 'SELECT 1'
The Flyway connection should also indicate the given database:
2019-05-15 16:15:02.114 INFO 66091 --- [ main] o.a.n.r.db.CustomFlywayConfiguration : Determined database type is MYSQL
2019-05-15 16:15:02.115 INFO 66091 --- [ main] o.a.n.r.db.CustomFlywayConfiguration : Setting migration locations to [classpath:db/migration/common, classpath:db/migration/mysql]
2019-05-15 16:15:02.373 INFO 66091 --- [ main] o.a.n.r.d.CustomFlywayMigrationStrategy : First time initializing database...
2019-05-15 16:15:02.380 INFO 66091 --- [ main] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 5.2.1 by Boxfuse
2019-05-15 16:15:02.403 INFO 66091 --- [ main] o.f.c.internal.database.DatabaseFactory : Database: jdbc:mysql://localhost:33051/test (MySQL 5.7)
For a full list of the available DataSource factories, consult the `nifi-registry-test` module.
## Getting Help
If you have questions, you can reach out to our mailing list: dev@nifi.apache.org
([archive](http://mail-archives.apache.org/mod_mbox/nifi-dev)). For more interactive discussions, community members can often be found in the following locations:

View File

@ -29,7 +29,7 @@
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-data-model</artifactId>
<version>${nifi.registry.version}</version>
<version>1.14.0-SNAPSHOT</version>
</dependency>
</dependencies>
<profiles>

View File

@ -75,17 +75,17 @@
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-data-model</artifactId>
<version>${nifi.registry.version}</version>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-flow-diff</artifactId>
<version>${nifi.registry.version}</version>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-client</artifactId>
<version>${nifi.registry.version}</version>
<version>1.14.0-SNAPSHOT</version>
</dependency>

View File

@ -68,12 +68,12 @@ language governing permissions and limitations under the License. -->
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-data-model</artifactId>
<version>${nifi.registry.version}</version>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-client</artifactId>
<version>${nifi.registry.version}</version>
<version>1.14.0-SNAPSHOT</version>
</dependency>
</dependencies>
<profiles>

View File

@ -185,17 +185,17 @@
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-data-model</artifactId>
<version>${nifi.registry.version}</version>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-flow-diff</artifactId>
<version>${nifi.registry.version}</version>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-client</artifactId>
<version>${nifi.registry.version}</version>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>

33
nifi-registry/build-and-run.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/sh
#
# 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.
REGISTRY_SCRIPT=`find nifi-registry-assembly/target/ -name nifi-registry.sh | head -1`
REGISTRY_BIN_DIR=$(dirname "${REGISTRY_SCRIPT}")
REGISTRY_DIR=$REGISTRY_BIN_DIR/..
SKIP_UI=$1
./${REGISTRY_SCRIPT} stop
if [ "$SKIP_UI" == "skipUi" ]; then
mvn clean install -Pcontrib-check --projects \!nifi-registry-web-ui
else
mvn clean install -Pcontrib-check
fi
./${REGISTRY_SCRIPT} start
tail -n 500 -f ${REGISTRY_DIR}/logs/nifi-registry-app.log

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,319 @@
Apache NiFi Registry
Copyright 2017-2020 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (https://www.apache.org/).
This includes derived works from the Apache NiFi (ASLv2 licensed) project (https://git-wip-us.apache.org/repos/asf?p=nifi.git):
Copyright 2015-2020 The Apache Software Foundation
This includes sources for bootstrapping, runtime, component API, security/authorization API
===========================================
Apache Software License v2
===========================================
The following binary components are provided under the Apache Software License v2
(ASLv2) Jetty
The following NOTICE information applies:
Jetty Web Container
Copyright 1995-2019 Mort Bay Consulting Pty Ltd.
(ASLv2) Apache Commons Codec
The following NOTICE information applies:
Apache Commons Codec
Copyright 2002-2014 The Apache Software Foundation
src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java
contains test data from https://aspell.net/test/orig/batch0.tab.
Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org)
===============================================================================
The content of package org.apache.commons.codec.language.bm has been translated
from the original php source code available at https://stevemorse.org/phoneticinfo.htm
with permission from the original authors.
Original source copyright:
Copyright (c) 2008 Alexander Beider & Stephen P. Morse.
(ASLv2) Apache Commons Lang
The following NOTICE information applies:
Apache Commons Lang
Copyright 2001-2017 The Apache Software Foundation
This product includes software from the Spring Framework,
under the Apache License 2.0 (see: StringUtils.containsWhitespace())
(ASLv2) Jackson JSON processor
The following NOTICE information applies:
# Jackson JSON processor
Jackson is a high-performance, Free/Open Source JSON processing library.
It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has
been in development since 2007.
It is currently developed by a community of developers, as well as supported
commercially by FasterXML.com.
## Licensing
Jackson core and extension components may licensed under different licenses.
To find the details that apply to this artifact see the accompanying LICENSE file.
For more information, including possible other licensing options, contact
FasterXML.com (https://fasterxml.com).
## Credits
A list of contributors may be found from CREDITS file, which is included
in some artifacts (usually source distributions); but is always available
from the source code management (SCM) system project uses.
(ASLv2) Java Native Access Platform
The following NOTICE information applies:
Java Native Access Platform
Copyright 2013 Timothy Wall, Matthias Bläsing
This product includes software developed by
The Apache Software Foundation (https://www.apache.org/).
===============================================================================
The BracketFinder (package org.apache.commons.math3.optimization.univariate)
and PowellOptimizer (package org.apache.commons.math3.optimization.general)
classes are based on the Python code in module "optimize.py" (version 0.5)
developed by Travis E. Oliphant for the SciPy library (https://www.scipy.org/)
Copyright © 2003-2009 SciPy Developers.
===============================================================================
The LinearConstraint, LinearObjectiveFunction, LinearOptimizer,
RelationShip, SimplexSolver and SimplexTableau classes in package
org.apache.commons.math3.optimization.linear include software developed by
Benjamin McCann (https://www.benmccann.com) and distributed with
the following copyright: Copyright 2009 Google Inc.
===============================================================================
This product includes software developed by the
University of Chicago, as Operator of Argonne National
Laboratory.
The LevenbergMarquardtOptimizer class in package
org.apache.commons.math3.optimization.general includes software
translated from the lmder, lmpar and qrsolv Fortran routines
from the Minpack package
Minpack Copyright Notice (1999) University of Chicago. All rights reserved
===============================================================================
The GraggBulirschStoerIntegrator class in package
org.apache.commons.math3.ode.nonstiff includes software translated
from the odex Fortran routine developed by E. Hairer and G. Wanner.
Original source copyright:
Copyright (c) 2004, Ernst Hairer
===============================================================================
The EigenDecompositionImpl class in package
org.apache.commons.math3.linear includes software translated
from some LAPACK Fortran routines. Original source copyright:
Copyright (c) 1992-2008 The University of Tennessee. All rights reserved.
===============================================================================
The MersenneTwister class in package org.apache.commons.math3.random
includes software translated from the 2002-01-26 version of
the Mersenne-Twister generator written in C by Makoto Matsumoto and Takuji
Nishimura. Original source copyright:
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
All rights reserved
===============================================================================
The LocalizedFormatsTest class in the unit tests is an adapted version of
the OrekitMessagesTest class from the orekit library distributed under the
terms of the Apache 2 licence. Original source copyright:
Copyright 2010 CS Systèmes d'Information
===============================================================================
The HermiteInterpolator class and its corresponding test have been imported from
the orekit library distributed under the terms of the Apache 2 licence. Original
source copyright:
Copyright 2010-2012 CS Systèmes d'Information
===============================================================================
The creation of the package "o.a.c.m.analysis.integration.gauss" was inspired
by an original code donated by Sébastien Brisard.
===============================================================================
(ASLv2) JSON-SMART
The following NOTICE information applies:
Copyright 2011 JSON-SMART authors
(ASLv2) JsonPath
The following NOTICE information applies:
Copyright 2011 JsonPath authors
(ASLv2) Classmate
The following NOTICE information applies
Java ClassMate library was originally written by Tatu Saloranta (tatu.saloranta@iki.fi)
Other developers who have contributed code are:
* Brian Langel
(ASLv2) Apache Commons IO
The following NOTICE information applies:
Apache Commons IO
Copyright 2002-2016 The Apache Software Foundation
(ASLv2) Apache log4j
The following NOTICE information applies:
Apache log4j
Copyright 2010 The Apache Software Foundation
(ASLv2) Spring Framework
The following NOTICE information applies:
Spring Framework 5.1.8.RELEASE
Copyright (c) 2002-2019 Pivotal, Inc.
(ASLv2) Spring Security
The following NOTICE information applies:
Spring Framework 5.1.5.RELEASE
Copyright (c) 2002-2019 Pivotal, Inc.
This product includes software developed by Spring Security
Project (https://www.springframework.org/security).
(ASLv2) Spring LDAP
The following NOTICE information applies:
Spring LDAP 2.3.2.RELEASE
Copyright (c) 2002-2017 Pivotal, Inc.
This product includes software developed by the Spring LDAP
Project (https://www.springframework.org/ldap).
(ASLv2) Apache Tomcat Embed EL
The following NOTICE information applies:
Apache Tomcat
Copyright 1999-2017 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (https://www.apache.org/).
This software contains code derived from netty-native
developed by the Netty project
(https://netty.io, https://github.com/netty/netty-tcnative/)
and from finagle-native developed at Twitter
(https://github.com/twitter/finagle).
The Windows Installer is built with the Nullsoft
Scriptable Install System (NSIS), which is
open source software. The original software and
related information is available at
https://nsis.sourceforge.net.
Java compilation software for JSP pages is provided by the Eclipse
JDT Core Batch Compiler component, which is open source software.
The original software and related information is available at
https://www.eclipse.org/jdt/core/.
For portions of the Tomcat JNI OpenSSL API and the OpenSSL JSSE integration
The org.apache.tomcat.jni and the org.apache.tomcat.net.openssl packages
are derivative work originating from the Netty project and the finagle-native
project developed at Twitter
* Copyright 2014 The Netty Project
* Copyright 2014 Twitter
The original XML Schemas for Java EE Deployment Descriptors:
- javaee_5.xsd
- javaee_web_services_1_2.xsd
- javaee_web_services_client_1_2.xsd
- javaee_6.xsd
- javaee_web_services_1_3.xsd
- javaee_web_services_client_1_3.xsd
- jsp_2_2.xsd
- web-app_3_0.xsd
- web-common_3_0.xsd
- web-fragment_3_0.xsd
- javaee_7.xsd
- javaee_web_services_1_4.xsd
- javaee_web_services_client_1_4.xsd
- jsp_2_3.xsd
- web-app_3_1.xsd
- web-common_3_1.xsd
- web-fragment_3_1.xsd
- javaee_8.xsd
- web-app_4_0.xsd
- web-common_4_0.xsd
- web-fragment_4_0.xsd
may be obtained from:
https://www.oracle.com/webfolder/technetwork/jsc/xml/ns/javaee/index.html
(ASLv2) SnakeYAML
The following NOTICE information applies:
Copyright (c) 2008, https://www.snakeyaml.org
(ASLv2) Swagger UI
The following NOTICE information applies:
Copyright 2017 SmartBear Software
(ASLv2) Nimbus OAuth 2.0 SDK with OpenID Connect extensions
The following NOTICE information applies:
Nimbus OAuth 2.0 SDK with OpenID Connect extensions
Copyright 2012-2020, Connect2id Ltd and contributors.
(ASLv2) Guava
The following NOTICE information applies:
Guava
Copyright 2015 The Guava Authors
************************
Common Development and Distribution License 1.1
************************
The following binary components are provided under the Common Development and Distribution License 1.1. See project link for details.
(CDDL 1.1) (GPL2 w/ CPE) JavaMail API (compat) (javax.mail:mail:jar:1.4.7 - https://kenai.com/projects/javamail/mail)
(CDDL 1.1) (GPL2 w/ CPE) Java Servlet API (javax.servlet:javax.servlet-api:jar:3.1.0 - https://servlet-spec.java.net)
(CDDL 1.1) (GPL2 w/ CPE) JavaServer Pages Standard Tag Library (javax.servlet.jsp.jstl:jstl:jar:1.2 - https://javaee.github.io/jstl-api/)
(CDDL 1.1) (GPL2 w/ CPE) javax.annotation API (javax.annotation:javax.annotation-api:jar:1.2 - https://jcp.org/en/jsr/detail?id=250)
(CDDL 1.1) (GPL2 w/ CPE) aopalliance-repackaged (org.glassfish.hk2.external:aopalliance-repackaged:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) asm-all-repackaged (org.glassfish.hk2.external:asm-all-repackaged:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) class-model (org.glassfish.hk2:class-model:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) config-types (org.glassfish.hk2:config-types:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) hk2 (org.glassfish.hk2:hk2:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) hk2-api (org.glassfish.hk2:hk2-api:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) hk2-utils (org.glassfish.hk2:hk2-utils:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) hk2-locator (org.glassfish.hk2:hk2-locator:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) hk2-config (org.glassfish.hk2:hk2-config:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) hk2-core (org.glassfish.hk2:hk2-core:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) hk2-runlevel (org.glassfish.hk2:hk2-runlevel:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) spring-bridge (org.glassfish.hk2:spring-bridge:jar:2.5.0-b42 - https://javaee.github.io/glassfish/)
(CDDL 1.1) (GPL2 w/ CPE) javax.inject:1 as OSGi bundle (org.glassfish.hk2.external:javax.inject:jar:2.4.0-b25 - https://hk2.java.net/external/javax.inject)
(CDDL 1.1) (GPL2 w/ CPE) javax.ws.rs-api (javax.ws.rs:javax.ws.rs-api:jar:2.1 - https://jax-rs-spec.java.net)
(CDDL 1.1) (GPL2 w/ CPE) javax.el (org.glassfish:javax.el:jar:3.0.1-b08 - https://github.com/javaee/el-spec)
(CDDL 1.1) (GPL2 w/ CPE) jersey-bean-validation (org.glassfish.jersey.ext:jersey-bean-validation:jar:2.27 - https://jersey.github.io/)
(CDDL 1.1) (GPL2 w/ CPE) jersey-client (org.glassfish.jersey.core:jersey-client:jar:2.27 - https://jersey.github.io/)
(CDDL 1.1) (GPL2 w/ CPE) jersey-common (org.glassfish.jersey.core:jersey-common:jar:2.27 - https://jersey.github.io/)
(CDDL 1.1) (GPL2 w/ CPE) jersey-container-servlet-core (org.glassfish.jersey.containers:jersey-container-servlet-core:jar:2.27 - https://jersey.github.io/)
(CDDL 1.1) (GPL2 w/ CPE) jersey-entity-filtering (org.glassfish.jersey.ext:jersey-entity-filtering:jar:2.27 - https://jersey.github.io/)
(CDDL 1.1) (GPL2 w/ CPE) jersey-hk2 (org.glassfish.jersey.inject:jersey-hk2:jar:2.27 - https://jersey.github.io/)
(CDDL 1.1) (GPL2 w/ CPE) jersey-media-jaxb (org.glassfish.jersey.media:jersey-media-jaxb:jar:2.27 - https://jersey.github.io/)
(CDDL 1.1) (GPL2 w/ CPE) jersey-media-json-jackson (org.glassfish.jersey.media:jersey-media-json-jackson:jar:2.27 - https://jersey.github.io/)
(CDDL 1.1) (GPL2 w/ CPE) jersey-server (org.glassfish.jersey.core:jersey-server:jar:2.27 - https://jersey.github.io/)
(CDDL 1.1) (GPL2 w/ CPE) jersey-spring4 (org.glassfish.jersey.ext:jersey-spring4:jar:2.27 - https://jersey.github.io/)
(CDDL 1.1) (GPL2 w/ CPE) OSGi resource locator bundle (org.glassfish.hk2:osgi-resource-locator:jar:1.0.1 - https://glassfish.org/osgi-resource-locator)
************************
Common Development and Distribution License 1.0
************************
The following binary components are provided under the Common Development and Distribution License 1.0. See project link for details.
(CDDL 1.0) JavaBeans Activation Framework (JAF) (javax.activation:activation:jar:1.1 - https://java.sun.com/products/javabeans/jaf/index.jsp)
************************
Eclipse Public License 1.0
************************
The following binary components are provided under the Eclipse Public License 1.0. See project link for details.
(EPL 1.0)(MPL 2.0) H2 Database (com.h2database:h2:jar:h2-1.4.199 - https://www.h2database.com/html/license.html)
(EPL 1.0)(LGPL 2.1) Logback Classic (ch.qos.logback:logback-classic:jar:1.2.3 - https://logback.qos.ch/)
(EPL 1.0)(LGPL 2.1) Logback Core (ch.qos.logback:logback-core:jar:1.2.3 - https://logback.qos.ch/)
(EPL 1.0) AspectJ Weaver (org.aspectj:aspectjweaver:jar:1.8.13 - https://www.eclipse.org/aspectj/)

View File

@ -0,0 +1,63 @@
<!--
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.
-->
# Apache NiFi Registry
Registry—a subproject of Apache NiFi—is a complementary application that provides a central location for storage and management of shared resources across one or more instances of NiFi and/or MiNiFi.
## Table of Contents
- [Requirements](#requirements)
- [Getting Started](#getting-started)
- [Getting Help](#getting-help)
- [License](#license)
## Requirements
* Java 1.8 (above 1.8.0_45)
## Getting Started
To start NiFi Registry:
- [linux/osx] execute bin/nifi-registry.sh start
- [windows] execute bin/run-nifi-registry.bat
- Direct your browser to http://localhost:18080/nifi-registry/
## Getting Help
If you have questions, you can reach out to our mailing list: dev@nifi.apache.org
([archive](http://mail-archives.apache.org/mod_mbox/nifi-dev)). For more interactive discussions, community members can often be found in the following locations:
- Apache NiFi Slack Workspace: https://apachenifi.slack.com/
New users can join the workspace using the following [invite link](https://s.apache.org/nifi-community-slack).
- IRC: #nifi on [irc.freenode.net](http://webchat.freenode.net/?channels=#nifi)
## License
Except as otherwise noted this software is licensed under the
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
Licensed 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.

View File

@ -0,0 +1,493 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry</artifactId>
<version>1.14.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-registry-assembly</artifactId>
<packaging>pom</packaging>
<description>This is the assembly for nifi-registry.</description>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>nifi-registry-${project.version}</finalName>
<attach>false</attach>
</configuration>
<executions>
<execution>
<id>make shared resource</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<archiverConfig>
<defaultDirectoryMode>0775</defaultDirectoryMode>
<directoryMode>0775</directoryMode>
<fileMode>0664</fileMode>
</archiverConfig>
<descriptors>
<descriptor>src/main/assembly/dependencies.xml</descriptor>
</descriptors>
<tarLongFileMode>posix</tarLongFileMode>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-utils</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-runtime</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-security-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-provider-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-web-ui</artifactId>
<type>war</type>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-web-api</artifactId>
<type>war</type>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-web-docs</artifactId>
<type>war</type>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-resources</artifactId>
<classifier>resources</classifier>
<scope>runtime</scope>
<type>zip</type>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-docs</artifactId>
<version>1.14.0-SNAPSHOT</version>
<classifier>resources</classifier>
<scope>runtime</scope>
<type>zip</type>
</dependency>
<!-- Dependencies required for running on Java 11 that will be placed in the lib/java11 dir -->
<!-- TODO: remove these once minimum Java version is 11 and these can be bundle directly into the application -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
</dependencies>
<properties>
<!-- nifi-registry.properties: web properties -->
<nifi.registry.web.war.directory>./lib</nifi.registry.web.war.directory>
<nifi.registry.web.http.host />
<nifi.registry.web.http.port>18080</nifi.registry.web.http.port>
<nifi.registry.web.https.host />
<nifi.registry.web.https.port />
<nifi.registry.jetty.work.dir>./work/jetty</nifi.registry.jetty.work.dir>
<nifi.registry.web.jetty.threads>200</nifi.registry.web.jetty.threads>
<nifi.registry.web.should.send.server.version>true</nifi.registry.web.should.send.server.version>
<!-- nifi-registry.properties: security properties -->
<nifi.registry.security.keystore />
<nifi.registry.security.keystoreType />
<nifi.registry.security.keystorePasswd />
<nifi.registry.security.keyPasswd />
<nifi.registry.security.truststore />
<nifi.registry.security.truststoreType />
<nifi.registry.security.truststorePasswd />
<nifi.registry.security.needClientAuth />
<nifi.registry.security.authorizers.configuration.file>./conf/authorizers.xml</nifi.registry.security.authorizers.configuration.file>
<nifi.registry.security.authorizer>managed-authorizer</nifi.registry.security.authorizer>
<nifi.registry.security.identity.providers.configuration.file>./conf/identity-providers.xml</nifi.registry.security.identity.providers.configuration.file>
<nifi.registry.security.identity.provider />
<!-- nifi-registry.properties: provider properties -->
<nifi.registry.providers.configuration.file>./conf/providers.xml</nifi.registry.providers.configuration.file>
<!-- nifi-registry.properties: registry alias properties -->
<nifi.registry.registry.alias.configuration.file>./conf/registry-aliases.xml</nifi.registry.registry.alias.configuration.file>
<!-- nifi-registry.properties: extension properties -->
<nifi.registry.extensions.working.directory>./work/extensions</nifi.registry.extensions.working.directory>
<nifi.registry.extension.dir.aws />
<!-- nifi-registry.properties: legacy database properties, used to migrate data from old DB to the new DB below -->
<nifi.registry.db.directory />
<nifi.registry.db.url.append />
<!-- nifi-registry.properties: database properties -->
<nifi.registry.db.url>jdbc:h2:./database/nifi-registry-primary;AUTOCOMMIT=OFF;DB_CLOSE_ON_EXIT=FALSE;LOCK_MODE=3;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE</nifi.registry.db.url>
<nifi.registry.db.driver.class>org.h2.Driver</nifi.registry.db.driver.class>
<nifi.registry.db.driver.directory />
<nifi.registry.db.username>nifireg</nifi.registry.db.username>
<nifi.registry.db.password>nifireg</nifi.registry.db.password>
<nifi.registry.db.maxConnections>5</nifi.registry.db.maxConnections>
<nifi.registry.db.sql.debug>false</nifi.registry.db.sql.debug>
<!-- nifi-registry.properties: kerberos properties -->
<nifi.registry.kerberos.krb5.file />
<nifi.registry.kerberos.spnego.principal />
<nifi.registry.kerberos.spnego.keytab.location />
<nifi.registry.kerberos.spnego.authentication.expiration>12 hours</nifi.registry.kerberos.spnego.authentication.expiration>
<!-- nifi-registry.properties: OIDC properties -->
<nifi.registry.security.user.oidc.discovery.url />
<nifi.registry.security.user.oidc.connect.timeout />
<nifi.registry.security.user.oidc.read.timeout />
<nifi.registry.security.user.oidc.client.id />
<nifi.registry.security.user.oidc.client.secret />
<nifi.registry.security.user.oidc.preferred.jwsalgorithm />
<!-- nifi.registry.properties: revision management properties -->
<nifi.registry.revisions.enabled>false</nifi.registry.revisions.enabled>
</properties>
<profiles>
<profile>
<id>rpm</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<properties>
<nifi.registry.run.as>nifi</nifi.registry.run.as>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-shared-resources</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.build.directory}/generated-resources</outputDirectory>
<includeArtifactIds>nifi-registry-resources</includeArtifactIds>
<includeGroupIds>org.apache.nifi.registry</includeGroupIds>
<excludeTransitive>false</excludeTransitive>
</configuration>
</execution>
<execution>
<id>unpack-docs</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.build.directory}/generated-docs</outputDirectory>
<includeArtifactIds>nifi-registry-docs</includeArtifactIds>
<includeGroupIds>org.apache.nifi.registry</includeGroupIds>
<excludeTransitive>false</excludeTransitive>
<excludes>LICENSE,NOTICE</excludes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<configuration>
<name>nifi-registry</name>
<summary>Apache NiFi Registry</summary>
<description>A sub-project of Apache NiFi that provides a central location for storage and management of shared resources across one or more instances of NiFi and/or MiNiFi.</description>
<license>Apache License, Version 2.0 and others (see included LICENSE file)</license>
<url>https://nifi.apache.org/registry.html</url>
<group>Utilities</group>
<prefix>/opt/nifi-registry</prefix>
<defineStatements>
<defineStatement>_use_internal_dependency_generator 0</defineStatement>
</defineStatements>
<defaultDirmode>750</defaultDirmode>
<defaultFilemode>640</defaultFilemode>
<defaultUsername>nifi</defaultUsername>
<defaultGroupname>nifi</defaultGroupname>
<!-- This allows the stanza to produce bootstrap.conf that is pre-configured with run.as=nifi
given the uid is created by the preinstallScriptlet below. Meanwhile, normal zip and tar.gz
ship without this particular property set -->
<installScriptlet>
<!-- It is important to note because of the way the plugin calls the command, the sed backup
parameter -i and the sed command do not beed to be wrapped by single or double quotes.
Also note maven properties are processed before RPM variables and before sed commands -->
<script>sed -i s/^run\.as=$/run\.as=${nifi.registry.run.as}/ $RPM_BUILD_ROOT/opt/nifi-registry/nifi-registry-${project.version}/conf/bootstrap.conf</script>
</installScriptlet>
<preinstallScriptlet>
<script>/usr/bin/getent group nifi &gt;/dev/null || /usr/sbin/groupadd -r nifi; /usr/bin/getent passwd nifi &gt;/dev/null || /usr/sbin/useradd -r -g nifi -d /opt/nifi-registry -s /sbin/nologin -c "NiFi System User" nifi</script>
</preinstallScriptlet>
</configuration>
<executions>
<execution>
<id>build-bin-rpm</id>
<goals>
<goal>attached-rpm</goal>
</goals>
<configuration>
<group>Apache NiFI</group>
<classifier>bin</classifier>
<provides>
<provide>nifi-registry</provide>
</provides>
<mappings>
<mapping>
<directory>/opt/nifi-registry/nifi-registry-${project.version}</directory>
</mapping>
<mapping>
<directory>/opt/nifi-registry/nifi-registry-${project.version}</directory>
<sources>
<source>
<location>./LICENSE</location>
</source>
<source>
<location>./NOTICE</location>
</source>
<source>
<location>./README.md</location>
<destination>README</destination>
</source>
</sources>
</mapping>
<mapping>
<directory>/opt/nifi-registry</directory>
</mapping>
<mapping>
<directory>/opt/nifi-registry/nifi-registry-${project.version}/bin</directory>
<filemode>750</filemode>
<sources>
<source>
<location>${project.build.directory}/generated-resources/bin/nifi-registry.sh</location>
<destination>nifi-registry.sh</destination>
<filter>true</filter>
</source>
<source>
<location>${project.build.directory}/generated-resources/bin/nifi-registry-env.sh</location>
<destination>nifi-registry-env.sh</destination>
<filter>true</filter>
</source>
</sources>
</mapping>
<mapping>
<directory>/opt/nifi-registry/nifi-registry-${project.version}/conf</directory>
<configuration>true</configuration>
<sources>
<source>
<location>${project.build.directory}/generated-resources/conf</location>
<filter>true</filter>
</source>
</sources>
</mapping>
<mapping>
<directory>/opt/nifi-registry/nifi-registry-${project.version}/lib</directory>
</mapping>
<!--
The lib/shared mapping includes the common dependencies between the registry
application (lib) and registry bootstrap (lib/bootstrap).
The lib/bootstrap mapping then excludes what was in lib/shared and includes
the other dependencies needed for the registry bootstrap.
The lib mapping excludes what was in lib/shared and excluded the bootstrap module,
and would bring in everything else from the lib directory of a non RPM build.
-->
<mapping>
<directory>/opt/nifi-registry/nifi-registry-${project.version}/lib</directory>
<dependency>
<excludes>
<exclude>org.apache.commons:commons-lang3</exclude>
<exclude>org.apache.nifi.registry:nifi-registry-utils</exclude>
<exclude>org.apache.nifi.registry:nifi-registry-bootstrap</exclude>
<exclude>org.apache.nifi.registry:nifi-registry-docs</exclude>
</excludes>
</dependency>
</mapping>
<mapping>
<directory>/opt/nifi-registry/nifi-registry-${project.version}/lib/bootstrap</directory>
<dependency>
<includes>
<include>org.slf4j:slf4j-api</include>
<include>ch.qos.logback:logback-classic</include>
<include>ch.qos.logback:logback-core</include>
<include>org.apache.nifi.registry:nifi-registry-bootstrap</include>
</includes>
<excludes>
<exclude>org.apache.commons:commons-lang3</exclude>
<exclude>org.apache.nifi.registry:nifi-registry-utils</exclude>
</excludes>
</dependency>
</mapping>
<mapping>
<directory>/opt/nifi-registry/nifi-registry-${project.version}/lib/shared</directory>
<dependency>
<includes>
<include>org.apache.commons:commons-lang3</include>
<include>org.apache.nifi.registry:nifi-registry-utils</include>
</includes>
</dependency>
</mapping>
<mapping>
<directory>/opt/nifi-registry/nifi-registry-${project.version}/docs</directory>
<sources>
<source>
<location>${project.build.directory}/generated-docs</location>
</source>
</sources>
</mapping>
</mappings>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>include-ranger</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-ranger-assembly</artifactId>
<version>1.14.0-SNAPSHOT</version>
<classifier>bin</classifier>
<scope>runtime</scope>
<type>zip</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-ranger-extensions</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.build.directory}/ext/ranger</outputDirectory>
<includeGroupIds>org.apache.nifi.registry</includeGroupIds>
<includeArtifactIds>nifi-registry-ranger-assembly</includeArtifactIds>
<excludeTransitive>false</excludeTransitive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>include-aws</id>
<activation>
<property>
<name>!skipAws</name>
</property>
</activation>
<properties>
<nifi.registry.extension.dir.aws>./ext/aws/lib</nifi.registry.extension.dir.aws>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-aws-assembly</artifactId>
<version>1.14.0-SNAPSHOT</version>
<classifier>bin</classifier>
<scope>runtime</scope>
<type>zip</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-aws-extensions</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.build.directory}/ext/aws</outputDirectory>
<includeGroupIds>org.apache.nifi.registry</includeGroupIds>
<includeArtifactIds>nifi-registry-aws-assembly</includeArtifactIds>
<excludeTransitive>false</excludeTransitive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,187 @@
<?xml version="1.0"?>
<!--
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.
-->
<assembly>
<id>bin</id>
<formats>
<format>dir</format>
<format>zip</format>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<baseDirectory>nifi-registry-${project.version}</baseDirectory>
<dependencySets>
<!-- Write out the dependencies shared between bootstrap and the main app to lib/shared -->
<dependencySet>
<scope>runtime</scope>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>lib/shared</outputDirectory>
<directoryMode>0770</directoryMode>
<fileMode>0664</fileMode>
<useTransitiveFiltering>true</useTransitiveFiltering>
<includes>
<include>nifi-registry-utils</include>
<include>commons-lang3</include>
</includes>
</dependencySet>
<!-- Write out the bootstrap lib component to its own dir -->
<dependencySet>
<scope>runtime</scope>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>lib/bootstrap</outputDirectory>
<directoryMode>0770</directoryMode>
<fileMode>0664</fileMode>
<useTransitiveFiltering>true</useTransitiveFiltering>
<includes>
<include>nifi-registry-bootstrap</include>
<include>slf4j-api</include>
<include>logback-classic</include>
</includes>
</dependencySet>
<!-- Write out the libs for java11 to its own dir -->
<!-- TODO: remove these once minimum Java version is 11 and these can be bundle directly into the application -->
<dependencySet>
<scope>runtime</scope>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>lib/java11</outputDirectory>
<directoryMode>0770</directoryMode>
<fileMode>0664</fileMode>
<useTransitiveFiltering>true</useTransitiveFiltering>
<includes>
<include>jakarta.xml.bind:jakarta.xml.bind-api</include>
<include>org.glassfish.jaxb:jaxb-runtime</include>
</includes>
</dependencySet>
<!-- Write out all dependency artifacts to lib directory -->
<dependencySet>
<scope>runtime</scope>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>lib</outputDirectory>
<directoryMode>0770</directoryMode>
<fileMode>0664</fileMode>
<useTransitiveFiltering>true</useTransitiveFiltering>
<excludes>
<exclude>nifi-registry-resources</exclude>
<exclude>nifi-registry-bootstrap</exclude>
<exclude>nifi-registry-utils</exclude>
<exclude>nifi-registry-docs</exclude>
<exclude>nifi-registry-ranger-assembly</exclude>
<exclude>nifi-registry-aws-assembly</exclude>
<!-- Exclude any jaxb dependencies because they will be in lib/java11 -->
<!-- TODO: remove these once minimum Java version is 11 and these can be bundle directly into the application -->
<exclude>jakarta.xml.bind:jakarta.xml.bind-api</exclude>
<exclude>org.glassfish.jaxb:jaxb-runtime</exclude>
</excludes>
</dependencySet>
<!-- Write out the conf directory contents -->
<dependencySet>
<scope>runtime</scope>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>./</outputDirectory>
<directoryMode>0770</directoryMode>
<fileMode>0664</fileMode>
<useTransitiveFiltering>true</useTransitiveFiltering>
<includes>
<include>nifi-registry-resources</include>
</includes>
<unpack>true</unpack>
<unpackOptions>
<filtered>true</filtered>
<includes>
<include>conf/*</include>
</includes>
</unpackOptions>
</dependencySet>
<!-- Write out the bin directory contents -->
<dependencySet>
<scope>runtime</scope>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>./</outputDirectory>
<directoryMode>0770</directoryMode>
<fileMode>0770</fileMode>
<useTransitiveFiltering>true</useTransitiveFiltering>
<includes>
<include>nifi-registry-resources</include>
</includes>
<unpack>true</unpack>
<unpackOptions>
<filtered>true</filtered>
<includes>
<include>bin/*</include>
</includes>
</unpackOptions>
</dependencySet>
<!-- Writes out the docs directory contents -->
<dependencySet>
<scope>runtime</scope>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>docs/</outputDirectory>
<useTransitiveFiltering>true</useTransitiveFiltering>
<includes>
<include>nifi-registry-docs</include>
</includes>
<unpack>true</unpack>
<unpackOptions>
<filtered>false</filtered>
<excludes>
<!-- LICENSE and NOTICE both covered by top-level -->
<exclude>LICENSE</exclude>
<exclude>NOTICE</exclude>
</excludes>
</unpackOptions>
</dependencySet>
</dependencySets>
<files>
<file>
<source>./README.md</source>
<outputDirectory>./</outputDirectory>
<destName>README</destName>
<fileMode>0644</fileMode>
<filtered>true</filtered>
</file>
<file>
<source>./LICENSE</source>
<outputDirectory>./</outputDirectory>
<destName>LICENSE</destName>
<fileMode>0644</fileMode>
<filtered>true</filtered>
</file>
<file>
<source>./NOTICE</source>
<outputDirectory>./</outputDirectory>
<destName>NOTICE</destName>
<fileMode>0644</fileMode>
<filtered>true</filtered>
</file>
</files>
<fileSets>
<fileSet>
<!-- Extensions are extracted by maven-dependency-plugin defined in pom.xml -->
<directory>${project.build.directory}/ext</directory>
<outputDirectory>ext</outputDirectory>
</fileSet>
</fileSets>
</assembly>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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. -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-core</artifactId>
<version>1.14.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-registry-bootstrap</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-utils</artifactId>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>4.4.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,108 @@
/*
* 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.registry.bootstrap;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import org.apache.nifi.registry.bootstrap.exception.InvalidCommandException;
public class BootstrapCodec {
private final RunNiFiRegistry runner;
private final BufferedReader reader;
private final BufferedWriter writer;
public BootstrapCodec(final RunNiFiRegistry runner, final InputStream in, final OutputStream out) {
this.runner = runner;
this.reader = new BufferedReader(new InputStreamReader(in));
this.writer = new BufferedWriter(new OutputStreamWriter(out));
}
public void communicate() throws IOException {
final String line = reader.readLine();
final String[] splits = line.split(" ");
if (splits.length < 0) {
throw new IOException("Received invalid command from NiFi Registry: " + line);
}
final String cmd = splits[0];
final String[] args;
if (splits.length == 1) {
args = new String[0];
} else {
args = Arrays.copyOfRange(splits, 1, splits.length);
}
try {
processRequest(cmd, args);
} catch (final InvalidCommandException ice) {
throw new IOException("Received invalid command from NiFi Registry: " + line + (ice.getMessage() == null ? "" : " - Details: " + ice.toString()));
}
}
private void processRequest(final String cmd, final String[] args) throws InvalidCommandException, IOException {
switch (cmd) {
case "PORT": {
if (args.length != 2) {
throw new InvalidCommandException();
}
final int port;
try {
port = Integer.parseInt(args[0]);
} catch (final NumberFormatException nfe) {
throw new InvalidCommandException("Invalid Port number; should be integer between 1 and 65535");
}
if (port < 1 || port > 65535) {
throw new InvalidCommandException("Invalid Port number; should be integer between 1 and 65535");
}
final String secretKey = args[1];
runner.setNiFiRegistryCommandControlPort(port, secretKey);
writer.write("OK");
writer.newLine();
writer.flush();
}
break;
case "STARTED": {
if (args.length != 1) {
throw new InvalidCommandException("STARTED command must contain a status argument");
}
if (!"true".equals(args[0]) && !"false".equals(args[0])) {
throw new InvalidCommandException("Invalid status for STARTED command; should be true or false, but was '" + args[0] + "'");
}
final boolean started = Boolean.parseBoolean(args[0]);
runner.setNiFiRegistryStarted(started);
writer.write("OK");
writer.newLine();
writer.flush();
}
break;
}
}
}

View File

@ -0,0 +1,141 @@
/*
* 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.registry.bootstrap;
import org.apache.nifi.registry.bootstrap.util.LimitingInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
public class NiFiRegistryListener {
private ServerSocket serverSocket;
private volatile Listener listener;
int start(final RunNiFiRegistry runner) throws IOException {
serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress("localhost", 0));
final int localPort = serverSocket.getLocalPort();
listener = new Listener(serverSocket, runner);
final Thread listenThread = new Thread(listener);
listenThread.setName("Listen to NiFi Registry");
listenThread.setDaemon(true);
listenThread.start();
return localPort;
}
public void stop() throws IOException {
final Listener listener = this.listener;
if (listener == null) {
return;
}
listener.stop();
}
private class Listener implements Runnable {
private final ServerSocket serverSocket;
private final ExecutorService executor;
private final RunNiFiRegistry runner;
private volatile boolean stopped = false;
public Listener(final ServerSocket serverSocket, final RunNiFiRegistry runner) {
this.serverSocket = serverSocket;
this.executor = Executors.newFixedThreadPool(2, new ThreadFactory() {
@Override
public Thread newThread(final Runnable runnable) {
final Thread t = Executors.defaultThreadFactory().newThread(runnable);
t.setDaemon(true);
t.setName("NiFi Registry Bootstrap Command Listener");
return t;
}
});
this.runner = runner;
}
public void stop() throws IOException {
stopped = true;
executor.shutdown();
try {
executor.awaitTermination(3, TimeUnit.SECONDS);
} catch (final InterruptedException ie) {
}
serverSocket.close();
}
@Override
public void run() {
while (!serverSocket.isClosed()) {
try {
if (stopped) {
return;
}
final Socket socket;
try {
socket = serverSocket.accept();
} catch (final IOException ioe) {
if (stopped) {
return;
}
throw ioe;
}
executor.submit(new Runnable() {
@Override
public void run() {
try {
// we want to ensure that we don't try to read data from an InputStream directly
// by a BufferedReader because any user on the system could open a socket and send
// a multi-gigabyte file without any new lines in order to crash the Bootstrap,
// which in turn may cause the Shutdown Hook to shutdown NiFi.
// So we will limit the amount of data to read to 4 KB
final InputStream limitingIn = new LimitingInputStream(socket.getInputStream(), 4096);
final BootstrapCodec codec = new BootstrapCodec(runner, limitingIn, socket.getOutputStream());
codec.communicate();
} catch (final Throwable t) {
System.out.println("Failed to communicate with NiFi Registry due to " + t);
t.printStackTrace();
} finally {
try {
socket.close();
} catch (final IOException ioe) {
}
}
}
});
} catch (final Throwable t) {
System.err.println("Failed to receive information from NiFi Registry due to " + t);
t.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,97 @@
/*
* 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.registry.bootstrap;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
public class ShutdownHook extends Thread {
private final Process nifiRegistryProcess;
private final RunNiFiRegistry runner;
private final int gracefulShutdownSeconds;
private final ExecutorService executor;
private volatile String secretKey;
public ShutdownHook(final Process nifiRegistryProcess, final RunNiFiRegistry runner, final String secretKey, final int gracefulShutdownSeconds, final ExecutorService executor) {
this.nifiRegistryProcess = nifiRegistryProcess;
this.runner = runner;
this.secretKey = secretKey;
this.gracefulShutdownSeconds = gracefulShutdownSeconds;
this.executor = executor;
}
void setSecretKey(final String secretKey) {
this.secretKey = secretKey;
}
@Override
public void run() {
executor.shutdown();
runner.setAutoRestartNiFiRegistry(false);
final int ccPort = runner.getNiFiRegistryCommandControlPort();
if (ccPort > 0) {
System.out.println("Initiating Shutdown of NiFi Registry...");
try {
final Socket socket = new Socket("localhost", ccPort);
final OutputStream out = socket.getOutputStream();
out.write(("SHUTDOWN " + secretKey + "\n").getBytes(StandardCharsets.UTF_8));
out.flush();
socket.close();
} catch (final IOException ioe) {
System.out.println("Failed to Shutdown NiFi Registry due to " + ioe);
}
}
runner.notifyStop();
System.out.println("Waiting for Apache NiFi Registry to finish shutting down...");
final long startWait = System.nanoTime();
while (RunNiFiRegistry.isAlive(nifiRegistryProcess)) {
final long waitNanos = System.nanoTime() - startWait;
final long waitSeconds = TimeUnit.NANOSECONDS.toSeconds(waitNanos);
if (waitSeconds >= gracefulShutdownSeconds && gracefulShutdownSeconds > 0) {
if (RunNiFiRegistry.isAlive(nifiRegistryProcess)) {
System.out.println("NiFi Registry has not finished shutting down after " + gracefulShutdownSeconds + " seconds. Killing process.");
nifiRegistryProcess.destroy();
}
break;
} else {
try {
Thread.sleep(1000L);
} catch (final InterruptedException ie) {
}
}
}
try {
final File statusFile = runner.getStatusFile();
if (!statusFile.delete()) {
System.err.println("Failed to delete status file " + statusFile.getAbsolutePath() + "; this file should be cleaned up manually");
}
}catch (IOException ex){
System.err.println("Failed to retrieve status file " + ex);
}
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.registry.bootstrap.exception;
public class InvalidCommandException extends Exception {
private static final long serialVersionUID = 1L;
public InvalidCommandException() {
super();
}
public InvalidCommandException(final String message) {
super(message);
}
public InvalidCommandException(final Throwable t) {
super(t);
}
public InvalidCommandException(final String message, final Throwable t) {
super(message, t);
}
}

View File

@ -0,0 +1,107 @@
/*
* 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.registry.bootstrap.util;
import java.io.IOException;
import java.io.InputStream;
public class LimitingInputStream extends InputStream {
private final InputStream in;
private final long limit;
private long bytesRead = 0;
public LimitingInputStream(final InputStream in, final long limit) {
this.in = in;
this.limit = limit;
}
@Override
public int read() throws IOException {
if (bytesRead >= limit) {
return -1;
}
final int val = in.read();
if (val > -1) {
bytesRead++;
}
return val;
}
@Override
public int read(final byte[] b) throws IOException {
if (bytesRead >= limit) {
return -1;
}
final int maxToRead = (int) Math.min(b.length, limit - bytesRead);
final int val = in.read(b, 0, maxToRead);
if (val > 0) {
bytesRead += val;
}
return val;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (bytesRead >= limit) {
return -1;
}
final int maxToRead = (int) Math.min(len, limit - bytesRead);
final int val = in.read(b, off, maxToRead);
if (val > 0) {
bytesRead += val;
}
return val;
}
@Override
public long skip(final long n) throws IOException {
final long skipped = in.skip(Math.min(n, limit - bytesRead));
bytesRead += skipped;
return skipped;
}
@Override
public int available() throws IOException {
return in.available();
}
@Override
public void close() throws IOException {
in.close();
}
@Override
public void mark(int readlimit) {
in.mark(readlimit);
}
@Override
public boolean markSupported() {
return in.markSupported();
}
@Override
public void reset() throws IOException {
in.reset();
}
}

View File

@ -0,0 +1,134 @@
/*
* 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.registry.bootstrap.util;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinNT;
import org.slf4j.Logger;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* OS specific utilities with generic method interfaces
*/
public final class OSUtils {
/**
* @param process NiFi Process Reference
* @param logger Logger Reference for Debug
* @return Returns pid or null in-case pid could not be determined
* This method takes {@link Process} and {@link Logger} and returns
* the platform specific ProcessId for Unix like systems, a.k.a <b>pid</b>
* In-case it fails to determine the pid, it will return Null.
* Purpose for the Logger is to log any interaction for debugging.
*/
private static Long getUnicesPid(final Process process, final Logger logger) {
try {
final Class<?> procClass = process.getClass();
final Field pidField = procClass.getDeclaredField("pid");
pidField.setAccessible(true);
final Object pidObject = pidField.get(process);
logger.debug("PID Object = {}", pidObject);
if (pidObject instanceof Number) {
return ((Number) pidObject).longValue();
}
return null;
} catch (final IllegalAccessException | NoSuchFieldException nsfe) {
logger.debug("Could not find PID for child process due to {}", nsfe);
return null;
}
}
/**
* @param process NiFi Registry Process Reference
* @param logger Logger Reference for Debug
* @return Returns pid or null in-case pid could not be determined
* This method takes {@link Process} and {@link Logger} and returns
* the platform specific Handle for Win32 Systems, a.k.a <b>pid</b>
* In-case it fails to determine the pid, it will return Null.
* Purpose for the Logger is to log any interaction for debugging.
*/
private static Long getWindowsProcessId(final Process process, final Logger logger) {
/* determine the pid on windows plattforms */
try {
Field f = process.getClass().getDeclaredField("handle");
f.setAccessible(true);
long handl = f.getLong(process);
Kernel32 kernel = Kernel32.INSTANCE;
WinNT.HANDLE handle = new WinNT.HANDLE();
handle.setPointer(Pointer.createConstant(handl));
int ret = kernel.GetProcessId(handle);
logger.debug("Detected pid: {}", ret);
return Long.valueOf(ret);
} catch (final IllegalAccessException | NoSuchFieldException nsfe) {
logger.debug("Could not find PID for child process due to {}", nsfe);
}
return null;
}
/**
* @param process NiFi Process Reference
* @param logger Logger Reference for Debug
* @return Returns pid or null in-case pid could not be determined
* This method takes {@link Process} and {@link Logger} and returns
* the platform specific ProcessId for Unix like systems or Handle for Win32 Systems, a.k.a <b>pid</b>
* In-case it fails to determine the pid, it will return Null.
* Purpose for the Logger is to log any interaction for debugging.
*/
public static Long getProcessId(final Process process, final Logger logger) {
/*
* NiFi Registry built with Java 1.8 and running on Java 9. Reflectively invoke Process.pid() on the given process
* instance to get the PID of this Java process. Reflection is required in this scenario due to NiFi Registry being
* compiled on Java 1.8, which does not have the Process API improvements available in Java 9.
*
* Otherwise, if NiFi is running on Java 1.8, attempt to get PID using capabilities available on Java 1.8.
*
* TODO: When minimum Java version updated to Java 9+, this class should be removed with the addition
* of the pid method to the Process API.
*/
Long pid = null;
if (!System.getProperty("java.version").startsWith("1.")) {
try {
Method pidMethod = process.getClass().getMethod("pid");
pidMethod.setAccessible(true);
Object pidMethodResult = pidMethod.invoke(process);
if (Long.class.isAssignableFrom(pidMethodResult.getClass())) {
pid = (Long) pidMethodResult;
} else {
logger.debug("Could not determine PID for child process because returned PID was not " +
"assignable to type " + Long.class.getName());
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
logger.debug("Could not find PID for child process due to {}", e);
}
} else if (process.getClass().getName().equals("java.lang.UNIXProcess")) {
pid = getUnicesPid(process, logger);
} else if (process.getClass().getName().equals("java.lang.Win32Process")
|| process.getClass().getName().equals("java.lang.ProcessImpl")) {
pid = getWindowsProcessId(process, logger);
}
return pid;
}
}

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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. -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-core</artifactId>
<version>1.14.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-registry-bundle-utils</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-data-model</artifactId>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
<profiles>
<profile>
<!-- This profile provides configuration to allow NiFi Registry to be compiled on JDKs above 1.8. -->
<id>jigsaw</id>
<activation>
<jdk>(1.8,)</jdk>
</activation>
<dependencies>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,32 @@
/*
* 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.registry.bundle.extract;
/**
* Exception to be thrown from a BundleExtractor when an issue occurs during extraction.
*/
public class BundleException extends RuntimeException {
public BundleException(String message) {
super(message);
}
public BundleException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.registry.bundle.extract;
import org.apache.nifi.registry.bundle.model.BundleDetails;
import java.io.IOException;
import java.io.InputStream;
/**
* Extracts the bundle metadata from the given InputStream.
*/
public interface BundleExtractor {
/**
* @param inputStream the input stream of the binary bundle
* @return the bundle metadata extracted from the input stream
* @throws IOException if an error occurs reading from the InputStream
*/
BundleDetails extract(InputStream inputStream) throws IOException;
}

View File

@ -0,0 +1,36 @@
/*
* 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.registry.bundle.extract.minificpp;
import org.apache.nifi.registry.bundle.model.BundleDetails;
import org.apache.nifi.registry.bundle.extract.BundleExtractor;
import java.io.IOException;
import java.io.InputStream;
/**
* ExtensionBundleExtractor for MiNiFi CPP extensions.
*/
public class MiNiFiCppBundleExtractor implements BundleExtractor {
@Override
public BundleDetails extract(final InputStream inputStream) throws IOException {
// TODO implement
throw new UnsupportedOperationException("Minifi CPP extensions are not yet supported");
}
}

View File

@ -0,0 +1,234 @@
/*
* 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.registry.bundle.extract.nar;
import org.apache.nifi.registry.bundle.extract.BundleException;
import org.apache.nifi.registry.bundle.extract.BundleExtractor;
import org.apache.nifi.registry.bundle.extract.nar.docs.ExtensionManifestParser;
import org.apache.nifi.registry.bundle.extract.nar.docs.JacksonExtensionManifestParser;
import org.apache.nifi.registry.bundle.model.BundleIdentifier;
import org.apache.nifi.registry.bundle.model.BundleDetails;
import org.apache.nifi.registry.extension.bundle.BuildInfo;
import org.apache.nifi.registry.extension.component.manifest.ExtensionManifest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Implementation of ExtensionBundleExtractor for NAR bundles.
*/
public class NarBundleExtractor implements BundleExtractor {
/**
* The name of the JarEntry that contains the extension-docs.xml file.
*/
private static String EXTENSION_DESCRIPTOR_ENTRY = "META-INF/docs/extension-manifest.xml";
/**
* The pattern of a JarEntry for additionalDetails.html entries.
*/
private static Pattern ADDITIONAL_DETAILS_ENTRY_PATTERN =
Pattern.compile("META-INF\\/docs\\/additional-details\\/(.+)\\/additionalDetails.html");
/**
* The format of the date string in the NAR MANIFEST for Built-Timestamp.
*/
private static String BUILT_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
/**
* Used in place of any build info that is not present.
*/
static String NA = "N/A";
@Override
public BundleDetails extract(final InputStream inputStream) throws IOException {
try (final JarInputStream jarInputStream = new JarInputStream(inputStream)) {
final Manifest manifest = jarInputStream.getManifest();
if (manifest == null) {
throw new BundleException("NAR bundles must contain a valid MANIFEST");
}
final Attributes attributes = manifest.getMainAttributes();
final BundleIdentifier bundleIdentifier = getBundleCoordinate(attributes);
final BundleIdentifier dependencyCoordinate = getDependencyBundleCoordinate(attributes);
final BuildInfo buildInfo = getBuildInfo(attributes);
final BundleDetails.Builder builder = new BundleDetails.Builder()
.coordinate(bundleIdentifier)
.addDependencyCoordinate(dependencyCoordinate)
.buildInfo(buildInfo);
parseExtensionDocs(jarInputStream, builder);
return builder.build();
}
}
private BundleIdentifier getBundleCoordinate(final Attributes attributes) {
try {
final String groupId = attributes.getValue(NarManifestEntry.NAR_GROUP.getManifestName());
final String artifactId = attributes.getValue(NarManifestEntry.NAR_ID.getManifestName());
final String version = attributes.getValue(NarManifestEntry.NAR_VERSION.getManifestName());
return new BundleIdentifier(groupId, artifactId, version);
} catch (Exception e) {
throw new BundleException("Unable to obtain bundle coordinate due to: " + e.getMessage(), e);
}
}
private BundleIdentifier getDependencyBundleCoordinate(final Attributes attributes) {
try {
final String dependencyGroupId = attributes.getValue(NarManifestEntry.NAR_DEPENDENCY_GROUP.getManifestName());
final String dependencyArtifactId = attributes.getValue(NarManifestEntry.NAR_DEPENDENCY_ID.getManifestName());
final String dependencyVersion = attributes.getValue(NarManifestEntry.NAR_DEPENDENCY_VERSION.getManifestName());
final BundleIdentifier dependencyCoordinate;
if (dependencyArtifactId != null) {
dependencyCoordinate = new BundleIdentifier(dependencyGroupId, dependencyArtifactId, dependencyVersion);
} else {
dependencyCoordinate = null;
}
return dependencyCoordinate;
} catch (Exception e) {
throw new BundleException("Unable to obtain bundle coordinate for dependency due to: " + e.getMessage(), e);
}
}
private BuildInfo getBuildInfo(final Attributes attributes) {
final String buildBranch = attributes.getValue(NarManifestEntry.BUILD_BRANCH.getManifestName());
final String buildTag = attributes.getValue(NarManifestEntry.BUILD_TAG.getManifestName());
final String buildRevision = attributes.getValue(NarManifestEntry.BUILD_REVISION.getManifestName());
final String buildTimestamp = attributes.getValue(NarManifestEntry.BUILD_TIMESTAMP.getManifestName());
final String buildJdk = attributes.getValue(NarManifestEntry.BUILD_JDK.getManifestName());
final String builtBy = attributes.getValue(NarManifestEntry.BUILT_BY.getManifestName());
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(BUILT_TIMESTAMP_FORMAT);
try {
final Date buildDate = simpleDateFormat.parse(buildTimestamp);
final BuildInfo buildInfo = new BuildInfo();
buildInfo.setBuildTool(isBlank(buildJdk) ? NA : buildJdk);
buildInfo.setBuildBranch(isBlank(buildBranch) ? NA : buildBranch);
buildInfo.setBuildTag(isBlank(buildTag) ? NA : buildTag);
buildInfo.setBuildRevision(isBlank(buildRevision) ? NA : buildRevision);
buildInfo.setBuilt(buildDate.getTime());
buildInfo.setBuiltBy(isBlank(builtBy) ? NA : builtBy);
buildInfo.setBuildFlags(NA);
return buildInfo;
} catch (ParseException e) {
throw new BundleException("Unable to parse " + NarManifestEntry.BUILD_TIMESTAMP.getManifestName(), e);
} catch (Exception e) {
throw new BundleException("Unable to create build info for bundle due to: " + e.getMessage(), e);
}
}
public boolean isBlank(String value) {
return (value == null || value.trim().isEmpty());
}
private void parseExtensionDocs(final JarInputStream jarInputStream, final BundleDetails.Builder builder) throws IOException {
JarEntry jarEntry;
boolean foundExtensionDocs = false;
while((jarEntry = jarInputStream.getNextJarEntry()) != null) {
final String jarEntryName = jarEntry.getName();
if (EXTENSION_DESCRIPTOR_ENTRY.equals(jarEntryName)) {
try {
final byte[] rawDocsContent = toByteArray(jarInputStream);
final ExtensionManifestParser docsParser = new JacksonExtensionManifestParser();
final InputStream inputStream = new NonCloseableInputStream(new ByteArrayInputStream(rawDocsContent));
final ExtensionManifest extensionManifest = docsParser.parse(inputStream);
builder.addExtensions(extensionManifest.getExtensions());
builder.systemApiVersion(extensionManifest.getSystemApiVersion());
foundExtensionDocs = true;
} catch (Exception e) {
throw new BundleException("Unable to obtain extension info for bundle due to: " + e.getMessage(), e);
}
} else {
final Matcher matcher = ADDITIONAL_DETAILS_ENTRY_PATTERN.matcher(jarEntryName);
if (matcher.matches()) {
final String extensionName = matcher.group(1);
final String additionalDetailsContent = new String(toByteArray(jarInputStream), StandardCharsets.UTF_8);
builder.addAdditionalDetails(extensionName, additionalDetailsContent);
}
}
}
if (!foundExtensionDocs) {
throw new BundleException("Unable to find descriptor at '" + EXTENSION_DESCRIPTOR_ENTRY + "'. " +
"This NAR may need to be rebuilt with the latest version of the NiFi NAR Maven Plugin.");
}
}
private byte[] toByteArray(final InputStream input) throws IOException {
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = input.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
return buffer.toByteArray();
}
private static class NonCloseableInputStream extends FilterInputStream {
private final InputStream toWrap;
public NonCloseableInputStream(final InputStream toWrap) {
super(toWrap);
this.toWrap = toWrap;
}
@Override
public int read() throws IOException {
return toWrap.read();
}
@Override
public int read(byte[] b) throws IOException {
return toWrap.read(b);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return toWrap.read(b, off, len);
}
@Override
public void close() throws IOException {
// do nothing
}
}
}

View File

@ -0,0 +1,48 @@
/*
* 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.registry.bundle.extract.nar;
/**
* Enumeration of entries that will be in a NAR MANIFEST file.
*/
public enum NarManifestEntry {
NAR_GROUP("Nar-Group"),
NAR_ID("Nar-Id"),
NAR_VERSION("Nar-Version"),
NAR_DEPENDENCY_GROUP("Nar-Dependency-Group"),
NAR_DEPENDENCY_ID("Nar-Dependency-Id"),
NAR_DEPENDENCY_VERSION("Nar-Dependency-Version"),
BUILD_TAG("Build-Tag"),
BUILD_REVISION("Build-Revision"),
BUILD_BRANCH("Build-Branch"),
BUILD_TIMESTAMP("Build-Timestamp"),
BUILD_JDK("Build-Jdk"),
BUILT_BY("Built-By"),
;
final String manifestName;
NarManifestEntry(String manifestName) {
this.manifestName = manifestName;
}
public String getManifestName() {
return manifestName;
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.registry.bundle.extract.nar.docs;
import org.apache.nifi.registry.extension.component.manifest.ExtensionManifest;
import java.io.InputStream;
/**
* Parses an InputStream that is expected to contain the content of META-INF/docs/extensions-manifest.xml from a NAR.
*/
public interface ExtensionManifestParser {
ExtensionManifest parse(InputStream inputStream);
}

View File

@ -0,0 +1,50 @@
/*
* 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.registry.bundle.extract.nar.docs;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
import org.apache.nifi.registry.bundle.extract.BundleException;
import org.apache.nifi.registry.extension.component.manifest.ExtensionManifest;
import java.io.IOException;
import java.io.InputStream;
/**
* Implementation of {@link ExtensionManifestParser} that uses Jackson XML to unmarshall the extension-manifest.xml content.
*/
public class JacksonExtensionManifestParser implements ExtensionManifestParser {
private final ObjectMapper mapper;
public JacksonExtensionManifestParser() {
this.mapper = new XmlMapper();
this.mapper.registerModule(new JaxbAnnotationModule());
this.mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
}
@Override
public ExtensionManifest parse(InputStream inputStream) {
try {
return mapper.readValue(inputStream, ExtensionManifest.class);
} catch (IOException e) {
throw new BundleException("Unable to parse extension manifest due to: " + e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,145 @@
/*
* 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.registry.bundle.model;
import org.apache.nifi.registry.bundle.extract.BundleExtractor;
import org.apache.nifi.registry.extension.bundle.BuildInfo;
import org.apache.nifi.registry.extension.component.manifest.Extension;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.apache.nifi.registry.bundle.util.BundleUtils.validateNotNull;
/**
* Details for a given bundle which are obtained from a given {@link BundleExtractor}.
*/
public class BundleDetails {
private final BundleIdentifier bundleIdentifier;
private final Set<BundleIdentifier> dependencies;
private final String systemApiVersion;
private final Set<Extension> extensions;
private final Map<String,String> additionalDetails;
private final BuildInfo buildInfo;
private BundleDetails(final Builder builder) {
this.bundleIdentifier = builder.bundleIdentifier;
this.dependencies = Collections.unmodifiableSet(new HashSet<>(builder.dependencies));
this.extensions = Collections.unmodifiableSet(new HashSet<>(builder.extensions));
this.additionalDetails = Collections.unmodifiableMap(new HashMap<>(builder.additionalDetails));
this.systemApiVersion = builder.systemApiVersion;
this.buildInfo = builder.buildInfo;
validateNotNull("Bundle Coordinate", this.bundleIdentifier);
validateNotNull("Dependency Coordinates", this.dependencies);
validateNotNull("Extension Details", this.extensions);
validateNotNull("System API Version", this.systemApiVersion);
validateNotNull("Build Details", this.buildInfo);
}
public BundleIdentifier getBundleIdentifier() {
return bundleIdentifier;
}
public Set<BundleIdentifier> getDependencies() {
return dependencies;
}
public String getSystemApiVersion() {
return systemApiVersion;
}
public Set<Extension> getExtensions() {
return extensions;
}
public Map<String, String> getAdditionalDetails() {
return additionalDetails;
}
public BuildInfo getBuildInfo() {
return buildInfo;
}
/**
* Builder for creating instances of BundleDetails.
*/
public static class Builder {
private BundleIdentifier bundleIdentifier;
private Set<BundleIdentifier> dependencies = new HashSet<>();
private Set<Extension> extensions = new HashSet<>();
private Map<String,String> additionalDetails = new HashMap<>();
private BuildInfo buildInfo;
private String systemApiVersion;
public Builder coordinate(final BundleIdentifier bundleIdentifier) {
this.bundleIdentifier = bundleIdentifier;
return this;
}
public Builder addDependencyCoordinate(final BundleIdentifier dependencyCoordinate) {
if (dependencyCoordinate != null) {
this.dependencies.add(dependencyCoordinate);
}
return this;
}
public Builder systemApiVersion(final String systemApiVersion) {
this.systemApiVersion = systemApiVersion;
return this;
}
public Builder addExtension(final Extension extension) {
if (extension != null) {
this.extensions.add(extension);
}
return this;
}
public Builder addExtensions(final List<Extension> extensions) {
if (extensions != null) {
this.extensions.addAll(extensions);
}
return this;
}
public Builder addAdditionalDetails(final String extensionName, final String additionalDetails) {
this.additionalDetails.put(extensionName, additionalDetails);
return this;
}
public Builder buildInfo(final BuildInfo buildInfo) {
this.buildInfo = buildInfo;
return this;
}
public BundleDetails build() {
return new BundleDetails(this);
}
}
}

View File

@ -0,0 +1,83 @@
/*
* 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.registry.bundle.model;
import static org.apache.nifi.registry.bundle.util.BundleUtils.validateNotBlank;
/**
* The identifier of an extension bundle (i.e group + artifact + version).
*/
public class BundleIdentifier {
private final String groupId;
private final String artifactId;
private final String version;
private final String identifier;
public BundleIdentifier(final String groupId, final String artifactId, final String version) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
validateNotBlank("Group Id", this.groupId);
validateNotBlank("Artifact Id", this.artifactId);
validateNotBlank("Version", this.version);
this.identifier = this.groupId + ":" + this.artifactId + ":" + this.version;
}
public String getGroupId() {
return groupId;
}
public String getArtifactId() {
return artifactId;
}
public String getVersion() {
return version;
}
public final String getIdentifier() {
return identifier;
}
@Override
public String toString() {
return identifier;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof BundleIdentifier)) {
return false;
}
final BundleIdentifier other = (BundleIdentifier) obj;
return getIdentifier().equals(other.getIdentifier());
}
@Override
public int hashCode() {
return 37 * this.identifier.hashCode();
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.registry.bundle.util;
public class BundleUtils {
public static boolean isBlank(final String value) {
return (value == null || value.trim().isEmpty());
}
public static void validateNotNull(String fieldName, Object value) {
if (value == null) {
throw new IllegalArgumentException(fieldName + " is required");
}
}
public static void validateNotBlank(String fieldName, String value) {
if (isBlank(value)) {
throw new IllegalArgumentException(fieldName + " is required");
}
}
}

View File

@ -0,0 +1,154 @@
/*
* 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.registry.bundle.extract.nar;
import org.apache.nifi.registry.bundle.extract.BundleException;
import org.apache.nifi.registry.bundle.extract.BundleExtractor;
import org.apache.nifi.registry.bundle.model.BundleIdentifier;
import org.apache.nifi.registry.bundle.model.BundleDetails;
import org.apache.nifi.registry.extension.bundle.BuildInfo;
import org.junit.Before;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class TestNarBundleExtractor {
private BundleExtractor extractor;
@Before
public void setup() {
this.extractor = new NarBundleExtractor();
}
@Test
public void testExtractFromGoodNarNoDependencies() throws IOException {
try (final InputStream in = new FileInputStream("src/test/resources/nars/nifi-framework-nar.nar")) {
final BundleDetails bundleDetails = extractor.extract(in);
assertNotNull(bundleDetails);
assertNotNull(bundleDetails.getBundleIdentifier());
assertNotNull(bundleDetails.getDependencies());
assertEquals(0, bundleDetails.getDependencies().size());
final BundleIdentifier bundleIdentifier = bundleDetails.getBundleIdentifier();
assertEquals("org.apache.nifi", bundleIdentifier.getGroupId());
assertEquals("nifi-framework-nar", bundleIdentifier.getArtifactId());
assertEquals("1.8.0", bundleIdentifier.getVersion());
assertNotNull(bundleDetails.getExtensions());
assertEquals(0, bundleDetails.getExtensions().size());
assertEquals("1.8.0", bundleDetails.getSystemApiVersion());
}
}
@Test
public void testExtractFromGoodNarWithDependencies() throws IOException {
try (final InputStream in = new FileInputStream("src/test/resources/nars/nifi-foo-nar.nar")) {
final BundleDetails bundleDetails = extractor.extract(in);
assertNotNull(bundleDetails);
assertNotNull(bundleDetails.getBundleIdentifier());
assertNotNull(bundleDetails.getDependencies());
assertEquals(1, bundleDetails.getDependencies().size());
final BundleIdentifier bundleIdentifier = bundleDetails.getBundleIdentifier();
assertEquals("org.apache.nifi", bundleIdentifier.getGroupId());
assertEquals("nifi-foo-nar", bundleIdentifier.getArtifactId());
assertEquals("1.8.0", bundleIdentifier.getVersion());
final BundleIdentifier dependencyCoordinate = bundleDetails.getDependencies().stream().findFirst().get();
assertEquals("org.apache.nifi", dependencyCoordinate.getGroupId());
assertEquals("nifi-bar-nar", dependencyCoordinate.getArtifactId());
assertEquals("2.0.0", dependencyCoordinate.getVersion());
final Map<String,String> additionalDetails = bundleDetails.getAdditionalDetails();
assertNotNull(additionalDetails);
assertEquals(0, additionalDetails.size());
}
}
@Test(expected = BundleException.class)
public void testExtractFromNarMissingRequiredManifestEntries() throws IOException {
try (final InputStream in = new FileInputStream("src/test/resources/nars/nifi-missing-manifest-entries.nar")) {
extractor.extract(in);
fail("Should have thrown exception");
}
}
@Test(expected = BundleException.class)
public void testExtractFromNarMissingManifest() throws IOException {
try (final InputStream in = new FileInputStream("src/test/resources/nars/nifi-missing-manifest.nar")) {
extractor.extract(in);
fail("Should have thrown exception");
}
}
@Test(expected = BundleException.class)
public void testExtractFromNarMissingExtensionDescriptor() throws IOException {
try (final InputStream in = new FileInputStream("src/test/resources/nars/nifi-foo-nar-missing-extension-descriptor.nar")) {
extractor.extract(in);
fail("Should have thrown exception");
}
}
@Test
public void testExtractFromNarWithDescriptorAndAdditionalDetails() throws IOException {
try (final InputStream in = new FileInputStream("src/test/resources/nars/nifi-hadoop-nar.nar")) {
final BundleDetails bundleDetails = extractor.extract(in);
assertNotNull(bundleDetails);
assertNotNull(bundleDetails.getBundleIdentifier());
assertNotNull(bundleDetails.getDependencies());
assertEquals(1, bundleDetails.getDependencies().size());
final BundleIdentifier bundleIdentifier = bundleDetails.getBundleIdentifier();
assertEquals("org.apache.nifi", bundleIdentifier.getGroupId());
assertEquals("nifi-hadoop-nar", bundleIdentifier.getArtifactId());
assertEquals("1.9.0-SNAPSHOT", bundleIdentifier.getVersion());
final BuildInfo buildDetails = bundleDetails.getBuildInfo();
assertNotNull(buildDetails);
assertEquals("1.8.0_162", buildDetails.getBuildTool());
assertEquals(NarBundleExtractor.NA, buildDetails.getBuildFlags());
assertEquals("master", buildDetails.getBuildBranch());
assertEquals("HEAD", buildDetails.getBuildTag());
assertEquals("1a937b6", buildDetails.getBuildRevision());
assertEquals("jsmith", buildDetails.getBuiltBy());
assertNotNull(buildDetails.getBuilt());
assertEquals("1.10.0-SNAPSHOT", bundleDetails.getSystemApiVersion());
assertNotNull(bundleDetails.getExtensions());
assertEquals(10, bundleDetails.getExtensions().size());
final Map<String,String> additionalDetails = bundleDetails.getAdditionalDetails();
assertNotNull(additionalDetails);
assertEquals(3, additionalDetails.size());
final String listHdfsKey = "org.apache.nifi.processors.hadoop.ListHDFS";
assertTrue(additionalDetails.containsKey(listHdfsKey));
assertTrue(additionalDetails.get(listHdfsKey).startsWith("<!DOCTYPE html>"));
assertTrue(additionalDetails.get(listHdfsKey).trim().endsWith("</html>"));
}
}
}

View File

@ -0,0 +1,161 @@
/*
* 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.registry.bundle.extract.nar.docs;
import org.apache.nifi.registry.extension.component.manifest.Extension;
import org.apache.nifi.registry.extension.component.manifest.ExtensionManifest;
import org.apache.nifi.registry.extension.component.manifest.ExtensionType;
import org.apache.nifi.registry.extension.component.manifest.ProvidedServiceAPI;
import org.apache.nifi.registry.extension.component.manifest.Restriction;
import org.junit.Before;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class TestJacksonExtensionManifestParser {
private ExtensionManifestParser parser;
@Before
public void setup() {
parser = new JacksonExtensionManifestParser();
}
@Test
public void testDocsWithProcessors() throws IOException {
final ExtensionManifest extensionManifest = parse("src/test/resources/descriptors/extension-manifest-hadoop-nar.xml");
assertNotNull(extensionManifest);
assertEquals("1.10.0-SNAPSHOT", extensionManifest.getSystemApiVersion());
final List<Extension> extensionDetails = extensionManifest.getExtensions();
assertEquals(10, extensionDetails.size());
final Extension putHdfsExtension = extensionDetails.stream()
.filter(e -> e.getName().equals("org.apache.nifi.processors.hadoop.PutHDFS"))
.findFirst()
.orElse(null);
assertNotNull(putHdfsExtension);
assertEquals(ExtensionType.PROCESSOR, putHdfsExtension.getType());
assertEquals("Write FlowFile data to Hadoop Distributed File System (HDFS)", putHdfsExtension.getDescription());
assertEquals(5, putHdfsExtension.getTags().size());
assertTrue(putHdfsExtension.getTags().contains("hadoop"));
assertTrue(putHdfsExtension.getTags().contains("HDFS"));
assertTrue(putHdfsExtension.getTags().contains("put"));
assertTrue(putHdfsExtension.getTags().contains("copy"));
assertTrue(putHdfsExtension.getTags().contains("filesystem"));
assertNull(putHdfsExtension.getProvidedServiceAPIs());
assertNotNull(putHdfsExtension.getProperties());
assertEquals(15, putHdfsExtension.getProperties().size());
assertNull(putHdfsExtension.getRestricted().getGeneralRestrictionExplanation());
final List<Restriction> restrictions = putHdfsExtension.getRestricted().getRestrictions();
assertNotNull(restrictions);
assertEquals(1, restrictions.size());
final Restriction restriction = restrictions.stream().findFirst().orElse(null);
assertEquals("write filesystem", restriction.getRequiredPermission());
assertEquals("Provides operator the ability to delete any file that NiFi has access to in HDFS or\n" +
" the local filesystem.", restriction.getExplanation().trim());
}
@Test
public void testDocsWithControllerService() throws IOException {
final ExtensionManifest extensionManifest = parse("src/test/resources/descriptors/extension-manifest-dbcp-service-nar.xml");
assertNotNull(extensionManifest);
assertEquals("1.10.0-SNAPSHOT", extensionManifest.getSystemApiVersion());
final List<Extension> extensions = extensionManifest.getExtensions();
assertEquals(2, extensions.size());
final Extension dbcpPoolExtension = extensions.stream()
.filter(e -> e.getName().equals("org.apache.nifi.dbcp.DBCPConnectionPool"))
.findFirst()
.orElse(null);
assertNotNull(dbcpPoolExtension);
assertEquals(ExtensionType.CONTROLLER_SERVICE, dbcpPoolExtension.getType());
assertEquals("Provides Database Connection Pooling Service. Connections can be asked from pool and returned\n" +
" after usage.", dbcpPoolExtension.getDescription().trim());
assertEquals(6, dbcpPoolExtension.getTags().size());
assertEquals(1, dbcpPoolExtension.getProvidedServiceAPIs().size());
final ProvidedServiceAPI providedServiceApi = dbcpPoolExtension.getProvidedServiceAPIs().iterator().next();
assertNotNull(providedServiceApi);
assertEquals("org.apache.nifi.dbcp.DBCPService", providedServiceApi.getClassName());
assertEquals("org.apache.nifi", providedServiceApi.getGroupId());
assertEquals("nifi-standard-services-api-nar", providedServiceApi.getArtifactId());
assertEquals("1.10.0-SNAPSHOT", providedServiceApi.getVersion());
}
@Test
public void testDocsWithReportingTask() throws IOException {
final ExtensionManifest extensionManifest = parse("src/test/resources/descriptors/extension-manifest-ambari-nar.xml");
assertNotNull(extensionManifest);
assertEquals("1.10.0-SNAPSHOT", extensionManifest.getSystemApiVersion());
final List<Extension> extensions = extensionManifest.getExtensions();
assertEquals(1, extensions.size());
final Extension reportingTask = extensions.stream()
.filter(e -> e.getName().equals("org.apache.nifi.reporting.ambari.AmbariReportingTask"))
.findFirst()
.orElse(null);
assertNotNull(reportingTask);
assertEquals(ExtensionType.REPORTING_TASK, reportingTask.getType());
assertNotNull(reportingTask.getDescription());
assertEquals(3, reportingTask.getTags().size());
assertTrue(reportingTask.getTags().contains("reporting"));
assertTrue(reportingTask.getTags().contains("metrics"));
assertTrue(reportingTask.getTags().contains("ambari"));
assertNull(reportingTask.getProvidedServiceAPIs());
}
@Test
public void testDocsForTestComponents() throws IOException {
final ExtensionManifest extensionManifest = parse("src/test/resources/descriptors/extension-manifest-test-components.xml");
assertNotNull(extensionManifest);
assertEquals("1.8.0", extensionManifest.getSystemApiVersion());
final List<Extension> extensionDetails = extensionManifest.getExtensions();
assertEquals(4, extensionDetails.size());
}
@Test
public void testDocsForMissingSystemApi() throws IOException {
final ExtensionManifest extensionManifest = parse("src/test/resources/descriptors/extension-manifest-missing-sys-api.xml");
assertNotNull(extensionManifest);
}
private ExtensionManifest parse(final String file) throws IOException {
try (final InputStream inputStream = new FileInputStream(file)) {
return parser.parse(inputStream);
}
}
}

View File

@ -0,0 +1,84 @@
<extensionManifest>
<systemApiVersion>1.10.0-SNAPSHOT</systemApiVersion>
<extensions>
<extension>
<name>org.apache.nifi.reporting.ambari.AmbariReportingTask</name>
<type>REPORTING_TASK</type>
<deprecationNotice/>
<description>Publishes metrics from NiFi to Ambari Metrics Service (AMS). Due to how the Ambari Metrics
Service works, this reporting task should be scheduled to run every 60 seconds. Each iteration it will
send the metrics from the previous iteration, and calculate the current metrics to be sent on next
iteration. Scheduling this reporting task at a frequency other than 60 seconds may produce unexpected
results.
</description>
<tags>
<tag>reporting</tag>
<tag>ambari</tag>
<tag>metrics</tag>
</tags>
<properties>
<property>
<name>Metrics Collector URL</name>
<displayName>Metrics Collector URL</displayName>
<description>The URL of the Ambari Metrics Collector Service</description>
<defaultValue>http://localhost:6188/ws/v1/timeline/metrics</defaultValue>
<allowableValues></allowableValues>
<required>true</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>Application ID</name>
<displayName>Application ID</displayName>
<description>The Application ID to be included in the metrics sent to Ambari</description>
<defaultValue>nifi</defaultValue>
<allowableValues></allowableValues>
<required>true</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>Hostname</name>
<displayName>Hostname</displayName>
<description>The Hostname of this NiFi instance to be included in the metrics sent to Ambari
</description>
<defaultValue>${hostname(true)}</defaultValue>
<allowableValues></allowableValues>
<required>true</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>Process Group ID</name>
<displayName>Process Group ID</displayName>
<description>If specified, the reporting task will send metrics about this process group only. If
not, the root process group is used and global metrics are sent.
</description>
<defaultValue></defaultValue>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
</properties>
<dynamicProperties></dynamicProperties>
<stateful></stateful>
<restricted></restricted>
<inputRequirement></inputRequirement>
<systemResourceConsiderations></systemResourceConsiderations>
<seeAlso/>
</extension>
</extensions>
</extensionManifest>

View File

@ -0,0 +1,325 @@
<extensionManifest>
<systemApiVersion>1.10.0-SNAPSHOT</systemApiVersion>
<extensions>
<extension>
<name>org.apache.nifi.dbcp.DBCPConnectionPoolLookup</name>
<type>CONTROLLER_SERVICE</type>
<deprecationNotice/>
<description>Provides a DBCPService that can be used to dynamically select another DBCPService. This service
requires an attribute named 'database.name' to be passed in when asking for a connection, and will throw
an exception if the attribute is missing. The value of 'database.name' will be used to select the
DBCPService that has been registered with that name. This will allow multiple DBCPServices to be defined
and registered, and then selected dynamically at runtime by tagging flow files with the appropriate
'database.name' attribute.
</description>
<tags>
<tag>dbcp</tag>
<tag>jdbc</tag>
<tag>database</tag>
<tag>connection</tag>
<tag>pooling</tag>
<tag>store</tag>
</tags>
<properties></properties>
<dynamicProperties>
<dynamicProperty>
<name>The</name>
<value>JDBC property value</value>
<description></description>
<expressionLanguageSupported>false</expressionLanguageSupported>
<expressionLanguageScope>NONE</expressionLanguageScope>
</dynamicProperty>
</dynamicProperties>
<stateful></stateful>
<restricted></restricted>
<inputRequirement></inputRequirement>
<systemResourceConsiderations></systemResourceConsiderations>
<seeAlso/>
<providedServiceAPIs>
<providedServiceAPI>
<className>org.apache.nifi.dbcp.DBCPService</className>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-standard-services-api-nar</artifactId>
<version>1.10.0-SNAPSHOT</version>
</providedServiceAPI>
</providedServiceAPIs>
</extension>
<extension>
<name>org.apache.nifi.dbcp.DBCPConnectionPool</name>
<type>CONTROLLER_SERVICE</type>
<deprecationNotice/>
<description>Provides Database Connection Pooling Service. Connections can be asked from pool and returned
after usage.
</description>
<tags>
<tag>dbcp</tag>
<tag>jdbc</tag>
<tag>database</tag>
<tag>connection</tag>
<tag>pooling</tag>
<tag>store</tag>
</tags>
<properties>
<property>
<name>Database Connection URL</name>
<displayName>Database Connection URL</displayName>
<description>A database connection URL used to connect to a database. May contain database system
name, host, port, database name and some parameters. The exact syntax of a database connection
URL is specified by your DBMS.
</description>
<defaultValue></defaultValue>
<allowableValues></allowableValues>
<required>true</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>Database Driver Class Name</name>
<displayName>Database Driver Class Name</displayName>
<description>Database driver class name</description>
<defaultValue></defaultValue>
<allowableValues></allowableValues>
<required>true</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>database-driver-locations</name>
<displayName>Database Driver Location(s)</displayName>
<description>Comma-separated list of files/folders and/or URLs containing the driver JAR and its
dependencies (if any). For example '/var/tmp/mariadb-java-client-1.1.7.jar'
</description>
<defaultValue></defaultValue>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>kerberos-credentials-service</name>
<displayName>Kerberos Credentials Service</displayName>
<description>Specifies the Kerberos Credentials Controller Service that should be used for
authenticating with Kerberos
</description>
<defaultValue></defaultValue>
<controllerServiceDefinition>
<className>org.apache.nifi.kerberos.KerberosCredentialsService</className>
<groupId>org.apache.nifi</groupId>
<artifactId>org.apache.nifi:nifi-standard-services-api-nar:nar:1.10.0-SNAPSHOT</artifactId>
<version>1.10.0-SNAPSHOT</version>
</controllerServiceDefinition>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>false</expressionLanguageSupported>
<expressionLanguageScope>NONE</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>Database User</name>
<displayName>Database User</displayName>
<description>Database user name</description>
<defaultValue></defaultValue>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>Password</name>
<displayName>Password</displayName>
<description>The password for the database user</description>
<defaultValue></defaultValue>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>true</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>Max Wait Time</name>
<displayName>Max Wait Time</displayName>
<description>The maximum amount of time that the pool will wait (when there are no available
connections) for a connection to be returned before failing, or -1 to wait indefinitely.
</description>
<defaultValue>500 millis</defaultValue>
<allowableValues></allowableValues>
<required>true</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>false</expressionLanguageSupported>
<expressionLanguageScope>NONE</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>Max Total Connections</name>
<displayName>Max Total Connections</displayName>
<description>The maximum number of active connections that can be allocated from this pool at the
same time, or negative for no limit.
</description>
<defaultValue>8</defaultValue>
<allowableValues></allowableValues>
<required>true</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>false</expressionLanguageSupported>
<expressionLanguageScope>NONE</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>Validation-query</name>
<displayName>Validation query</displayName>
<description>Validation query used to validate connections before returning them. When connection is
invalid, it get's dropped and new valid connection will be returned. Note!! Using validation
might have some performance penalty.
</description>
<defaultValue></defaultValue>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>dbcp-min-idle-conns</name>
<displayName>Minimum Idle Connections</displayName>
<description>The minimum number of connections that can remain idle in the pool, without extra ones
being created, or zero to create none.
</description>
<defaultValue>0</defaultValue>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>dbcp-max-idle-conns</name>
<displayName>Max Idle Connections</displayName>
<description>The maximum number of connections that can remain idle in the pool, without extra ones
being released, or negative for no limit.
</description>
<defaultValue>8</defaultValue>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>dbcp-max-conn-lifetime</name>
<displayName>Max Connection Lifetime</displayName>
<description>The maximum lifetime in milliseconds of a connection. After this time is exceeded the
connection will fail the next activation, passivation or validation test. A value of zero or
less means the connection has an infinite lifetime.
</description>
<defaultValue>-1</defaultValue>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>dbcp-time-between-eviction-runs</name>
<displayName>Time Between Eviction Runs</displayName>
<description>The number of milliseconds to sleep between runs of the idle connection evictor thread.
When non-positive, no idle connection evictor thread will be run.
</description>
<defaultValue>-1</defaultValue>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>dbcp-min-evictable-idle-time</name>
<displayName>Minimum Evictable Idle Time</displayName>
<description>The minimum amount of time a connection may sit idle in the pool before it is eligible
for eviction.
</description>
<defaultValue>30 mins</defaultValue>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
<property>
<name>dbcp-soft-min-evictable-idle-time</name>
<displayName>Soft Minimum Evictable Idle Time</displayName>
<description>The minimum amount of time a connection may sit idle in the pool before it is eligible
for eviction by the idle connection evictor, with the extra condition that at least a minimum
number of idle connections remain in the pool. When the not-soft version of this option is set
to a positive value, it is examined first by the idle connection evictor: when idle connections
are visited by the evictor, idle time is first compared against it (without considering the
number of idle connections in the pool) and then against this soft option, including the minimum
idle connections constraint.
</description>
<defaultValue>-1</defaultValue>
<allowableValues></allowableValues>
<required>false</required>
<sensitive>false</sensitive>
<expressionLanguageSupported>true</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
<dynamic>false</dynamic>
</property>
</properties>
<dynamicProperties>
<dynamicProperty>
<name>JDBC property name</name>
<value>JDBC property value</value>
<description>Specifies a property name and value to be set on the JDBC connection(s). If Expression
Language is used, evaluation will be performed upon the controller service being enabled. Note
that no flow file input (attributes, e.g.) is available for use in Expression Language
constructs for these properties.
</description>
<expressionLanguageSupported>false</expressionLanguageSupported>
<expressionLanguageScope>VARIABLE_REGISTRY</expressionLanguageScope>
</dynamicProperty>
</dynamicProperties>
<stateful></stateful>
<restricted></restricted>
<inputRequirement></inputRequirement>
<systemResourceConsiderations></systemResourceConsiderations>
<seeAlso/>
<providedServiceAPIs>
<providedServiceAPI>
<className>org.apache.nifi.dbcp.DBCPService</className>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-standard-services-api-nar</artifactId>
<version>1.10.0-SNAPSHOT</version>
</providedServiceAPI>
</providedServiceAPIs>
</extension>
</extensions>
</extensionManifest>

View File

@ -0,0 +1,4 @@
<extensionManifest>
<systemApiVersion></systemApiVersion>
<extensions></extensions>
</extensionManifest>

View File

@ -0,0 +1,56 @@
<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>
</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>

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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. -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-core</artifactId>
<version>1.14.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-registry-client</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-data-model</artifactId>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-security-utils</artifactId>
<version>1.14.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>${jersey.client.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.client.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>${jersey.client.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
<version>${jersey.client.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey.client.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${org.slf4j.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,59 @@
/*
* 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.registry.client;
import java.io.IOException;
/**
* Client for interacting with the AccessResource.
*/
public interface AccessClient {
/**
* Get an access token by authenticating with a username and password aginst the configured identity provider.
*
* @param username the username
* @param password the password
* @return the access token
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
String getToken(String username, String password) throws NiFiRegistryException, IOException;
/**
* Gets an access token via spnego. It is expected that the caller of this method has wrapped the call
* in a {@code doAs()} using a {@link javax.security.auth.Subject}.
*
* @return the token
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
String getTokenFromKerberosTicket() throws NiFiRegistryException, IOException;
/**
* Performs a logout for the user represented by the given token.
*
* @param token the toke to authenticate with
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
void logout(String token) throws NiFiRegistryException, IOException;
}

View File

@ -0,0 +1,86 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.bucket.Bucket;
import org.apache.nifi.registry.field.Fields;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import java.io.IOException;
import java.util.List;
/**
* Client for interacting with buckets.
*/
public interface BucketClient {
/**
* Creates the given bucket.
*
* @param bucket the bucket to create
* @return the created bucket with containing identifier that was generated
*/
Bucket create(Bucket bucket) throws NiFiRegistryException, IOException;
/**
* Gets the bucket with the given id.
*
* @param bucketId the id of the bucket to retrieve
* @return the bucket with the given id
*/
Bucket get(String bucketId) throws NiFiRegistryException, IOException;
/**
* Updates the given bucket. Only the name and description can be updated.
*
* @param bucket the bucket with updates, must contain the id
* @return the updated bucket
*/
Bucket update(Bucket bucket) throws NiFiRegistryException, IOException;
/**
* Deletes the bucket with the given id.
*
* @param bucketId the id of the bucket to delete
* @return the deleted bucket
*/
Bucket delete(String bucketId) throws NiFiRegistryException, IOException;
/**
* Deletes the bucket with the given id and revision
*
* @param bucketId the id of the bucket to delete
* @param revision the revision info for the bucket being deleted
* @return the deleted bucket
*/
Bucket delete(String bucketId, RevisionInfo revision) throws NiFiRegistryException, IOException;
/**
* Gets the fields that can be used to sort/search buckets.
*
* @return the bucket fields
*/
Fields getFields() throws NiFiRegistryException, IOException;
/**
* Gets all buckets.
*
* @return the list of all buckets
*/
List<Bucket> getAll() throws NiFiRegistryException, IOException;
}

View File

@ -0,0 +1,84 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.extension.bundle.Bundle;
import org.apache.nifi.registry.extension.bundle.BundleFilterParams;
import java.io.IOException;
import java.util.List;
/**
* Client for interacting with extension bundles.
*/
public interface BundleClient {
/**
* Retrieves all extension bundles located in buckets the current user is authorized for.
*
* @return the list of extension bundles
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<Bundle> getAll() throws IOException, NiFiRegistryException;
/**
* Retrieves all extension bundles matching the specified filters, located in buckets the current user is authorized for.
*
* @param filterParams the filter params
* @return the list of extension bundles
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<Bundle> getAll(BundleFilterParams filterParams) throws IOException, NiFiRegistryException;
/**
* Retrieves the extension bundles located in the given bucket.
*
* @param bucketId the bucket id
* @return the list of bundles in the bucket
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<Bundle> getByBucket(String bucketId) throws IOException, NiFiRegistryException;
/**
* Retrieves the extension bundle with the given id.
*
* @param bundleId the id of the bundle
* @return the bundle with the given id
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
Bundle get(String bundleId) throws IOException, NiFiRegistryException;
/**
* Deletes the extension bundle with the given id, and all of its versions.
*
* @param bundleId the bundle id
* @return the deleted bundle
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
Bundle delete(String bundleId) throws IOException, NiFiRegistryException;
}

View File

@ -0,0 +1,209 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.extension.bundle.BundleType;
import org.apache.nifi.registry.extension.bundle.BundleVersion;
import org.apache.nifi.registry.extension.bundle.BundleVersionFilterParams;
import org.apache.nifi.registry.extension.bundle.BundleVersionMetadata;
import org.apache.nifi.registry.extension.component.manifest.Extension;
import org.apache.nifi.registry.extension.component.ExtensionMetadata;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
* Client for interacting with extension bundle versions.
*/
public interface BundleVersionClient {
/**
* Uploads a version of an extension bundle to NiFi Registry where the bundle content comes from an InputStream.
*
* @param bucketId the bucket where the extension bundle will leave
* @param bundleType the type of bundle being uploaded
* @param bundleContentStream the input stream with the binary content of the bundle
* @return the BundleVersion entity
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
BundleVersion create(String bucketId, BundleType bundleType, InputStream bundleContentStream)
throws IOException, NiFiRegistryException;
/**
* Uploads a version of an extension bundle to NiFi Registry where the bundle content comes from an InputStream.
*
* @param bucketId the bucket where the extension bundle will leave
* @param bundleType the type of bundle being uploaded
* @param bundleContentStream the input stream with the binary content of the bundle
* @param sha256 the optional SHA-256 in hex form
* @return the BundleVersion entity
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
BundleVersion create(String bucketId, BundleType bundleType, InputStream bundleContentStream, String sha256)
throws IOException, NiFiRegistryException;
/**
* Uploads a version of an extension bundle to NiFi Registry where the bundle content comes from a File.
*
* @param bucketId the bucket where the extension bundle will leave
* @param bundleType the type of bundle being uploaded
* @param bundleFile the file with the binary content of the bundle
* @return the BundleVersion entity
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
BundleVersion create(String bucketId, BundleType bundleType, File bundleFile)
throws IOException, NiFiRegistryException;
/**
* Uploads a version of an extension bundle to NiFi Registry where the bundle content comes from a File.
*
* @param bucketId the bucket where the extension bundle will leave
* @param bundleType the type of bundle being uploaded
* @param bundleFile the file with the binary content of the bundle
* @param sha256 the optional SHA-256 in hex form
* @return the BundleVersion entity
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
BundleVersion create(String bucketId, BundleType bundleType, File bundleFile, String sha256)
throws IOException, NiFiRegistryException;
/**
* Retrieves all the extension bundle versions located in buckets the current user is authorized for, and
* matching any of the provided filter params.
*
* @param filterParams the filter params
* @return the list of bundle version metadata
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<BundleVersionMetadata> getBundleVersions(BundleVersionFilterParams filterParams)
throws IOException, NiFiRegistryException;
/**
* Retrieves the metadata about the versions of the given bundle.
*
* @param bundleId the bundle id
* @return the list of version metadata
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<BundleVersionMetadata> getBundleVersions(String bundleId) throws IOException, NiFiRegistryException;
/**
* Retrieves bundle version entity for the given bundle id and version string.
*
* The entity contains all of the information about the version, such as the bucket, bundle, and version metadata.
*
* The binary content of the bundle can be obtained by calling {@method getBundleVersionContent}.
*
* @param bundleId the bundle id
* @param version the bundle version
* @return the BundleVersion entity
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
BundleVersion getBundleVersion(String bundleId, String version) throws IOException, NiFiRegistryException;
/**
* Retrieves the metadata about the extensions in the given bundle version.
*
* @param bundleId the bundle id
* @param version the bundle version
* @return the list of metadata about the extensions
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<ExtensionMetadata> getExtensions(String bundleId, String version) throws IOException, NiFiRegistryException;
/**
* Retrieves the full extension info for the extension with the given name in the given bundle version.
*
* @param bundleId the bundle id
* @param version the version of the bundle
* @param name the name of the extension
* @return the extension info
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
Extension getExtension(String bundleId, String version, String name) throws IOException, NiFiRegistryException;
/**
* Obtains an InputStream for the html docs of the given extension.
*
* @param bundleId the bundle id
* @param version the version of the bundle
* @param name the name of the extensions
* @return the InputStream for the extension docs
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
InputStream getExtensionDocs(String bundleId, String version, String name) throws IOException, NiFiRegistryException;
/**
* Obtains an InputStream for the binary content for the version of the given bundle.
*
* @param bundleId the bundle id
* @param version the version
* @return the InputStream for the bundle version content
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
InputStream getBundleVersionContent(String bundleId, String version) throws IOException, NiFiRegistryException;
/**
* Writes the binary content for the version of the given the bundle to the specified directory.
*
* @param bundleId the bundle id
* @param version the bundle version
* @param directory the directory to write to
* @return the File object for the bundle that was written
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
File writeBundleVersionContent(String bundleId, String version, File directory) throws IOException, NiFiRegistryException;
/**
* Deletes the given extension bundle version.
*
* @param bundleId the bundle id
* @param version the bundle version
* @return the deleted bundle versions
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
BundleVersion delete(String bundleId, String version) throws IOException, NiFiRegistryException;
}

View File

@ -0,0 +1,62 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.extension.component.ExtensionFilterParams;
import org.apache.nifi.registry.extension.component.ExtensionMetadataContainer;
import org.apache.nifi.registry.extension.component.TagCount;
import org.apache.nifi.registry.extension.component.manifest.ProvidedServiceAPI;
import java.io.IOException;
import java.util.List;
/**
* Client for obtaining information about extensions.
*/
public interface ExtensionClient {
/**
* Retrieves extensions according to the given filter params.
*
* @param filterParams the filter params
* @return the metadata for the extensions matching the filter params
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
ExtensionMetadataContainer findExtensions(ExtensionFilterParams filterParams) throws IOException, NiFiRegistryException;
/**
* Retrieves extensions that provide the given service API.
*
* @param providedServiceAPI the service API
* @return the metadata for extensions that provided the service API
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
ExtensionMetadataContainer findExtensions(ProvidedServiceAPI providedServiceAPI) throws IOException, NiFiRegistryException;
/**
* @return all of the tags known the registry with their corresponding counts
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<TagCount> getTagCounts() throws IOException, NiFiRegistryException;
}

View File

@ -0,0 +1,210 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.extension.component.manifest.Extension;
import org.apache.nifi.registry.extension.repo.ExtensionRepoArtifact;
import org.apache.nifi.registry.extension.repo.ExtensionRepoBucket;
import org.apache.nifi.registry.extension.repo.ExtensionRepoExtensionMetadata;
import org.apache.nifi.registry.extension.repo.ExtensionRepoGroup;
import org.apache.nifi.registry.extension.repo.ExtensionRepoVersion;
import org.apache.nifi.registry.extension.repo.ExtensionRepoVersionSummary;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
/**
* Client for interacting with the extension repository.
*/
public interface ExtensionRepoClient {
/**
* Gets the buckets in the extension repo.
*
* @return the list of extension repo buckets.
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<ExtensionRepoBucket> getBuckets() throws IOException, NiFiRegistryException;
/**
* Gets the extension repo groups in the specified bucket.
*
* @param bucketName the bucket name
* @return the list of groups
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<ExtensionRepoGroup> getGroups(String bucketName) throws IOException, NiFiRegistryException;
/**
* Gets the extension repo artifacts in the given bucket and group.
*
* @param bucketName the bucket name
* @param groupId the group id
* @return the list of artifacts
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<ExtensionRepoArtifact> getArtifacts(String bucketName, String groupId) throws IOException, NiFiRegistryException;
/**
* Gets the extension repo versions for the given bucket, group, artifact.
*
* @param bucketName the bucket name
* @param groupId the group id
* @param artifactId the artifact id
* @return the list of version summaries
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<ExtensionRepoVersionSummary> getVersions(String bucketName, String groupId, String artifactId)
throws IOException, NiFiRegistryException;
/**
* Gets the extension repo version for the given bucket, group, artifact, and version.
*
* @param bucketName the bucket name
* @param groupId the group id
* @param artifactId the artifact id
* @param version the version
* @return the extension repo version
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
ExtensionRepoVersion getVersion(String bucketName, String groupId, String artifactId, String version)
throws IOException, NiFiRegistryException;
/**
* Gets the metadata about the extensions for the given bucket, group, artifact, and version.
*
* @param bucketName the bucket name
* @param groupId the group id
* @param artifactId the artifact id
* @param version the version
* @return the list of extension metadata
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
List<ExtensionRepoExtensionMetadata> getVersionExtensions(String bucketName, String groupId, String artifactId, String version)
throws IOException, NiFiRegistryException;
/**
* Gets the metadata about the extension with the given name in the given bucket, group, artifact, and version.
*
* @param bucketName the bucket name
* @param groupId the group id
* @param artifactId the artifact id
* @param version the version
* @param extensionName the extension name
* @return the extension info
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
Extension getVersionExtension(String bucketName, String groupId, String artifactId, String version, String extensionName)
throws IOException, NiFiRegistryException;
/**
* Gets an InputStream for the html docs of the extension with the given name in the given bucket, group, artifact, and version.
*
* @param bucketName the bucket name
* @param groupId the group id
* @param artifactId the artifact id
* @param version the version
* @param extensionName the extension name
* @return the InputStream for the html docs
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
InputStream getVersionExtensionDocs(String bucketName, String groupId, String artifactId, String version, String extensionName)
throws IOException, NiFiRegistryException;
/**
* Gets an InputStream for the binary content of the specified version.
*
* @param bucketName the bucket name
* @param groupId the group id
* @param artifactId the artifact id
* @param version the version
* @return the input stream
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
InputStream getVersionContent(String bucketName, String groupId, String artifactId, String version)
throws IOException, NiFiRegistryException;
/**
* Writes the binary content for the version of the given the bundle to the specified directory.
*
* @param bucketName the bucket name
* @param groupId the group id
* @param artifactId the artifact id
* @param version the version
* @param directory the directory to write to
* @return the File object for the bundle that was written
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
File writeBundleVersionContent(String bucketName, String groupId, String artifactId, String version, File directory)
throws IOException, NiFiRegistryException;
/**
* Gets the hex representation of the SHA-256 hash of the binary content for the given version.
*
* @param bucketName the bucket name
* @param groupId the group id
* @param artifactId the artifact id
* @param version the version
* @return the SHA-256 hex string
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
String getVersionSha256(String bucketName, String groupId, String artifactId, String version)
throws IOException, NiFiRegistryException;
/**
* Gets the hex representation of the SHA-256 hash of the binary content for the given version.
*
* If the version is a SNAPSHOT version, there may be more than one instance of the SNAPSHOT version in different
* buckets. In this case the instance with the latest created timestamp will be used to obtain the checksum.
*
* @param groupId the group id
* @param artifactId the artifact id
* @param version the version
* @return the SHA-256 hex string
*
* @throws IOException if an I/O error occurs
* @throws NiFiRegistryException if an non I/O error occurs
*/
Optional<String> getVersionSha256(String groupId, String artifactId, String version)
throws IOException, NiFiRegistryException;
}

View File

@ -0,0 +1,132 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.diff.VersionedFlowDifference;
import org.apache.nifi.registry.field.Fields;
import org.apache.nifi.registry.flow.VersionedFlow;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import java.io.IOException;
import java.util.List;
/**
* Client for interacting with flows.
*/
public interface FlowClient {
/**
* Create the given flow in the given bucket.
*
* @param flow the flow to create
* @return the created flow with the identifier populated
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlow create(VersionedFlow flow) throws NiFiRegistryException, IOException;
/**
* Gets the flow with the given id in the given bucket.
*
* The list of snapshot metadata will NOT be populated.
*
* @param bucketId a bucket id
* @param flowId a flow id
* @return the flow with the given id in the given bucket
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlow get(String bucketId, String flowId) throws NiFiRegistryException, IOException;
/**
* Gets the flow with the given id.
*
* @param flowId a flow id
* @return the flow with the given id
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlow get(String flowId) throws NiFiRegistryException, IOException;
/**
* Updates the given flow with in the given bucket.
*
* The identifier of the flow must be populated in the flow object, and only the name and description can be updated.
*
* @param bucketId a bucket id
* @param flow the flow with updates
* @return the updated flow
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlow update(String bucketId, VersionedFlow flow) throws NiFiRegistryException, IOException;
/**
* Deletes the flow with the given id in the given bucket.
*
* @param bucketId a bucket id
* @param flowId the id of the flow to delete
* @return the deleted flow
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlow delete(String bucketId, String flowId) throws NiFiRegistryException, IOException;
/**
* Deletes the flow with the given id in the given bucket.
*
* @param bucketId a bucket id
* @param flowId the id of the flow to delete
* @param revision the revision information for the entity being deleted
* @return the deleted flow
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlow delete(String bucketId, String flowId, RevisionInfo revision) throws NiFiRegistryException, IOException;
/**
* Gets the field info for flows.
*
* @return field info for flows
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
Fields getFields() throws NiFiRegistryException, IOException;
/**
* Gets the flows for a given bucket.
*
* @param bucketId a bucket id
* @return the flows in the given bucket
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
List<VersionedFlow> getByBucket(String bucketId) throws NiFiRegistryException, IOException;
/**
*
* @param bucketId a bucket id
* @param flowId the flow that is under inspection
* @param versionA the first version to use in the comparison
* @param versionB the second flow to use in the comparison
* @return the list of differences between the 2 flow versions grouped by component
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlowDifference diff(final String bucketId, final String flowId,
final Integer versionA, final Integer versionB) throws NiFiRegistryException, IOException;
}

View File

@ -0,0 +1,133 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
import java.io.IOException;
import java.util.List;
/**
* Client for interacting with snapshots.
*/
public interface FlowSnapshotClient {
/**
* Creates a new snapshot/version for the given flow.
*
* The snapshot object must have the version populated, and will receive an error if the submitted version is
* not the next one-up version.
*
* @param snapshot the new snapshot
* @return the created snapshot
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlowSnapshot create(VersionedFlowSnapshot snapshot) throws NiFiRegistryException, IOException;
/**
* Gets the snapshot for the given bucket, flow, and version.
*
* @param bucketId the bucket id
* @param flowId the flow id
* @param version the version
* @return the snapshot with the given version of the given flow in the given bucket
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlowSnapshot get(String bucketId, String flowId, int version) throws NiFiRegistryException, IOException;
/**
* Gets the snapshot for the given flow and version.
*
* @param flowId the flow id
* @param version the version
* @return the snapshot with the given version of the given flow
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlowSnapshot get(String flowId, int version) throws NiFiRegistryException, IOException;
/**
* Gets the latest snapshot for the given flow.
*
* @param bucketId the bucket id
* @param flowId the flow id
* @return the snapshot with the latest version for the given flow
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlowSnapshot getLatest(String bucketId, String flowId) throws NiFiRegistryException, IOException;
/**
* Gets the latest snapshot for the given flow.
*
* @param flowId the flow id
* @return the snapshot with the latest version for the given flow
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlowSnapshot getLatest(String flowId) throws NiFiRegistryException, IOException;
/**
* Gets the latest snapshot metadata for the given flow.
*
* @param bucketId the bucket id
* @param flowId the flow id
* @return the snapshot metadata for the latest version of the given flow
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlowSnapshotMetadata getLatestMetadata(String bucketId, String flowId) throws NiFiRegistryException, IOException;
/**
* Gets the latest snapshot metadata for the given flow.
*
* @param flowId the flow id
* @return the snapshot metadata for the latest version of the given flow
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
VersionedFlowSnapshotMetadata getLatestMetadata(String flowId) throws NiFiRegistryException, IOException;
/**
* Gets a list of the metadata for all snapshots of a given flow.
*
* The contents of each snapshot are not part of the response.
*
* @param bucketId the bucket id
* @param flowId the flow id
* @return the list of snapshot metadata
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
List<VersionedFlowSnapshotMetadata> getSnapshotMetadata(String bucketId, String flowId) throws NiFiRegistryException, IOException;
/**
* Gets a list of the metadata for all snapshots of a given flow.
*
* The contents of each snapshot are not part of the response.
*
* @param flowId the flow id
* @return the list of snapshot metadata
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
List<VersionedFlowSnapshotMetadata> getSnapshotMetadata(String flowId) throws NiFiRegistryException, IOException;
}

View File

@ -0,0 +1,64 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.bucket.BucketItem;
import org.apache.nifi.registry.field.Fields;
import java.io.IOException;
import java.util.List;
/**
* Client for interacting with bucket items.
*
* Bucket items contain the common fields across anything stored in the registry.
*
* Each item contains a type field and a link to the URI of the specific item.
*
* i.e. The link field of a flow item would contain the URI to the specific flow.
*/
public interface ItemsClient {
/**
* Gets all bucket items in the registry.
*
* @return the list of all bucket items
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
List<BucketItem> getAll() throws NiFiRegistryException, IOException;
/**
* Gets all bucket items for the given bucket.
*
* @param bucketId the bucket id
* @return the list of items in the given bucket
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
List<BucketItem> getByBucket(String bucketId) throws NiFiRegistryException, IOException;
/**
* Gets the field info for bucket items.
*
* @return the list of field info
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
Fields getFields() throws NiFiRegistryException, IOException;
}

View File

@ -0,0 +1,267 @@
/*
* 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.registry.client;
import java.io.Closeable;
/**
* A client for interacting with the REST API of a NiFi registry instance.
*/
public interface NiFiRegistryClient extends Closeable {
/**
* @return the client for interacting with buckets
*/
BucketClient getBucketClient();
/**
* @deprecated use getBucketClient(RequestConfig requestConfig)
*
* @return the client for interacting with buckets on behalf of the given proxied entities
*/
BucketClient getBucketClient(String ... proxiedEntity);
/**
* @return the client for interacting with buckets using the given request config
*/
BucketClient getBucketClient(RequestConfig requestConfig);
//-------------------------------------------------------------------------------------------
/**
* @return the client for interacting with flows
*/
FlowClient getFlowClient();
/**
* @deprecated use getFlowClient(RequestConfig requestConfig)
*
* @return the client for interacting with flows on behalf of the given proxied entities
*/
FlowClient getFlowClient(String ... proxiedEntity);
/**
* @return the client for interacting with flows using the given request config
*/
FlowClient getFlowClient(RequestConfig requestConfig);
//-------------------------------------------------------------------------------------------
/**
* @return the client for interacting with flows/snapshots
*/
FlowSnapshotClient getFlowSnapshotClient();
/**
* @deprecated use getFlowSnapshotClient(RequestConfig requestConfig)
*
* @return the client for interacting with flows/snapshots on behalf of the given proxied entities
*/
FlowSnapshotClient getFlowSnapshotClient(String ... proxiedEntity);
/**
* @return the client for interacting with flows/snapshots using the given request config
*/
FlowSnapshotClient getFlowSnapshotClient(RequestConfig requestConfig);
//-------------------------------------------------------------------------------------------
/**
* @return the client for interacting with bucket items
*/
ItemsClient getItemsClient();
/**
* @deprecated use getItemsClient(RequestConfig requestConfig)
*
* @return the client for interacting with bucket items on behalf of the given proxied entities
*/
ItemsClient getItemsClient(String ... proxiedEntity);
/**
* @return the client for interacting with bucket items using the given request config
*/
ItemsClient getItemsClient(RequestConfig requestConfig);
//-------------------------------------------------------------------------------------------
/**
* @return the client for obtaining information about the current user
*/
UserClient getUserClient();
/**
* @deprecated use getUserClient(RequestConfig requestConfig)
*
* @return the client for obtaining information about the current user based on the given proxied entities
*/
UserClient getUserClient(String ... proxiedEntity);
/**
* @return the client for obtaining information about the current user based on the request config
*/
UserClient getUserClient(RequestConfig requestConfig);
//-------------------------------------------------------------------------------------------
/**
* @return the client for interacting with extension bundles
*/
BundleClient getBundleClient();
/**
* @deprecated use getBundleClient(RequestConfig requestConfig)
*
* @return the client for interacting with extension bundles on behalf of the given proxied entities
*/
BundleClient getBundleClient(String ... proxiedEntity);
/**
* @return the client for interacting with extension bundles using the given request config
*/
BundleClient getBundleClient(RequestConfig requestConfig);
//-------------------------------------------------------------------------------------------
/**
* @return the client for interacting with extension bundle versions
*/
BundleVersionClient getBundleVersionClient();
/**
* @deprecated use getBundleVersionClient(RequestConfig requestConfig)
*
* @return the client for interacting with extension bundle versions on behalf of the given proxied entities
*/
BundleVersionClient getBundleVersionClient(String ... proxiedEntity);
/**
* @return the client for interacting with extension bundle versions using the given request config
*/
BundleVersionClient getBundleVersionClient(RequestConfig requestConfig);
//-------------------------------------------------------------------------------------------
/**
* @return the client for interacting with the extension repository
*/
ExtensionRepoClient getExtensionRepoClient();
/**
* @deprecated use getExtensionRepoClient(RequestConfig requestConfig)
*
* @return the client for interacting with the extension repository on behalf of the given proxied entities
*/
ExtensionRepoClient getExtensionRepoClient(String ... proxiedEntity);
/**
* @return the client for interacting with the extension repository using the given request config
*/
ExtensionRepoClient getExtensionRepoClient(RequestConfig requestConfig);
//-------------------------------------------------------------------------------------------
/**
* @return the client for interacting with extensions
*/
ExtensionClient getExtensionClient();
/**
* @deprecated use getExtensionClient(RequestConfig requestConfig)
*
* @return the client for interacting with extensions on behalf of the given proxied entities
*/
ExtensionClient getExtensionClient(String ... proxiedEntity);
/**
* @return the client for interacting with extensions using the given request config
*/
ExtensionClient getExtensionClient(RequestConfig requestConfig);
//-------------------------------------------------------------------------------------------
/**
* Returns client for interacting with tenants.
*
* @return the client for interacting with tenants
*/
TenantsClient getTenantsClient();
/**
* Returns client for interacting with tenants.
*
* @deprecated use getTenantsClient(RequestConfig requestConfig)
*
* @param proxiedEntity The given proxied entities.
*
* @return the client for interacting with tenants on behalf of the given proxied entities.
*/
TenantsClient getTenantsClient(String ... proxiedEntity);
/**
* @return the client for interacting with tenants using the given request config
*/
TenantsClient getTenantsClient(RequestConfig requestConfig);
//-------------------------------------------------------------------------------------------
/**
* Returns client for interacting with access policies.
*
* @return the client for interacting with access policies
*/
PoliciesClient getPoliciesClient();
/**
* Returns client for interacting with access policies.
*
* @deprecated use getPoliciesClient(RequestConfig requestConfig)
*
* @param proxiedEntity The given proxied entities.
*
* @return the client for interacting with access policies on behalf of the given proxied entities.
*/
PoliciesClient getPoliciesClient(String ... proxiedEntity);
/**
* @return the client for interacting with access policies using the given request config
*/
PoliciesClient getPoliciesClient(RequestConfig requestConfig);
//-------------------------------------------------------------------------------------------
/**
* @return the client for obtaining access tokens
*/
AccessClient getAccessClient();
//-------------------------------------------------------------------------------------------
/**
* The builder interface that implementations should provide for obtaining the client.
*/
interface Builder {
Builder config(NiFiRegistryClientConfig clientConfig);
NiFiRegistryClientConfig getConfig();
NiFiRegistryClient build();
}
}

View File

@ -0,0 +1,272 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.security.util.CertificateUtils;
import org.apache.nifi.registry.security.util.KeyStoreUtils;
import org.apache.nifi.registry.security.util.KeystoreType;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
/**
* Configuration for a NiFiRegistryClient.
*/
public class NiFiRegistryClientConfig {
public static final String DEFAULT_PROTOCOL = CertificateUtils.getHighestCurrentSupportedTlsProtocolVersion();
private final String baseUrl;
private final SSLContext sslContext;
private final String keystoreFilename;
private final String keystorePass;
private final String keyPass;
private final KeystoreType keystoreType;
private final String truststoreFilename;
private final String truststorePass;
private final KeystoreType truststoreType;
private final String protocol;
private final HostnameVerifier hostnameVerifier;
private final Integer readTimeout;
private final Integer connectTimeout;
private NiFiRegistryClientConfig(final Builder builder) {
this.baseUrl = builder.baseUrl;
this.sslContext = builder.sslContext;
this.keystoreFilename = builder.keystoreFilename;
this.keystorePass = builder.keystorePass;
this.keyPass = builder.keyPass;
this.keystoreType = builder.keystoreType;
this.truststoreFilename = builder.truststoreFilename;
this.truststorePass = builder.truststorePass;
this.truststoreType = builder.truststoreType;
this.protocol = builder.protocol == null ? DEFAULT_PROTOCOL : builder.protocol;
this.hostnameVerifier = builder.hostnameVerifier;
this.readTimeout = builder.readTimeout;
this.connectTimeout = builder.connectTimeout;
}
public String getBaseUrl() {
return baseUrl;
}
public SSLContext getSslContext() {
if (sslContext != null) {
return sslContext;
}
final KeyManagerFactory keyManagerFactory;
if (keystoreFilename != null && keystorePass != null && keystoreType != null) {
try {
// prepare the keystore
final KeyStore keyStore = KeyStoreUtils.getKeyStore(keystoreType.name());
try (final InputStream keyStoreStream = new FileInputStream(new File(keystoreFilename))) {
keyStore.load(keyStoreStream, keystorePass.toCharArray());
}
keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
if (keyPass == null) {
keyManagerFactory.init(keyStore, keystorePass.toCharArray());
} else {
keyManagerFactory.init(keyStore, keyPass.toCharArray());
}
} catch (final Exception e) {
throw new IllegalStateException("Failed to load Keystore", e);
}
} else {
keyManagerFactory = null;
}
final TrustManagerFactory trustManagerFactory;
if (truststoreFilename != null && truststorePass != null && truststoreType != null) {
try {
// prepare the truststore
final KeyStore trustStore = KeyStoreUtils.getKeyStore(truststoreType.name());
try (final InputStream trustStoreStream = new FileInputStream(new File(truststoreFilename))) {
trustStore.load(trustStoreStream, truststorePass.toCharArray());
}
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
} catch (final Exception e) {
throw new IllegalStateException("Failed to load Truststore", e);
}
} else {
trustManagerFactory = null;
}
if (keyManagerFactory != null || trustManagerFactory != null) {
try {
// initialize the ssl context
KeyManager[] keyManagers = keyManagerFactory != null ? keyManagerFactory.getKeyManagers() : null;
TrustManager[] trustManagers = trustManagerFactory != null ? trustManagerFactory.getTrustManagers() : null;
final SSLContext sslContext = SSLContext.getInstance(getProtocol());
sslContext.init(keyManagers, trustManagers, new SecureRandom());
sslContext.getDefaultSSLParameters().setNeedClientAuth(true);
return sslContext;
} catch (final Exception e) {
throw new IllegalStateException("Created keystore and truststore but failed to initialize SSLContext", e);
}
} else {
return null;
}
}
public String getKeystoreFilename() {
return keystoreFilename;
}
public String getKeystorePass() {
return keystorePass;
}
public String getKeyPass() {
return keyPass;
}
public KeystoreType getKeystoreType() {
return keystoreType;
}
public String getTruststoreFilename() {
return truststoreFilename;
}
public String getTruststorePass() {
return truststorePass;
}
public KeystoreType getTruststoreType() {
return truststoreType;
}
public String getProtocol() {
return protocol;
}
public HostnameVerifier getHostnameVerifier() {
return hostnameVerifier;
}
public Integer getReadTimeout() {
return readTimeout;
}
public Integer getConnectTimeout() {
return connectTimeout;
}
/**
* Builder for client configuration.
*/
public static class Builder {
private String baseUrl;
private SSLContext sslContext;
private String keystoreFilename;
private String keystorePass;
private String keyPass;
private KeystoreType keystoreType;
private String truststoreFilename;
private String truststorePass;
private KeystoreType truststoreType;
private String protocol;
private HostnameVerifier hostnameVerifier;
private Integer readTimeout;
private Integer connectTimeout;
public Builder baseUrl(final String baseUrl) {
this.baseUrl = baseUrl;
return this;
}
public Builder sslContext(final SSLContext sslContext) {
this.sslContext = sslContext;
return this;
}
public Builder keystoreFilename(final String keystoreFilename) {
this.keystoreFilename = keystoreFilename;
return this;
}
public Builder keystorePassword(final String keystorePass) {
this.keystorePass = keystorePass;
return this;
}
public Builder keyPassword(final String keyPass) {
this.keyPass = keyPass;
return this;
}
public Builder keystoreType(final KeystoreType keystoreType) {
this.keystoreType = keystoreType;
return this;
}
public Builder truststoreFilename(final String truststoreFilename) {
this.truststoreFilename = truststoreFilename;
return this;
}
public Builder truststorePassword(final String truststorePass) {
this.truststorePass = truststorePass;
return this;
}
public Builder truststoreType(final KeystoreType truststoreType) {
this.truststoreType = truststoreType;
return this;
}
public Builder protocol(final String protocol) {
this.protocol = protocol;
return this;
}
public Builder hostnameVerifier(final HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameVerifier;
return this;
}
public Builder readTimeout(final Integer readTimeout) {
this.readTimeout = readTimeout;
return this;
}
public Builder connectTimeout(final Integer connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
public NiFiRegistryClientConfig build() {
return new NiFiRegistryClientConfig(this);
}
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.registry.client;
/**
* Indicates an error interacting with the NiFi registry for a reason other than IOException.
*/
public class NiFiRegistryException extends Exception {
public NiFiRegistryException(final String message) {
super(message);
}
public NiFiRegistryException(final String message, final Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.authorization.AccessPolicy;
import java.io.IOException;
public interface PoliciesClient {
/**
* Returns a given access policy.
*
* @param resource The action allowed by the access policy.
* @param action The resource managed by the access policy.
*
* @return The access policy.
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
AccessPolicy getAccessPolicy(String action, String resource) throws NiFiRegistryException, IOException;
/**
* Creates a new access policy.
*
* @param policy The access policy to be created. Note: identifier will be ignored and assigned by NiFi Registry.
*
* @return The created access with an assigned identifier.
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
AccessPolicy createAccessPolicy(AccessPolicy policy) throws NiFiRegistryException, IOException;
/**
* Updates an existing access policy.
*
* @param policy The access policy with new attributes.
*
* @return The updated access policy.
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
AccessPolicy updateAccessPolicy(AccessPolicy policy) throws NiFiRegistryException, IOException;
}

View File

@ -0,0 +1,32 @@
/*
* 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.registry.client;
import java.util.Map;
/**
* Configuration applied to each client request.
*/
public interface RequestConfig {
/**
* @return the headers to apply to each request
*/
Map<String,String> getHeaders();
}

View File

@ -0,0 +1,166 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.authorization.User;
import org.apache.nifi.registry.authorization.UserGroup;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import java.io.IOException;
import java.util.List;
public interface TenantsClient {
/**
* Returns all users.
*
* @return The list of users.
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
List<User> getUsers() throws NiFiRegistryException, IOException;
/**
* Returns a user with a given identifier.
*
* @param id Identifier of the user.
*
* @return The user.
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
User getUser(String id) throws NiFiRegistryException, IOException;
/**
* Creates a new user in NiFi Registry.
*
* @param user The new user. Note: identifier will be ignored and assigned be NiFi Registry.
*
* @return The created user with an assigned identifier.
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
User createUser(User user) throws NiFiRegistryException, IOException;
/**
* Updates an existing user.
*
* @param user The user with the new attributes.
*
* @return The updated user.
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
User updateUser(User user) throws NiFiRegistryException, IOException;
/**
* Deletes an existing user.
*
* @param id identifier of the user
* @return the deleted user
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
User deleteUser(String id) throws NiFiRegistryException, IOException;
/**
* Deletes an existing user.
*
* @param id identifier of the user
* @param revisionInfo the revision info for the user to delete
* @return the deleted user
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
User deleteUser(String id, RevisionInfo revisionInfo) throws NiFiRegistryException, IOException;
/**
* Returns all user groups.
*
* @return The list of user groups.
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
List<UserGroup> getUserGroups() throws NiFiRegistryException, IOException;
/**
* Returns a user group with a given identifier.
*
* @param id Identifier of the user group.
*
* @return The user group.
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
UserGroup getUserGroup(String id) throws NiFiRegistryException, IOException;
/**
* Creates a new user group.
*
* @param group The user group to be created. Note: identifier will be ignored and assigned by NiFi Registry.
*
* @return The created user group with an assigned identifier.
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
UserGroup createUserGroup(UserGroup group) throws NiFiRegistryException, IOException;
/**
* Updates an existing user group.
*
* @param group The user group with new attributes.
*
* @return The user group after store.
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
UserGroup updateUserGroup(UserGroup group) throws NiFiRegistryException, IOException;
/**
* Deletes an existing group.
*
* @param id identifier of the group
* @return the deleted group
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
UserGroup deleteUserGroup(String id) throws NiFiRegistryException, IOException;
/**
* Deletes an existing group.
*
* @param id identifier of the group
* @param revisionInfo the revision info for the group to delete
* @return the deleted group
*
* @throws NiFiRegistryException Thrown in case of unsuccessful execution.
* @throws IOException Thrown when there is an issue while communicating with NiFi Registry.
*/
UserGroup deleteUserGroup(String id, RevisionInfo revisionInfo) throws NiFiRegistryException, IOException;
}

View File

@ -0,0 +1,42 @@
/*
* 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.registry.client;
import org.apache.nifi.registry.authorization.CurrentUser;
import java.io.IOException;
public interface UserClient {
/**
* Obtains the access status of the current user.
*
* If the UserClient was obtained with proxied entities, then the access status should represent the status
* of the last identity in the chain.
*
* If the UserClient was obtained without proxied entities, then it would represent the identity of the certificate
* in the keystore used by the client.
*
* If the registry is not in secure mode, the anonymous identity is expected to be returned along with a flag indicating
* the user is anonymous.
*
* @return the access status of the current user
* @throws NiFiRegistryException if the proxying user is not a valid proxy or identity claim is otherwise invalid
*/
CurrentUser getAccessStatus() throws NiFiRegistryException, IOException;
}

View File

@ -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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
public class AbstractCRUDJerseyClient extends AbstractJerseyClient {
protected final WebTarget baseTarget;
public AbstractCRUDJerseyClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(requestConfig);
this.baseTarget = baseTarget;
}
protected <T> T get(
String id,
Class<T> entityType,
String entityTypeName,
String entityPath
) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(id)) {
throw new IllegalArgumentException(entityTypeName + " id cannot be blank");
}
return executeAction("Error retrieving " + entityTypeName.toLowerCase(), () -> {
final WebTarget target = baseTarget.path(entityPath).path(id);
return getRequestBuilder(target).get(entityType);
});
}
protected <T> T create(
T entity,
Class<T> entityType,
String entityTypeName,
String entityPath
) throws NiFiRegistryException, IOException {
if (entity == null) {
throw new IllegalArgumentException(entityTypeName + " cannot be null");
}
return executeAction("Error creating " + entityTypeName.toLowerCase(), () -> {
final WebTarget target = baseTarget.path(entityPath);
return getRequestBuilder(target).post(
Entity.entity(entity, MediaType.APPLICATION_JSON_TYPE), entityType
);
});
}
protected <T> T update(
T entity,
String id,
Class<T> entityType,
String entityTypeName,
String entityPath
) throws NiFiRegistryException, IOException {
if (entity == null) {
throw new IllegalArgumentException(entityTypeName + " cannot be null");
}
return executeAction("Error updating " + entityTypeName.toLowerCase(), () -> {
final WebTarget target = baseTarget.path(entityPath).path(id);
return getRequestBuilder(target).put(
Entity.entity(entity, MediaType.APPLICATION_JSON_TYPE), entityType
);
});
}
protected <T> T delete(
String id,
RevisionInfo revisionInfo,
Class<T> entityType,
String entityTypeName,
String entityPath
) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(id)) {
throw new IllegalArgumentException(entityTypeName + " id cannot be blank");
}
return executeAction("Error deleting " + entityTypeName.toLowerCase(), () -> {
WebTarget target = baseTarget.path(entityPath).path(id);
target = addRevisionQueryParams(target, revisionInfo);
return getRequestBuilder(target).delete(entityType);
});
}
}

View File

@ -0,0 +1,152 @@
/*
* 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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
/**
* Base class for the client operations to share exception handling.
*
* Sub-classes should always execute a request from getRequestBuilder(target) to ensure proper headers are sent.
*/
public class AbstractJerseyClient {
private static final RequestConfig EMPTY_REQUEST_CONFIG = () -> Collections.emptyMap();
private final RequestConfig requestConfig;
public AbstractJerseyClient(final RequestConfig requestConfig) {
this.requestConfig = (requestConfig == null ? EMPTY_REQUEST_CONFIG : requestConfig);
}
protected RequestConfig getRequestConfig() {
return this.requestConfig;
}
/**
* Adds query parameters for the given RevisionInfo if populated.
*
* @param target the WebTarget
* @param revision the RevisionInfo
* @return the target with query params added
*/
protected WebTarget addRevisionQueryParams(WebTarget target, RevisionInfo revision) {
if (revision == null) {
return target;
}
WebTarget localTarget = target;
final Long version = revision.getVersion();
if (version != null) {
localTarget = localTarget.queryParam("version", version.longValue());
}
final String clientId = revision.getClientId();
if (!StringUtils.isBlank(clientId)) {
localTarget = localTarget.queryParam("clientId", clientId);
}
return localTarget;
}
/**
* Creates a new Invocation.Builder for the given WebTarget with the headers added to the builder.
*
* @param webTarget the target for the request
* @return the builder for the target with the headers added
*/
protected Invocation.Builder getRequestBuilder(final WebTarget webTarget) {
final Invocation.Builder requestBuilder = webTarget.request();
final Map<String,String> headers = requestConfig.getHeaders();
headers.entrySet().stream().forEach(e -> requestBuilder.header(e.getKey(), e.getValue()));
return requestBuilder;
}
/**
* Executes the given action and returns the result.
*
* @param action the action to execute
* @param errorMessage the message to use if a NiFiRegistryException is thrown
* @param <T> the return type of the action
* @return the result of the action
* @throws NiFiRegistryException if any exception other than IOException is encountered
* @throws IOException if an I/O error occurs communicating with the registry
*/
protected <T> T executeAction(final String errorMessage, final NiFiRegistryAction<T> action) throws NiFiRegistryException, IOException {
try {
return action.execute();
} catch (final Exception e) {
final Throwable ioeCause = getIOExceptionCause(e);
if (ioeCause == null) {
final StringBuilder errorMessageBuilder = new StringBuilder(errorMessage);
// see if we have a WebApplicationException, and if so add the response body to the error message
if (e instanceof WebApplicationException) {
final Response response = ((WebApplicationException) e).getResponse();
final String responseBody = response.readEntity(String.class);
errorMessageBuilder.append(": ").append(responseBody);
}
throw new NiFiRegistryException(errorMessageBuilder.toString(), e);
} else {
throw (IOException) ioeCause;
}
}
}
/**
* An action to execute with the given return type.
*
* @param <T> the return type of the action
*/
protected interface NiFiRegistryAction<T> {
T execute();
}
/**
* @param e an exception that was encountered interacting with the registry
* @return the IOException that caused this exception, or null if the an IOException did not cause this exception
*/
protected Throwable getIOExceptionCause(final Throwable e) {
if (e == null) {
return null;
}
if (e instanceof IOException) {
return e;
}
return getIOExceptionCause(e.getCause());
}
}

View File

@ -0,0 +1,81 @@
/*
* 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.registry.client.impl;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.bucket.BucketItem;
import org.apache.nifi.registry.bucket.BucketItemType;
import org.apache.nifi.registry.extension.bundle.Bundle;
import org.apache.nifi.registry.flow.VersionedFlow;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class BucketItemDeserializer extends StdDeserializer<BucketItem[]> {
public BucketItemDeserializer() {
super(BucketItem[].class);
}
@Override
public BucketItem[] deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
final JsonNode arrayNode = jsonParser.getCodec().readTree(jsonParser);
final List<BucketItem> bucketItems = new ArrayList<>();
final Iterator<JsonNode> nodeIter = arrayNode.elements();
while (nodeIter.hasNext()) {
final JsonNode node = nodeIter.next();
final String type = node.get("type").asText();
if (StringUtils.isBlank(type)) {
throw new IllegalStateException("BucketItem type cannot be null or blank");
}
final BucketItemType bucketItemType;
try {
bucketItemType = BucketItemType.valueOf(type);
} catch (Exception e) {
throw new IllegalStateException("Unknown type for BucketItem: " + type, e);
}
switch (bucketItemType) {
case Flow:
final VersionedFlow versionedFlow = jsonParser.getCodec().treeToValue(node, VersionedFlow.class);
bucketItems.add(versionedFlow);
break;
case Bundle:
final Bundle bundle = jsonParser.getCodec().treeToValue(node, Bundle.class);
bucketItems.add(bundle);
break;
default:
throw new IllegalStateException("Unknown type for BucketItem: " + bucketItemType);
}
}
return bucketItems.toArray(new BucketItem[bucketItems.size()]);
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import javax.ws.rs.core.Response;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public class ClientUtils {
public static File getExtensionBundleVersionContent(final Response response, final File outputDirectory) {
final String contentDispositionHeader = response.getHeaderString("Content-Disposition");
if (StringUtils.isBlank(contentDispositionHeader)) {
throw new IllegalStateException("Content-Disposition header was blank or missing");
}
final int equalsIndex = contentDispositionHeader.lastIndexOf("=");
final String filename = contentDispositionHeader.substring(equalsIndex + 1).trim();
final File bundleFile = new File(outputDirectory, filename);
try (final InputStream responseInputStream = response.readEntity(InputStream.class)) {
Files.copy(responseInputStream, bundleFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
return bundleFile;
} catch (Exception e) {
throw new IllegalStateException("Unable to write bundle content due to: " + e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,92 @@
/*
* 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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.client.AccessClient;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.client.impl.request.BasicAuthRequestConfig;
import org.apache.nifi.registry.client.impl.request.BearerTokenRequestConfig;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import java.io.IOException;
import java.util.Map;
/**
* Jersey implementation of AccessClient.
*/
public class JerseyAccessClient extends AbstractJerseyClient implements AccessClient {
private final WebTarget accessTarget;
public JerseyAccessClient(final WebTarget baseTarget) {
super(null);
this.accessTarget = baseTarget.path("/access");
}
@Override
public String getToken(final String username, final String password) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(username)) {
throw new IllegalArgumentException("Username is required");
}
if (StringUtils.isBlank(password)) {
throw new IllegalArgumentException("Password is required");
}
return executeAction("Error performing login", () -> {
final WebTarget target = accessTarget.path("token/login");
final Invocation.Builder requestBuilder = getRequestBuilder(target);
final RequestConfig basicCredsConfig = new BasicAuthRequestConfig(username, password);
final Map<String,String> basicAuthHeaders = basicCredsConfig.getHeaders();
basicAuthHeaders.entrySet().stream().forEach(e -> requestBuilder.header(e.getKey(), e.getValue()));
return requestBuilder.post(Entity.json(null), String.class);
});
}
@Override
public String getTokenFromKerberosTicket() throws NiFiRegistryException, IOException {
return executeAction("Error performing kerberos login", () -> {
final WebTarget target = accessTarget.path("token/kerberos");
return getRequestBuilder(target).post(Entity.json(null), String.class);
});
}
@Override
public void logout(final String token) throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(token)) {
throw new IllegalArgumentException("Token is required");
}
executeAction("Error performing logout", () -> {
final WebTarget target = accessTarget.path("logout");
final Invocation.Builder requestBuilder = getRequestBuilder(target);
final RequestConfig tokenConfig = new BearerTokenRequestConfig(token);
final Map<String,String> bearerHeaders = tokenConfig.getHeaders();
bearerHeaders.entrySet().stream().forEach(e -> requestBuilder.header(e.getKey(), e.getValue()));
requestBuilder.delete();
return null;
});
}
}

View File

@ -0,0 +1,148 @@
/*
* 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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.bucket.Bucket;
import org.apache.nifi.registry.client.BucketClient;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.field.Fields;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Jersey implementation of BucketClient.
*/
public class JerseyBucketClient extends AbstractJerseyClient implements BucketClient {
private final WebTarget bucketsTarget;
public JerseyBucketClient(final WebTarget baseTarget) {
this(baseTarget, null);
}
public JerseyBucketClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(requestConfig);
this.bucketsTarget = baseTarget.path("/buckets");
}
@Override
public Bucket create(final Bucket bucket) throws NiFiRegistryException, IOException {
if (bucket == null) {
throw new IllegalArgumentException("Bucket cannot be null");
}
return executeAction("Error creating bucket", () -> {
return getRequestBuilder(bucketsTarget)
.post(
Entity.entity(bucket, MediaType.APPLICATION_JSON),
Bucket.class
);
});
}
@Override
public Bucket get(final String bucketId) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket ID cannot be blank");
}
return executeAction("Error retrieving bucket", () -> {
final WebTarget target = bucketsTarget
.path("/{bucketId}")
.resolveTemplate("bucketId", bucketId);
return getRequestBuilder(target).get(Bucket.class);
});
}
@Override
public Bucket update(final Bucket bucket) throws NiFiRegistryException, IOException {
if (bucket == null) {
throw new IllegalArgumentException("Bucket cannot be null");
}
if (StringUtils.isBlank(bucket.getIdentifier())) {
throw new IllegalArgumentException("Bucket Identifier must be provided");
}
return executeAction("Error updating bucket", () -> {
final WebTarget target = bucketsTarget
.path("/{bucketId}")
.resolveTemplate("bucketId", bucket.getIdentifier());
return getRequestBuilder(target)
.put(
Entity.entity(bucket, MediaType.APPLICATION_JSON),
Bucket.class
);
});
}
@Override
public Bucket delete(final String bucketId) throws NiFiRegistryException, IOException {
return delete(bucketId, null);
}
@Override
public Bucket delete(final String bucketId, final RevisionInfo revision) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket ID cannot be blank");
}
return executeAction("Error deleting bucket", () -> {
WebTarget target = bucketsTarget
.path("/{bucketId}")
.resolveTemplate("bucketId", bucketId);
target = addRevisionQueryParams(target, revision);
return getRequestBuilder(target).delete(Bucket.class);
});
}
@Override
public Fields getFields() throws NiFiRegistryException, IOException {
return executeAction("Error retrieving bucket field info", () -> {
final WebTarget target = bucketsTarget
.path("/fields");
return getRequestBuilder(target).get(Fields.class);
});
}
@Override
public List<Bucket> getAll() throws NiFiRegistryException, IOException {
return executeAction("Error retrieving all buckets", () -> {
final Bucket[] buckets = getRequestBuilder(bucketsTarget).get(Bucket[].class);
return buckets == null ? Collections.emptyList() : Arrays.asList(buckets);
});
}
}

View File

@ -0,0 +1,121 @@
/*
* 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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.client.BundleClient;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.extension.bundle.Bundle;
import org.apache.nifi.registry.extension.bundle.BundleFilterParams;
import javax.ws.rs.client.WebTarget;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Jersey implementation of BundleClient.
*/
public class JerseyBundleClient extends AbstractJerseyClient implements BundleClient {
private final WebTarget bucketExtensionBundlesTarget;
private final WebTarget extensionBundlesTarget;
public JerseyBundleClient(final WebTarget baseTarget) {
this(baseTarget, null);
}
public JerseyBundleClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(requestConfig);
this.bucketExtensionBundlesTarget = baseTarget.path("buckets/{bucketId}/bundles");
this.extensionBundlesTarget = baseTarget.path("bundles");
}
@Override
public List<Bundle> getAll() throws IOException, NiFiRegistryException {
return getAll(null);
}
@Override
public List<Bundle> getAll(final BundleFilterParams filterParams) throws IOException, NiFiRegistryException {
return executeAction("Error getting extension bundles", () -> {
WebTarget target = extensionBundlesTarget;
if (filterParams != null) {
if (!StringUtils.isBlank(filterParams.getBucketName())) {
target = target.queryParam("bucketName", filterParams.getBucketName());
}
if (!StringUtils.isBlank(filterParams.getGroupId())) {
target = target.queryParam("groupId", filterParams.getGroupId());
}
if (!StringUtils.isBlank(filterParams.getArtifactId())) {
target = target.queryParam("artifactId", filterParams.getArtifactId());
}
}
final Bundle[] bundles = getRequestBuilder(target).get(Bundle[].class);
return bundles == null ? Collections.emptyList() : Arrays.asList(bundles);
});
}
@Override
public List<Bundle> getByBucket(final String bucketId) throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket id cannot be null or blank");
}
return executeAction("Error getting extension bundles for bucket", () -> {
WebTarget target = bucketExtensionBundlesTarget.resolveTemplate("bucketId", bucketId);
final Bundle[] bundles = getRequestBuilder(target).get(Bundle[].class);
return bundles == null ? Collections.emptyList() : Arrays.asList(bundles);
});
}
@Override
public Bundle get(final String bundleId) throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bundleId)) {
throw new IllegalArgumentException("Bundle id cannot be null or blank");
}
return executeAction("Error getting extension bundle", () -> {
WebTarget target = extensionBundlesTarget
.path("{bundleId}")
.resolveTemplate("bundleId", bundleId);
return getRequestBuilder(target).get(Bundle.class);
});
}
@Override
public Bundle delete(final String bundleId) throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bundleId)) {
throw new IllegalArgumentException("Bundle id cannot be null or blank");
}
return executeAction("Error deleting extension bundle", () -> {
WebTarget target = extensionBundlesTarget
.path("{bundleId}")
.resolveTemplate("bundleId", bundleId);
return getRequestBuilder(target).delete(Bundle.class);
});
}
}

View File

@ -0,0 +1,370 @@
/*
* 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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.client.BundleVersionClient;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.extension.bundle.BundleType;
import org.apache.nifi.registry.extension.bundle.BundleVersion;
import org.apache.nifi.registry.extension.bundle.BundleVersionFilterParams;
import org.apache.nifi.registry.extension.bundle.BundleVersionMetadata;
import org.apache.nifi.registry.extension.component.ExtensionMetadata;
import org.apache.nifi.registry.extension.component.manifest.Extension;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
import org.glassfish.jersey.media.multipart.file.StreamDataBodyPart;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Jersey implementation of BundleVersionClient.
*/
public class JerseyBundleVersionClient extends AbstractJerseyClient implements BundleVersionClient {
private final WebTarget bucketExtensionBundlesTarget;
private final WebTarget extensionBundlesTarget;
public JerseyBundleVersionClient(final WebTarget baseTarget) {
this(baseTarget, null);
}
public JerseyBundleVersionClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(requestConfig);
this.bucketExtensionBundlesTarget = baseTarget.path("buckets/{bucketId}/bundles");
this.extensionBundlesTarget = baseTarget.path("bundles");
}
@Override
public BundleVersion create(final String bucketId, final BundleType bundleType, final InputStream bundleContentStream)
throws IOException, NiFiRegistryException {
return create(bucketId, bundleType, bundleContentStream, null);
}
@Override
public BundleVersion create(final String bucketId, final BundleType bundleType, final InputStream bundleContentStream, final String sha256)
throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket id cannot be null or blank");
}
if (bundleType == null) {
throw new IllegalArgumentException("Bundle type cannot be null");
}
if (bundleContentStream == null) {
throw new IllegalArgumentException("Bundle content cannot be null");
}
return executeAction("Error creating extension bundle version", () -> {
final WebTarget target = bucketExtensionBundlesTarget
.path("{bundleType}")
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("bundleType", bundleType.toString());
final StreamDataBodyPart streamBodyPart = new StreamDataBodyPart("file", bundleContentStream);
final FormDataMultiPart multipart = new FormDataMultiPart();
multipart.bodyPart(streamBodyPart);
if (!StringUtils.isBlank(sha256)) {
multipart.field("sha256", sha256);
}
return getRequestBuilder(target)
.post(
Entity.entity(multipart, multipart.getMediaType()),
BundleVersion.class
);
});
}
@Override
public BundleVersion create(final String bucketId, final BundleType bundleType, final File bundleFile)
throws IOException, NiFiRegistryException {
return create(bucketId, bundleType, bundleFile, null);
}
@Override
public BundleVersion create(final String bucketId, final BundleType bundleType, final File bundleFile, final String sha256)
throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket id cannot be null or blank");
}
if (bundleType == null) {
throw new IllegalArgumentException("Bundle type cannot be null");
}
if (bundleFile == null) {
throw new IllegalArgumentException("Bundle file cannot be null");
}
return executeAction("Error creating extension bundle version", () -> {
final WebTarget target = bucketExtensionBundlesTarget
.path("{bundleType}")
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("bundleType", bundleType.toString());
final FileDataBodyPart fileBodyPart = new FileDataBodyPart("file", bundleFile, MediaType.APPLICATION_OCTET_STREAM_TYPE);
final FormDataMultiPart multipart = new FormDataMultiPart();
multipart.bodyPart(fileBodyPart);
if (!StringUtils.isBlank(sha256)) {
multipart.field("sha256", sha256);
}
return getRequestBuilder(target)
.post(
Entity.entity(multipart, multipart.getMediaType()),
BundleVersion.class
);
});
}
@Override
public List<BundleVersionMetadata> getBundleVersions(final BundleVersionFilterParams filterParams)
throws IOException, NiFiRegistryException {
return executeAction("Error getting extension bundle versions", () -> {
WebTarget target = extensionBundlesTarget.path("/versions");
if (filterParams != null) {
if (!StringUtils.isBlank(filterParams.getGroupId())) {
target = target.queryParam("groupId", filterParams.getGroupId());
}
if (!StringUtils.isBlank(filterParams.getArtifactId())) {
target = target.queryParam("artifactId", filterParams.getArtifactId());
}
if (!StringUtils.isBlank(filterParams.getVersion())) {
target = target.queryParam("version", filterParams.getVersion());
}
}
final BundleVersionMetadata[] bundleVersions = getRequestBuilder(target).get(BundleVersionMetadata[].class);
return bundleVersions == null ? Collections.emptyList() : Arrays.asList(bundleVersions);
});
}
@Override
public List<BundleVersionMetadata> getBundleVersions(final String bundleId)
throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bundleId)) {
throw new IllegalArgumentException("Bundle id cannot be null or blank");
}
return executeAction("Error getting extension bundle versions", () -> {
final WebTarget target = extensionBundlesTarget
.path("{bundleId}/versions")
.resolveTemplate("bundleId", bundleId);
final BundleVersionMetadata[] bundleVersions = getRequestBuilder(target).get(BundleVersionMetadata[].class);
return bundleVersions == null ? Collections.emptyList() : Arrays.asList(bundleVersions);
});
}
@Override
public BundleVersion getBundleVersion(final String bundleId, final String version)
throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bundleId)) {
throw new IllegalArgumentException("Bundle id cannot be null or blank");
}
if (StringUtils.isBlank(version)) {
throw new IllegalArgumentException("Version cannot be null or blank");
}
return executeAction("Error getting extension bundle version", () -> {
final WebTarget target = extensionBundlesTarget
.path("{bundleId}/versions/{version}")
.resolveTemplate("bundleId", bundleId)
.resolveTemplate("version", version);
return getRequestBuilder(target).get(BundleVersion.class);
});
}
@Override
public List<ExtensionMetadata> getExtensions(final String bundleId, final String version)
throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bundleId)) {
throw new IllegalArgumentException("Bundle id cannot be null or blank");
}
if (StringUtils.isBlank(version)) {
throw new IllegalArgumentException("Version cannot be null or blank");
}
return executeAction("Error getting extension bundle metadata", () -> {
final WebTarget target = extensionBundlesTarget
.path("{bundleId}/versions/{version}/extensions")
.resolveTemplate("bundleId", bundleId)
.resolveTemplate("version", version);
final ExtensionMetadata[] extensions = getRequestBuilder(target).get(ExtensionMetadata[].class);
return extensions == null ? Collections.emptyList() : Arrays.asList(extensions);
});
}
@Override
public Extension getExtension(final String bundleId, final String version, final String name) throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bundleId)) {
throw new IllegalArgumentException("Bundle id cannot be null or blank");
}
if (StringUtils.isBlank(version)) {
throw new IllegalArgumentException("Version cannot be null or blank");
}
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("Extension name cannot be null or blank");
}
return executeAction("Error getting extension", () -> {
final WebTarget target = extensionBundlesTarget
.path("{bundleId}/versions/{version}/extensions/{name}")
.resolveTemplate("bundleId", bundleId)
.resolveTemplate("version", version)
.resolveTemplate("name", name);
final Extension extension = getRequestBuilder(target).get(Extension.class);
return extension;
});
}
@Override
public InputStream getExtensionDocs(final String bundleId, final String version, final String name) throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bundleId)) {
throw new IllegalArgumentException("Bundle id cannot be null or blank");
}
if (StringUtils.isBlank(version)) {
throw new IllegalArgumentException("Version cannot be null or blank");
}
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("Extension name cannot be null or blank");
}
return executeAction("Error getting extension", () -> {
final WebTarget target = extensionBundlesTarget
.path("{bundleId}/versions/{version}/extensions/{name}/docs")
.resolveTemplate("bundleId", bundleId)
.resolveTemplate("version", version)
.resolveTemplate("name", name);
return getRequestBuilder(target)
.accept(MediaType.TEXT_HTML)
.get()
.readEntity(InputStream.class);
});
}
@Override
public InputStream getBundleVersionContent(final String bundleId, final String version)
throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bundleId)) {
throw new IllegalArgumentException("Bundle id cannot be null or blank");
}
if (StringUtils.isBlank(version)) {
throw new IllegalArgumentException("Version cannot be null or blank");
}
return executeAction("Error getting extension bundle version", () -> {
final WebTarget target = extensionBundlesTarget
.path("{bundleId}/versions/{version}/content")
.resolveTemplate("bundleId", bundleId)
.resolveTemplate("version", version);
return getRequestBuilder(target)
.accept(MediaType.APPLICATION_OCTET_STREAM_TYPE)
.get()
.readEntity(InputStream.class);
});
}
@Override
public File writeBundleVersionContent(final String bundleId, final String version, final File directory)
throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bundleId)) {
throw new IllegalArgumentException("Bundle id cannot be null or blank");
}
if (StringUtils.isBlank(version)) {
throw new IllegalArgumentException("Version cannot be null or blank");
}
if (directory == null || !directory.exists() || !directory.isDirectory()) {
throw new IllegalArgumentException("Directory must exist and be a valid directory");
}
return executeAction("Error getting extension bundle version", () -> {
final WebTarget target = extensionBundlesTarget
.path("{bundleId}/versions/{version}/content")
.resolveTemplate("bundleId", bundleId)
.resolveTemplate("version", version);
final Response response = getRequestBuilder(target)
.accept(MediaType.APPLICATION_OCTET_STREAM_TYPE)
.get();
return ClientUtils.getExtensionBundleVersionContent(response, directory);
});
}
@Override
public BundleVersion delete(final String bundleId, final String version) throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bundleId)) {
throw new IllegalArgumentException("Bundle id cannot be null or blank");
}
if (StringUtils.isBlank(version)) {
throw new IllegalArgumentException("Version cannot be null or blank");
}
return executeAction("Error deleting extension bundle version", () -> {
final WebTarget target = extensionBundlesTarget
.path("{bundleId}/versions/{version}")
.resolveTemplate("bundleId", bundleId)
.resolveTemplate("version", version);
return getRequestBuilder(target).delete(BundleVersion.class);
});
}
}

View File

@ -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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.client.ExtensionClient;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.extension.bundle.BundleType;
import org.apache.nifi.registry.extension.component.ExtensionFilterParams;
import org.apache.nifi.registry.extension.component.ExtensionMetadataContainer;
import org.apache.nifi.registry.extension.component.TagCount;
import org.apache.nifi.registry.extension.component.manifest.ExtensionType;
import org.apache.nifi.registry.extension.component.manifest.ProvidedServiceAPI;
import javax.ws.rs.client.WebTarget;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class JerseyExtensionClient extends AbstractJerseyClient implements ExtensionClient {
private final WebTarget extensionsTarget;
public JerseyExtensionClient(final WebTarget baseTarget) {
this(baseTarget, null);
}
public JerseyExtensionClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(requestConfig);
this.extensionsTarget = baseTarget.path("extensions");
}
@Override
public ExtensionMetadataContainer findExtensions(final ExtensionFilterParams filterParams)
throws IOException, NiFiRegistryException {
return executeAction("Error retrieving extensions", () -> {
WebTarget target = extensionsTarget;
if (filterParams != null) {
final BundleType bundleType = filterParams.getBundleType();
if (bundleType != null) {
target = target.queryParam("bundleType", bundleType.toString());
}
final ExtensionType extensionType = filterParams.getExtensionType();
if (extensionType != null) {
target = target.queryParam("extensionType", extensionType.toString());
}
final Set<String> tags = filterParams.getTags();
if (tags != null) {
for (final String tag : tags) {
target = target.queryParam("tag", tag);
}
}
}
return getRequestBuilder(target).get(ExtensionMetadataContainer.class);
});
}
@Override
public ExtensionMetadataContainer findExtensions(final ProvidedServiceAPI serviceAPI) throws IOException, NiFiRegistryException {
if (serviceAPI == null
|| StringUtils.isBlank(serviceAPI.getClassName())
|| StringUtils.isBlank(serviceAPI.getGroupId())
|| StringUtils.isBlank(serviceAPI.getArtifactId())
|| StringUtils.isBlank(serviceAPI.getVersion())) {
throw new IllegalArgumentException("Provided service API must be specified with a class, group, artifact, and version");
}
return executeAction("Error retrieving extensions", () -> {
WebTarget target = extensionsTarget.path("provided-service-api");
target = target.queryParam("className", serviceAPI.getClassName());
target = target.queryParam("groupId", serviceAPI.getGroupId());
target = target.queryParam("artifactId", serviceAPI.getArtifactId());
target = target.queryParam("version", serviceAPI.getVersion());
return getRequestBuilder(target).get(ExtensionMetadataContainer.class);
});
}
@Override
public List<TagCount> getTagCounts() throws IOException, NiFiRegistryException {
return executeAction("Error retrieving tag counts", () -> {
final WebTarget target = extensionsTarget.path("tags");
final TagCount[] tagCounts = getRequestBuilder(target).get(TagCount[].class);
return tagCounts == null ? Collections.emptyList() : Arrays.asList(tagCounts);
});
}
}

View File

@ -0,0 +1,335 @@
/*
* 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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.client.ExtensionRepoClient;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.extension.component.manifest.Extension;
import org.apache.nifi.registry.extension.repo.ExtensionRepoArtifact;
import org.apache.nifi.registry.extension.repo.ExtensionRepoBucket;
import org.apache.nifi.registry.extension.repo.ExtensionRepoExtensionMetadata;
import org.apache.nifi.registry.extension.repo.ExtensionRepoGroup;
import org.apache.nifi.registry.extension.repo.ExtensionRepoVersion;
import org.apache.nifi.registry.extension.repo.ExtensionRepoVersionSummary;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class JerseyExtensionRepoClient extends AbstractJerseyClient implements ExtensionRepoClient {
private WebTarget extensionRepoTarget;
public JerseyExtensionRepoClient(final WebTarget baseTarget) {
this(baseTarget, null);
}
public JerseyExtensionRepoClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(requestConfig);
this.extensionRepoTarget = baseTarget.path("extension-repository");
}
@Override
public List<ExtensionRepoBucket> getBuckets() throws IOException, NiFiRegistryException {
return executeAction("Error retrieving buckets for extension repo", () -> {
final ExtensionRepoBucket[] repoBuckets = getRequestBuilder(extensionRepoTarget).get(ExtensionRepoBucket[].class);
return repoBuckets == null ? Collections.emptyList() : Arrays.asList(repoBuckets);
});
}
@Override
public List<ExtensionRepoGroup> getGroups(final String bucketName) throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bucketName)) {
throw new IllegalArgumentException("Bucket name cannot be null or blank");
}
return executeAction("Error retrieving groups for extension repo", () -> {
final WebTarget target = extensionRepoTarget
.path("{bucketName}")
.resolveTemplate("bucketName", bucketName);
final ExtensionRepoGroup[] repoGroups = getRequestBuilder(target).get(ExtensionRepoGroup[].class);
return repoGroups == null ? Collections.emptyList() : Arrays.asList(repoGroups);
});
}
@Override
public List<ExtensionRepoArtifact> getArtifacts(final String bucketName, final String groupId)
throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bucketName)) {
throw new IllegalArgumentException("Bucket name cannot be null or blank");
}
if (StringUtils.isBlank(groupId)) {
throw new IllegalArgumentException("Group id cannot be null or blank");
}
return executeAction("Error retrieving artifacts for extension repo", () -> {
final WebTarget target = extensionRepoTarget
.path("{bucketName}/{groupId}")
.resolveTemplate("bucketName", bucketName)
.resolveTemplate("groupId", groupId);
final ExtensionRepoArtifact[] repoArtifacts = getRequestBuilder(target).get(ExtensionRepoArtifact[].class);
return repoArtifacts == null ? Collections.emptyList() : Arrays.asList(repoArtifacts);
});
}
@Override
public List<ExtensionRepoVersionSummary> getVersions(final String bucketName, final String groupId, final String artifactId)
throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(bucketName)) {
throw new IllegalArgumentException("Bucket name cannot be null or blank");
}
if (StringUtils.isBlank(groupId)) {
throw new IllegalArgumentException("Group id cannot be null or blank");
}
if (StringUtils.isBlank(artifactId)) {
throw new IllegalArgumentException("Artifact id cannot be null or blank");
}
return executeAction("Error retrieving versions for extension repo", () -> {
final WebTarget target = extensionRepoTarget
.path("{bucketName}/{groupId}/{artifactId}")
.resolveTemplate("bucketName", bucketName)
.resolveTemplate("groupId", groupId)
.resolveTemplate("artifactId", artifactId);
final ExtensionRepoVersionSummary[] repoVersions = getRequestBuilder(target).get(ExtensionRepoVersionSummary[].class);
return repoVersions == null ? Collections.emptyList() : Arrays.asList(repoVersions);
});
}
@Override
public ExtensionRepoVersion getVersion(final String bucketName, final String groupId, final String artifactId, final String version)
throws IOException, NiFiRegistryException {
validate(bucketName, groupId, artifactId, version);
return executeAction("Error retrieving versions for extension repo", () -> {
final WebTarget target = extensionRepoTarget
.path("{bucketName}/{groupId}/{artifactId}/{version}")
.resolveTemplate("bucketName", bucketName)
.resolveTemplate("groupId", groupId)
.resolveTemplate("artifactId", artifactId)
.resolveTemplate("version", version);
return getRequestBuilder(target).get(ExtensionRepoVersion.class);
});
}
@Override
public List<ExtensionRepoExtensionMetadata> getVersionExtensions(final String bucketName, final String groupId, final String artifactId, final String version)
throws IOException, NiFiRegistryException {
validate(bucketName, groupId, artifactId, version);
return executeAction("Error retrieving versions for extension repo", () -> {
final WebTarget target = extensionRepoTarget
.path("{bucketName}/{groupId}/{artifactId}/{version}/extensions")
.resolveTemplate("bucketName", bucketName)
.resolveTemplate("groupId", groupId)
.resolveTemplate("artifactId", artifactId)
.resolveTemplate("version", version);
final ExtensionRepoExtensionMetadata[] extensions = getRequestBuilder(target).get(ExtensionRepoExtensionMetadata[].class);
return extensions == null ? Collections.emptyList() : Arrays.asList(extensions);
});
}
@Override
public Extension getVersionExtension(final String bucketName, final String groupId, final String artifactId,
final String version, final String extensionName)
throws IOException, NiFiRegistryException {
validate(bucketName, groupId, artifactId, version);
if (StringUtils.isBlank(extensionName)) {
throw new IllegalArgumentException("Extension name is required");
}
return executeAction("Error retrieving versions for extension repo", () -> {
final WebTarget target = extensionRepoTarget
.path("{bucketName}/{groupId}/{artifactId}/{version}/extensions/{extensionName}")
.resolveTemplate("bucketName", bucketName)
.resolveTemplate("groupId", groupId)
.resolveTemplate("artifactId", artifactId)
.resolveTemplate("version", version)
.resolveTemplate("extensionName", extensionName);
final Extension extension = getRequestBuilder(target).get(Extension.class);
return extension;
});
}
@Override
public InputStream getVersionExtensionDocs(final String bucketName, final String groupId, final String artifactId,
final String version, final String extensionName)
throws IOException, NiFiRegistryException {
validate(bucketName, groupId, artifactId, version);
if (StringUtils.isBlank(extensionName)) {
throw new IllegalArgumentException("Extension name is required");
}
return executeAction("Error retrieving versions for extension repo", () -> {
final WebTarget target = extensionRepoTarget
.path("{bucketName}/{groupId}/{artifactId}/{version}/extensions/{extensionName}/docs")
.resolveTemplate("bucketName", bucketName)
.resolveTemplate("groupId", groupId)
.resolveTemplate("artifactId", artifactId)
.resolveTemplate("version", version)
.resolveTemplate("extensionName", extensionName);
return getRequestBuilder(target)
.accept(MediaType.TEXT_HTML)
.get()
.readEntity(InputStream.class);
});
}
@Override
public InputStream getVersionContent(final String bucketName, final String groupId, final String artifactId, final String version)
throws IOException, NiFiRegistryException {
validate(bucketName, groupId, artifactId, version);
return executeAction("Error retrieving version content for extension repo", () -> {
final WebTarget target = extensionRepoTarget
.path("{bucketName}/{groupId}/{artifactId}/{version}/content")
.resolveTemplate("bucketName", bucketName)
.resolveTemplate("groupId", groupId)
.resolveTemplate("artifactId", artifactId)
.resolveTemplate("version", version);
return getRequestBuilder(target)
.accept(MediaType.APPLICATION_OCTET_STREAM_TYPE)
.get()
.readEntity(InputStream.class);
});
}
@Override
public File writeBundleVersionContent(final String bucketName, final String groupId, final String artifactId, final String version, final File directory)
throws IOException, NiFiRegistryException {
validate(bucketName, groupId, artifactId, version);
if (directory == null) {
throw new IllegalArgumentException("Directory cannot be null");
}
return executeAction("Error retrieving version content for extension repo", () -> {
final WebTarget target = extensionRepoTarget
.path("{bucketName}/{groupId}/{artifactId}/{version}/content")
.resolveTemplate("bucketName", bucketName)
.resolveTemplate("groupId", groupId)
.resolveTemplate("artifactId", artifactId)
.resolveTemplate("version", version);
final Response response = getRequestBuilder(target)
.accept(MediaType.APPLICATION_OCTET_STREAM_TYPE)
.get();
return ClientUtils.getExtensionBundleVersionContent(response, directory);
});
}
@Override
public String getVersionSha256(final String bucketName, final String groupId, final String artifactId, final String version)
throws IOException, NiFiRegistryException {
validate(bucketName, groupId, artifactId, version);
return executeAction("Error retrieving version content for extension repo", () -> {
final WebTarget target = extensionRepoTarget
.path("{bucketName}/{groupId}/{artifactId}/{version}/sha256")
.resolveTemplate("bucketName", bucketName)
.resolveTemplate("groupId", groupId)
.resolveTemplate("artifactId", artifactId)
.resolveTemplate("version", version);
return getRequestBuilder(target).accept(MediaType.TEXT_PLAIN_TYPE).get(String.class);
});
}
@Override
public Optional<String> getVersionSha256(final String groupId, final String artifactId, final String version)
throws IOException, NiFiRegistryException {
if (StringUtils.isBlank(groupId)) {
throw new IllegalArgumentException("Group id cannot be null or blank");
}
if (StringUtils.isBlank(artifactId)) {
throw new IllegalArgumentException("Artifact id cannot be null or blank");
}
if (StringUtils.isBlank(version)) {
throw new IllegalArgumentException("Version cannot be null or blank");
}
return executeAction("Error retrieving version content for extension repo", () -> {
final WebTarget target = extensionRepoTarget
.path("{groupId}/{artifactId}/{version}/sha256")
.resolveTemplate("groupId", groupId)
.resolveTemplate("artifactId", artifactId)
.resolveTemplate("version", version);
try {
final String sha256 = getRequestBuilder(target).accept(MediaType.TEXT_PLAIN_TYPE).get(String.class);
return Optional.of(sha256);
} catch (NotFoundException nfe) {
return Optional.empty();
}
});
}
private void validate(String bucketName, String groupId, String artifactId, String version) {
if (StringUtils.isBlank(bucketName)) {
throw new IllegalArgumentException("Bucket name cannot be null or blank");
}
if (StringUtils.isBlank(groupId)) {
throw new IllegalArgumentException("Group id cannot be null or blank");
}
if (StringUtils.isBlank(artifactId)) {
throw new IllegalArgumentException("Artifact id cannot be null or blank");
}
if (StringUtils.isBlank(version)) {
throw new IllegalArgumentException("Version cannot be null or blank");
}
}
}

View File

@ -0,0 +1,214 @@
/*
* 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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.client.FlowClient;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.diff.VersionedFlowDifference;
import org.apache.nifi.registry.field.Fields;
import org.apache.nifi.registry.flow.VersionedFlow;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Jersey implementation of FlowClient.
*/
public class JerseyFlowClient extends AbstractJerseyClient implements FlowClient {
private final WebTarget flowsTarget;
private final WebTarget bucketFlowsTarget;
public JerseyFlowClient(final WebTarget baseTarget) {
this(baseTarget, null);
}
public JerseyFlowClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(requestConfig);
this.flowsTarget = baseTarget.path("/flows");
this.bucketFlowsTarget = baseTarget.path("/buckets/{bucketId}/flows");
}
@Override
public VersionedFlow create(final VersionedFlow flow) throws NiFiRegistryException, IOException {
if (flow == null) {
throw new IllegalArgumentException("VersionedFlow cannot be null");
}
final String bucketId = flow.getBucketIdentifier();
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
return executeAction("Error creating flow", () -> {
final WebTarget target = bucketFlowsTarget
.resolveTemplate("bucketId", bucketId);
return getRequestBuilder(target)
.post(
Entity.entity(flow, MediaType.APPLICATION_JSON),
VersionedFlow.class
);
});
}
@Override
public VersionedFlow get(final String bucketId, final String flowId) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
return executeAction("Error retrieving flow", () -> {
final WebTarget target = bucketFlowsTarget
.path("/{flowId}")
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("flowId", flowId);
return getRequestBuilder(target).get(VersionedFlow.class);
});
}
@Override
public VersionedFlow get(final String flowId) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
// this uses the flowsTarget because its calling /flows/{flowId} without knowing a bucketId
return executeAction("Error retrieving flow", () -> {
final WebTarget target = flowsTarget
.path("/{flowId}")
.resolveTemplate("flowId", flowId);
return getRequestBuilder(target).get(VersionedFlow.class);
});
}
@Override
public VersionedFlow update(final String bucketId, final VersionedFlow flow) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
if (flow == null) {
throw new IllegalArgumentException("VersionedFlow cannot be null");
}
if (StringUtils.isBlank(flow.getIdentifier())) {
throw new IllegalArgumentException("VersionedFlow identifier must be provided");
}
return executeAction("Error updating flow", () -> {
final WebTarget target = bucketFlowsTarget
.path("/{flowId}")
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("flowId", flow.getIdentifier());
return getRequestBuilder(target)
.put(
Entity.entity(flow, MediaType.APPLICATION_JSON),
VersionedFlow.class
);
});
}
@Override
public VersionedFlow delete(final String bucketId, final String flowId) throws NiFiRegistryException, IOException {
return delete(bucketId, flowId, null);
}
@Override
public VersionedFlow delete(final String bucketId, final String flowId, final RevisionInfo revision)
throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
return executeAction("Error deleting flow", () -> {
WebTarget target = bucketFlowsTarget
.path("/{flowId}")
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("flowId", flowId);
target = addRevisionQueryParams(target, revision);
return getRequestBuilder(target).delete(VersionedFlow.class);
});
}
@Override
public Fields getFields() throws NiFiRegistryException, IOException {
return executeAction("Error retrieving fields info for flows", () -> {
final WebTarget target = flowsTarget.path("/fields");
return getRequestBuilder(target).get(Fields.class);
});
}
@Override
public List<VersionedFlow> getByBucket(final String bucketId) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
return executeAction("Error getting flows for bucket", () -> {
WebTarget target = bucketFlowsTarget;
target = target.resolveTemplate("bucketId", bucketId);
final VersionedFlow[] versionedFlows = getRequestBuilder(target).get(VersionedFlow[].class);
return versionedFlows == null ? Collections.emptyList() : Arrays.asList(versionedFlows);
});
}
@Override
public VersionedFlowDifference diff(final String bucketId, final String flowId,
final Integer versionA, final Integer versionB) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
return executeAction("Error retrieving flow", () -> {
final WebTarget target = bucketFlowsTarget
.path("/{flowId}/diff/{versionA}/{versionB}")
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("flowId", flowId)
.resolveTemplate("versionA", versionA)
.resolveTemplate("versionB", versionB);
return getRequestBuilder(target).get(VersionedFlowDifference.class);
});
}
}

View File

@ -0,0 +1,246 @@
/*
* 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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.client.FlowSnapshotClient;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Jersey implementation of FlowSnapshotClient.
*/
public class JerseyFlowSnapshotClient extends AbstractJerseyClient implements FlowSnapshotClient {
final WebTarget bucketFlowSnapshotTarget;
final WebTarget flowsFlowSnapshotTarget;
public JerseyFlowSnapshotClient(final WebTarget baseTarget) {
this(baseTarget, null);
}
public JerseyFlowSnapshotClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(requestConfig);
this.bucketFlowSnapshotTarget = baseTarget.path("/buckets/{bucketId}/flows/{flowId}/versions");
this.flowsFlowSnapshotTarget = baseTarget.path("/flows/{flowId}/versions");
}
@Override
public VersionedFlowSnapshot create(final VersionedFlowSnapshot snapshot)
throws NiFiRegistryException, IOException {
if (snapshot.getSnapshotMetadata() == null) {
throw new IllegalArgumentException("Snapshot Metadata cannot be null");
}
final String bucketId = snapshot.getSnapshotMetadata().getBucketIdentifier();
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
final String flowId = snapshot.getSnapshotMetadata().getFlowIdentifier();
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
return executeAction("Error creating snapshot", () -> {
final WebTarget target = bucketFlowSnapshotTarget
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("flowId", flowId);
return getRequestBuilder(target)
.post(
Entity.entity(snapshot, MediaType.APPLICATION_JSON),
VersionedFlowSnapshot.class
);
});
}
@Override
public VersionedFlowSnapshot get(final String bucketId, final String flowId, final int version)
throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
if (version < 1) {
throw new IllegalArgumentException("Version must be greater than 1");
}
return executeAction("Error retrieving flow snapshot", () -> {
final WebTarget target = bucketFlowSnapshotTarget
.path("/{version}")
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("flowId", flowId)
.resolveTemplate("version", version);
return getRequestBuilder(target).get(VersionedFlowSnapshot.class);
});
}
@Override
public VersionedFlowSnapshot get(final String flowId, final int version)
throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
if (version < 1) {
throw new IllegalArgumentException("Version must be greater than 1");
}
return executeAction("Error retrieving flow snapshot", () -> {
final WebTarget target = flowsFlowSnapshotTarget
.path("/{version}")
.resolveTemplate("flowId", flowId)
.resolveTemplate("version", version);
return getRequestBuilder(target).get(VersionedFlowSnapshot.class);
});
}
@Override
public VersionedFlowSnapshot getLatest(final String bucketId, final String flowId)
throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
return executeAction("Error retrieving latest snapshot", () -> {
final WebTarget target = bucketFlowSnapshotTarget
.path("/latest")
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("flowId", flowId);
return getRequestBuilder(target).get(VersionedFlowSnapshot.class);
});
}
@Override
public VersionedFlowSnapshot getLatest(final String flowId)
throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
return executeAction("Error retrieving latest snapshot", () -> {
final WebTarget target = flowsFlowSnapshotTarget
.path("/latest")
.resolveTemplate("flowId", flowId);
return getRequestBuilder(target).get(VersionedFlowSnapshot.class);
});
}
@Override
public VersionedFlowSnapshotMetadata getLatestMetadata(final String bucketId, final String flowId) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
return executeAction("Error retrieving latest snapshot metadata", () -> {
final WebTarget target = bucketFlowSnapshotTarget
.path("/latest/metadata")
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("flowId", flowId);
return getRequestBuilder(target).get(VersionedFlowSnapshotMetadata.class);
});
}
@Override
public VersionedFlowSnapshotMetadata getLatestMetadata(final String flowId) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
return executeAction("Error retrieving latest snapshot metadata", () -> {
final WebTarget target = flowsFlowSnapshotTarget
.path("/latest/metadata")
.resolveTemplate("flowId", flowId);
return getRequestBuilder(target).get(VersionedFlowSnapshotMetadata.class);
});
}
@Override
@SuppressWarnings("unchecked")
public List<VersionedFlowSnapshotMetadata> getSnapshotMetadata(final String bucketId, final String flowId)
throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
return executeAction("Error retrieving snapshot metadata", () -> {
final WebTarget target = bucketFlowSnapshotTarget
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("flowId", flowId);
final VersionedFlowSnapshotMetadata[] snapshots = getRequestBuilder(target)
.get(VersionedFlowSnapshotMetadata[].class);
return snapshots == null ? Collections.emptyList() : Arrays.asList(snapshots);
});
}
@Override
@SuppressWarnings("unchecked")
public List<VersionedFlowSnapshotMetadata> getSnapshotMetadata(final String flowId)
throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(flowId)) {
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
return executeAction("Error retrieving snapshot metadata", () -> {
final WebTarget target = flowsFlowSnapshotTarget
.resolveTemplate("flowId", flowId);
final VersionedFlowSnapshotMetadata[] snapshots = getRequestBuilder(target)
.get(VersionedFlowSnapshotMetadata[].class);
return snapshots == null ? Collections.emptyList() : Arrays.asList(snapshots);
});
}
}

View File

@ -0,0 +1,85 @@
/*
* 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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.bucket.BucketItem;
import org.apache.nifi.registry.client.ItemsClient;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.field.Fields;
import javax.ws.rs.client.WebTarget;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Jersey implementation of ItemsClient.
*/
public class JerseyItemsClient extends AbstractJerseyClient implements ItemsClient {
private final WebTarget itemsTarget;
public JerseyItemsClient(final WebTarget baseTarget) {
this(baseTarget, null);
}
public JerseyItemsClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(requestConfig);
this.itemsTarget = baseTarget.path("/items");
}
@Override
public List<BucketItem> getAll() throws NiFiRegistryException, IOException {
return executeAction("", () -> {
WebTarget target = itemsTarget;
final BucketItem[] bucketItems = getRequestBuilder(target).get(BucketItem[].class);
return bucketItems == null ? Collections.emptyList() : Arrays.asList(bucketItems);
});
}
@Override
public List<BucketItem> getByBucket(final String bucketId)
throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
return executeAction("", () -> {
WebTarget target = itemsTarget
.path("/{bucketId}")
.resolveTemplate("bucketId", bucketId);
final BucketItem[] bucketItems = getRequestBuilder(target).get(BucketItem[].class);
return bucketItems == null ? Collections.emptyList() : Arrays.asList(bucketItems);
});
}
@Override
public Fields getFields() throws NiFiRegistryException, IOException {
return executeAction("", () -> {
final WebTarget target = itemsTarget.path("/fields");
return getRequestBuilder(target).get(Fields.class);
});
}
}

View File

@ -0,0 +1,363 @@
/*
* 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.registry.client.impl;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.bucket.BucketItem;
import org.apache.nifi.registry.client.AccessClient;
import org.apache.nifi.registry.client.BucketClient;
import org.apache.nifi.registry.client.BundleClient;
import org.apache.nifi.registry.client.BundleVersionClient;
import org.apache.nifi.registry.client.ExtensionClient;
import org.apache.nifi.registry.client.ExtensionRepoClient;
import org.apache.nifi.registry.client.FlowClient;
import org.apache.nifi.registry.client.FlowSnapshotClient;
import org.apache.nifi.registry.client.ItemsClient;
import org.apache.nifi.registry.client.NiFiRegistryClient;
import org.apache.nifi.registry.client.NiFiRegistryClientConfig;
import org.apache.nifi.registry.client.PoliciesClient;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.client.TenantsClient;
import org.apache.nifi.registry.client.UserClient;
import org.apache.nifi.registry.client.impl.request.ProxiedEntityRequestConfig;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.RequestEntityProcessing;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import java.io.IOException;
import java.net.URI;
/**
* A NiFiRegistryClient that uses Jersey Client.
*/
public class JerseyNiFiRegistryClient implements NiFiRegistryClient {
static final String NIFI_REGISTRY_CONTEXT = "nifi-registry-api";
static final int DEFAULT_CONNECT_TIMEOUT = 10000;
static final int DEFAULT_READ_TIMEOUT = 10000;
private final Client client;
private final WebTarget baseTarget;
private final BucketClient bucketClient;
private final FlowClient flowClient;
private final FlowSnapshotClient flowSnapshotClient;
private final ItemsClient itemsClient;
private JerseyNiFiRegistryClient(final NiFiRegistryClient.Builder builder) {
final NiFiRegistryClientConfig registryClientConfig = builder.getConfig();
if (registryClientConfig == null) {
throw new IllegalArgumentException("NiFiRegistryClientConfig cannot be null");
}
String baseUrl = registryClientConfig.getBaseUrl();
if (StringUtils.isBlank(baseUrl)) {
throw new IllegalArgumentException("Base URL cannot be blank");
}
if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
}
if (!baseUrl.endsWith(NIFI_REGISTRY_CONTEXT)) {
baseUrl = baseUrl + "/" + NIFI_REGISTRY_CONTEXT;
}
try {
new URI(baseUrl);
} catch (final Exception e) {
throw new IllegalArgumentException("Invalid base URL: " + e.getMessage(), e);
}
final SSLContext sslContext = registryClientConfig.getSslContext();
final HostnameVerifier hostnameVerifier = registryClientConfig.getHostnameVerifier();
final ClientBuilder clientBuilder = ClientBuilder.newBuilder();
if (sslContext != null) {
clientBuilder.sslContext(sslContext);
}
if (hostnameVerifier != null) {
clientBuilder.hostnameVerifier(hostnameVerifier);
}
final int connectTimeout = registryClientConfig.getConnectTimeout() == null ? DEFAULT_CONNECT_TIMEOUT : registryClientConfig.getConnectTimeout();
final int readTimeout = registryClientConfig.getReadTimeout() == null ? DEFAULT_READ_TIMEOUT : registryClientConfig.getReadTimeout();
final ClientConfig clientConfig = new ClientConfig();
clientConfig.property(ClientProperties.CONNECT_TIMEOUT, connectTimeout);
clientConfig.property(ClientProperties.READ_TIMEOUT, readTimeout);
clientConfig.property(ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.CHUNKED);
clientConfig.register(jacksonJaxbJsonProvider());
clientBuilder.withConfig(clientConfig);
this.client = clientBuilder
.register(MultiPartFeature.class)
.build();
this.baseTarget = client.target(baseUrl);
this.bucketClient = new JerseyBucketClient(baseTarget);
this.flowClient = new JerseyFlowClient(baseTarget);
this.flowSnapshotClient = new JerseyFlowSnapshotClient(baseTarget);
this.itemsClient = new JerseyItemsClient(baseTarget);
}
@Override
public BucketClient getBucketClient() {
return this.bucketClient;
}
@Override
public BucketClient getBucketClient(String... proxiedEntity) {
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
return new JerseyBucketClient(baseTarget, requestConfig);
}
@Override
public BucketClient getBucketClient(RequestConfig requestConfig) {
return new JerseyBucketClient(baseTarget, requestConfig);
}
@Override
public FlowClient getFlowClient() {
return this.flowClient;
}
@Override
public FlowClient getFlowClient(String... proxiedEntity) {
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
return new JerseyFlowClient(baseTarget, requestConfig);
}
@Override
public FlowClient getFlowClient(RequestConfig requestConfig) {
return new JerseyFlowClient(baseTarget, requestConfig);
}
@Override
public FlowSnapshotClient getFlowSnapshotClient() {
return this.flowSnapshotClient;
}
@Override
public FlowSnapshotClient getFlowSnapshotClient(String... proxiedEntity) {
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
return new JerseyFlowSnapshotClient(baseTarget, requestConfig);
}
@Override
public FlowSnapshotClient getFlowSnapshotClient(RequestConfig requestConfig) {
return new JerseyFlowSnapshotClient(baseTarget, requestConfig);
}
@Override
public ItemsClient getItemsClient() {
return this.itemsClient;
}
@Override
public ItemsClient getItemsClient(String... proxiedEntity) {
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
return new JerseyItemsClient(baseTarget, requestConfig);
}
@Override
public ItemsClient getItemsClient(RequestConfig requestConfig) {
return new JerseyItemsClient(baseTarget, requestConfig);
}
@Override
public UserClient getUserClient() {
return new JerseyUserClient(baseTarget);
}
@Override
public UserClient getUserClient(String... proxiedEntity) {
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
return new JerseyUserClient(baseTarget, requestConfig);
}
@Override
public UserClient getUserClient(RequestConfig requestConfig) {
return new JerseyUserClient(baseTarget, requestConfig);
}
@Override
public BundleClient getBundleClient() {
return new JerseyBundleClient(baseTarget);
}
@Override
public BundleClient getBundleClient(String... proxiedEntity) {
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
return new JerseyBundleClient(baseTarget, requestConfig);
}
@Override
public BundleClient getBundleClient(RequestConfig requestConfig) {
return new JerseyBundleClient(baseTarget, requestConfig);
}
@Override
public BundleVersionClient getBundleVersionClient() {
return new JerseyBundleVersionClient(baseTarget);
}
@Override
public BundleVersionClient getBundleVersionClient(String... proxiedEntity) {
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
return new JerseyBundleVersionClient(baseTarget, requestConfig);
}
@Override
public BundleVersionClient getBundleVersionClient(RequestConfig requestConfig) {
return new JerseyBundleVersionClient(baseTarget, requestConfig);
}
@Override
public ExtensionRepoClient getExtensionRepoClient() {
return new JerseyExtensionRepoClient(baseTarget);
}
@Override
public ExtensionRepoClient getExtensionRepoClient(String... proxiedEntity) {
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
return new JerseyExtensionRepoClient(baseTarget, requestConfig);
}
@Override
public ExtensionRepoClient getExtensionRepoClient(RequestConfig requestConfig) {
return new JerseyExtensionRepoClient(baseTarget, requestConfig);
}
@Override
public ExtensionClient getExtensionClient() {
return new JerseyExtensionClient(baseTarget);
}
@Override
public ExtensionClient getExtensionClient(String... proxiedEntity) {
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
return new JerseyExtensionClient(baseTarget, requestConfig);
}
@Override
public ExtensionClient getExtensionClient(RequestConfig requestConfig) {
return new JerseyExtensionClient(baseTarget, requestConfig);
}
@Override
public TenantsClient getTenantsClient() {
return new JerseyTenantsClient(baseTarget);
}
@Override
public TenantsClient getTenantsClient(String... proxiedEntity) {
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
return new JerseyTenantsClient(baseTarget, requestConfig);
}
@Override
public TenantsClient getTenantsClient(RequestConfig requestConfig) {
return new JerseyTenantsClient(baseTarget, requestConfig);
}
@Override
public PoliciesClient getPoliciesClient() {
return new JerseyPoliciesClient(baseTarget);
}
@Override
public PoliciesClient getPoliciesClient(String... proxiedEntity) {
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
return new JerseyPoliciesClient(baseTarget, requestConfig);
}
@Override
public PoliciesClient getPoliciesClient(RequestConfig requestConfig) {
return new JerseyPoliciesClient(baseTarget, requestConfig);
}
@Override
public AccessClient getAccessClient() {
return new JerseyAccessClient(baseTarget);
}
@Override
public void close() throws IOException {
if (this.client != null) {
try {
this.client.close();
} catch (Exception e) {
}
}
}
/**
* Builder for creating a JerseyNiFiRegistryClient.
*/
public static class Builder implements NiFiRegistryClient.Builder {
private NiFiRegistryClientConfig clientConfig;
@Override
public Builder config(final NiFiRegistryClientConfig clientConfig) {
this.clientConfig = clientConfig;
return this;
}
@Override
public NiFiRegistryClientConfig getConfig() {
return clientConfig;
}
@Override
public NiFiRegistryClient build() {
return new JerseyNiFiRegistryClient(this);
}
}
private static JacksonJaxbJsonProvider jacksonJaxbJsonProvider() {
JacksonJaxbJsonProvider jacksonJaxbJsonProvider = new JacksonJaxbJsonProvider();
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL));
mapper.setAnnotationIntrospector(new JaxbAnnotationIntrospector(mapper.getTypeFactory()));
// Ignore unknown properties so that deployed client remain compatible with future versions of NiFi Registry that add new fields
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SimpleModule module = new SimpleModule();
module.addDeserializer(BucketItem[].class, new BucketItemDeserializer());
mapper.registerModule(module);
jacksonJaxbJsonProvider.setMapper(mapper);
return jacksonJaxbJsonProvider;
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.registry.client.impl;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.authorization.AccessPolicy;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.PoliciesClient;
import org.apache.nifi.registry.client.RequestConfig;
import javax.ws.rs.client.WebTarget;
import java.io.IOException;
public class JerseyPoliciesClient extends AbstractCRUDJerseyClient implements PoliciesClient {
public static final String ACCESS_POLICY = "Access policy";
public static final String POLICIES_PATH = "policies";
public JerseyPoliciesClient(final WebTarget baseTarget) {
this(baseTarget, null);
}
public JerseyPoliciesClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(baseTarget, requestConfig);
}
@Override
public AccessPolicy getAccessPolicy(String action, String resource) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(resource) || StringUtils.isBlank(action)) {
throw new IllegalArgumentException("Resource and action cannot be null");
}
return executeAction("Error retrieving access policy", () -> {
final WebTarget target = baseTarget.path(POLICIES_PATH).path(action).path(resource);
return getRequestBuilder(target).get(AccessPolicy.class);
});
}
@Override
public AccessPolicy createAccessPolicy(final AccessPolicy policy) throws NiFiRegistryException, IOException {
return create(policy, AccessPolicy.class, ACCESS_POLICY, POLICIES_PATH);
}
@Override
public AccessPolicy updateAccessPolicy(final AccessPolicy policy) throws NiFiRegistryException, IOException {
return update(policy, policy.getIdentifier(), AccessPolicy.class, ACCESS_POLICY, POLICIES_PATH);
}
}

View File

@ -0,0 +1,112 @@
/*
* 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.registry.client.impl;
import org.apache.nifi.registry.authorization.User;
import org.apache.nifi.registry.authorization.UserGroup;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.client.TenantsClient;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import javax.ws.rs.client.WebTarget;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class JerseyTenantsClient extends AbstractCRUDJerseyClient implements TenantsClient {
public static final String USER = "User";
public static final String USERS_PATH = "users";
public static final String USER_GROUP = "User group";
public static final String USER_GROUPS_PATH = "user-groups";
public JerseyTenantsClient(final WebTarget baseTarget) {
this(baseTarget, null);
}
public JerseyTenantsClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(baseTarget.path("/tenants"), requestConfig);
}
@Override
public List<User> getUsers() throws NiFiRegistryException, IOException {
return executeAction("Error retrieving users", () -> {
final WebTarget target = baseTarget.path(USERS_PATH);
return Arrays.asList(getRequestBuilder(target).get(User[].class));
});
}
@Override
public User getUser(final String id) throws NiFiRegistryException, IOException {
return get(id, User.class, USER, USERS_PATH);
}
@Override
public User createUser(final User user) throws NiFiRegistryException, IOException {
return create(user, User.class, USER, USERS_PATH);
}
@Override
public User updateUser(final User user) throws NiFiRegistryException, IOException {
return update(user, user.getIdentifier(), User.class, USER, USERS_PATH);
}
@Override
public User deleteUser(final String id) throws NiFiRegistryException, IOException {
return delete(id, null, User.class, USER, USERS_PATH);
}
@Override
public User deleteUser(final String id, final RevisionInfo revisionInfo) throws NiFiRegistryException, IOException {
return delete(id, revisionInfo, User.class, USER, USERS_PATH);
}
@Override
public List<UserGroup> getUserGroups() throws NiFiRegistryException, IOException {
return executeAction("Error retrieving users", () -> {
final WebTarget target = baseTarget.path(USER_GROUPS_PATH);
return Arrays.asList(getRequestBuilder(target).get(UserGroup[].class));
});
}
@Override
public UserGroup getUserGroup(final String id) throws NiFiRegistryException, IOException {
return get(id, UserGroup.class, USER_GROUP, USER_GROUPS_PATH);
}
@Override
public UserGroup createUserGroup(final UserGroup group) throws NiFiRegistryException, IOException {
return create(group, UserGroup.class, USER_GROUP, USER_GROUPS_PATH);
}
@Override
public UserGroup updateUserGroup(final UserGroup group) throws NiFiRegistryException, IOException {
return update(group, group.getIdentifier(), UserGroup.class, USER_GROUP, USER_GROUPS_PATH);
}
@Override
public UserGroup deleteUserGroup(final String id) throws NiFiRegistryException, IOException {
return delete(id, null, UserGroup.class, USER_GROUP, USER_GROUPS_PATH);
}
@Override
public UserGroup deleteUserGroup(final String id, final RevisionInfo revisionInfo) throws NiFiRegistryException, IOException {
return delete(id, revisionInfo, UserGroup.class, USER_GROUP, USER_GROUPS_PATH);
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.registry.client.impl;
import org.apache.nifi.registry.authorization.CurrentUser;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.client.UserClient;
import javax.ws.rs.client.WebTarget;
import java.io.IOException;
public class JerseyUserClient extends AbstractJerseyClient implements UserClient {
private final WebTarget accessTarget;
public JerseyUserClient(final WebTarget baseTarget) {
this(baseTarget, null);
}
public JerseyUserClient(final WebTarget baseTarget, final RequestConfig requestConfig) {
super(requestConfig);
this.accessTarget = baseTarget.path("/access");
}
@Override
public CurrentUser getAccessStatus() throws NiFiRegistryException, IOException {
return executeAction("Error retrieving access status for the current user", () -> {
return getRequestBuilder(accessTarget).get(CurrentUser.class);
});
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.registry.client.impl.request;
import org.apache.commons.lang3.Validate;
import org.apache.nifi.registry.client.RequestConfig;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
/**
* Implementation of RequestConfig for a request with basic auth.
*/
public class BasicAuthRequestConfig implements RequestConfig {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String BASIC = "Basic";
private final String username;
private final String password;
public BasicAuthRequestConfig(final String username, final String password) {
this.username = Validate.notBlank(username);
this.password = Validate.notBlank(password);
}
@Override
public Map<String, String> getHeaders() {
final String basicCreds = username + ":" + password;
final byte[] basicCredsBytes = basicCreds.getBytes(StandardCharsets.UTF_8);
final Base64.Encoder encoder = Base64.getEncoder();
final String encodedBasicCreds = encoder.encodeToString(basicCredsBytes);
final Map<String,String> headers = new HashMap<>();
headers.put(AUTHORIZATION_HEADER, BASIC + " " + encodedBasicCreds);
return headers;
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.registry.client.impl.request;
import org.apache.commons.lang3.Validate;
import org.apache.nifi.registry.client.RequestConfig;
import java.util.HashMap;
import java.util.Map;
/**
* Implementation of RequestConfig for a request with a bearer token.
*/
public class BearerTokenRequestConfig implements RequestConfig {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String BEARER = "Bearer";
private final String token;
public BearerTokenRequestConfig(final String token) {
this.token = Validate.notBlank(token);
}
@Override
public Map<String, String> getHeaders() {
final Map<String,String> headers = new HashMap<>();
headers.put(AUTHORIZATION_HEADER, BEARER + " " + token);
return headers;
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.registry.client.impl.request;
import org.apache.commons.lang3.Validate;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.security.util.ProxiedEntitiesUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Implementation of RequestConfig that produces headers for a request with proxied-entities.
*/
public class ProxiedEntityRequestConfig implements RequestConfig {
private final String proxiedEntitiesChain;
public ProxiedEntityRequestConfig(final String... proxiedEntities) {
Validate.notNull(proxiedEntities);
this.proxiedEntitiesChain = ProxiedEntitiesUtils.getProxiedEntitiesChain(proxiedEntities);
}
@Override
public Map<String, String> getHeaders() {
final Map<String,String> headers = new HashMap<>();
if (proxiedEntitiesChain != null) {
headers.put(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN, proxiedEntitiesChain);
}
return headers;
}
}

View File

@ -0,0 +1,48 @@
/*
* 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.registry.client.impl.request;
import org.apache.nifi.registry.client.RequestConfig;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
public class TestBasicAuthRequestConfig {
@Test
public void testBasicAuthRequestConfig() {
final String username = "user1";
final String password = "password";
final String basicCreds = username + ":" + password;
final String expectedHeaderValue = "Basic " + Base64.getEncoder().encodeToString(basicCreds.getBytes(StandardCharsets.UTF_8));
final RequestConfig requestConfig = new BasicAuthRequestConfig(username, password);
final Map<String,String> headers = requestConfig.getHeaders();
assertNotNull(headers);
assertEquals(1, headers.size());
final String authorizationHeaderValue = headers.get("Authorization");
assertEquals(expectedHeaderValue, authorizationHeaderValue);
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.registry.client.impl.request;
import org.apache.nifi.registry.client.RequestConfig;
import org.junit.Test;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class TestBearerTokenRequestConfig {
@Test
public void testBearerTokenRequestConfig() {
final String token = "some-token";
final String expectedHeaderValue = "Bearer " + token;
final RequestConfig requestConfig = new BearerTokenRequestConfig(token);
final Map<String,String> headers = requestConfig.getHeaders();
assertNotNull(headers);
assertEquals(1, headers.size());
final String authorizationHeaderValue = headers.get("Authorization");
assertEquals(expectedHeaderValue, authorizationHeaderValue);
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.registry.client.impl.request;
import org.apache.nifi.registry.client.RequestConfig;
import org.apache.nifi.registry.security.util.ProxiedEntitiesUtils;
import org.junit.Test;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class TestProxiedEntityRequestConfig {
@Test
public void testSingleProxiedEntity() {
final String proxiedEntity = "user1";
final String expectedProxiedEntitiesChain = "<user1>";
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(proxiedEntity);
final Map<String,String> headers = requestConfig.getHeaders();
assertNotNull(headers);
assertEquals(1, headers.size());
final String proxiedEntitiesChainHeaderValue = headers.get(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN);
assertEquals(expectedProxiedEntitiesChain, proxiedEntitiesChainHeaderValue);
}
@Test
public void testMultipleProxiedEntity() {
final String proxiedEntity1 = "user1";
final String proxiedEntity2 = "user2";
final String proxiedEntity3 = "user3";
final String expectedProxiedEntitiesChain = "<user1><user2><user3>";
final RequestConfig requestConfig = new ProxiedEntityRequestConfig(
proxiedEntity1, proxiedEntity2, proxiedEntity3);
final Map<String,String> headers = requestConfig.getHeaders();
assertNotNull(headers);
assertEquals(1, headers.size());
final String proxiedEntitiesChainHeaderValue = headers.get(ProxiedEntitiesUtils.PROXY_ENTITIES_CHAIN);
assertEquals(expectedProxiedEntitiesChain, proxiedEntitiesChainHeaderValue);
}
}

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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. -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-core</artifactId>
<version>1.14.0-SNAPSHOT</version>
</parent>
<artifactId>nifi-registry-data-model</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-revision-entity-model</artifactId>
<version>1.14.0-SNAPSHOT</version>
</dependency>
</dependencies>
<profiles>
<profile>
<!-- This profile provides configuration to allow NiFi Registry to be compiled on JDKs above 1.8. -->
<id>jigsaw</id>
<activation>
<jdk>(1.8,)</jdk>
</activation>
<dependencies>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,78 @@
/*
* 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.registry;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@ApiModel
public class RegistryConfiguration {
private Boolean supportsManagedAuthorizer;
private Boolean supportsConfigurableAuthorizer;
private Boolean supportsConfigurableUsersAndGroups;
/**
* @return whether this NiFi Registry supports a managed authorizer. Managed authorizers can visualize users, groups,
* and policies in the UI. This value is read only
*/
@ApiModelProperty(
value = "Whether this NiFi Registry supports a managed authorizer. Managed authorizers can visualize users, groups, and policies in the UI.",
readOnly = true
)
public Boolean getSupportsManagedAuthorizer() {
return supportsManagedAuthorizer;
}
public void setSupportsManagedAuthorizer(Boolean supportsManagedAuthorizer) {
this.supportsManagedAuthorizer = supportsManagedAuthorizer;
}
/**
* @return whether this NiFi Registry supports configurable users and groups. This value is read only
*/
@ApiModelProperty(
value = "Whether this NiFi Registry supports configurable users and groups.",
readOnly = true
)
public Boolean getSupportsConfigurableUsersAndGroups() {
return supportsConfigurableUsersAndGroups;
}
public void setSupportsConfigurableUsersAndGroups(Boolean supportsConfigurableUsersAndGroups) {
this.supportsConfigurableUsersAndGroups = supportsConfigurableUsersAndGroups;
}
/**
* @return whether this NiFi Registry supports a configurable authorizer. This value is read only
*/
@ApiModelProperty(
value = "Whether this NiFi Registry supports a configurable authorizer.",
readOnly = true
)
public Boolean getSupportsConfigurableAuthorizer() {
return supportsConfigurableAuthorizer;
}
public void setSupportsConfigurableAuthorizer(Boolean supportsConfigurableAuthorizer) {
this.supportsConfigurableAuthorizer = supportsConfigurableAuthorizer;
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.registry.authorization;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* Access policy details, including the users and user groups to which the policy applies.
*/
@ApiModel
public class AccessPolicy extends AccessPolicySummary {
private Set<Tenant> users;
private Set<Tenant> userGroups;
@ApiModelProperty(value = "The set of user IDs associated with this access policy.")
public Set<Tenant> getUsers() {
return users;
}
public void setUsers(Set<Tenant> users) {
this.users = users;
}
public void addUsers(Collection<? extends Tenant> users) {
if (users != null) {
if (this.users == null) {
this.users = new HashSet<>();
}
this.users.addAll(users);
}
}
@ApiModelProperty(value = "The set of user group IDs associated with this access policy.")
public Set<Tenant> getUserGroups() {
return userGroups;
}
public void setUserGroups(Set<Tenant> userGroups) {
this.userGroups = userGroups;
}
public void addUserGroups(Collection<? extends Tenant> userGroups) {
if (userGroups != null) {
if (this.userGroups == null) {
this.userGroups = new HashSet<>();
}
this.userGroups.addAll(userGroups);
}
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.registry.authorization;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.apache.nifi.registry.revision.entity.RevisableEntity;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
/**
* Access policy summary of which actions ("read', "write", "delete") are allowable for a specified web resource.
*/
@ApiModel
public class AccessPolicySummary implements RevisableEntity {
private String identifier;
private String resource;
private String action;
private Boolean configurable;
private RevisionInfo revision;
@ApiModelProperty(value = "The id of the policy. Set by server at creation time.", readOnly = true)
@Override
public String getIdentifier() {
return identifier;
}
@Override
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
@ApiModelProperty(value = "The resource for this access policy.", required = true
)
public String getResource() {
return resource;
}
public void setResource(String resource) {
this.resource = resource;
}
@ApiModelProperty(
value = "The action associated with this access policy.",
allowableValues = "read, write, delete",
required = true
)
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
@ApiModelProperty(value = "Indicates if this access policy is configurable, based on which Authorizer has been configured to manage it.", readOnly = true)
public Boolean getConfigurable() {
return configurable;
}
public void setConfigurable(Boolean configurable) {
this.configurable = configurable;
}
@ApiModelProperty(
value = "The revision of this entity used for optimistic-locking during updates.",
readOnly = true
)
@Override
public RevisionInfo getRevision() {
return revision;
}
@Override
public void setRevision(RevisionInfo revision) {
this.revision = revision;
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.registry.authorization;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel
public class CurrentUser {
private String identity;
private boolean anonymous;
private boolean loginSupported;
private boolean oidcLoginSupported;
private ResourcePermissions resourcePermissions;
@ApiModelProperty(value = "The identity of the current user", readOnly = true)
public String getIdentity() {
return identity;
}
public void setIdentity(String identity) {
this.identity = identity;
}
@ApiModelProperty(value = "Indicates if the current user is anonymous", readOnly = true)
public boolean isAnonymous() {
return anonymous;
}
public void setAnonymous(boolean anonymous) {
this.anonymous = anonymous;
}
@ApiModelProperty(value = "Indicates if the NiFi Registry instance supports logging in")
public boolean isLoginSupported() {
return loginSupported;
}
@ApiModelProperty(value = "Indicates if the NiFi Registry instance supports logging in with an OIDC provider")
public boolean isOIDCLoginSupported() {
return oidcLoginSupported;
}
public void setLoginSupported(boolean loginSupported) {
this.loginSupported = loginSupported;
}
public void setOIDCLoginSupported(boolean oidcLoginSupported) {
this.oidcLoginSupported = oidcLoginSupported;
}
@ApiModelProperty(value = "The access that the current user has to top level resources", readOnly = true)
public ResourcePermissions getResourcePermissions() {
return resourcePermissions;
}
public void setResourcePermissions(ResourcePermissions resourcePermissions) {
this.resourcePermissions = resourcePermissions;
}
}

View File

@ -0,0 +1,130 @@
/*
* 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.registry.authorization;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel
public class Permissions {
private boolean canRead = false;
private boolean canWrite = false;
private boolean canDelete = false;
public Permissions() {
}
public Permissions(Permissions permissions) {
if (permissions == null) {
throw new IllegalArgumentException("Cannot call copy constructor with null argument");
}
this.canRead = permissions.getCanRead();
this.canWrite = permissions.getCanWrite();
this.canDelete = permissions.getCanDelete();
}
/**
* @return Indicates whether the user can read a given resource.
*/
@ApiModelProperty(
value = "Indicates whether the user can read a given resource.",
readOnly = true
)
public boolean getCanRead() {
return canRead;
}
public void setCanRead(boolean canRead) {
this.canRead = canRead;
}
public Permissions withCanRead(boolean canRead) {
setCanRead(canRead);
return this;
}
/**
* @return Indicates whether the user can write a given resource.
*/
@ApiModelProperty(
value = "Indicates whether the user can write a given resource.",
readOnly = true
)
public boolean getCanWrite() {
return canWrite;
}
public void setCanWrite(boolean canWrite) {
this.canWrite = canWrite;
}
public Permissions withCanWrite(boolean canWrite) {
setCanWrite(canWrite);
return this;
}
/**
* @return Indicates whether the user can delete a given resource.
*/
@ApiModelProperty(
value = "Indicates whether the user can delete a given resource.",
readOnly = true
)
public boolean getCanDelete() {
return canDelete;
}
public void setCanDelete(boolean canDelete) {
this.canDelete = canDelete;
}
public Permissions withCanDelete(boolean canDelete) {
setCanDelete(canDelete);
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Permissions that = (Permissions) o;
if (canRead != that.canRead) return false;
if (canWrite != that.canWrite) return false;
return canDelete == that.canDelete;
}
@Override
public int hashCode() {
int result = (canRead ? 1 : 0);
result = 31 * result + (canWrite ? 1 : 0);
result = 31 * result + (canDelete ? 1 : 0);
return result;
}
@Override
public String toString() {
return "Permissions{" +
"canRead=" + canRead +
", canWrite=" + canWrite +
", canDelete=" + canDelete +
'}';
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.registry.authorization;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel
public class Resource {
private String identifier;
private String name;
/**
* The name of the resource.
*
* @return The name of the resource
*/
@ApiModelProperty(value = "The name of the resource.", readOnly = true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* The identifier of the resource.
*
* @return The identifier of the resource
*/
@ApiModelProperty(value = "The identifier of the resource.", readOnly = true)
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
}

View File

@ -0,0 +1,127 @@
/*
* 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.registry.authorization;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel
public class ResourcePermissions {
private Permissions buckets = new Permissions();
private Permissions tenants = new Permissions();
private Permissions policies = new Permissions();
private Permissions proxy = new Permissions();
@ApiModelProperty(
value = "The access that the current user has to any top level resources (a logical 'OR' of all other values)",
readOnly = true)
public Permissions getAnyTopLevelResource() {
return new Permissions()
.withCanRead(buckets.getCanRead()
|| tenants.getCanRead()
|| policies.getCanRead()
|| proxy.getCanRead())
.withCanWrite(buckets.getCanWrite()
|| tenants.getCanWrite()
|| policies.getCanWrite()
|| proxy.getCanWrite())
.withCanDelete(buckets.getCanDelete()
|| tenants.getCanDelete()
|| policies.getCanDelete()
|| proxy.getCanDelete());
}
@ApiModelProperty(
value = "The access that the current user has to the top level /buckets resource of this NiFi Registry (i.e., access to all buckets)",
readOnly = true)
public Permissions getBuckets() {
return buckets;
}
public void setBuckets(Permissions buckets) {
this.buckets = buckets;
}
@ApiModelProperty(
value = "The access that the current user has to the top level /tenants resource of this NiFi Registry",
readOnly = true)
public Permissions getTenants() {
return tenants;
}
public void setTenants(Permissions tenants) {
this.tenants = tenants;
}
@ApiModelProperty(
value = "The access that the current user has to the top level /policies resource of this NiFi Registry",
readOnly = true)
public Permissions getPolicies() {
return policies;
}
public void setPolicies(Permissions policies) {
this.policies = policies;
}
@ApiModelProperty(
value = "The access that the current user has to the top level /proxy resource of this NiFi Registry",
readOnly = true)
public Permissions getProxy() {
return proxy;
}
public void setProxy(Permissions proxy) {
this.proxy = proxy;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ResourcePermissions that = (ResourcePermissions) o;
if (buckets != null ? !buckets.equals(that.buckets) : that.buckets != null)
return false;
if (tenants != null ? !tenants.equals(that.tenants) : that.tenants != null)
return false;
if (policies != null ? !policies.equals(that.policies) : that.policies != null)
return false;
return proxy != null ? proxy.equals(that.proxy) : that.proxy == null;
}
@Override
public int hashCode() {
int result = buckets != null ? buckets.hashCode() : 0;
result = 31 * result + (tenants != null ? tenants.hashCode() : 0);
result = 31 * result + (policies != null ? policies.hashCode() : 0);
result = 31 * result + (proxy != null ? proxy.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "ResourcePermissions{" +
"buckets=" + buckets +
", tenants=" + tenants +
", policies=" + policies +
", proxy=" + proxy +
'}';
}
}

View File

@ -0,0 +1,135 @@
/*
* 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.registry.authorization;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.apache.nifi.registry.revision.entity.RevisableEntity;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* A tenant of this NiFi Registry
*/
@ApiModel
public class Tenant implements RevisableEntity {
private String identifier;
private String identity;
private Boolean configurable;
private ResourcePermissions resourcePermissions;
private Set<AccessPolicySummary> accessPolicies;
private RevisionInfo revision;
public Tenant() {}
public Tenant(String identifier, String identity) {
this.identifier = identifier;
this.identity = identity;
}
/**
* @return tenant's unique identifier
*/
@ApiModelProperty(
value = "The computer-generated identifier of the tenant.",
readOnly = true)
@Override
public String getIdentifier() {
return identifier;
}
@Override
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
/**
* @return tenant's identity
*/
@ApiModelProperty(
value = "The human-facing identity of the tenant. This can only be changed if the tenant is configurable.",
required = true)
public String getIdentity() {
return identity;
}
public void setIdentity(String identity) {
this.identity = identity;
}
@ApiModelProperty(
value = "Indicates if this tenant is configurable, based on which UserGroupProvider has been configured to manage it.",
readOnly = true)
public Boolean getConfigurable() {
return configurable;
}
public void setConfigurable(Boolean configurable) {
this.configurable = configurable;
}
@ApiModelProperty(
value = "A summary top-level resource access policies granted to this tenant.",
readOnly = true
)
public ResourcePermissions getResourcePermissions() {
return resourcePermissions;
}
public void setResourcePermissions(ResourcePermissions resourcePermissions) {
this.resourcePermissions = resourcePermissions;
}
@ApiModelProperty(
value = "The access policies granted to this tenant.",
readOnly = true
)
public Set<AccessPolicySummary> getAccessPolicies() {
return accessPolicies;
}
public void setAccessPolicies(Set<AccessPolicySummary> accessPolicies) {
this.accessPolicies = accessPolicies;
}
public void addAccessPolicies(Collection<AccessPolicySummary> accessPolicies) {
if (accessPolicies != null) {
if (this.accessPolicies == null) {
this.accessPolicies = new HashSet<>();
}
this.accessPolicies.addAll(accessPolicies);
}
}
@ApiModelProperty(
value = "The revision of this entity used for optimistic-locking during updates.",
readOnly = true
)
@Override
public RevisionInfo getRevision() {
return revision;
}
@Override
public void setRevision(RevisionInfo revision) {
this.revision = revision;
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.registry.authorization;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@ApiModel
public class User extends Tenant {
private Set<Tenant> userGroups;
public User() {}
public User(String identifier, String identity) {
super(identifier, identity);
}
@ApiModelProperty(
value = "The groups to which the user belongs.",
readOnly = true
)
public Set<Tenant> getUserGroups() {
return userGroups;
}
public void setUserGroups(Set<Tenant> userGroups) {
this.userGroups = userGroups;
}
public void addUserGroups(Collection<? extends Tenant> userGroups) {
if (userGroups != null) {
if (this.userGroups == null) {
this.userGroups = new HashSet<>();
}
this.userGroups.addAll(userGroups);
}
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.registry.authorization;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* A user group, used to apply a single set of authorization policies to a group of users.
*/
@ApiModel
public class UserGroup extends Tenant {
private Set<Tenant> users;
public UserGroup() {}
public UserGroup(String identifier, String identity) {
super(identifier, identity);
}
/**
* @return The users that belong to this user group.
*/
@ApiModelProperty(value = "The users that belong to this user group. This can only be changed if this group is configurable.")
public Set<Tenant> getUsers() {
return users;
}
public void setUsers(Set<Tenant> users) {
this.users = users;
}
public void addUsers(Collection<? extends Tenant> users) {
if (users != null) {
if (this.users == null) {
this.users = new HashSet<>();
}
this.users.addAll(users);
}
}
public void addUser(Tenant user) {
if (user != null) {
if (this.users == null) {
this.users = new HashSet<>();
}
this.users.add(user);
}
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.registry.bucket;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.apache.nifi.registry.authorization.Permissions;
import org.apache.nifi.registry.link.LinkableEntity;
import org.apache.nifi.registry.revision.entity.RevisableEntity;
import org.apache.nifi.registry.revision.entity.RevisionInfo;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.Objects;
@XmlRootElement
@ApiModel
public class Bucket extends LinkableEntity implements RevisableEntity {
@NotBlank
private String identifier;
@NotBlank
private String name;
@Min(1)
private long createdTimestamp;
private String description;
private Boolean allowBundleRedeploy;
private Boolean allowPublicRead;
private Permissions permissions;
private RevisionInfo revision;
@ApiModelProperty(value = "An ID to uniquely identify this object.", readOnly = true)
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
@ApiModelProperty(value = "The name of the bucket.", required = true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ApiModelProperty(value = "The timestamp of when the bucket was first created. This is set by the server at creation time.", readOnly = true)
public long getCreatedTimestamp() {
return createdTimestamp;
}
public void setCreatedTimestamp(long createdTimestamp) {
this.createdTimestamp = createdTimestamp;
}
@ApiModelProperty("A description of the bucket.")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@ApiModelProperty("Indicates if this bucket allows the same version of an extension bundle to be redeployed and thus overwrite the existing artifact. By default this is false.")
public Boolean isAllowBundleRedeploy() {
return allowBundleRedeploy;
}
public void setAllowBundleRedeploy(final Boolean allowBundleRedeploy) {
this.allowBundleRedeploy = allowBundleRedeploy;
}
@ApiModelProperty("Indicates if this bucket allows read access to unauthenticated anonymous users")
public Boolean isAllowPublicRead() {
return allowPublicRead;
}
public void setAllowPublicRead(final Boolean allowPublicRead) {
this.allowPublicRead = allowPublicRead;
}
@ApiModelProperty(value = "The access that the current user has to this bucket.", readOnly = true)
public Permissions getPermissions() {
return permissions;
}
public void setPermissions(Permissions permissions) {
this.permissions = permissions;
}
@ApiModelProperty(
value = "The revision of this entity used for optimistic-locking during updates.",
readOnly = true
)
@Override
public RevisionInfo getRevision() {
return revision;
}
@Override
public void setRevision(RevisionInfo revision) {
this.revision = revision;
}
@Override
public int hashCode() {
return Objects.hashCode(this.getIdentifier());
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Bucket other = (Bucket) obj;
return Objects.equals(this.getIdentifier(), other.getIdentifier());
}
}

View File

@ -0,0 +1,156 @@
/*
* 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.registry.bucket;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.apache.nifi.registry.authorization.Permissions;
import org.apache.nifi.registry.link.LinkableEntity;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.Objects;
@ApiModel
public abstract class BucketItem extends LinkableEntity {
@NotBlank
private String identifier;
@NotBlank
private String name;
private String description;
@NotBlank
private String bucketIdentifier;
// read-only
private String bucketName;
@Min(1)
private long createdTimestamp;
@Min(1)
private long modifiedTimestamp;
@NotNull
private final BucketItemType type;
private Permissions permissions;
public BucketItem(final BucketItemType type) {
this.type = type;
}
@ApiModelProperty(value = "An ID to uniquely identify this object.", readOnly = true)
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
@ApiModelProperty(value = "The name of the item.", required = true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ApiModelProperty("A description of the item.")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@ApiModelProperty(value = "The identifier of the bucket this items belongs to. This cannot be changed after the item is created.", required = true)
public String getBucketIdentifier() {
return bucketIdentifier;
}
public void setBucketIdentifier(String bucketIdentifier) {
this.bucketIdentifier = bucketIdentifier;
}
@ApiModelProperty(value = "The name of the bucket this items belongs to.", readOnly = true)
public String getBucketName() {
return bucketName;
}
public void setBucketName(String bucketName) {
this.bucketName = bucketName;
}
@ApiModelProperty(value = "The timestamp of when the item was created, as milliseconds since epoch.", readOnly = true)
public long getCreatedTimestamp() {
return createdTimestamp;
}
public void setCreatedTimestamp(long createdTimestamp) {
this.createdTimestamp = createdTimestamp;
}
@ApiModelProperty(value = "The timestamp of when the item was last modified, as milliseconds since epoch.", readOnly = true)
public long getModifiedTimestamp() {
return modifiedTimestamp;
}
public void setModifiedTimestamp(long modifiedTimestamp) {
this.modifiedTimestamp = modifiedTimestamp;
}
@ApiModelProperty(value = "The type of item.", required = true)
public BucketItemType getType() {
return type;
}
@ApiModelProperty(value = "The access that the current user has to the bucket containing this item.", readOnly = true)
public Permissions getPermissions() {
return permissions;
}
public void setPermissions(Permissions permissions) {
this.permissions = permissions;
}
@Override
public int hashCode() {
return Objects.hashCode(this.getIdentifier());
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final BucketItem other = (BucketItem) obj;
return Objects.equals(this.getIdentifier(), other.getIdentifier());
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.registry.bucket;
/**
* Type of item in a bucket.
*/
public enum BucketItemType {
// The case of these enum names matches what we want to return in
// the BucketItem.type field when serialized in an API response.
Flow,
Bundle;
}

View File

@ -0,0 +1,77 @@
/*
* 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.registry.diff;
import io.swagger.annotations.ApiModelProperty;
/**
* Represents a specific, individual difference that has changed between 2 versions.
* The change data and textual descriptions of the change are included for client consumption.
*/
public class ComponentDifference {
private String valueA;
private String valueB;
private String changeDescription;
private String differenceType;
private String differenceTypeDescription;
@ApiModelProperty("The earlier value from the difference.")
public String getValueA() {
return valueA;
}
public void setValueA(String valueA) {
this.valueA = valueA;
}
@ApiModelProperty("The newer value from the difference.")
public String getValueB() {
return valueB;
}
public void setValueB(String valueB) {
this.valueB = valueB;
}
@ApiModelProperty("The description of the change.")
public String getChangeDescription() {
return changeDescription;
}
public void setChangeDescription(String changeDescription) {
this.changeDescription = changeDescription;
}
@ApiModelProperty("The key to the difference.")
public String getDifferenceType() {
return differenceType;
}
public void setDifferenceType(String differenceType) {
this.differenceType = differenceType;
}
@ApiModelProperty("The description of the change type.")
public String getDifferenceTypeDescription() {
return differenceTypeDescription;
}
public void setDifferenceTypeDescription(String differenceTypeDescription) {
this.differenceTypeDescription = differenceTypeDescription;
}
}

Some files were not shown because too many files have changed in this diff Show More