exceptionfactory commented on code in PR #11032:
URL: https://github.com/apache/nifi/pull/11032#discussion_r3770370419


##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/cassandra/CassandraSessionProvider.java:
##########
@@ -0,0 +1,340 @@
+/*
+ * 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.service.cassandra;
+
+import com.datastax.oss.driver.api.core.ConsistencyLevel;
+import com.datastax.oss.driver.api.core.CqlIdentifier;
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.CqlSessionBuilder;
+import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+import 
com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder;
+import com.datastax.oss.driver.api.core.metadata.Metadata;
+import com.datastax.oss.driver.api.core.metadata.Node;
+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.cassandra.CassandraClient;
+import org.apache.nifi.cassandra.CassandraConnectionService;
+import org.apache.nifi.cassandra.CompressionType;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.ssl.SSLContextService;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+
+@Tags({ "cassandra", "dbcp", "database", "connection", "pooling" })
+@CapabilityDescription("Provides connection session for Cassandra processors 
to work with Apache Cassandra.")
+public class CassandraSessionProvider extends AbstractControllerService 
implements CassandraConnectionService {
+
+    public static final int DEFAULT_CASSANDRA_PORT = 9042;
+
+    private static final String[] CONSISTENCY_LEVELS = {
+            ConsistencyLevel.ANY.name(),
+            ConsistencyLevel.ONE.name(),
+            ConsistencyLevel.TWO.name(),
+            ConsistencyLevel.THREE.name(),
+            ConsistencyLevel.QUORUM.name(),
+            ConsistencyLevel.ALL.name(),
+            ConsistencyLevel.LOCAL_ONE.name(),
+            ConsistencyLevel.LOCAL_QUORUM.name(),
+            ConsistencyLevel.EACH_QUORUM.name(),
+            ConsistencyLevel.SERIAL.name(),
+            ConsistencyLevel.LOCAL_SERIAL.name()
+    };
+
+    public static final PropertyDescriptor CONTACT_POINTS = new 
PropertyDescriptor.Builder()
+            .name("Cassandra Contact Points")
+            .description("""
+                    Contact points are addresses of Cassandra nodes. The list 
of contact points should be
+                    comma-separated and in hostname:port format. Example 
node1:port,node2:port,....
+                    The default client port for Cassandra is 9042, but the 
port(s) must be explicitly specified.
+                    """)
+            .required(true)
+            .addValidator(StandardValidators.HOSTNAME_PORT_LIST_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor KEYSPACE = new 
PropertyDescriptor.Builder()
+            .name("Keyspace")
+            .description("""
+                    The Cassandra Keyspace to connect to. If no keyspace is 
specified, the query will need to include
+                    keyspace name before any table reference. For query-native 
processors, or processors that support
+                    the Table property, provide the table as 
<KEYSPACE>.<TABLE>.
+                    """)
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new 
PropertyDescriptor.Builder()
+            .name("SSL Context Service")
+            .description("""
+                    The SSL Context Service used to provide the client 
certificate information for establishing secure
+                    TLS/SSL connections to the Cassandra cluster.
+                    """)
+            .required(false)
+            .identifiesControllerService(SSLContextService.class)

Review Comment:
   This should use the more generic `SSLContextProvider` interface.
   ```suggestion
               .identifiesControllerService(SSLContextProvider.class)
   ```



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/cassandra/CassandraSessionProvider.java:
##########
@@ -0,0 +1,340 @@
+/*
+ * 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.service.cassandra;
+
+import com.datastax.oss.driver.api.core.ConsistencyLevel;
+import com.datastax.oss.driver.api.core.CqlIdentifier;
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.CqlSessionBuilder;
+import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+import 
com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder;
+import com.datastax.oss.driver.api.core.metadata.Metadata;
+import com.datastax.oss.driver.api.core.metadata.Node;
+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.cassandra.CassandraClient;
+import org.apache.nifi.cassandra.CassandraConnectionService;
+import org.apache.nifi.cassandra.CompressionType;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.ssl.SSLContextService;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+
+@Tags({ "cassandra", "dbcp", "database", "connection", "pooling" })
+@CapabilityDescription("Provides connection session for Cassandra processors 
to work with Apache Cassandra.")
+public class CassandraSessionProvider extends AbstractControllerService 
implements CassandraConnectionService {
+
+    public static final int DEFAULT_CASSANDRA_PORT = 9042;
+
+    private static final String[] CONSISTENCY_LEVELS = {
+            ConsistencyLevel.ANY.name(),
+            ConsistencyLevel.ONE.name(),
+            ConsistencyLevel.TWO.name(),
+            ConsistencyLevel.THREE.name(),
+            ConsistencyLevel.QUORUM.name(),
+            ConsistencyLevel.ALL.name(),
+            ConsistencyLevel.LOCAL_ONE.name(),
+            ConsistencyLevel.LOCAL_QUORUM.name(),
+            ConsistencyLevel.EACH_QUORUM.name(),
+            ConsistencyLevel.SERIAL.name(),
+            ConsistencyLevel.LOCAL_SERIAL.name()
+    };
+
+    public static final PropertyDescriptor CONTACT_POINTS = new 
PropertyDescriptor.Builder()
+            .name("Cassandra Contact Points")
+            .description("""
+                    Contact points are addresses of Cassandra nodes. The list 
of contact points should be
+                    comma-separated and in hostname:port format. Example 
node1:port,node2:port,....
+                    The default client port for Cassandra is 9042, but the 
port(s) must be explicitly specified.
+                    """)
+            .required(true)
+            .addValidator(StandardValidators.HOSTNAME_PORT_LIST_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor KEYSPACE = new 
PropertyDescriptor.Builder()
+            .name("Keyspace")
+            .description("""
+                    The Cassandra Keyspace to connect to. If no keyspace is 
specified, the query will need to include
+                    keyspace name before any table reference. For query-native 
processors, or processors that support
+                    the Table property, provide the table as 
<KEYSPACE>.<TABLE>.
+                    """)
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new 
PropertyDescriptor.Builder()
+            .name("SSL Context Service")
+            .description("""
+                    The SSL Context Service used to provide the client 
certificate information for establishing secure
+                    TLS/SSL connections to the Cassandra cluster.
+                    """)
+            .required(false)
+            .identifiesControllerService(SSLContextService.class)
+            .build();
+
+    public static final PropertyDescriptor USERNAME = new 
PropertyDescriptor.Builder()
+            .name("Username")
+            .description("Username to access the Cassandra cluster")
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PASSWORD = new 
PropertyDescriptor.Builder()
+            .name("Password")
+            .description("Password to access the Cassandra cluster")
+            .required(false)
+            .sensitive(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONSISTENCY_LEVEL = new 
PropertyDescriptor.Builder()
+            .name("Consistency Level")
+            .description("The strategy for how many replicas must respond 
before results are returned.")
+            .required(true)
+            .allowableValues(CONSISTENCY_LEVELS)
+            .defaultValue(ConsistencyLevel.QUORUM.name())
+            .build();
+
+    public static final PropertyDescriptor COMPRESSION_TYPE = new 
PropertyDescriptor.Builder()
+            .name("Compression Type")
+            .description("""
+                    Specifies the compression type used for transport-level 
requests and responses.Note that Snappy
+                    compression is not supported when using Protocol V5.
+                    """)
+            .required(false)
+            .allowableValues(CompressionType.class)
+            .defaultValue(CompressionType.NONE.getValue())
+            .build();
+
+    public static final PropertyDescriptor READ_TIMEOUT = new 
PropertyDescriptor.Builder()
+            .name("Read Timeout")
+            .description("Read timeout. 0 means no timeout. If no value is 
set, the underlying default will be used.")
+            .required(false)

Review Comment:
   A default value should be provided



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/cassandra/CassandraSessionProvider.java:
##########
@@ -0,0 +1,340 @@
+/*
+ * 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.service.cassandra;
+
+import com.datastax.oss.driver.api.core.ConsistencyLevel;
+import com.datastax.oss.driver.api.core.CqlIdentifier;
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.CqlSessionBuilder;
+import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+import 
com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder;
+import com.datastax.oss.driver.api.core.metadata.Metadata;
+import com.datastax.oss.driver.api.core.metadata.Node;
+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.cassandra.CassandraClient;
+import org.apache.nifi.cassandra.CassandraConnectionService;
+import org.apache.nifi.cassandra.CompressionType;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.ssl.SSLContextService;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+
+@Tags({ "cassandra", "dbcp", "database", "connection", "pooling" })
+@CapabilityDescription("Provides connection session for Cassandra processors 
to work with Apache Cassandra.")
+public class CassandraSessionProvider extends AbstractControllerService 
implements CassandraConnectionService {
+
+    public static final int DEFAULT_CASSANDRA_PORT = 9042;
+
+    private static final String[] CONSISTENCY_LEVELS = {
+            ConsistencyLevel.ANY.name(),
+            ConsistencyLevel.ONE.name(),
+            ConsistencyLevel.TWO.name(),
+            ConsistencyLevel.THREE.name(),
+            ConsistencyLevel.QUORUM.name(),
+            ConsistencyLevel.ALL.name(),
+            ConsistencyLevel.LOCAL_ONE.name(),
+            ConsistencyLevel.LOCAL_QUORUM.name(),
+            ConsistencyLevel.EACH_QUORUM.name(),
+            ConsistencyLevel.SERIAL.name(),
+            ConsistencyLevel.LOCAL_SERIAL.name()
+    };
+
+    public static final PropertyDescriptor CONTACT_POINTS = new 
PropertyDescriptor.Builder()
+            .name("Cassandra Contact Points")
+            .description("""
+                    Contact points are addresses of Cassandra nodes. The list 
of contact points should be
+                    comma-separated and in hostname:port format. Example 
node1:port,node2:port,....
+                    The default client port for Cassandra is 9042, but the 
port(s) must be explicitly specified.
+                    """)
+            .required(true)
+            .addValidator(StandardValidators.HOSTNAME_PORT_LIST_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor KEYSPACE = new 
PropertyDescriptor.Builder()
+            .name("Keyspace")
+            .description("""
+                    The Cassandra Keyspace to connect to. If no keyspace is 
specified, the query will need to include
+                    keyspace name before any table reference. For query-native 
processors, or processors that support
+                    the Table property, provide the table as 
<KEYSPACE>.<TABLE>.
+                    """)
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new 
PropertyDescriptor.Builder()
+            .name("SSL Context Service")
+            .description("""
+                    The SSL Context Service used to provide the client 
certificate information for establishing secure
+                    TLS/SSL connections to the Cassandra cluster.
+                    """)
+            .required(false)
+            .identifiesControllerService(SSLContextService.class)
+            .build();
+
+    public static final PropertyDescriptor USERNAME = new 
PropertyDescriptor.Builder()
+            .name("Username")
+            .description("Username to access the Cassandra cluster")
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PASSWORD = new 
PropertyDescriptor.Builder()
+            .name("Password")
+            .description("Password to access the Cassandra cluster")
+            .required(false)
+            .sensitive(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONSISTENCY_LEVEL = new 
PropertyDescriptor.Builder()
+            .name("Consistency Level")
+            .description("The strategy for how many replicas must respond 
before results are returned.")
+            .required(true)
+            .allowableValues(CONSISTENCY_LEVELS)
+            .defaultValue(ConsistencyLevel.QUORUM.name())
+            .build();
+
+    public static final PropertyDescriptor COMPRESSION_TYPE = new 
PropertyDescriptor.Builder()
+            .name("Compression Type")
+            .description("""
+                    Specifies the compression type used for transport-level 
requests and responses.Note that Snappy
+                    compression is not supported when using Protocol V5.
+                    """)
+            .required(false)
+            .allowableValues(CompressionType.class)
+            .defaultValue(CompressionType.NONE.getValue())
+            .build();
+
+    public static final PropertyDescriptor READ_TIMEOUT = new 
PropertyDescriptor.Builder()
+            .name("Read Timeout")
+            .description("Read timeout. 0 means no timeout. If no value is 
set, the underlying default will be used.")
+            .required(false)
+            .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONNECT_TIMEOUT = new 
PropertyDescriptor.Builder()
+            .name("Connect Timeout")
+            .description("Connection timeout. 0 means no timeout. If no value 
is set, the underlying default will be used.")
+            .required(false)

Review Comment:
   A default value should be provided.



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/cassandra/CassandraSessionProvider.java:
##########
@@ -0,0 +1,340 @@
+/*
+ * 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.service.cassandra;
+
+import com.datastax.oss.driver.api.core.ConsistencyLevel;
+import com.datastax.oss.driver.api.core.CqlIdentifier;
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.CqlSessionBuilder;
+import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+import 
com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder;
+import com.datastax.oss.driver.api.core.metadata.Metadata;
+import com.datastax.oss.driver.api.core.metadata.Node;
+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.cassandra.CassandraClient;
+import org.apache.nifi.cassandra.CassandraConnectionService;
+import org.apache.nifi.cassandra.CompressionType;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.ssl.SSLContextService;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+
+@Tags({ "cassandra", "dbcp", "database", "connection", "pooling" })
+@CapabilityDescription("Provides connection session for Cassandra processors 
to work with Apache Cassandra.")
+public class CassandraSessionProvider extends AbstractControllerService 
implements CassandraConnectionService {
+
+    public static final int DEFAULT_CASSANDRA_PORT = 9042;
+
+    private static final String[] CONSISTENCY_LEVELS = {
+            ConsistencyLevel.ANY.name(),
+            ConsistencyLevel.ONE.name(),
+            ConsistencyLevel.TWO.name(),
+            ConsistencyLevel.THREE.name(),
+            ConsistencyLevel.QUORUM.name(),
+            ConsistencyLevel.ALL.name(),
+            ConsistencyLevel.LOCAL_ONE.name(),
+            ConsistencyLevel.LOCAL_QUORUM.name(),
+            ConsistencyLevel.EACH_QUORUM.name(),
+            ConsistencyLevel.SERIAL.name(),
+            ConsistencyLevel.LOCAL_SERIAL.name()
+    };
+
+    public static final PropertyDescriptor CONTACT_POINTS = new 
PropertyDescriptor.Builder()
+            .name("Cassandra Contact Points")
+            .description("""
+                    Contact points are addresses of Cassandra nodes. The list 
of contact points should be
+                    comma-separated and in hostname:port format. Example 
node1:port,node2:port,....
+                    The default client port for Cassandra is 9042, but the 
port(s) must be explicitly specified.
+                    """)
+            .required(true)
+            .addValidator(StandardValidators.HOSTNAME_PORT_LIST_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor KEYSPACE = new 
PropertyDescriptor.Builder()
+            .name("Keyspace")
+            .description("""
+                    The Cassandra Keyspace to connect to. If no keyspace is 
specified, the query will need to include
+                    keyspace name before any table reference. For query-native 
processors, or processors that support
+                    the Table property, provide the table as 
<KEYSPACE>.<TABLE>.
+                    """)
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new 
PropertyDescriptor.Builder()
+            .name("SSL Context Service")
+            .description("""
+                    The SSL Context Service used to provide the client 
certificate information for establishing secure
+                    TLS/SSL connections to the Cassandra cluster.
+                    """)
+            .required(false)
+            .identifiesControllerService(SSLContextService.class)
+            .build();
+
+    public static final PropertyDescriptor USERNAME = new 
PropertyDescriptor.Builder()
+            .name("Username")
+            .description("Username to access the Cassandra cluster")
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PASSWORD = new 
PropertyDescriptor.Builder()
+            .name("Password")
+            .description("Password to access the Cassandra cluster")
+            .required(false)
+            .sensitive(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONSISTENCY_LEVEL = new 
PropertyDescriptor.Builder()
+            .name("Consistency Level")
+            .description("The strategy for how many replicas must respond 
before results are returned.")
+            .required(true)
+            .allowableValues(CONSISTENCY_LEVELS)
+            .defaultValue(ConsistencyLevel.QUORUM.name())
+            .build();
+
+    public static final PropertyDescriptor COMPRESSION_TYPE = new 
PropertyDescriptor.Builder()
+            .name("Compression Type")
+            .description("""
+                    Specifies the compression type used for transport-level 
requests and responses.Note that Snappy
+                    compression is not supported when using Protocol V5.
+                    """)
+            .required(false)
+            .allowableValues(CompressionType.class)
+            .defaultValue(CompressionType.NONE.getValue())
+            .build();
+
+    public static final PropertyDescriptor READ_TIMEOUT = new 
PropertyDescriptor.Builder()
+            .name("Read Timeout")
+            .description("Read timeout. 0 means no timeout. If no value is 
set, the underlying default will be used.")
+            .required(false)
+            .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONNECT_TIMEOUT = new 
PropertyDescriptor.Builder()
+            .name("Connect Timeout")
+            .description("Connection timeout. 0 means no timeout. If no value 
is set, the underlying default will be used.")
+            .required(false)
+            .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor LOCAL_DATACENTER = new 
PropertyDescriptor.Builder()
+            .name("Local Datacenter")
+            .description("The local datacenter name for the Cassandra cluster 
(required by driver 4.x).")
+            .required(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    private CqlSession cassandraSession;
+    private CassandraClient cassandraClient;
+    private String clusterAddress;

Review Comment:
   These should be defined after all static variables



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/test/java/org/apache/nifi/service/cassandra/TestCassandraSessionProvider.java:
##########
@@ -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.service.cassandra;
+
+import org.apache.nifi.cassandra.CassandraClient;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.util.NoOpProcessor;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+class TestCassandraSessionProvider {
+
+    private static final String SERVICE_ID = "cassandra-session-provider";
+
+    private static TestRunner runner;
+    private static CassandraSessionProvider sessionProvider;
+    private static CassandraClient mockClient;
+
+    @BeforeEach
+    void setup() throws InitializationException {
+        sessionProvider = spy(new CassandraSessionProvider());
+        
doNothing().when(sessionProvider).connectToCassandra(any(ConfigurationContext.class));
+        mockClient = mock(CassandraClient.class);
+        doReturn(mockClient).when(sessionProvider).getClient();
+
+        runner = TestRunners.newTestRunner(NoOpProcessor.class);
+        runner.setValidateExpressionUsage(false);
+        runner.addControllerService(SERVICE_ID, sessionProvider);
+    }
+
+    @Test
+    void testGetPropertyDescriptors() {

Review Comment:
   This test is not necessary and should be removed



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/cassandra/CassandraSessionProvider.java:
##########
@@ -0,0 +1,340 @@
+/*
+ * 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.service.cassandra;
+
+import com.datastax.oss.driver.api.core.ConsistencyLevel;
+import com.datastax.oss.driver.api.core.CqlIdentifier;
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.CqlSessionBuilder;
+import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+import 
com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder;
+import com.datastax.oss.driver.api.core.metadata.Metadata;
+import com.datastax.oss.driver.api.core.metadata.Node;
+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.cassandra.CassandraClient;
+import org.apache.nifi.cassandra.CassandraConnectionService;
+import org.apache.nifi.cassandra.CompressionType;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.ssl.SSLContextService;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+
+@Tags({ "cassandra", "dbcp", "database", "connection", "pooling" })
+@CapabilityDescription("Provides connection session for Cassandra processors 
to work with Apache Cassandra.")
+public class CassandraSessionProvider extends AbstractControllerService 
implements CassandraConnectionService {
+
+    public static final int DEFAULT_CASSANDRA_PORT = 9042;
+
+    private static final String[] CONSISTENCY_LEVELS = {
+            ConsistencyLevel.ANY.name(),
+            ConsistencyLevel.ONE.name(),
+            ConsistencyLevel.TWO.name(),
+            ConsistencyLevel.THREE.name(),
+            ConsistencyLevel.QUORUM.name(),
+            ConsistencyLevel.ALL.name(),
+            ConsistencyLevel.LOCAL_ONE.name(),
+            ConsistencyLevel.LOCAL_QUORUM.name(),
+            ConsistencyLevel.EACH_QUORUM.name(),
+            ConsistencyLevel.SERIAL.name(),
+            ConsistencyLevel.LOCAL_SERIAL.name()
+    };
+
+    public static final PropertyDescriptor CONTACT_POINTS = new 
PropertyDescriptor.Builder()
+            .name("Cassandra Contact Points")
+            .description("""
+                    Contact points are addresses of Cassandra nodes. The list 
of contact points should be
+                    comma-separated and in hostname:port format. Example 
node1:port,node2:port,....
+                    The default client port for Cassandra is 9042, but the 
port(s) must be explicitly specified.
+                    """)
+            .required(true)
+            .addValidator(StandardValidators.HOSTNAME_PORT_LIST_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor KEYSPACE = new 
PropertyDescriptor.Builder()
+            .name("Keyspace")
+            .description("""
+                    The Cassandra Keyspace to connect to. If no keyspace is 
specified, the query will need to include
+                    keyspace name before any table reference. For query-native 
processors, or processors that support
+                    the Table property, provide the table as 
<KEYSPACE>.<TABLE>.
+                    """)
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new 
PropertyDescriptor.Builder()
+            .name("SSL Context Service")
+            .description("""
+                    The SSL Context Service used to provide the client 
certificate information for establishing secure
+                    TLS/SSL connections to the Cassandra cluster.
+                    """)
+            .required(false)
+            .identifiesControllerService(SSLContextService.class)
+            .build();
+
+    public static final PropertyDescriptor USERNAME = new 
PropertyDescriptor.Builder()
+            .name("Username")
+            .description("Username to access the Cassandra cluster")
+            .required(false)

Review Comment:
   Should the Username be required?



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/test/java/org/apache/nifi/service/cassandra/TestCassandraSessionProvider.java:
##########
@@ -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.service.cassandra;
+
+import org.apache.nifi.cassandra.CassandraClient;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.util.NoOpProcessor;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+class TestCassandraSessionProvider {
+
+    private static final String SERVICE_ID = "cassandra-session-provider";
+
+    private static TestRunner runner;
+    private static CassandraSessionProvider sessionProvider;
+    private static CassandraClient mockClient;
+
+    @BeforeEach
+    void setup() throws InitializationException {
+        sessionProvider = spy(new CassandraSessionProvider());
+        
doNothing().when(sessionProvider).connectToCassandra(any(ConfigurationContext.class));
+        mockClient = mock(CassandraClient.class);
+        doReturn(mockClient).when(sessionProvider).getClient();
+
+        runner = TestRunners.newTestRunner(NoOpProcessor.class);
+        runner.setValidateExpressionUsage(false);
+        runner.addControllerService(SERVICE_ID, sessionProvider);
+    }
+
+    @Test
+    void testGetPropertyDescriptors() {
+        List<PropertyDescriptor> properties = 
sessionProvider.getPropertyDescriptors();
+
+        assertEquals(10, properties.size(), "Property count mismatch");
+        
assertTrue(properties.contains(CassandraSessionProvider.CONSISTENCY_LEVEL));
+        
assertTrue(properties.contains(CassandraSessionProvider.CONTACT_POINTS));
+        assertTrue(properties.contains(CassandraSessionProvider.KEYSPACE));
+        assertTrue(properties.contains(CassandraSessionProvider.PASSWORD));
+        
assertTrue(properties.contains(CassandraSessionProvider.PROP_SSL_CONTEXT_SERVICE));
+        assertTrue(properties.contains(CassandraSessionProvider.USERNAME));
+        
assertTrue(properties.contains(CassandraSessionProvider.LOCAL_DATACENTER));
+    }
+
+    @Test
+    void testDefaultsBeforeEnabling() {
+        assertNotNull(sessionProvider.getClient(), "Client should return 
mocked client");
+    }
+
+    @Test
+    void testEnableServiceWithValidProperties() throws InitializationException 
{
+
+        runner.setProperty(sessionProvider, 
CassandraSessionProvider.CONTACT_POINTS, "localhost:9042");
+        runner.setProperty(sessionProvider, 
CassandraSessionProvider.LOCAL_DATACENTER, "datacenter1");
+        runner.setProperty(sessionProvider, 
CassandraSessionProvider.CONSISTENCY_LEVEL, "QUORUM");
+
+        runner.enableControllerService(sessionProvider);
+        runner.assertValid(sessionProvider);
+        assertNotNull(sessionProvider.getClient(), "Cassandra client should be 
mocked after enabling service");
+    }
+
+    @Test
+    void testEnableServiceMissingRequiredProperties() {
+        runner.setProperty(sessionProvider, 
CassandraSessionProvider.CONTACT_POINTS, "localhost:9042");
+        runner.assertNotValid(sessionProvider);
+    }
+
+    @Test
+    void testConnectToCassandraIsCalledOnEnable() throws 
InitializationException {
+        runner.setProperty(sessionProvider, 
CassandraSessionProvider.CONTACT_POINTS, "localhost:9042");
+        runner.setProperty(sessionProvider, 
CassandraSessionProvider.LOCAL_DATACENTER, "datacenter1");
+        runner.enableControllerService(sessionProvider);
+
+        verify(sessionProvider, 
times(1)).connectToCassandra(any(ConfigurationContext.class));
+    }

Review Comment:
   This test appears to just exercise the spied instance, and should be removed



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/pom.xml:
##########
@@ -0,0 +1,70 @@
+<?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";>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-cassandra-bundle</artifactId>
+        <version>2.10.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>nifi-cassandra-services</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <version>2.10.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-cassandra-services-api</artifactId>
+            <scope>provided</scope>
+            <version>2.10.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cassandra</groupId>
+            <artifactId>java-driver-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-ssl-context-service-api</artifactId>
+            <version>2.10.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-framework-api</artifactId>
+            <version>2.10.0-SNAPSHOT</version>
+        </dependency>

Review Comment:
   This should be removed



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/cassandra/CassandraSessionProvider.java:
##########
@@ -0,0 +1,340 @@
+/*
+ * 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.service.cassandra;
+
+import com.datastax.oss.driver.api.core.ConsistencyLevel;
+import com.datastax.oss.driver.api.core.CqlIdentifier;
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.CqlSessionBuilder;
+import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+import 
com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder;
+import com.datastax.oss.driver.api.core.metadata.Metadata;
+import com.datastax.oss.driver.api.core.metadata.Node;
+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.cassandra.CassandraClient;
+import org.apache.nifi.cassandra.CassandraConnectionService;
+import org.apache.nifi.cassandra.CompressionType;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.ssl.SSLContextService;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+
+@Tags({ "cassandra", "dbcp", "database", "connection", "pooling" })
+@CapabilityDescription("Provides connection session for Cassandra processors 
to work with Apache Cassandra.")
+public class CassandraSessionProvider extends AbstractControllerService 
implements CassandraConnectionService {
+
+    public static final int DEFAULT_CASSANDRA_PORT = 9042;
+
+    private static final String[] CONSISTENCY_LEVELS = {
+            ConsistencyLevel.ANY.name(),
+            ConsistencyLevel.ONE.name(),
+            ConsistencyLevel.TWO.name(),
+            ConsistencyLevel.THREE.name(),
+            ConsistencyLevel.QUORUM.name(),
+            ConsistencyLevel.ALL.name(),
+            ConsistencyLevel.LOCAL_ONE.name(),
+            ConsistencyLevel.LOCAL_QUORUM.name(),
+            ConsistencyLevel.EACH_QUORUM.name(),
+            ConsistencyLevel.SERIAL.name(),
+            ConsistencyLevel.LOCAL_SERIAL.name()
+    };
+
+    public static final PropertyDescriptor CONTACT_POINTS = new 
PropertyDescriptor.Builder()
+            .name("Cassandra Contact Points")
+            .description("""
+                    Contact points are addresses of Cassandra nodes. The list 
of contact points should be
+                    comma-separated and in hostname:port format. Example 
node1:port,node2:port,....
+                    The default client port for Cassandra is 9042, but the 
port(s) must be explicitly specified.
+                    """)
+            .required(true)
+            .addValidator(StandardValidators.HOSTNAME_PORT_LIST_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor KEYSPACE = new 
PropertyDescriptor.Builder()
+            .name("Keyspace")
+            .description("""
+                    The Cassandra Keyspace to connect to. If no keyspace is 
specified, the query will need to include
+                    keyspace name before any table reference. For query-native 
processors, or processors that support
+                    the Table property, provide the table as 
<KEYSPACE>.<TABLE>.
+                    """)
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new 
PropertyDescriptor.Builder()
+            .name("SSL Context Service")
+            .description("""
+                    The SSL Context Service used to provide the client 
certificate information for establishing secure
+                    TLS/SSL connections to the Cassandra cluster.
+                    """)
+            .required(false)
+            .identifiesControllerService(SSLContextService.class)
+            .build();
+
+    public static final PropertyDescriptor USERNAME = new 
PropertyDescriptor.Builder()
+            .name("Username")
+            .description("Username to access the Cassandra cluster")
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PASSWORD = new 
PropertyDescriptor.Builder()
+            .name("Password")
+            .description("Password to access the Cassandra cluster")
+            .required(false)
+            .sensitive(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONSISTENCY_LEVEL = new 
PropertyDescriptor.Builder()
+            .name("Consistency Level")
+            .description("The strategy for how many replicas must respond 
before results are returned.")
+            .required(true)
+            .allowableValues(CONSISTENCY_LEVELS)
+            .defaultValue(ConsistencyLevel.QUORUM.name())
+            .build();
+
+    public static final PropertyDescriptor COMPRESSION_TYPE = new 
PropertyDescriptor.Builder()
+            .name("Compression Type")
+            .description("""
+                    Specifies the compression type used for transport-level 
requests and responses.Note that Snappy
+                    compression is not supported when using Protocol V5.
+                    """)
+            .required(false)
+            .allowableValues(CompressionType.class)
+            .defaultValue(CompressionType.NONE.getValue())
+            .build();
+
+    public static final PropertyDescriptor READ_TIMEOUT = new 
PropertyDescriptor.Builder()
+            .name("Read Timeout")
+            .description("Read timeout. 0 means no timeout. If no value is 
set, the underlying default will be used.")
+            .required(false)
+            .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONNECT_TIMEOUT = new 
PropertyDescriptor.Builder()
+            .name("Connect Timeout")
+            .description("Connection timeout. 0 means no timeout. If no value 
is set, the underlying default will be used.")
+            .required(false)
+            .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor LOCAL_DATACENTER = new 
PropertyDescriptor.Builder()
+            .name("Local Datacenter")
+            .description("The local datacenter name for the Cassandra cluster 
(required by driver 4.x).")

Review Comment:
   The reference to the driver version should be removed



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/cassandra/CassandraSessionProvider.java:
##########
@@ -0,0 +1,340 @@
+/*
+ * 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.service.cassandra;
+
+import com.datastax.oss.driver.api.core.ConsistencyLevel;
+import com.datastax.oss.driver.api.core.CqlIdentifier;
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.CqlSessionBuilder;
+import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+import 
com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder;
+import com.datastax.oss.driver.api.core.metadata.Metadata;
+import com.datastax.oss.driver.api.core.metadata.Node;
+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.cassandra.CassandraClient;
+import org.apache.nifi.cassandra.CassandraConnectionService;
+import org.apache.nifi.cassandra.CompressionType;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.ssl.SSLContextService;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+
+@Tags({ "cassandra", "dbcp", "database", "connection", "pooling" })
+@CapabilityDescription("Provides connection session for Cassandra processors 
to work with Apache Cassandra.")
+public class CassandraSessionProvider extends AbstractControllerService 
implements CassandraConnectionService {
+
+    public static final int DEFAULT_CASSANDRA_PORT = 9042;
+
+    private static final String[] CONSISTENCY_LEVELS = {
+            ConsistencyLevel.ANY.name(),
+            ConsistencyLevel.ONE.name(),
+            ConsistencyLevel.TWO.name(),
+            ConsistencyLevel.THREE.name(),
+            ConsistencyLevel.QUORUM.name(),
+            ConsistencyLevel.ALL.name(),
+            ConsistencyLevel.LOCAL_ONE.name(),
+            ConsistencyLevel.LOCAL_QUORUM.name(),
+            ConsistencyLevel.EACH_QUORUM.name(),
+            ConsistencyLevel.SERIAL.name(),
+            ConsistencyLevel.LOCAL_SERIAL.name()
+    };
+
+    public static final PropertyDescriptor CONTACT_POINTS = new 
PropertyDescriptor.Builder()
+            .name("Cassandra Contact Points")
+            .description("""
+                    Contact points are addresses of Cassandra nodes. The list 
of contact points should be
+                    comma-separated and in hostname:port format. Example 
node1:port,node2:port,....
+                    The default client port for Cassandra is 9042, but the 
port(s) must be explicitly specified.
+                    """)
+            .required(true)
+            .addValidator(StandardValidators.HOSTNAME_PORT_LIST_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor KEYSPACE = new 
PropertyDescriptor.Builder()
+            .name("Keyspace")
+            .description("""
+                    The Cassandra Keyspace to connect to. If no keyspace is 
specified, the query will need to include
+                    keyspace name before any table reference. For query-native 
processors, or processors that support
+                    the Table property, provide the table as 
<KEYSPACE>.<TABLE>.
+                    """)
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new 
PropertyDescriptor.Builder()
+            .name("SSL Context Service")
+            .description("""
+                    The SSL Context Service used to provide the client 
certificate information for establishing secure
+                    TLS/SSL connections to the Cassandra cluster.
+                    """)
+            .required(false)
+            .identifiesControllerService(SSLContextService.class)
+            .build();
+
+    public static final PropertyDescriptor USERNAME = new 
PropertyDescriptor.Builder()
+            .name("Username")
+            .description("Username to access the Cassandra cluster")
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PASSWORD = new 
PropertyDescriptor.Builder()
+            .name("Password")
+            .description("Password to access the Cassandra cluster")
+            .required(false)
+            .sensitive(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONSISTENCY_LEVEL = new 
PropertyDescriptor.Builder()
+            .name("Consistency Level")
+            .description("The strategy for how many replicas must respond 
before results are returned.")
+            .required(true)
+            .allowableValues(CONSISTENCY_LEVELS)
+            .defaultValue(ConsistencyLevel.QUORUM.name())
+            .build();
+
+    public static final PropertyDescriptor COMPRESSION_TYPE = new 
PropertyDescriptor.Builder()
+            .name("Compression Type")
+            .description("""
+                    Specifies the compression type used for transport-level 
requests and responses.Note that Snappy
+                    compression is not supported when using Protocol V5.
+                    """)
+            .required(false)

Review Comment:
   A property with a default value should be required



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/cassandra/CassandraSessionProvider.java:
##########
@@ -0,0 +1,340 @@
+/*
+ * 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.service.cassandra;
+
+import com.datastax.oss.driver.api.core.ConsistencyLevel;
+import com.datastax.oss.driver.api.core.CqlIdentifier;
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.CqlSessionBuilder;
+import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+import 
com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder;
+import com.datastax.oss.driver.api.core.metadata.Metadata;
+import com.datastax.oss.driver.api.core.metadata.Node;
+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.cassandra.CassandraClient;
+import org.apache.nifi.cassandra.CassandraConnectionService;
+import org.apache.nifi.cassandra.CompressionType;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.ssl.SSLContextService;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+
+@Tags({ "cassandra", "dbcp", "database", "connection", "pooling" })
+@CapabilityDescription("Provides connection session for Cassandra processors 
to work with Apache Cassandra.")
+public class CassandraSessionProvider extends AbstractControllerService 
implements CassandraConnectionService {
+
+    public static final int DEFAULT_CASSANDRA_PORT = 9042;
+
+    private static final String[] CONSISTENCY_LEVELS = {
+            ConsistencyLevel.ANY.name(),
+            ConsistencyLevel.ONE.name(),
+            ConsistencyLevel.TWO.name(),
+            ConsistencyLevel.THREE.name(),
+            ConsistencyLevel.QUORUM.name(),
+            ConsistencyLevel.ALL.name(),
+            ConsistencyLevel.LOCAL_ONE.name(),
+            ConsistencyLevel.LOCAL_QUORUM.name(),
+            ConsistencyLevel.EACH_QUORUM.name(),
+            ConsistencyLevel.SERIAL.name(),
+            ConsistencyLevel.LOCAL_SERIAL.name()
+    };
+
+    public static final PropertyDescriptor CONTACT_POINTS = new 
PropertyDescriptor.Builder()
+            .name("Cassandra Contact Points")
+            .description("""
+                    Contact points are addresses of Cassandra nodes. The list 
of contact points should be
+                    comma-separated and in hostname:port format. Example 
node1:port,node2:port,....
+                    The default client port for Cassandra is 9042, but the 
port(s) must be explicitly specified.
+                    """)
+            .required(true)
+            .addValidator(StandardValidators.HOSTNAME_PORT_LIST_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor KEYSPACE = new 
PropertyDescriptor.Builder()
+            .name("Keyspace")
+            .description("""
+                    The Cassandra Keyspace to connect to. If no keyspace is 
specified, the query will need to include
+                    keyspace name before any table reference. For query-native 
processors, or processors that support
+                    the Table property, provide the table as 
<KEYSPACE>.<TABLE>.
+                    """)
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new 
PropertyDescriptor.Builder()
+            .name("SSL Context Service")
+            .description("""
+                    The SSL Context Service used to provide the client 
certificate information for establishing secure
+                    TLS/SSL connections to the Cassandra cluster.
+                    """)
+            .required(false)
+            .identifiesControllerService(SSLContextService.class)
+            .build();
+
+    public static final PropertyDescriptor USERNAME = new 
PropertyDescriptor.Builder()
+            .name("Username")
+            .description("Username to access the Cassandra cluster")
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PASSWORD = new 
PropertyDescriptor.Builder()
+            .name("Password")
+            .description("Password to access the Cassandra cluster")
+            .required(false)
+            .sensitive(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONSISTENCY_LEVEL = new 
PropertyDescriptor.Builder()
+            .name("Consistency Level")
+            .description("The strategy for how many replicas must respond 
before results are returned.")
+            .required(true)
+            .allowableValues(CONSISTENCY_LEVELS)
+            .defaultValue(ConsistencyLevel.QUORUM.name())
+            .build();
+
+    public static final PropertyDescriptor COMPRESSION_TYPE = new 
PropertyDescriptor.Builder()
+            .name("Compression Type")
+            .description("""
+                    Specifies the compression type used for transport-level 
requests and responses.Note that Snappy
+                    compression is not supported when using Protocol V5.
+                    """)
+            .required(false)
+            .allowableValues(CompressionType.class)
+            .defaultValue(CompressionType.NONE.getValue())
+            .build();
+
+    public static final PropertyDescriptor READ_TIMEOUT = new 
PropertyDescriptor.Builder()
+            .name("Read Timeout")
+            .description("Read timeout. 0 means no timeout. If no value is 
set, the underlying default will be used.")
+            .required(false)
+            .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONNECT_TIMEOUT = new 
PropertyDescriptor.Builder()
+            .name("Connect Timeout")
+            .description("Connection timeout. 0 means no timeout. If no value 
is set, the underlying default will be used.")
+            .required(false)
+            .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor LOCAL_DATACENTER = new 
PropertyDescriptor.Builder()
+            .name("Local Datacenter")
+            .description("The local datacenter name for the Cassandra cluster 
(required by driver 4.x).")
+            .required(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    private CqlSession cassandraSession;
+    private CassandraClient cassandraClient;
+    private String clusterAddress;
+
+    private static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = 
List.of(
+                    CONTACT_POINTS,
+                    CONSISTENCY_LEVEL,
+                    COMPRESSION_TYPE,
+                    KEYSPACE,
+                    USERNAME,
+                    PASSWORD,
+                    PROP_SSL_CONTEXT_SERVICE,
+                    READ_TIMEOUT,
+                    CONNECT_TIMEOUT,
+                    LOCAL_DATACENTER
+    );
+
+    @Override
+    public List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return PROPERTY_DESCRIPTORS;
+    }
+
+    @OnEnabled
+    public void onEnabled(final ConfigurationContext context) throws 
InitializationException {
+        try {
+            connectToCassandra(context);
+        } catch (Exception e) {
+            throw new InitializationException("Failed to connect to Cassandra 
cluster. Please check your configuration.", e);

Review Comment:
   ```suggestion
               throw new InitializationException("Failed to connect to 
Cassandra cluster", e);
   ```



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/test/java/org/apache/nifi/service/cassandra/TestCassandraSessionProvider.java:
##########
@@ -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.service.cassandra;
+
+import org.apache.nifi.cassandra.CassandraClient;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.util.NoOpProcessor;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+class TestCassandraSessionProvider {

Review Comment:
   ```suggestion
   class CassandraSessionProviderTest {
   ```



##########
pom.xml:
##########
@@ -499,7 +503,12 @@
             <dependency>
                 <groupId>org.xerial.snappy</groupId>
                 <artifactId>snappy-java</artifactId>
-                <version>1.1.10.8</version>
+                <version>${snappy-java.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.cassandra</groupId>
+                <artifactId>java-driver-core</artifactId>

Review Comment:
   This should be moved down to the Cassandra bundle



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/pom.xml:
##########
@@ -0,0 +1,70 @@
+<?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";>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-cassandra-bundle</artifactId>
+        <version>2.10.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>nifi-cassandra-services</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <version>2.10.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-cassandra-services-api</artifactId>
+            <scope>provided</scope>
+            <version>2.10.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cassandra</groupId>
+            <artifactId>java-driver-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-ssl-context-service-api</artifactId>

Review Comment:
   This should be provided



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/cassandra/CassandraSessionProvider.java:
##########
@@ -0,0 +1,340 @@
+/*
+ * 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.service.cassandra;
+
+import com.datastax.oss.driver.api.core.ConsistencyLevel;
+import com.datastax.oss.driver.api.core.CqlIdentifier;
+import com.datastax.oss.driver.api.core.CqlSession;
+import com.datastax.oss.driver.api.core.CqlSessionBuilder;
+import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
+import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
+import 
com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder;
+import com.datastax.oss.driver.api.core.metadata.Metadata;
+import com.datastax.oss.driver.api.core.metadata.Node;
+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.cassandra.CassandraClient;
+import org.apache.nifi.cassandra.CassandraConnectionService;
+import org.apache.nifi.cassandra.CompressionType;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.ssl.SSLContextService;
+
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+
+@Tags({ "cassandra", "dbcp", "database", "connection", "pooling" })
+@CapabilityDescription("Provides connection session for Cassandra processors 
to work with Apache Cassandra.")
+public class CassandraSessionProvider extends AbstractControllerService 
implements CassandraConnectionService {
+
+    public static final int DEFAULT_CASSANDRA_PORT = 9042;
+
+    private static final String[] CONSISTENCY_LEVELS = {
+            ConsistencyLevel.ANY.name(),
+            ConsistencyLevel.ONE.name(),
+            ConsistencyLevel.TWO.name(),
+            ConsistencyLevel.THREE.name(),
+            ConsistencyLevel.QUORUM.name(),
+            ConsistencyLevel.ALL.name(),
+            ConsistencyLevel.LOCAL_ONE.name(),
+            ConsistencyLevel.LOCAL_QUORUM.name(),
+            ConsistencyLevel.EACH_QUORUM.name(),
+            ConsistencyLevel.SERIAL.name(),
+            ConsistencyLevel.LOCAL_SERIAL.name()
+    };
+
+    public static final PropertyDescriptor CONTACT_POINTS = new 
PropertyDescriptor.Builder()
+            .name("Cassandra Contact Points")
+            .description("""
+                    Contact points are addresses of Cassandra nodes. The list 
of contact points should be
+                    comma-separated and in hostname:port format. Example 
node1:port,node2:port,....
+                    The default client port for Cassandra is 9042, but the 
port(s) must be explicitly specified.
+                    """)
+            .required(true)
+            .addValidator(StandardValidators.HOSTNAME_PORT_LIST_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor KEYSPACE = new 
PropertyDescriptor.Builder()
+            .name("Keyspace")
+            .description("""
+                    The Cassandra Keyspace to connect to. If no keyspace is 
specified, the query will need to include
+                    keyspace name before any table reference. For query-native 
processors, or processors that support
+                    the Table property, provide the table as 
<KEYSPACE>.<TABLE>.
+                    """)
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new 
PropertyDescriptor.Builder()
+            .name("SSL Context Service")
+            .description("""
+                    The SSL Context Service used to provide the client 
certificate information for establishing secure
+                    TLS/SSL connections to the Cassandra cluster.
+                    """)
+            .required(false)
+            .identifiesControllerService(SSLContextService.class)
+            .build();
+
+    public static final PropertyDescriptor USERNAME = new 
PropertyDescriptor.Builder()
+            .name("Username")
+            .description("Username to access the Cassandra cluster")
+            .required(false)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PASSWORD = new 
PropertyDescriptor.Builder()
+            .name("Password")
+            .description("Password to access the Cassandra cluster")
+            .required(false)
+            .sensitive(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONSISTENCY_LEVEL = new 
PropertyDescriptor.Builder()
+            .name("Consistency Level")
+            .description("The strategy for how many replicas must respond 
before results are returned.")
+            .required(true)
+            .allowableValues(CONSISTENCY_LEVELS)
+            .defaultValue(ConsistencyLevel.QUORUM.name())
+            .build();
+
+    public static final PropertyDescriptor COMPRESSION_TYPE = new 
PropertyDescriptor.Builder()
+            .name("Compression Type")
+            .description("""
+                    Specifies the compression type used for transport-level 
requests and responses.Note that Snappy
+                    compression is not supported when using Protocol V5.
+                    """)
+            .required(false)
+            .allowableValues(CompressionType.class)
+            .defaultValue(CompressionType.NONE.getValue())
+            .build();
+
+    public static final PropertyDescriptor READ_TIMEOUT = new 
PropertyDescriptor.Builder()
+            .name("Read Timeout")
+            .description("Read timeout. 0 means no timeout. If no value is 
set, the underlying default will be used.")
+            .required(false)
+            .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor CONNECT_TIMEOUT = new 
PropertyDescriptor.Builder()
+            .name("Connect Timeout")
+            .description("Connection timeout. 0 means no timeout. If no value 
is set, the underlying default will be used.")
+            .required(false)
+            .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor LOCAL_DATACENTER = new 
PropertyDescriptor.Builder()
+            .name("Local Datacenter")
+            .description("The local datacenter name for the Cassandra cluster 
(required by driver 4.x).")
+            .required(true)
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
+    private CqlSession cassandraSession;
+    private CassandraClient cassandraClient;
+    private String clusterAddress;
+
+    private static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = 
List.of(
+                    CONTACT_POINTS,
+                    CONSISTENCY_LEVEL,
+                    COMPRESSION_TYPE,
+                    KEYSPACE,
+                    USERNAME,
+                    PASSWORD,
+                    PROP_SSL_CONTEXT_SERVICE,
+                    READ_TIMEOUT,
+                    CONNECT_TIMEOUT,
+                    LOCAL_DATACENTER
+    );
+
+    @Override
+    public List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return PROPERTY_DESCRIPTORS;
+    }
+
+    @OnEnabled
+    public void onEnabled(final ConfigurationContext context) throws 
InitializationException {
+        try {
+            connectToCassandra(context);
+        } catch (Exception e) {
+            throw new InitializationException("Failed to connect to Cassandra 
cluster. Please check your configuration.", e);
+        }
+    }
+
+    @OnDisabled
+    public void onDisabled() {
+        if (cassandraSession != null) {
+            cassandraSession.close();
+            cassandraSession = null;
+        }
+        cassandraClient = null;
+    }
+
+    @Override
+    public CassandraClient getClient() {
+        if (cassandraClient == null) {
+            throw new ProcessException("""
+                Cassandra Client is not initialized. The Controller Service 
failed to connect to the cluster.
+            """);
+        }

Review Comment:
   This check and exception seem unnecessary



##########
nifi-extension-bundles/nifi-cassandra-bundle/nifi-cassandra-services/pom.xml:
##########
@@ -0,0 +1,70 @@
+<?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";>
+
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-cassandra-bundle</artifactId>
+        <version>2.10.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>nifi-cassandra-services</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <version>2.10.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-cassandra-services-api</artifactId>
+            <scope>provided</scope>
+            <version>2.10.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cassandra</groupId>
+            <artifactId>java-driver-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-ssl-context-service-api</artifactId>
+            <version>2.10.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-framework-api</artifactId>
+            <version>2.10.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-mock</artifactId>
+            <version>2.10.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>at.yawk.lz4</groupId>
+            <artifactId>lz4-java</artifactId>
+            <version>1.10.4</version>
+        </dependency>

Review Comment:
   This should be inherited from the parent pom.xml



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to