From b8af44d81bff403654289e7837237088e4dc7a40 Mon Sep 17 00:00:00 2001 From: Matthew Burgess Date: Tue, 23 Nov 2021 11:11:14 -0500 Subject: [PATCH] NIFI-6871: Added HikariCPConnectionPool controller service This closes #3890 Signed-off-by: David Handermann --- .../nifi-dbcp-service-nar/pom.xml | 5 + ...g.apache.nifi.controller.ControllerService | 2 +- .../nifi-hikari-dbcp-service/pom.xml | 109 ++++++ .../nifi/dbcp/HikariCPConnectionPool.java | 362 ++++++++++++++++++ ...g.apache.nifi.controller.ControllerService | 15 + .../nifi/dbcp/HikariCPConnectionPoolTest.java | 257 +++++++++++++ .../nifi-dbcp-service-bundle/pom.xml | 1 + 7 files changed, 750 insertions(+), 1 deletion(-) create mode 100644 nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-hikari-dbcp-service/pom.xml create mode 100644 nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-hikari-dbcp-service/src/main/java/org/apache/nifi/dbcp/HikariCPConnectionPool.java create mode 100644 nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-hikari-dbcp-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService create mode 100644 nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-hikari-dbcp-service/src/test/java/org/apache/nifi/dbcp/HikariCPConnectionPoolTest.java diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service-nar/pom.xml b/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service-nar/pom.xml index 18380c4823..37cbe63853 100644 --- a/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service-nar/pom.xml +++ b/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service-nar/pom.xml @@ -38,5 +38,10 @@ nifi-dbcp-service 1.16.0-SNAPSHOT + + org.apache.nifi + nifi-hikari-dbcp-service + 1.16.0-SNAPSHOT + diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService b/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService index f87799650f..57bc3c6947 100644 --- a/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService +++ b/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService @@ -14,4 +14,4 @@ # limitations under the License. org.apache.nifi.dbcp.DBCPConnectionPool org.apache.nifi.dbcp.DBCPConnectionPoolLookup -org.apache.nifi.record.sink.db.DatabaseRecordSink \ No newline at end of file +org.apache.nifi.record.sink.db.DatabaseRecordSink diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-hikari-dbcp-service/pom.xml b/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-hikari-dbcp-service/pom.xml new file mode 100644 index 0000000000..7c9c85e1c4 --- /dev/null +++ b/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-hikari-dbcp-service/pom.xml @@ -0,0 +1,109 @@ + + + 4.0.0 + + org.apache.nifi + nifi-dbcp-service-bundle + 1.16.0-SNAPSHOT + + nifi-hikari-dbcp-service + jar + + 10.14.2.0 + + + + org.apache.nifi + nifi-dbcp-service-api + 1.16.0-SNAPSHOT + provided + + + org.apache.nifi + nifi-api + + + org.apache.nifi + nifi-utils + 1.16.0-SNAPSHOT + + + org.apache.nifi + nifi-service-utils + 1.16.0-SNAPSHOT + + + org.apache.nifi + nifi-security-kerberos + 1.16.0-SNAPSHOT + + + org.apache.nifi + nifi-kerberos-user-service-api + + + com.zaxxer + HikariCP + 4.0.3 + + + org.apache.nifi + nifi-mock + 1.16.0-SNAPSHOT + test + + + org.slf4j + jcl-over-slf4j + + + org.apache.derby + derby + ${derby.version} + + + org.apache.derby + derbynet + ${derby.version} + test + + + org.apache.derby + derbytools + ${derby.version} + test + + + org.apache.derby + derbyclient + ${derby.version} + test + + + + + + org.apache.rat + apache-rat-plugin + + + src/test/resources/fake.keytab + + + + + + \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-hikari-dbcp-service/src/main/java/org/apache/nifi/dbcp/HikariCPConnectionPool.java b/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-hikari-dbcp-service/src/main/java/org/apache/nifi/dbcp/HikariCPConnectionPool.java new file mode 100644 index 0000000000..040c6cb6ce --- /dev/null +++ b/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-hikari-dbcp-service/src/main/java/org/apache/nifi/dbcp/HikariCPConnectionPool.java @@ -0,0 +1,362 @@ +/* + * 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.dbcp; + +import com.zaxxer.hikari.HikariDataSource; +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.annotation.behavior.DynamicProperty; +import org.apache.nifi.annotation.behavior.RequiresInstanceClassLoading; +import org.apache.nifi.annotation.documentation.CapabilityDescription; +import org.apache.nifi.annotation.documentation.Tags; +import org.apache.nifi.annotation.lifecycle.OnDisabled; +import org.apache.nifi.annotation.lifecycle.OnEnabled; +import org.apache.nifi.components.PropertyDescriptor; +import org.apache.nifi.components.PropertyValue; +import org.apache.nifi.components.resource.ResourceCardinality; +import org.apache.nifi.components.resource.ResourceType; +import org.apache.nifi.controller.AbstractControllerService; +import org.apache.nifi.controller.ConfigurationContext; +import org.apache.nifi.expression.AttributeExpression; +import org.apache.nifi.expression.ExpressionLanguageScope; +import org.apache.nifi.kerberos.KerberosUserService; +import org.apache.nifi.processor.exception.ProcessException; +import org.apache.nifi.processor.util.StandardValidators; +import org.apache.nifi.security.krb.KerberosAction; +import org.apache.nifi.security.krb.KerberosUser; + +import javax.security.auth.login.LoginException; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * Implementation of Database Connection Pooling Service. HikariCP is used for connection pooling functionality. + */ +@RequiresInstanceClassLoading +@Tags({"dbcp", "hikari", "jdbc", "database", "connection", "pooling", "store"}) +@CapabilityDescription("Provides Database Connection Pooling Service based on HikariCP. Connections can be asked from pool and returned after usage.") +@DynamicProperty(name = "JDBC property name", value = "JDBC property value", expressionLanguageScope = ExpressionLanguageScope.VARIABLE_REGISTRY, + 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.") +public class HikariCPConnectionPool extends AbstractControllerService implements DBCPService { + /** + * Property Name Prefix for Sensitive Dynamic Properties + */ + protected static final String SENSITIVE_PROPERTY_PREFIX = "SENSITIVE."; + protected static final long INFINITE_MILLISECONDS = -1L; + + private static final String DEFAULT_TOTAL_CONNECTIONS = "10"; + private static final String DEFAULT_MAX_CONN_LIFETIME = "-1"; + + public static final PropertyDescriptor DATABASE_URL = new PropertyDescriptor.Builder() + .name("hikaricp-connection-url") + .displayName("Database Connection URL") + .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.") + .defaultValue(null) + .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .required(true) + .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY) + .build(); + + public static final PropertyDescriptor DB_DRIVERNAME = new PropertyDescriptor.Builder() + .name("hikaricp-driver-classname") + .displayName("Database Driver Class Name") + .description("The fully-qualified class name of the JDBC driver. Example: com.mysql.jdbc.Driver") + .defaultValue(null) + .required(true) + .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY) + .build(); + + public static final PropertyDescriptor DB_DRIVER_LOCATION = new PropertyDescriptor.Builder() + .name("hikaricp-driver-locations") + .displayName("Database Driver Location(s)") + .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'") + .defaultValue(null) + .required(false) + .identifiesExternalResource(ResourceCardinality.MULTIPLE, ResourceType.FILE, ResourceType.DIRECTORY, ResourceType.URL) + .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY) + .dynamicallyModifiesClasspath(true) + .build(); + + public static final PropertyDescriptor DB_USER = new PropertyDescriptor.Builder() + .name("hikaricp-username") + .displayName("Database User") + .description("Database user name") + .defaultValue(null) + .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY) + .build(); + + public static final PropertyDescriptor DB_PASSWORD = new PropertyDescriptor.Builder() + .name("hikaricp-password") + .displayName("Password") + .description("The password for the database user") + .defaultValue(null) + .required(false) + .sensitive(true) + .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) + .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY) + .build(); + + public static final PropertyDescriptor MAX_WAIT_TIME = new PropertyDescriptor.Builder() + .name("hikaricp-max-wait-time") + .displayName("Max Wait Time") + .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 0