From 245b9e2f4227b96b7acbda2722d7cc7ef2f1987c Mon Sep 17 00:00:00 2001 From: exceptionfactory Date: Mon, 18 Mar 2024 16:17:21 -0500 Subject: [PATCH] NIFI-12920 Removed nifi-cassandra-bundle --- nifi-assembly/NOTICE | 5 - nifi-assembly/pom.xml | 18 - nifi-code-coverage/pom.xml | 20 - nifi-dependency-check-maven/suppressions.xml | 20 - .../pom.xml | 102 --- .../CassandraDistributedMapCache.java | 241 ------ .../nifi/controller/cassandra/QueryUtils.java | 44 -- ...g.apache.nifi.controller.ControllerService | 15 - .../src/test/java/.gitignore | 0 .../nifi-cassandra-nar/pom.xml | 51 -- .../src/main/resources/META-INF/LICENSE | 342 -------- .../src/main/resources/META-INF/NOTICE | 328 -------- .../nifi-cassandra-processors/pom.xml | 108 --- .../cassandra/AbstractCassandraProcessor.java | 533 ------------- .../processors/cassandra/PutCassandraQL.java | 406 ---------- .../cassandra/PutCassandraRecord.java | 471 ----------- .../processors/cassandra/QueryCassandra.java | 736 ------------------ .../org.apache.nifi.processor.Processor | 17 - .../AbstractCassandraProcessorTest.java | 320 -------- .../cassandra/CassandraQueryTestUtil.java | 219 ------ .../cassandra/PutCassandraQLTest.java | 434 ----------- .../cassandra/PutCassandraRecordIT.java | 132 ---- .../PutCassandraRecordInsertTest.java | 116 --- .../cassandra/PutCassandraRecordTest.java | 552 ------------- .../PutCassandraRecordUpdateTest.java | 289 ------- .../cassandra/QueryCassandraIT.java | 183 ----- .../cassandra/QueryCassandraTest.java | 594 -------------- .../nifi-cassandra-services-api-nar/pom.xml | 40 - .../src/main/resources/META-INF/LICENSE | 266 ------- .../src/main/resources/META-INF/NOTICE | 227 ------ .../nifi-cassandra-services-api/pom.xml | 40 - .../CassandraSessionProviderService.java | 35 - .../nifi-cassandra-services-nar/pom.xml | 56 -- .../src/main/resources/META-INF/LICENSE | 352 --------- .../src/main/resources/META-INF/NOTICE | 292 ------- .../nifi-cassandra-services/pom.xml | 62 -- .../service/CassandraSessionProvider.java | 315 -------- ...g.apache.nifi.controller.ControllerService | 16 - .../nifi/service/MockCassandraProcessor.java | 51 -- .../service/TestCassandraSessionProvider.java | 59 -- .../nifi-cassandra-bundle/pom.xml | 58 -- nifi-nar-bundles/pom.xml | 6 - 42 files changed, 8171 deletions(-) delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/pom.xml delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/java/org/apache/nifi/controller/cassandra/CassandraDistributedMapCache.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/java/org/apache/nifi/controller/cassandra/QueryUtils.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/test/java/.gitignore delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/pom.xml delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/src/main/resources/META-INF/LICENSE delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/src/main/resources/META-INF/NOTICE delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/pom.xml delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/AbstractCassandraProcessor.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/PutCassandraQL.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/PutCassandraRecord.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/QueryCassandra.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/AbstractCassandraProcessorTest.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/CassandraQueryTestUtil.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraQLTest.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordIT.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordInsertTest.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordTest.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordUpdateTest.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/QueryCassandraIT.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/QueryCassandraTest.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/pom.xml delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/src/main/resources/META-INF/LICENSE delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/src/main/resources/META-INF/NOTICE delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api/pom.xml delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api/src/main/java/org/apache/nifi/cassandra/CassandraSessionProviderService.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/pom.xml delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/src/main/resources/META-INF/LICENSE delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/src/main/resources/META-INF/NOTICE delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/pom.xml delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/CassandraSessionProvider.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/test/java/org/apache/nifi/service/MockCassandraProcessor.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/test/java/org/apache/nifi/service/TestCassandraSessionProvider.java delete mode 100644 nifi-nar-bundles/nifi-cassandra-bundle/pom.xml diff --git a/nifi-assembly/NOTICE b/nifi-assembly/NOTICE index 0bee30ce6c6b..777b0f01bf5e 100644 --- a/nifi-assembly/NOTICE +++ b/nifi-assembly/NOTICE @@ -2040,11 +2040,6 @@ The following binary components are provided under the Apache Software License v the terms of a BSD style license. The original software and related information is available at http://www.jcraft.com/jsch/. - - (ASLv2) DataStax Java Driver for Apache Cassandra - Core - The following NOTICE information applies: - DataStax Java Driver for Apache Cassandra - Core - Copyright (C) 2012-2017 DataStax Inc. (ASLv2) bytebuffer-collections The following NOTICE information applies: bytebuffer-collections diff --git a/nifi-assembly/pom.xml b/nifi-assembly/pom.xml index 5e4c223e9393..8c328885ea1f 100644 --- a/nifi-assembly/pom.xml +++ b/nifi-assembly/pom.xml @@ -530,24 +530,6 @@ language governing permissions and limitations under the License. --> 2.0.0-SNAPSHOT nar - - org.apache.nifi - nifi-cassandra-nar - 2.0.0-SNAPSHOT - nar - - - org.apache.nifi - nifi-cassandra-services-api-nar - 2.0.0-SNAPSHOT - nar - - - org.apache.nifi - nifi-cassandra-services-nar - 2.0.0-SNAPSHOT - nar - org.apache.nifi nifi-registry-nar diff --git a/nifi-code-coverage/pom.xml b/nifi-code-coverage/pom.xml index 1bfa872870e6..f9cec5b9d14c 100644 --- a/nifi-code-coverage/pom.xml +++ b/nifi-code-coverage/pom.xml @@ -838,26 +838,6 @@ nifi-box-services-api 2.0.0-SNAPSHOT - - org.apache.nifi - nifi-cassandra-distributedmapcache-service - 2.0.0-SNAPSHOT - - - org.apache.nifi - nifi-cassandra-processors - 2.0.0-SNAPSHOT - - - org.apache.nifi - nifi-cassandra-services - 2.0.0-SNAPSHOT - - - org.apache.nifi - nifi-cassandra-services-api - 2.0.0-SNAPSHOT - org.apache.nifi nifi-cdc-api diff --git a/nifi-dependency-check-maven/suppressions.xml b/nifi-dependency-check-maven/suppressions.xml index 08d0a6db6957..be9ecb301dfa 100644 --- a/nifi-dependency-check-maven/suppressions.xml +++ b/nifi-dependency-check-maven/suppressions.xml @@ -404,26 +404,6 @@ ^pkg:maven/com\.google\.guava/guava@.*$ CVE-2020-8908 - - CVE-2021-44521 applies to Apache Cassandra Server - ^pkg:maven/com\.datastax\.cassandra/cassandra\-driver\-extras@.*$ - CVE-2021-44521 - - - CVE-2020-17516 applies to Apache Cassandra Server - ^pkg:maven/com\.datastax\.cassandra/cassandra\-driver\-extras@.*$ - CVE-2020-17516 - - - CVE-2019-2684 applies to Apache Cassandra Server - ^pkg:maven/com\.datastax\.cassandra/cassandra\-driver\-extras@.*$ - CVE-2019-2684 - - - CVE-2020-13946 applies to Apache Cassandra Server - ^pkg:maven/com\.datastax\.cassandra/cassandra\-driver\-extras@.*$ - CVE-2020-13946 - Bundled versions of jQuery DataTables are not used ^pkg:javascript/jquery\.datatables@.*$ diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/pom.xml b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/pom.xml deleted file mode 100644 index 5fed4ec5a1a2..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/pom.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - 4.0.0 - - - org.apache.nifi - nifi-cassandra-bundle - 2.0.0-SNAPSHOT - - - nifi-cassandra-distributedmapcache-service - jar - - - - org.apache.nifi - nifi-api - - - org.apache.nifi - nifi-record-serialization-service-api - 2.0.0-SNAPSHOT - provided - - - org.apache.nifi - nifi-utils - - - org.apache.nifi - nifi-record - - - org.apache.nifi - nifi-avro-record-utils - 2.0.0-SNAPSHOT - - - com.fasterxml.jackson.core - jackson-databind - - - org.apache.nifi - nifi-mock - - - org.apache.nifi - nifi-mock-record-utils - 2.0.0-SNAPSHOT - test - - - org.apache.nifi - nifi-distributed-cache-client-service-api - 2.0.0-SNAPSHOT - - - org.apache.nifi - nifi-cassandra-services-api - 2.0.0-SNAPSHOT - - - - org.apache.nifi - nifi-cassandra-services - 2.0.0-SNAPSHOT - test - - - org.apache.nifi - nifi-ssl-context-service-api - test - - - - org.testcontainers - cassandra - ${testcontainers.version} - test - - - org.testcontainers - junit-jupiter - ${testcontainers.version} - test - - - diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/java/org/apache/nifi/controller/cassandra/CassandraDistributedMapCache.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/java/org/apache/nifi/controller/cassandra/CassandraDistributedMapCache.java deleted file mode 100644 index 6c1bd8e94710..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/java/org/apache/nifi/controller/cassandra/CassandraDistributedMapCache.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.nifi.controller.cassandra; - -import com.datastax.driver.core.BoundStatement; -import com.datastax.driver.core.PreparedStatement; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.Row; -import com.datastax.driver.core.Session; -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.CassandraSessionProviderService; -import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.controller.AbstractControllerService; -import org.apache.nifi.controller.ConfigurationContext; -import org.apache.nifi.distributed.cache.client.Deserializer; -import org.apache.nifi.distributed.cache.client.DistributedMapCacheClient; -import org.apache.nifi.distributed.cache.client.Serializer; -import org.apache.nifi.expression.ExpressionLanguageScope; -import org.apache.nifi.processor.util.StandardValidators; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.apache.nifi.controller.cassandra.QueryUtils.createDeleteStatement; -import static org.apache.nifi.controller.cassandra.QueryUtils.createExistsQuery; -import static org.apache.nifi.controller.cassandra.QueryUtils.createFetchQuery; -import static org.apache.nifi.controller.cassandra.QueryUtils.createInsertStatement; - -@Tags({"map", "cache", "distributed", "cassandra"}) -@CapabilityDescription("Provides a DistributedMapCache client that is based on Apache Cassandra.") -public class CassandraDistributedMapCache extends AbstractControllerService implements DistributedMapCacheClient { - public static final PropertyDescriptor SESSION_PROVIDER = new PropertyDescriptor.Builder() - .name("cassandra-dmc-session-provider") - .displayName("Session Provider") - .description("The client service that will configure the cassandra client connection.") - .required(true) - .identifiesControllerService(CassandraSessionProviderService.class) - .build(); - - public static final PropertyDescriptor TABLE_NAME = new PropertyDescriptor.Builder() - .name("cassandra-dmc-table-name") - .displayName("Table Name") - .description("The name of the table where the cache will be stored.") - .required(true) - .addValidator(StandardValidators.NON_EMPTY_EL_VALIDATOR) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .build(); - - public static final PropertyDescriptor KEY_FIELD_NAME = new PropertyDescriptor.Builder() - .name("cassandra-dmc-key-field-name") - .displayName("Key Field Name") - .description("The name of the field that acts as the unique key. (The CQL type should be \"blob\")") - .required(true) - .addValidator(StandardValidators.NON_EMPTY_EL_VALIDATOR) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .build(); - - public static final PropertyDescriptor VALUE_FIELD_NAME = new PropertyDescriptor.Builder() - .name("cassandra-dmc-value-field-name") - .displayName("Value Field Name") - .description("The name of the field that will store the value. (The CQL type should be \"blob\")") - .required(true) - .addValidator(StandardValidators.NON_EMPTY_EL_VALIDATOR) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .build(); - - public static final PropertyDescriptor TTL = new PropertyDescriptor.Builder() - .name("cassandra-dmc-ttl") - .displayName("TTL") - .description("If configured, this will set a TTL (Time to Live) for each row inserted into the table so that " + - "old cache items expire after a certain period of time.") - .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .required(false) - .build(); - - public static final List DESCRIPTORS = Collections.unmodifiableList(Arrays.asList( - SESSION_PROVIDER, TABLE_NAME, KEY_FIELD_NAME, VALUE_FIELD_NAME, TTL - )); - - private CassandraSessionProviderService sessionProviderService; - private String tableName; - private String keyField; - private String valueField; - private Long ttl; - - private Session session; - private PreparedStatement deleteStatement; - private PreparedStatement existsStatement; - private PreparedStatement fetchStatement; - private PreparedStatement insertStatement; - - @Override - protected List getSupportedPropertyDescriptors() { - return DESCRIPTORS; - } - - @OnEnabled - public void onEnabled(ConfigurationContext context) { - sessionProviderService = context.getProperty(SESSION_PROVIDER).asControllerService(CassandraSessionProviderService.class); - tableName = context.getProperty(TABLE_NAME).evaluateAttributeExpressions().getValue(); - keyField = context.getProperty(KEY_FIELD_NAME).evaluateAttributeExpressions().getValue(); - valueField = context.getProperty(VALUE_FIELD_NAME).evaluateAttributeExpressions().getValue(); - if (context.getProperty(TTL).isSet()) { - ttl = context.getProperty(TTL).evaluateAttributeExpressions().asTimePeriod(TimeUnit.SECONDS); - } - - session = sessionProviderService.getCassandraSession(); - - deleteStatement = session.prepare(createDeleteStatement(keyField, tableName)); - existsStatement = session.prepare(createExistsQuery(keyField, tableName)); - fetchStatement = session.prepare(createFetchQuery(keyField, valueField, tableName)); - insertStatement = session.prepare(createInsertStatement(keyField, valueField, tableName, ttl)); - } - - @OnDisabled - public void onDisabled() { - session = null; - deleteStatement = null; - existsStatement = null; - fetchStatement = null; - insertStatement = null; - } - - @Override - public boolean putIfAbsent(K k, V v, Serializer keySerializer, Serializer valueSerializer) throws IOException { - if (containsKey(k, keySerializer)) { - return false; - } else { - put(k, v, keySerializer, valueSerializer); - return true; - } - } - - @Override - public V getAndPutIfAbsent(K k, V v, Serializer keySerializer, Serializer valueSerializer, Deserializer deserializer) throws IOException { - V got = get(k, keySerializer, deserializer); - boolean wasAbsent = putIfAbsent(k, v, keySerializer, valueSerializer); - - return !wasAbsent ? got : null; - } - - @Override - public boolean containsKey(K k, Serializer serializer) throws IOException { - byte[] key = serializeKey(k, serializer); - - BoundStatement statement = existsStatement.bind(); - ByteBuffer buffer = ByteBuffer.wrap(key); - statement.setBytes(0, buffer); - ResultSet rs =session.execute(statement); - Iterator iterator = rs.iterator(); - - if (iterator.hasNext()) { - Row row = iterator.next(); - long value = row.getLong("exist_count"); - return value > 0; - } else { - return false; - } - } - - @Override - public void put(K k, V v, Serializer keySerializer, Serializer valueSerializer) throws IOException { - BoundStatement statement = insertStatement.bind(); - statement.setBytes(0, ByteBuffer.wrap(serializeKey(k, keySerializer))); - statement.setBytes(1, ByteBuffer.wrap(serializeValue(v, valueSerializer))); - session.execute(statement); - } - - @Override - public V get(K k, Serializer serializer, Deserializer deserializer) throws IOException { - BoundStatement boundStatement = fetchStatement.bind(); - boundStatement.setBytes(0, ByteBuffer.wrap(serializeKey(k, serializer))); - ResultSet rs = session.execute(boundStatement); - Iterator iterator = rs.iterator(); - if (!iterator.hasNext()) { - return null; - } - - Row fetched = iterator.next(); - ByteBuffer buffer = fetched.getBytes(valueField); - - byte[] content = buffer.array(); - - return deserializer.deserialize(content); - } - - @Override - public void close() throws IOException { - - } - - @Override - public boolean remove(K k, Serializer serializer) throws IOException { - BoundStatement delete = deleteStatement.bind(); - delete.setBytes(0, ByteBuffer.wrap(serializeKey(k, serializer))); - session.execute(delete); - - return true; - } - - private byte[] serializeKey(K k, Serializer keySerializer) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - keySerializer.serialize(k, out); - out.close(); - - return out.toByteArray(); - } - - private byte[] serializeValue(V v, Serializer valueSerializer) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - valueSerializer.serialize(v, out); - out.close(); - - return out.toByteArray(); - } -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/java/org/apache/nifi/controller/cassandra/QueryUtils.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/java/org/apache/nifi/controller/cassandra/QueryUtils.java deleted file mode 100644 index cc42dd75d84f..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/java/org/apache/nifi/controller/cassandra/QueryUtils.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.nifi.controller.cassandra; - -public class QueryUtils { - private QueryUtils() {} - - public static String createDeleteStatement(String keyField, String table) { - return String.format("DELETE FROM %s WHERE %s = ?", table, keyField); - } - - public static String createExistsQuery(String keyField, String table) { - return String.format("SELECT COUNT(*) as exist_count FROM %s WHERE %s = ?", table, keyField); - } - - public static String createFetchQuery(String keyField, String valueField, String table) { - return String.format("SELECT %s FROM %s WHERE %s = ?", valueField, table, keyField); - } - - public static String createInsertStatement(String keyField, String valueField, String table, Long ttl) { - String retVal = String.format("INSERT INTO %s (%s, %s) VALUES(?, ?)", table, keyField, valueField); - - if (ttl != null) { - retVal += String.format(" using ttl %d", ttl); - } - - return retVal; - } -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService deleted file mode 100644 index c3d32c851298..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService +++ /dev/null @@ -1,15 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -org.apache.nifi.controller.cassandra.CassandraDistributedMapCache \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/test/java/.gitignore b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-distributedmapcache-service/src/test/java/.gitignore deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/pom.xml b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/pom.xml deleted file mode 100644 index 3063f64ef8bd..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/pom.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - 4.0.0 - - - org.apache.nifi - nifi-cassandra-bundle - 2.0.0-SNAPSHOT - - - nifi-cassandra-nar - nar - - - - - - com.google.guava - guava - provided - - - - - - - org.apache.nifi - nifi-cassandra-services-api-nar - 2.0.0-SNAPSHOT - nar - - - org.apache.nifi - nifi-cassandra-processors - - - diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/src/main/resources/META-INF/LICENSE b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/src/main/resources/META-INF/LICENSE deleted file mode 100644 index 2672f6e36a1d..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/src/main/resources/META-INF/LICENSE +++ /dev/null @@ -1,342 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -APACHE NIFI SUBCOMPONENTS: - -The Apache NiFi project contains subcomponents with separate copyright -notices and license terms. Your use of the source code for the these -subcomponents is subject to the terms and conditions of the following -licenses. - -This product bundles 'libffi' which is available under an MIT style license. - libffi - Copyright (c) 1996-2014 Anthony Green, Red Hat, Inc and others. - see https://github.com/java-native-access/jna/blob/master/native/libffi/LICENSE - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - ``Software''), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -This product bundles 'asm' which is available under a 3-Clause BSD style license. -For details see http://asm.ow2.org/asmdex-license.html - - Copyright (c) 2012 France Télécom - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - THE POSSIBILITY OF SUCH DAMAGE. - - The binary distribution of this product bundles 'Bouncy Castle JDK 1.5' - under an MIT style license. - - Copyright (c) 2000 - 2015 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - -The binary distribution of this product bundles 'JNR x86asm' under an MIT -style license. - - Copyright (C) 2010 Wayne Meissner - Copyright (c) 2008-2009, Petr Kobalicek - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - - The binary distribution of this product bundles 'ParaNamer' and 'Paranamer Core' - which is available under a BSD style license. - - Copyright (c) 2006 Paul Hammant & ThoughtWorks Inc - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - THE POSSIBILITY OF SUCH DAMAGE. diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/src/main/resources/META-INF/NOTICE b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/src/main/resources/META-INF/NOTICE deleted file mode 100644 index 6bd6684a5156..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-nar/src/main/resources/META-INF/NOTICE +++ /dev/null @@ -1,328 +0,0 @@ -nifi-cassandra-nar -Copyright 2016-2020 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). - -****************** -Apache Software License v2 -****************** - -The following binary components are provided under the Apache Software License v2 - - (ASLv2) DataStax Java Driver for Apache Cassandra - Core - The following NOTICE information applies: - DataStax Java Driver for Apache Cassandra - Core - Copyright (C) 2012-2017 DataStax Inc. - - (ASLv2) Apache Avro - The following NOTICE information applies: - Apache Avro - Copyright 2009-2017 The Apache Software Foundation - - (ASLv2) Jackson JSON processor - The following NOTICE information applies: - # Jackson JSON processor - - Jackson is a high-performance, Free/Open Source JSON processing library. - It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has - been in development since 2007. - It is currently developed by a community of developers, as well as supported - commercially by FasterXML.com. - - ## Licensing - - Jackson core and extension components may licensed under different licenses. - To find the details that apply to this artifact see the accompanying LICENSE file. - For more information, including possible other licensing options, contact - FasterXML.com (http://fasterxml.com). - - ## Credits - - A list of contributors may be found from CREDITS file, which is included - in some artifacts (usually source distributions); but is always available - from the source code management (SCM) system project uses. - - (ASLv2) Apache Commons Codec - The following NOTICE information applies: - Apache Commons Codec - Copyright 2002-2014 The Apache Software Foundation - - src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java - contains test data from http://aspell.net/test/orig/batch0.tab. - Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org) - - =============================================================================== - - The content of package org.apache.commons.codec.language.bm has been translated - from the original php source code available at http://stevemorse.org/phoneticinfo.htm - with permission from the original authors. - Original source copyright: - Copyright (c) 2008 Alexander Beider & Stephen P. Morse. - - (ASLv2) Apache Commons Compress - The following NOTICE information applies: - Apache Commons Compress - Copyright 2002-2017 The Apache Software Foundation - - The files in the package org.apache.commons.compress.archivers.sevenz - were derived from the LZMA SDK, version 9.20 (C/ and CPP/7zip/), - which has been placed in the public domain: - - "LZMA SDK is placed in the public domain." (http://www.7-zip.org/sdk.html) - - (ASLv2) Apache Commons IO - The following NOTICE information applies: - Apache Commons IO - Copyright 2002-2016 The Apache Software Foundation - - (ASLv2) Apache Commons Lang - The following NOTICE information applies: - Apache Commons Lang - Copyright 2001-2017 The Apache Software Foundation - - This product includes software from the Spring Framework, - under the Apache License 2.0 (see: StringUtils.containsWhitespace()) - - (ASLv2) Guava - The following NOTICE information applies: - Guava - Copyright 2015 The Guava Authors - - (ASLv2) Dropwizard Metrics - The following NOTICE information applies: - Copyright (c) 2010-2013 Coda Hale, Yammer.com - - This product includes software developed by Coda Hale and Yammer, Inc. - - This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64, - LongAdder), which was released with the following comments: - - Written by Doug Lea with assistance from members of JCP JSR-166 - Expert Group and released to the public domain, as explained at - http://creativecommons.org/publicdomain/zero/1.0/ - - (ASLv2) The Netty Project - The following NOTICE information applies: - Copyright 2014 The Netty Project - ------------------------------------------------------------------------------- - This product contains the extensions to Java Collections Framework which has - been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene: - - * LICENSE: - * license/LICENSE.jsr166y.txt (Public Domain) - * HOMEPAGE: - * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/ - * http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/ - - This product contains a modified version of Robert Harder's Public Domain - Base64 Encoder and Decoder, which can be obtained at: - - * LICENSE: - * license/LICENSE.base64.txt (Public Domain) - * HOMEPAGE: - * http://iharder.sourceforge.net/current/java/base64/ - - This product contains a modified portion of 'Webbit', an event based - WebSocket and HTTP server, which can be obtained at: - - * LICENSE: - * license/LICENSE.webbit.txt (BSD License) - * HOMEPAGE: - * https://github.com/joewalnes/webbit - - This product contains a modified portion of 'SLF4J', a simple logging - facade for Java, which can be obtained at: - - * LICENSE: - * license/LICENSE.slf4j.txt (MIT License) - * HOMEPAGE: - * http://www.slf4j.org/ - - This product contains a modified portion of 'Apache Harmony', an open source - Java SE, which can be obtained at: - - * LICENSE: - * license/LICENSE.harmony.txt (Apache License 2.0) - * HOMEPAGE: - * http://archive.apache.org/dist/harmony/ - - This product contains a modified portion of 'jbzip2', a Java bzip2 compression - and decompression library written by Matthew J. Francis. It can be obtained at: - - * LICENSE: - * license/LICENSE.jbzip2.txt (MIT License) - * HOMEPAGE: - * https://code.google.com/p/jbzip2/ - - This product contains a modified portion of 'libdivsufsort', a C API library to construct - the suffix array and the Burrows-Wheeler transformed string for any input string of - a constant-size alphabet written by Yuta Mori. It can be obtained at: - - * LICENSE: - * license/LICENSE.libdivsufsort.txt (MIT License) - * HOMEPAGE: - * https://github.com/y-256/libdivsufsort - - This product contains a modified portion of Nitsan Wakart's 'JCTools', Java Concurrency Tools for the JVM, - which can be obtained at: - - * LICENSE: - * license/LICENSE.jctools.txt (ASL2 License) - * HOMEPAGE: - * https://github.com/JCTools/JCTools - - This product optionally depends on 'JZlib', a re-implementation of zlib in - pure Java, which can be obtained at: - - * LICENSE: - * license/LICENSE.jzlib.txt (BSD style License) - * HOMEPAGE: - * http://www.jcraft.com/jzlib/ - - This product optionally depends on 'Compress-LZF', a Java library for encoding and - decoding data in LZF format, written by Tatu Saloranta. It can be obtained at: - - * LICENSE: - * license/LICENSE.compress-lzf.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/ning/compress - - This product optionally depends on 'lz4', a LZ4 Java compression - and decompression library written by Adrien Grand. It can be obtained at: - - * LICENSE: - * license/LICENSE.lz4.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/jpountz/lz4-java - - This product optionally depends on 'lzma-java', a LZMA Java compression - and decompression library, which can be obtained at: - - * LICENSE: - * license/LICENSE.lzma-java.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/jponge/lzma-java - - This product contains a modified portion of 'jfastlz', a Java port of FastLZ compression - and decompression library written by William Kinney. It can be obtained at: - - * LICENSE: - * license/LICENSE.jfastlz.txt (MIT License) - * HOMEPAGE: - * https://code.google.com/p/jfastlz/ - - This product contains a modified portion of and optionally depends on 'Protocol Buffers', Google's data - interchange format, which can be obtained at: - - * LICENSE: - * license/LICENSE.protobuf.txt (New BSD License) - * HOMEPAGE: - * https://github.com/google/protobuf - - This product optionally depends on 'Bouncy Castle Crypto APIs' to generate - a temporary self-signed X.509 certificate when the JVM does not provide the - equivalent functionality. It can be obtained at: - - * LICENSE: - * license/LICENSE.bouncycastle.txt (MIT License) - * HOMEPAGE: - * http://www.bouncycastle.org/ - - This product optionally depends on 'Snappy', a compression library produced - by Google Inc, which can be obtained at: - - * LICENSE: - * license/LICENSE.snappy.txt (New BSD License) - * HOMEPAGE: - * https://github.com/google/snappy - - This product optionally depends on 'JBoss Marshalling', an alternative Java - serialization API, which can be obtained at: - - * LICENSE: - * license/LICENSE.jboss-marshalling.txt (GNU LGPL 2.1) - * HOMEPAGE: - * http://www.jboss.org/jbossmarshalling - - This product optionally depends on 'Caliper', Google's micro- - benchmarking framework, which can be obtained at: - - * LICENSE: - * license/LICENSE.caliper.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/google/caliper - - This product optionally depends on 'Apache Log4J', a logging framework, which - can be obtained at: - - * LICENSE: - * license/LICENSE.log4j.txt (Apache License 2.0) - * HOMEPAGE: - * http://logging.apache.org/log4j/ - - This product optionally depends on 'Aalto XML', an ultra-high performance - non-blocking XML processor, which can be obtained at: - - * LICENSE: - * license/LICENSE.aalto-xml.txt (Apache License 2.0) - * HOMEPAGE: - * http://wiki.fasterxml.com/AaltoHome - - This product contains a modified version of 'HPACK', a Java implementation of - the HTTP/2 HPACK algorithm written by Twitter. It can be obtained at: - - * LICENSE: - * license/LICENSE.hpack.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/twitter/hpack - - This product contains a modified portion of 'Apache Commons Lang', a Java library - provides utilities for the java.lang API, which can be obtained at: - - * LICENSE: - * license/LICENSE.commons-lang.txt (Apache License 2.0) - * HOMEPAGE: - * https://commons.apache.org/proper/commons-lang/ - - This product contains a forked and modified version of Tomcat Native - - * LICENSE: - * ASL2 - * HOMEPAGE: - * http://tomcat.apache.org/native-doc/ - * https://svn.apache.org/repos/asf/tomcat/native/ - - (ASLv2) Objenesis - The following NOTICE information applies: - Objenesis - Copyright 2006-2013 Joe Walnes, Henri Tremblay, Leonardo Mesquita - - (ASLv2) Snappy Java - The following NOTICE information applies: - This product includes software developed by Google - Snappy: http://code.google.com/p/snappy/ (New BSD License) - - This product includes software developed by Apache - PureJavaCrc32C from apache-hadoop-common http://hadoop.apache.org/ - (Apache 2.0 license) - - This library containd statically linked libstdc++. This inclusion is allowed by - "GCC RUntime Library Exception" - http://gcc.gnu.org/onlinedocs/libstdc++/manual/license.html - -************************ -Eclipse Public License 1.0 -************************ - -The following binary components are provided under the Eclipse Public License 1.0. See project link for details. - - (EPL 1.0) JNR Posix ( jnr.posix ) https://github.com/jnr/jnr-posix/blob/master/LICENSE.txt - -***************** -Public Domain -***************** - -The following binary components are provided to the 'Public Domain'. See project link for details. - - (Public Domain) XZ for Java (org.tukaani:xz:jar:1.5 - http://tukaani.org/xz/java.html diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/pom.xml b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/pom.xml deleted file mode 100644 index 45094b06a040..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/pom.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - - 4.0.0 - - - org.apache.nifi - nifi-cassandra-bundle - 2.0.0-SNAPSHOT - - - nifi-cassandra-processors - jar - - - - org.apache.nifi - nifi-api - - - org.apache.nifi - nifi-utils - - - org.apache.nifi - nifi-properties - - - com.datastax.cassandra - cassandra-driver-core - ${cassandra.sdk.version} - provided - - - com.datastax.cassandra - cassandra-driver-extras - ${cassandra.sdk.version} - - - org.apache.nifi - nifi-ssl-context-service-api - - - org.apache.avro - avro - - - org.apache.nifi - nifi-cassandra-services-api - provided - - - org.apache.nifi - nifi-cassandra-services - 2.0.0-SNAPSHOT - test - - - org.apache.nifi - nifi-record-serialization-service-api - compile - - - org.apache.nifi - nifi-record - compile - - - - org.apache.nifi - nifi-mock - - - org.apache.nifi - nifi-mock-record-utils - - - org.apache.commons - commons-text - - - - org.testcontainers - cassandra - ${testcontainers.version} - test - - - org.testcontainers - junit-jupiter - ${testcontainers.version} - test - - - diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/AbstractCassandraProcessor.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/AbstractCassandraProcessor.java deleted file mode 100644 index 844f0c5925b3..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/AbstractCassandraProcessor.java +++ /dev/null @@ -1,533 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.CodecRegistry; -import com.datastax.driver.core.ConsistencyLevel; -import com.datastax.driver.core.DataType; -import com.datastax.driver.core.Metadata; -import com.datastax.driver.core.ProtocolOptions; -import com.datastax.driver.core.RemoteEndpointAwareJdkSSLOptions; -import com.datastax.driver.core.Row; -import com.datastax.driver.core.SSLOptions; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.TypeCodec; -import com.datastax.driver.core.exceptions.AuthenticationException; -import com.datastax.driver.core.exceptions.NoHostAvailableException; -import com.datastax.driver.extras.codecs.arrays.ObjectArrayCodec; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; -import javax.net.ssl.SSLContext; -import org.apache.avro.Schema; -import org.apache.avro.SchemaBuilder; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.annotation.lifecycle.OnScheduled; -import org.apache.nifi.cassandra.CassandraSessionProviderService; -import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.components.PropertyValue; -import org.apache.nifi.components.ValidationContext; -import org.apache.nifi.components.ValidationResult; -import org.apache.nifi.expression.ExpressionLanguageScope; -import org.apache.nifi.logging.ComponentLog; -import org.apache.nifi.processor.AbstractProcessor; -import org.apache.nifi.processor.ProcessContext; -import org.apache.nifi.processor.Relationship; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.processor.util.StandardValidators; -import org.apache.nifi.security.util.ClientAuth; -import org.apache.nifi.ssl.SSLContextService; - -/** - * AbstractCassandraProcessor is a base class for Cassandra processors and contains logic and variables common to most - * processors integrating with Apache Cassandra. - */ -public abstract class AbstractCassandraProcessor extends AbstractProcessor { - - public static final int DEFAULT_CASSANDRA_PORT = 9042; - - // Common descriptors - static final PropertyDescriptor CONNECTION_PROVIDER_SERVICE = new PropertyDescriptor.Builder() - .name("cassandra-connection-provider") - .displayName("Cassandra Connection Provider") - .description("Specifies the Cassandra connection providing controller service to be used to connect to Cassandra cluster.") - .required(false) - .identifiesControllerService(CassandraSessionProviderService.class) - .build(); - - 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(false) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .addValidator(StandardValidators.HOSTNAME_PORT_LIST_VALIDATOR) - .build(); - - 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 the keyspace name before any table reference, in case of 'query' native processors or " + - "if the processor exposes the 'Table' property, the keyspace name has to be provided with the " + - "table name in the form of .") - .required(false) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) - .build(); - - static final PropertyDescriptor PROP_SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder() - .name("SSL Context Service") - .description("The SSL Context Service used to provide client certificate information for TLS/SSL " - + "connections.") - .required(false) - .identifiesControllerService(SSLContextService.class) - .build(); - - static final PropertyDescriptor CLIENT_AUTH = new PropertyDescriptor.Builder() - .name("Client Auth") - .description("Client authentication policy when connecting to secure (TLS/SSL) cluster. " - + "Possible values are REQUIRED, WANT, NONE. This property is only used when an SSL Context " - + "has been defined and enabled.") - .required(false) - .allowableValues(ClientAuth.values()) - .defaultValue("REQUIRED") - .build(); - - static final PropertyDescriptor USERNAME = new PropertyDescriptor.Builder() - .name("Username") - .description("Username to access the Cassandra cluster") - .required(false) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) - .build(); - - static final PropertyDescriptor PASSWORD = new PropertyDescriptor.Builder() - .name("Password") - .description("Password to access the Cassandra cluster") - .required(false) - .sensitive(true) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) - .build(); - - 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(false) - .allowableValues(ConsistencyLevel.values()) - .defaultValue("ONE") - .build(); - - static final PropertyDescriptor COMPRESSION_TYPE = new PropertyDescriptor.Builder() - .name("Compression Type") - .description("Enable compression at transport-level requests and responses") - .required(false) - .allowableValues(ProtocolOptions.Compression.values()) - .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) - .defaultValue("NONE") - .build(); - - static final PropertyDescriptor CHARSET = new PropertyDescriptor.Builder() - .name("Character Set") - .description("Specifies the character set of the record data.") - .required(true) - .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES) - .defaultValue("UTF-8") - .addValidator(StandardValidators.CHARACTER_SET_VALIDATOR) - .build(); - - static final Relationship REL_SUCCESS = new Relationship.Builder() - .name("success") - .description("A FlowFile is transferred to this relationship if the operation completed successfully.") - .build(); - - static final Relationship REL_FAILURE = new Relationship.Builder() - .name("failure") - .description("A FlowFile is transferred to this relationship if the operation failed.") - .build(); - - static final Relationship REL_RETRY = new Relationship.Builder().name("retry") - .description("A FlowFile is transferred to this relationship if the operation cannot be completed but attempting " - + "it again may succeed.") - .build(); - - protected static List descriptors = new ArrayList<>(); - - static { - descriptors.add(CONNECTION_PROVIDER_SERVICE); - descriptors.add(CONTACT_POINTS); - descriptors.add(KEYSPACE); - descriptors.add(PROP_SSL_CONTEXT_SERVICE); - descriptors.add(CLIENT_AUTH); - descriptors.add(USERNAME); - descriptors.add(PASSWORD); - descriptors.add(CONSISTENCY_LEVEL); - descriptors.add(COMPRESSION_TYPE); - descriptors.add(CHARSET); - } - - protected final AtomicReference cluster = new AtomicReference<>(null); - protected final AtomicReference cassandraSession = new AtomicReference<>(null); - - protected static final CodecRegistry codecRegistry = new CodecRegistry(); - - @Override - protected Collection customValidate(ValidationContext validationContext) { - Set results = new HashSet<>(); - - // Ensure that if username or password is set, then the other is too - String userName = validationContext.getProperty(USERNAME).evaluateAttributeExpressions().getValue(); - String password = validationContext.getProperty(PASSWORD).evaluateAttributeExpressions().getValue(); - - if (StringUtils.isEmpty(userName) != StringUtils.isEmpty(password)) { - results.add(new ValidationResult.Builder().subject("Username / Password configuration").valid(false).explanation( - "If username or password is specified, then the other must be specified as well").build()); - } - - // Ensure that both Connection provider service and the processor specific configurations are not provided - boolean connectionProviderIsSet = validationContext.getProperty(CONNECTION_PROVIDER_SERVICE).isSet(); - boolean contactPointsIsSet = validationContext.getProperty(CONTACT_POINTS).isSet(); - - if (connectionProviderIsSet && contactPointsIsSet) { - results.add(new ValidationResult.Builder().subject("Cassandra configuration").valid(false).explanation("both " + CONNECTION_PROVIDER_SERVICE.getDisplayName() + - " and processor level Cassandra configuration cannot be provided at the same time.").build()); - } - - if (!connectionProviderIsSet && !contactPointsIsSet) { - results.add(new ValidationResult.Builder().subject("Cassandra configuration").valid(false).explanation("either " + CONNECTION_PROVIDER_SERVICE.getDisplayName() + - " or processor level Cassandra configuration has to be provided.").build()); - } - - return results; - } - - @OnScheduled - public void onScheduled(ProcessContext context) { - final boolean connectionProviderIsSet = context.getProperty(CONNECTION_PROVIDER_SERVICE).isSet(); - - registerAdditionalCodecs(); - - if (connectionProviderIsSet) { - CassandraSessionProviderService sessionProvider = context.getProperty(CONNECTION_PROVIDER_SERVICE).asControllerService(CassandraSessionProviderService.class); - cluster.set(sessionProvider.getCluster()); - cassandraSession.set(sessionProvider.getCassandraSession()); - return; - } - - try { - connectToCassandra(context); - } catch (NoHostAvailableException nhae) { - getLogger().error("No host in the Cassandra cluster can be contacted successfully to execute this statement", nhae); - getLogger().error(nhae.getCustomMessage(10, true, false)); - throw new ProcessException(nhae); - } catch (AuthenticationException ae) { - getLogger().error("Invalid username/password combination", ae); - throw new ProcessException(ae); - } - } - - void connectToCassandra(ProcessContext context) { - if (cluster.get() == null) { - ComponentLog log = getLogger(); - final String contactPointList = context.getProperty(CONTACT_POINTS).evaluateAttributeExpressions().getValue(); - final String consistencyLevel = context.getProperty(CONSISTENCY_LEVEL).getValue(); - final String compressionType = context.getProperty(COMPRESSION_TYPE).getValue(); - List contactPoints = getContactPoints(contactPointList); - - // Set up the client for secure (SSL/TLS communications) if configured to do so - final SSLContextService sslService = context.getProperty(PROP_SSL_CONTEXT_SERVICE).asControllerService(SSLContextService.class); - final SSLContext sslContext; - - if (sslService != null) { - sslContext = sslService.createContext(); - } else { - sslContext = null; - } - - final String username, password; - PropertyValue usernameProperty = context.getProperty(USERNAME).evaluateAttributeExpressions(); - PropertyValue passwordProperty = context.getProperty(PASSWORD).evaluateAttributeExpressions(); - - if (usernameProperty != null && passwordProperty != null) { - username = usernameProperty.getValue(); - password = passwordProperty.getValue(); - } else { - username = null; - password = null; - } - - // Create the cluster and connect to it - Cluster newCluster = createCluster(contactPoints, sslContext, username, password, compressionType); - PropertyValue keyspaceProperty = context.getProperty(KEYSPACE).evaluateAttributeExpressions(); - - final Session newSession; - // For Java 11, the getValue() call was added so the test could pass - if (keyspaceProperty != null && keyspaceProperty.getValue() != null) { - newSession = newCluster.connect(keyspaceProperty.getValue()); - } else { - newSession = newCluster.connect(); - } - - newCluster.getConfiguration().getQueryOptions().setConsistencyLevel(ConsistencyLevel.valueOf(consistencyLevel)); - Metadata metadata = newCluster.getMetadata(); - - log.info("Connected to Cassandra cluster: {}", new Object[]{metadata.getClusterName()}); - - cluster.set(newCluster); - cassandraSession.set(newSession); - } - } - - protected void registerAdditionalCodecs() { - // Conversion between a String[] and a list of varchar - CodecRegistry.DEFAULT_INSTANCE.register(new ObjectArrayCodec<>( - DataType.list(DataType.varchar()), - String[].class, - TypeCodec.varchar())); - } - - /** - * Uses a Cluster.Builder to create a Cassandra cluster reference using the given parameters - * - * @param contactPoints The contact points (hostname:port list of Cassandra nodes) - * @param sslContext The SSL context (used for secure connections) - * @param username The username for connection authentication - * @param password The password for connection authentication - * @param compressionType Enable compression at transport-level requests and responses. - * @return A reference to the Cluster object associated with the given Cassandra configuration - */ - protected Cluster createCluster(List contactPoints, SSLContext sslContext, - String username, String password, String compressionType) { - Cluster.Builder builder = Cluster.builder().addContactPointsWithPorts(contactPoints); - if (sslContext != null) { - final SSLOptions sslOptions = RemoteEndpointAwareJdkSSLOptions.builder() - .withSSLContext(sslContext) - .build(); - - builder = builder.withSSL(sslOptions); - if (ProtocolOptions.Compression.SNAPPY.name().equals(compressionType)) { - builder = builder.withCompression(ProtocolOptions.Compression.SNAPPY); - } else if (ProtocolOptions.Compression.LZ4.name().equals(compressionType)) { - builder = builder.withCompression(ProtocolOptions.Compression.LZ4); - } - } - - if (username != null && password != null) { - builder = builder.withCredentials(username, password); - } - - return builder.build(); - } - - public void stop(ProcessContext context) { - // We don't want to close the connection when using 'Cassandra Connection Provider' - // because each time @OnUnscheduled/@OnShutdown annotated method is triggered on a - // processor, the connection would be closed which is not ideal for a centralized - // connection provider controller service - if (!context.getProperty(CONNECTION_PROVIDER_SERVICE).isSet()) { - if (cassandraSession.get() != null) { - cassandraSession.get().close(); - cassandraSession.set(null); - } - if (cluster.get() != null) { - cluster.get().close(); - cluster.set(null); - } - } - } - - - protected static Object getCassandraObject(Row row, int i, DataType dataType) { - if (dataType.equals(DataType.blob())) { - return row.getBytes(i); - - } else if (dataType.equals(DataType.varint()) || dataType.equals(DataType.decimal())) { - // Avro can't handle BigDecimal and BigInteger as numbers - it will throw an - // AvroRuntimeException such as: "Unknown datum type: java.math.BigDecimal: 38" - return row.getObject(i).toString(); - - } else if (dataType.equals(DataType.cboolean())) { - return row.getBool(i); - - } else if (dataType.equals(DataType.cint())) { - return row.getInt(i); - - } else if (dataType.equals(DataType.bigint()) - || dataType.equals(DataType.counter())) { - return row.getLong(i); - - } else if (dataType.equals(DataType.ascii()) - || dataType.equals(DataType.text()) - || dataType.equals(DataType.varchar())) { - return row.getString(i); - - } else if (dataType.equals(DataType.cfloat())) { - return row.getFloat(i); - - } else if (dataType.equals(DataType.cdouble())) { - return row.getDouble(i); - - } else if (dataType.equals(DataType.timestamp())) { - return row.getTimestamp(i); - } else if (dataType.equals(DataType.date())) { - return row.getDate(i); - - } else if (dataType.equals(DataType.time())) { - return row.getTime(i); - - } else if (dataType.isCollection()) { - - List typeArguments = dataType.getTypeArguments(); - if (typeArguments == null || typeArguments.size() == 0) { - throw new IllegalArgumentException("Column[" + i + "] " + dataType.getName() - + " is a collection but no type arguments were specified!"); - } - // Get the first type argument, to be used for lists and sets (and the first in a map) - DataType firstArg = typeArguments.get(0); - TypeCodec firstCodec = codecRegistry.codecFor(firstArg); - if (dataType.equals(DataType.set(firstArg))) { - return row.getSet(i, firstCodec.getJavaType()); - } else if (dataType.equals(DataType.list(firstArg))) { - return row.getList(i, firstCodec.getJavaType()); - } else { - // Must be an n-arg collection like map - DataType secondArg = typeArguments.get(1); - TypeCodec secondCodec = codecRegistry.codecFor(secondArg); - if (dataType.equals(DataType.map(firstArg, secondArg))) { - return row.getMap(i, firstCodec.getJavaType(), secondCodec.getJavaType()); - } - } - - } else { - // The different types that we support are numbers (int, long, double, float), - // as well as boolean values and Strings. Since Avro doesn't provide - // timestamp types, we want to convert those to Strings. So we will cast anything other - // than numbers or booleans to strings by using the toString() method. - return row.getObject(i).toString(); - } - return null; - } - - /** - * This method will create a schema a union field consisting of null and the specified type. - * - * @param dataType The data type of the field - */ - protected static Schema getUnionFieldType(String dataType) { - return SchemaBuilder.builder().unionOf().nullBuilder().endNull().and().type(getSchemaForType(dataType)).endUnion(); - } - - /** - * This method will create an Avro schema for the specified type. - * - * @param dataType The data type of the field - */ - protected static Schema getSchemaForType(final String dataType) { - final SchemaBuilder.TypeBuilder typeBuilder = SchemaBuilder.builder(); - final Schema returnSchema = switch (dataType) { - case "string" -> typeBuilder.stringType(); - case "boolean" -> typeBuilder.booleanType(); - case "int" -> typeBuilder.intType(); - case "long" -> typeBuilder.longType(); - case "float" -> typeBuilder.floatType(); - case "double" -> typeBuilder.doubleType(); - case "bytes" -> typeBuilder.bytesType(); - default -> throw new IllegalArgumentException("Unknown Avro primitive type: " + dataType); - }; - return returnSchema; - } - - protected static String getPrimitiveAvroTypeFromCassandraType(final DataType dataType) { - // Map types from Cassandra to Avro where possible - if (dataType.equals(DataType.ascii()) - || dataType.equals(DataType.text()) - || dataType.equals(DataType.varchar()) - // Nonstandard types represented by this processor as a string - || dataType.equals(DataType.timestamp()) - || dataType.equals(DataType.timeuuid()) - || dataType.equals(DataType.uuid()) - || dataType.equals(DataType.inet()) - || dataType.equals(DataType.varint())) { - return "string"; - - } else if (dataType.equals(DataType.cboolean())) { - return "boolean"; - - } else if (dataType.equals(DataType.cint())) { - return "int"; - - } else if (dataType.equals(DataType.bigint()) - || dataType.equals(DataType.counter())) { - return "long"; - - } else if (dataType.equals(DataType.cfloat())) { - return "float"; - - } else if (dataType.equals(DataType.cdouble())) { - return "double"; - - } else if (dataType.equals(DataType.blob())) { - return "bytes"; - - } else { - throw new IllegalArgumentException("createSchema: Unknown Cassandra data type " + dataType.getName() - + " cannot be converted to Avro type"); - } - } - - protected static DataType getPrimitiveDataTypeFromString(String dataTypeName) { - Set primitiveTypes = DataType.allPrimitiveTypes(); - for (DataType primitiveType : primitiveTypes) { - if (primitiveType.toString().equals(dataTypeName)) { - return primitiveType; - } - } - return null; - } - - /** - * Gets a list of InetSocketAddress objects that correspond to host:port entries for Cassandra contact points - * - * @param contactPointList A comma-separated list of Cassandra contact points (host:port,host2:port2, etc.) - * @return List of InetSocketAddresses for the Cassandra contact points - */ - protected List getContactPoints(String contactPointList) { - - if (contactPointList == null) { - return null; - } - final String[] contactPointStringList = contactPointList.split(","); - List contactPoints = new ArrayList<>(); - - for (String contactPointEntry : contactPointStringList) { - - String[] addresses = contactPointEntry.split(":"); - final String hostName = addresses[0].trim(); - final int port = (addresses.length > 1) ? Integer.parseInt(addresses[1].trim()) : DEFAULT_CASSANDRA_PORT; - - contactPoints.add(new InetSocketAddress(hostName, port)); - } - return contactPoints; - } -} - diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/PutCassandraQL.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/PutCassandraQL.java deleted file mode 100644 index cb00be83ce5c..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/PutCassandraQL.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.BoundStatement; -import com.datastax.driver.core.DataType; -import com.datastax.driver.core.PreparedStatement; -import com.datastax.driver.core.ResultSetFuture; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.TypeCodec; -import com.datastax.driver.core.exceptions.InvalidTypeException; -import com.datastax.driver.core.exceptions.NoHostAvailableException; -import com.datastax.driver.core.exceptions.QueryExecutionException; -import com.datastax.driver.core.exceptions.QueryValidationException; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.cache.CacheBuilder; -import org.apache.nifi.annotation.behavior.SystemResourceConsideration; -import org.apache.nifi.annotation.behavior.InputRequirement; -import org.apache.nifi.annotation.behavior.ReadsAttribute; -import org.apache.nifi.annotation.behavior.ReadsAttributes; -import org.apache.nifi.annotation.behavior.SupportsBatching; -import org.apache.nifi.annotation.behavior.SystemResource; -import org.apache.nifi.annotation.documentation.CapabilityDescription; -import org.apache.nifi.annotation.documentation.Tags; -import org.apache.nifi.annotation.lifecycle.OnScheduled; -import org.apache.nifi.annotation.lifecycle.OnStopped; -import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.expression.ExpressionLanguageScope; -import org.apache.nifi.flowfile.FlowFile; -import org.apache.nifi.logging.ComponentLog; -import org.apache.nifi.processor.ProcessContext; -import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Relationship; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.processor.io.InputStreamCallback; -import org.apache.nifi.processor.util.StandardValidators; -import org.apache.nifi.stream.io.StreamUtils; -import org.apache.nifi.util.StringUtils; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -@SupportsBatching -@Tags({"cassandra", "cql", "put", "insert", "update", "set"}) -@InputRequirement(InputRequirement.Requirement.INPUT_REQUIRED) -@CapabilityDescription("Execute provided Cassandra Query Language (CQL) statement on a Cassandra 1.x, 2.x, or 3.0.x cluster. " - + "The content of an incoming FlowFile is expected to be the CQL command to execute. The CQL command may use " - + "the ? to escape parameters. In this case, the parameters to use must exist as FlowFile attributes with the " - + "naming convention cql.args.N.type and cql.args.N.value, where N is a positive integer. The cql.args.N.type " - + "is expected to be a lowercase string indicating the Cassandra type.") -@ReadsAttributes({ - @ReadsAttribute(attribute = "cql.args.N.type", - description = "Incoming FlowFiles are expected to be parameterized CQL statements. The type of each " - + "parameter is specified as a lowercase string corresponding to the Cassandra data type (text, " - + "int, boolean, e.g.). In the case of collections, the primitive type(s) of the elements in the " - + "collection should be comma-delimited, follow the collection type, and be enclosed in angle brackets " - + "(< and >), for example set or map."), - @ReadsAttribute(attribute = "cql.args.N.value", - description = "Incoming FlowFiles are expected to be parameterized CQL statements. The value of the " - + "parameters are specified as cql.args.1.value, cql.args.2.value, cql.args.3.value, and so on. The " - + " type of the cql.args.1.value parameter is specified by the cql.args.1.type attribute.") -}) -@SystemResourceConsideration(resource = SystemResource.MEMORY) -public class PutCassandraQL extends AbstractCassandraProcessor { - - public static final PropertyDescriptor STATEMENT_TIMEOUT = new PropertyDescriptor.Builder() - .name("Max Wait Time") - .displayName("Max Wait Time") - .description("The maximum amount of time allowed for a running CQL select query. Must be of format " - + " where is a non-negative integer and TimeUnit is a supported " - + "Time Unit, such as: nanos, millis, secs, mins, hrs, days. A value of zero means there is no limit. ") - .defaultValue("0 seconds") - .required(true) - .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES) - .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR) - .build(); - - public static final PropertyDescriptor STATEMENT_CACHE_SIZE = new PropertyDescriptor.Builder() - .name("putcql-stmt-cache-size") - .displayName("Statement Cache Size") - .description("The maximum number of CQL Prepared Statements to cache. This can improve performance if many incoming flow files have the same CQL statement " - + "with different values for the parameters. If this property is set to zero, the cache is effectively disabled.") - .defaultValue("0") - .required(true) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .addValidator(StandardValidators.NON_NEGATIVE_INTEGER_VALIDATOR) - .build(); - - private final static List propertyDescriptors; - - private final static Set relationships; - - private static final Pattern CQL_TYPE_ATTRIBUTE_PATTERN = Pattern.compile("cql\\.args\\.(\\d+)\\.type"); - - // Matches on top-level type (primitive types like text,int) and also for collections (like list and map) - private static final Pattern CQL_TYPE_PATTERN = Pattern.compile("([^<]+)(<([^,>]+)(,([^,>]+))*>)?"); - - /** - * LRU cache for the compiled patterns. The size of the cache is determined by the value of the Statement Cache Size property - */ - @VisibleForTesting - private ConcurrentMap statementCache; - - /* - * Will ensure that the list of property descriptors is build only once. - * Will also create a Set of relationships - */ - static { - List _propertyDescriptors = new ArrayList<>(); - _propertyDescriptors.addAll(descriptors); - _propertyDescriptors.add(STATEMENT_TIMEOUT); - _propertyDescriptors.add(STATEMENT_CACHE_SIZE); - propertyDescriptors = Collections.unmodifiableList(_propertyDescriptors); - - Set _relationships = new HashSet<>(); - _relationships.add(REL_SUCCESS); - _relationships.add(REL_FAILURE); - _relationships.add(REL_RETRY); - relationships = Collections.unmodifiableSet(_relationships); - } - - @Override - protected List getSupportedPropertyDescriptors() { - return propertyDescriptors; - } - - @Override - public Set getRelationships() { - return relationships; - } - - - @OnScheduled - public void onScheduled(final ProcessContext context) { - super.onScheduled(context); - - // Initialize the prepared statement cache - int statementCacheSize = context.getProperty(STATEMENT_CACHE_SIZE).evaluateAttributeExpressions().asInteger(); - statementCache = CacheBuilder.newBuilder() - .maximumSize(statementCacheSize) - .build() - .asMap(); - } - - @Override - public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException { - ComponentLog logger = getLogger(); - FlowFile flowFile = session.get(); - if (flowFile == null) { - return; - } - - final long startNanos = System.nanoTime(); - final long statementTimeout = context.getProperty(STATEMENT_TIMEOUT).evaluateAttributeExpressions(flowFile).asTimePeriod(TimeUnit.MILLISECONDS); - final Charset charset = Charset.forName(context.getProperty(CHARSET).evaluateAttributeExpressions(flowFile).getValue()); - - // The documentation for the driver recommends the session remain open the entire time the processor is running - // and states that it is thread-safe. This is why connectionSession is not in a try-with-resources. - final Session connectionSession = cassandraSession.get(); - - String cql = getCQL(session, flowFile, charset); - try { - PreparedStatement statement = statementCache.get(cql); - if(statement == null) { - statement = connectionSession.prepare(cql); - statementCache.put(cql, statement); - } - BoundStatement boundStatement = statement.bind(); - - Map attributes = flowFile.getAttributes(); - for (final Map.Entry entry : attributes.entrySet()) { - final String key = entry.getKey(); - final Matcher matcher = CQL_TYPE_ATTRIBUTE_PATTERN.matcher(key); - if (matcher.matches()) { - final int parameterIndex = Integer.parseInt(matcher.group(1)); - String paramType = entry.getValue(); - if (StringUtils.isEmpty(paramType)) { - throw new ProcessException("Value of the " + key + " attribute is null or empty, it must contain a valid value"); - } - - paramType = paramType.trim(); - final String valueAttrName = "cql.args." + parameterIndex + ".value"; - final String parameterValue = attributes.get(valueAttrName); - - try { - setStatementObject(boundStatement, parameterIndex - 1, valueAttrName, parameterValue, paramType); - } catch (final InvalidTypeException | IllegalArgumentException e) { - throw new ProcessException("The value of the " + valueAttrName + " is '" + parameterValue - + "', which cannot be converted into the necessary data type: " + paramType, e); - } - } - } - - try { - ResultSetFuture future = connectionSession.executeAsync(boundStatement); - if (statementTimeout > 0) { - future.getUninterruptibly(statementTimeout, TimeUnit.MILLISECONDS); - } else { - future.getUninterruptibly(); - } - // Emit a Provenance SEND event - final long transmissionMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos); - - // This isn't a real URI but since Cassandra is distributed we just use the cluster name - String transitUri = "cassandra://" + connectionSession.getCluster().getMetadata().getClusterName(); - session.getProvenanceReporter().send(flowFile, transitUri, transmissionMillis, true); - session.transfer(flowFile, REL_SUCCESS); - - } catch (final TimeoutException e) { - throw new ProcessException(e); - } - - - } catch (final NoHostAvailableException nhae) { - getLogger().error("No host in the Cassandra cluster can be contacted successfully to execute this statement", nhae); - // Log up to 10 error messages. Otherwise if a 1000-node cluster was specified but there was no connectivity, - // a thousand error messages would be logged. However we would like information from Cassandra itself, so - // cap the error limit at 10, format the messages, and don't include the stack trace (it is displayed by the - // logger message above). - getLogger().error(nhae.getCustomMessage(10, true, false)); - flowFile = session.penalize(flowFile); - session.transfer(flowFile, REL_RETRY); - - } catch (final QueryExecutionException qee) { - logger.error("Cannot execute the statement with the requested consistency level successfully", qee); - flowFile = session.penalize(flowFile); - session.transfer(flowFile, REL_RETRY); - - } catch (final QueryValidationException qve) { - logger.error("The CQL statement {} is invalid due to syntax error, authorization issue, or another " - + "validation problem; routing {} to failure", cql, flowFile, qve); - flowFile = session.penalize(flowFile); - session.transfer(flowFile, REL_FAILURE); - - } catch (final ProcessException e) { - logger.error("Unable to execute CQL select statement {} for {} due to {}; routing to failure", - new Object[]{cql, flowFile, e}); - flowFile = session.penalize(flowFile); - session.transfer(flowFile, REL_FAILURE); - } - } - - /** - * Determines the CQL statement that should be executed for the given FlowFile - * - * @param session the session that can be used to access the given FlowFile - * @param flowFile the FlowFile whose CQL statement should be executed - * @return the CQL that is associated with the given FlowFile - */ - - private String getCQL(final ProcessSession session, final FlowFile flowFile, final Charset charset) { - // Read the CQL from the FlowFile's content - final byte[] buffer = new byte[(int) flowFile.getSize()]; - session.read(flowFile, new InputStreamCallback() { - @Override - public void process(final InputStream in) throws IOException { - StreamUtils.fillBuffer(in, buffer); - } - }); - - // Create the PreparedStatement string to use for this FlowFile. - return new String(buffer, charset); - } - - /** - * Determines how to map the given value to the appropriate Cassandra data type and returns the object as - * represented by the given type. This can be used in a Prepared/BoundStatement. - * - * @param statement the BoundStatement for setting objects on - * @param paramIndex the index of the parameter at which to set the object - * @param attrName the name of the attribute that the parameter is coming from - for logging purposes - * @param paramValue the value of the CQL parameter to set - * @param paramType the Cassandra data type of the CQL parameter to set - * @throws IllegalArgumentException if the PreparedStatement throws a CQLException when calling the appropriate setter - */ - protected void setStatementObject(final BoundStatement statement, final int paramIndex, final String attrName, - final String paramValue, final String paramType) throws IllegalArgumentException { - if (paramValue == null) { - statement.setToNull(paramIndex); - return; - } else if (paramType == null) { - throw new IllegalArgumentException("Parameter type for " + attrName + " cannot be null"); - - } else { - // Parse the top-level type and any parameterized types (for collections) - final Matcher matcher = CQL_TYPE_PATTERN.matcher(paramType); - - // If the matcher doesn't match, this should fall through to the exception at the bottom - if (matcher.find() && matcher.groupCount() > 1) { - String mainTypeString = matcher.group(1).toLowerCase(); - DataType mainType = getPrimitiveDataTypeFromString(mainTypeString); - if (mainType != null) { - TypeCodec typeCodec = codecRegistry.codecFor(mainType); - - // Need the right statement.setXYZ() method - if (mainType.equals(DataType.ascii()) - || mainType.equals(DataType.text()) - || mainType.equals(DataType.varchar()) - || mainType.equals(DataType.inet()) - || mainType.equals(DataType.varint())) { - // These are strings, so just use the paramValue - statement.setString(paramIndex, paramValue); - - } else if (mainType.equals(DataType.cboolean())) { - statement.setBool(paramIndex, (boolean) typeCodec.parse(paramValue)); - - } else if (mainType.equals(DataType.cint())) { - statement.setInt(paramIndex, (int) typeCodec.parse(paramValue)); - - } else if (mainType.equals(DataType.bigint()) - || mainType.equals(DataType.counter())) { - statement.setLong(paramIndex, (long) typeCodec.parse(paramValue)); - - } else if (mainType.equals(DataType.cfloat())) { - statement.setFloat(paramIndex, (float) typeCodec.parse(paramValue)); - - } else if (mainType.equals(DataType.cdouble())) { - statement.setDouble(paramIndex, (double) typeCodec.parse(paramValue)); - - } else if (mainType.equals(DataType.blob())) { - statement.setBytes(paramIndex, (ByteBuffer) typeCodec.parse(paramValue)); - - } else if (mainType.equals(DataType.timestamp())) { - statement.setTimestamp(paramIndex, (Date) typeCodec.parse(paramValue)); - } else if (mainType.equals(DataType.timeuuid()) - || mainType.equals(DataType.uuid())) { - statement.setUUID(paramIndex, (UUID) typeCodec.parse(paramValue)); - } - return; - } else { - // Get the first parameterized type - if (matcher.groupCount() > 2) { - String firstParamTypeName = matcher.group(3); - DataType firstParamType = getPrimitiveDataTypeFromString(firstParamTypeName); - if (firstParamType == null) { - throw new IllegalArgumentException("Nested collections are not supported"); - } - - // Check for map type - if (DataType.Name.MAP.toString().equalsIgnoreCase(mainTypeString)) { - if (matcher.groupCount() > 4) { - String secondParamTypeName = matcher.group(5); - DataType secondParamType = getPrimitiveDataTypeFromString(secondParamTypeName); - DataType mapType = DataType.map(firstParamType, secondParamType); - statement.setMap(paramIndex, (Map) codecRegistry.codecFor(mapType).parse(paramValue)); - return; - } - } else { - // Must be set or list - if (DataType.Name.SET.toString().equalsIgnoreCase(mainTypeString)) { - DataType setType = DataType.set(firstParamType); - statement.setSet(paramIndex, (Set) codecRegistry.codecFor(setType).parse(paramValue)); - return; - } else if (DataType.Name.LIST.toString().equalsIgnoreCase(mainTypeString)) { - DataType listType = DataType.list(firstParamType); - statement.setList(paramIndex, (List) codecRegistry.codecFor(listType).parse(paramValue)); - return; - } - } - } else { - throw new IllegalArgumentException( - "Collection type " + mainTypeString + " needs parameterized type(s), such as set"); - } - - } - } - - } - throw new IllegalArgumentException("Cannot create object of type " + paramType + " using input " + paramValue); - } - - @OnStopped - public void stop(ProcessContext context) { - super.stop(context); - statementCache.clear(); - } -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/PutCassandraRecord.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/PutCassandraRecord.java deleted file mode 100644 index 7f6cb9b17caa..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/PutCassandraRecord.java +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.BatchStatement; -import com.datastax.driver.core.ConsistencyLevel; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.Statement; -import com.datastax.driver.core.querybuilder.Assignment; -import com.datastax.driver.core.querybuilder.Insert; -import com.datastax.driver.core.querybuilder.QueryBuilder; -import com.datastax.driver.core.querybuilder.Update; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.annotation.behavior.InputRequirement; -import org.apache.nifi.annotation.behavior.ReadsAttribute; -import org.apache.nifi.annotation.behavior.ReadsAttributes; -import org.apache.nifi.annotation.documentation.CapabilityDescription; -import org.apache.nifi.annotation.documentation.Tags; -import org.apache.nifi.annotation.lifecycle.OnShutdown; -import org.apache.nifi.annotation.lifecycle.OnUnscheduled; -import org.apache.nifi.components.AllowableValue; -import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.components.ValidationContext; -import org.apache.nifi.components.ValidationResult; -import org.apache.nifi.expression.ExpressionLanguageScope; -import org.apache.nifi.flowfile.FlowFile; -import org.apache.nifi.processor.ProcessContext; -import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Relationship; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.processor.util.StandardValidators; -import org.apache.nifi.serialization.RecordReader; -import org.apache.nifi.serialization.RecordReaderFactory; -import org.apache.nifi.serialization.record.DataType; -import org.apache.nifi.serialization.record.Record; -import org.apache.nifi.serialization.record.RecordFieldType; -import org.apache.nifi.serialization.record.RecordSchema; -import org.apache.nifi.serialization.record.type.ArrayDataType; -import org.apache.nifi.serialization.record.util.DataTypeUtils; -import org.apache.nifi.util.StopWatch; - -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -import static java.lang.String.format; - -@Tags({"cassandra", "cql", "put", "insert", "update", "set", "record"}) -@InputRequirement(InputRequirement.Requirement.INPUT_REQUIRED) -@CapabilityDescription("This is a record aware processor that reads the content of the incoming FlowFile as individual records using the " + - "configured 'Record Reader' and writes them to Apache Cassandra using native protocol version 3 or higher.") -@ReadsAttributes({ - @ReadsAttribute(attribute = "cql.statement.type", description = "If 'Use cql.statement.type Attribute' is selected for the Statement " + - "Type property, the value of the cql.statement.type Attribute will be used to determine which type of statement (UPDATE, INSERT) " + - "will be generated and executed"), - @ReadsAttribute(attribute = "cql.update.method", description = "If 'Use cql.update.method Attribute' is selected for the Update " + - "Method property, the value of the cql.update.method Attribute will be used to determine which operation (Set, Increment, Decrement) " + - "will be used to generate and execute the Update statement. Ignored if the Statement Type property is not set to UPDATE"), - @ReadsAttribute(attribute = "cql.batch.statement.type", description = "If 'Use cql.batch.statement.type Attribute' is selected for the Batch " + - "Statement Type property, the value of the cql.batch.statement.type Attribute will be used to determine which type of batch statement " + - "(LOGGED, UNLOGGED, COUNTER) will be generated and executed") -}) -public class PutCassandraRecord extends AbstractCassandraProcessor { - static final AllowableValue UPDATE_TYPE = new AllowableValue("UPDATE", "UPDATE", - "Use an UPDATE statement."); - static final AllowableValue INSERT_TYPE = new AllowableValue("INSERT", "INSERT", - "Use an INSERT statement."); - static final AllowableValue STATEMENT_TYPE_USE_ATTR_TYPE = new AllowableValue("USE_ATTR", "Use cql.statement.type Attribute", - "The value of the cql.statement.type Attribute will be used to determine which type of statement (UPDATE, INSERT) " + - "will be generated and executed"); - static final String STATEMENT_TYPE_ATTRIBUTE = "cql.statement.type"; - - static final AllowableValue INCR_TYPE = new AllowableValue("INCREMENT", "Increment", - "Use an increment operation (+=) for the Update statement."); - static final AllowableValue SET_TYPE = new AllowableValue("SET", "Set", - "Use a set operation (=) for the Update statement."); - static final AllowableValue DECR_TYPE = new AllowableValue("DECREMENT", "Decrement", - "Use a decrement operation (-=) for the Update statement."); - static final AllowableValue UPDATE_METHOD_USE_ATTR_TYPE = new AllowableValue("USE_ATTR", "Use cql.update.method Attribute", - "The value of the cql.update.method Attribute will be used to determine which operation (Set, Increment, Decrement) " + - "will be used to generate and execute the Update statement."); - static final String UPDATE_METHOD_ATTRIBUTE = "cql.update.method"; - - static final AllowableValue LOGGED_TYPE = new AllowableValue("LOGGED", "LOGGED", - "Use a LOGGED batch statement"); - static final AllowableValue UNLOGGED_TYPE = new AllowableValue("UNLOGGED", "UNLOGGED", - "Use an UNLOGGED batch statement"); - static final AllowableValue COUNTER_TYPE = new AllowableValue("COUNTER", "COUNTER", - "Use a COUNTER batch statement"); - static final AllowableValue BATCH_STATEMENT_TYPE_USE_ATTR_TYPE = new AllowableValue("USE_ATTR", "Use cql.batch.statement.type Attribute", - "The value of the cql.batch.statement.type Attribute will be used to determine which type of batch statement (LOGGED, UNLOGGED or COUNTER) " + - "will be used to generate and execute the Update statement."); - static final String BATCH_STATEMENT_TYPE_ATTRIBUTE = "cql.batch.statement.type"; - - static final PropertyDescriptor RECORD_READER_FACTORY = new PropertyDescriptor.Builder() - .name("put-cassandra-record-reader") - .displayName("Record Reader") - .description("Specifies the type of Record Reader controller service to use for parsing the incoming data " + - "and determining the schema") - .identifiesControllerService(RecordReaderFactory.class) - .required(true) - .build(); - - static final PropertyDescriptor STATEMENT_TYPE = new PropertyDescriptor.Builder() - .name("put-cassandra-record-statement-type") - .displayName("Statement Type") - .description("Specifies the type of CQL Statement to generate.") - .required(true) - .defaultValue(INSERT_TYPE.getValue()) - .allowableValues(UPDATE_TYPE, INSERT_TYPE, STATEMENT_TYPE_USE_ATTR_TYPE) - .build(); - - static final PropertyDescriptor UPDATE_METHOD = new PropertyDescriptor.Builder() - .name("put-cassandra-record-update-method") - .displayName("Update Method") - .description("Specifies the method to use to SET the values. This property is used if the Statement Type is " + - "UPDATE and ignored otherwise.") - .required(false) - .defaultValue(SET_TYPE.getValue()) - .allowableValues(INCR_TYPE, DECR_TYPE, SET_TYPE, UPDATE_METHOD_USE_ATTR_TYPE) - .build(); - - static final PropertyDescriptor UPDATE_KEYS = new PropertyDescriptor.Builder() - .name("put-cassandra-record-update-keys") - .displayName("Update Keys") - .description("A comma-separated list of column names that uniquely identifies a row in the database for UPDATE statements. " - + "If the Statement Type is UPDATE and this property is not set, the conversion to CQL will fail. " - + "This property is ignored if the Statement Type is not UPDATE.") - .addValidator(StandardValidators.createListValidator(true, false, StandardValidators.NON_EMPTY_VALIDATOR)) - .required(false) - .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES) - .build(); - - static final PropertyDescriptor TABLE = new PropertyDescriptor.Builder() - .name("put-cassandra-record-table") - .displayName("Table name") - .description("The name of the Cassandra table to which the records have to be written.") - .required(true) - .addValidator(StandardValidators.NON_EMPTY_EL_VALIDATOR) - .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES) - .build(); - - static final PropertyDescriptor BATCH_SIZE = new PropertyDescriptor.Builder() - .name("put-cassandra-record-batch-size") - .displayName("Batch size") - .description("Specifies the number of 'Insert statements' to be grouped together to execute as a batch (BatchStatement)") - .defaultValue("100") - .addValidator(StandardValidators.POSITIVE_INTEGER_VALIDATOR) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .required(true) - .build(); - - static final PropertyDescriptor BATCH_STATEMENT_TYPE = new PropertyDescriptor.Builder() - .name("put-cassandra-record-batch-statement-type") - .displayName("Batch Statement Type") - .description("Specifies the type of 'Batch Statement' to be used.") - .allowableValues(LOGGED_TYPE, UNLOGGED_TYPE, COUNTER_TYPE, BATCH_STATEMENT_TYPE_USE_ATTR_TYPE) - .defaultValue(LOGGED_TYPE.getValue()) - .required(false) - .build(); - - static final PropertyDescriptor CONSISTENCY_LEVEL = new PropertyDescriptor.Builder() - .fromPropertyDescriptor(AbstractCassandraProcessor.CONSISTENCY_LEVEL) - .allowableValues(ConsistencyLevel.SERIAL.name(), ConsistencyLevel.LOCAL_SERIAL.name()) - .defaultValue(ConsistencyLevel.SERIAL.name()) - .build(); - - private final static List propertyDescriptors = Collections.unmodifiableList(Arrays.asList( - CONNECTION_PROVIDER_SERVICE, CONTACT_POINTS, KEYSPACE, TABLE, STATEMENT_TYPE, UPDATE_KEYS, UPDATE_METHOD, CLIENT_AUTH, USERNAME, PASSWORD, - RECORD_READER_FACTORY, BATCH_SIZE, CONSISTENCY_LEVEL, BATCH_STATEMENT_TYPE, PROP_SSL_CONTEXT_SERVICE)); - - private final static Set relationships = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(REL_SUCCESS, REL_FAILURE))); - - @Override - protected List getSupportedPropertyDescriptors() { - return propertyDescriptors; - } - - @Override - public Set getRelationships() { - return relationships; - } - - @Override - public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException { - FlowFile inputFlowFile = session.get(); - - if (inputFlowFile == null) { - return; - } - - final String cassandraTable = context.getProperty(TABLE).evaluateAttributeExpressions(inputFlowFile).getValue(); - final RecordReaderFactory recordParserFactory = context.getProperty(RECORD_READER_FACTORY).asControllerService(RecordReaderFactory.class); - final int batchSize = context.getProperty(BATCH_SIZE).evaluateAttributeExpressions().asInteger(); - final String serialConsistencyLevel = context.getProperty(CONSISTENCY_LEVEL).getValue(); - final String updateKeys = context.getProperty(UPDATE_KEYS).evaluateAttributeExpressions(inputFlowFile).getValue(); - - // Get the statement type from the attribute if necessary - final String statementTypeProperty = context.getProperty(STATEMENT_TYPE).getValue(); - String statementType = statementTypeProperty; - if (STATEMENT_TYPE_USE_ATTR_TYPE.getValue().equals(statementTypeProperty)) { - statementType = inputFlowFile.getAttribute(STATEMENT_TYPE_ATTRIBUTE); - } - - // Get the update method from the attribute if necessary - final String updateMethodProperty = context.getProperty(UPDATE_METHOD).getValue(); - String updateMethod = updateMethodProperty; - if (UPDATE_METHOD_USE_ATTR_TYPE.getValue().equals(updateMethodProperty)) { - updateMethod = inputFlowFile.getAttribute(UPDATE_METHOD_ATTRIBUTE); - } - - - // Get the batch statement type from the attribute if necessary - final String batchStatementTypeProperty = context.getProperty(BATCH_STATEMENT_TYPE).getValue(); - String batchStatementType = batchStatementTypeProperty; - if (BATCH_STATEMENT_TYPE_USE_ATTR_TYPE.getValue().equals(batchStatementTypeProperty)) { - batchStatementType = inputFlowFile.getAttribute(BATCH_STATEMENT_TYPE_ATTRIBUTE).toUpperCase(); - } - if (StringUtils.isEmpty(batchStatementType)) { - throw new IllegalArgumentException(format("Batch Statement Type is not specified, FlowFile %s", inputFlowFile)); - } - - final BatchStatement batchStatement; - final Session connectionSession = cassandraSession.get(); - final AtomicInteger recordsAdded = new AtomicInteger(0); - final StopWatch stopWatch = new StopWatch(true); - - boolean error = false; - - try (final InputStream inputStream = session.read(inputFlowFile); - final RecordReader reader = recordParserFactory.createRecordReader(inputFlowFile, inputStream, getLogger())){ - - // throw an exception if statement type is not set - if (StringUtils.isEmpty(statementType)) { - throw new IllegalArgumentException(format("Statement Type is not specified, FlowFile %s", inputFlowFile)); - } - - // throw an exception if the statement type is set to update and updateKeys is empty - if (UPDATE_TYPE.getValue().equalsIgnoreCase(statementType) && StringUtils.isEmpty(updateKeys)) { - throw new IllegalArgumentException(format("Update Keys are not specified, FlowFile %s", inputFlowFile)); - } - - // throw an exception if the Update Method is Increment or Decrement and the batch statement type is not UNLOGGED or COUNTER - if (INCR_TYPE.getValue().equalsIgnoreCase(updateMethod) || DECR_TYPE.getValue().equalsIgnoreCase(updateMethod)) { - if (!(UNLOGGED_TYPE.getValue().equalsIgnoreCase(batchStatementType) || COUNTER_TYPE.getValue().equalsIgnoreCase(batchStatementType))) { - throw new IllegalArgumentException(format("Increment/Decrement Update Method can only be used with COUNTER " + - "or UNLOGGED Batch Statement Type, FlowFile %s", inputFlowFile)); - } - } - - final RecordSchema schema = reader.getSchema(); - Record record; - - batchStatement = new BatchStatement(BatchStatement.Type.valueOf(batchStatementType)); - batchStatement.setSerialConsistencyLevel(ConsistencyLevel.valueOf(serialConsistencyLevel)); - - while((record = reader.nextRecord()) != null) { - Map recordContentMap = (Map) DataTypeUtils - .convertRecordFieldtoObject(record, RecordFieldType.RECORD.getRecordDataType(record.getSchema())); - - Statement query; - if (INSERT_TYPE.getValue().equalsIgnoreCase(statementType)) { - query = generateInsert(cassandraTable, schema, recordContentMap); - } else if (UPDATE_TYPE.getValue().equalsIgnoreCase(statementType)) { - query = generateUpdate(cassandraTable, schema, updateKeys, updateMethod, recordContentMap); - } else { - throw new IllegalArgumentException(format("Statement Type %s is not valid, FlowFile %s", statementType, inputFlowFile)); - } - - if (getLogger().isDebugEnabled()) { - getLogger().debug("Query: {}", query.toString()); - } - - batchStatement.add(query); - - if (recordsAdded.incrementAndGet() == batchSize) { - connectionSession.execute(batchStatement); - batchStatement.clear(); - recordsAdded.set(0); - } - } - - if (batchStatement.size() != 0) { - connectionSession.execute(batchStatement); - batchStatement.clear(); - } - - } catch (Exception e) { - error = true; - getLogger().error("Unable to write the records into Cassandra table due to {}", new Object[] {e}); - session.transfer(inputFlowFile, REL_FAILURE); - } finally { - if (!error) { - stopWatch.stop(); - long duration = stopWatch.getDuration(TimeUnit.MILLISECONDS); - String transitUri = "cassandra://" + connectionSession.getCluster().getMetadata().getClusterName() + "." + cassandraTable; - - session.getProvenanceReporter().send(inputFlowFile, transitUri, "Inserted " + recordsAdded.get() + " records", duration); - session.transfer(inputFlowFile, REL_SUCCESS); - } - } - - } - - protected Statement generateUpdate(String cassandraTable, RecordSchema schema, String updateKeys, String updateMethod, Map recordContentMap) { - Update updateQuery; - - // Split up the update key names separated by a comma, should not be empty - final Set updateKeyNames; - updateKeyNames = Arrays.stream(updateKeys.split(",")) - .map(String::trim) - .filter(StringUtils::isNotEmpty) - .collect(Collectors.toSet()); - if (updateKeyNames.isEmpty()) { - throw new IllegalArgumentException("No Update Keys were specified"); - } - - // Verify if all update keys are present in the record - for (String updateKey : updateKeyNames) { - if (!schema.getFieldNames().contains(updateKey)) { - throw new IllegalArgumentException("Update key '" + updateKey + "' is not present in the record schema"); - } - } - - // Prepare keyspace/table names - if (cassandraTable.contains(".")) { - String[] keyspaceAndTable = cassandraTable.split("\\."); - updateQuery = QueryBuilder.update(keyspaceAndTable[0], keyspaceAndTable[1]); - } else { - updateQuery = QueryBuilder.update(cassandraTable); - } - - // Loop through the field names, setting those that are not in the update key set, and using those - // in the update key set as conditions. - for (String fieldName : schema.getFieldNames()) { - Object fieldValue = recordContentMap.get(fieldName); - - if (updateKeyNames.contains(fieldName)) { - updateQuery.where(QueryBuilder.eq(fieldName, fieldValue)); - } else { - Assignment assignment; - if (SET_TYPE.getValue().equalsIgnoreCase(updateMethod)) { - assignment = QueryBuilder.set(fieldName, fieldValue); - } else if (INCR_TYPE.getValue().equalsIgnoreCase(updateMethod)) { - assignment = QueryBuilder.incr(fieldName, convertFieldObjectToLong(fieldName, fieldValue)); - } else if (DECR_TYPE.getValue().equalsIgnoreCase(updateMethod)) { - assignment = QueryBuilder.decr(fieldName, convertFieldObjectToLong(fieldName, fieldValue)); - } else { - throw new IllegalArgumentException("Update Method '" + updateMethod + "' is not valid."); - } - updateQuery.with(assignment); - } - } - return updateQuery; - } - - private Long convertFieldObjectToLong(String name, Object value) { - if (!(value instanceof Number)) { - throw new IllegalArgumentException("Field '" + name + "' is not of type Number"); - } - return ((Number) value).longValue(); - } - - @Override - protected Collection customValidate(ValidationContext validationContext) { - Set results = (Set) super.customValidate(validationContext); - - String statementType = validationContext.getProperty(STATEMENT_TYPE).getValue(); - - if (UPDATE_TYPE.getValue().equalsIgnoreCase(statementType)) { - // Check that update keys are set - String updateKeys = validationContext.getProperty(UPDATE_KEYS).getValue(); - if (StringUtils.isEmpty(updateKeys)) { - results.add(new ValidationResult.Builder().subject("Update statement configuration").valid(false).explanation( - "if the Statement Type is set to Update, then the Update Keys must be specified as well").build()); - } - - // Check that if the update method is set to increment or decrement that the batch statement type is set to - // unlogged or counter (or USE_ATTR_TYPE, which we cannot check at this point). - String updateMethod = validationContext.getProperty(UPDATE_METHOD).getValue(); - String batchStatementType = validationContext.getProperty(BATCH_STATEMENT_TYPE).getValue(); - if (INCR_TYPE.getValue().equalsIgnoreCase(updateMethod) - || DECR_TYPE.getValue().equalsIgnoreCase(updateMethod)) { - if (!(COUNTER_TYPE.getValue().equalsIgnoreCase(batchStatementType) - || UNLOGGED_TYPE.getValue().equalsIgnoreCase(batchStatementType) - || BATCH_STATEMENT_TYPE_USE_ATTR_TYPE.getValue().equalsIgnoreCase(batchStatementType))) { - results.add(new ValidationResult.Builder().subject("Update method configuration").valid(false).explanation( - "if the Update Method is set to Increment or Decrement, then the Batch Statement Type must be set " + - "to either COUNTER or UNLOGGED").build()); - } - } - } - - return results; - } - - protected Statement generateInsert(String cassandraTable, RecordSchema schema, Map recordContentMap) { - Insert insertQuery; - if (cassandraTable.contains(".")) { - String[] keyspaceAndTable = cassandraTable.split("\\."); - insertQuery = QueryBuilder.insertInto(keyspaceAndTable[0], keyspaceAndTable[1]); - } else { - insertQuery = QueryBuilder.insertInto(cassandraTable); - } - for (String fieldName : schema.getFieldNames()) { - Object value = recordContentMap.get(fieldName); - - if (value != null && value.getClass().isArray()) { - Object[] array = (Object[]) value; - - if (array.length > 0) { - if (array[0] instanceof Byte) { - Object[] temp = (Object[]) value; - byte[] newArray = new byte[temp.length]; - for (int x = 0; x < temp.length; x++) { - newArray[x] = (Byte) temp[x]; - } - value = ByteBuffer.wrap(newArray); - } - } - } - - if (schema.getDataType(fieldName).isPresent()) { - DataType fieldDataType = schema.getDataType(fieldName).get(); - if (fieldDataType.getFieldType() == RecordFieldType.ARRAY) { - if (((ArrayDataType)fieldDataType).getElementType().getFieldType() == RecordFieldType.STRING) { - value = Arrays.stream((Object[])value).toArray(String[]::new); - } - } - } - - insertQuery.value(fieldName, value); - } - return insertQuery; - } - - @OnUnscheduled - public void stop(ProcessContext context) { - super.stop(context); - } - - @OnShutdown - public void shutdown(ProcessContext context) { - super.stop(context); - } - -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/QueryCassandra.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/QueryCassandra.java deleted file mode 100644 index 67bd8e1d0b22..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/java/org/apache/nifi/processors/cassandra/QueryCassandra.java +++ /dev/null @@ -1,736 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.ColumnDefinitions; -import com.datastax.driver.core.DataType; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.Row; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.exceptions.NoHostAvailableException; -import com.datastax.driver.core.exceptions.QueryExecutionException; -import com.datastax.driver.core.exceptions.QueryValidationException; -import com.google.common.annotations.VisibleForTesting; -import org.apache.avro.Schema; -import org.apache.avro.SchemaBuilder; -import org.apache.avro.file.DataFileWriter; -import org.apache.avro.generic.GenericData; -import org.apache.avro.generic.GenericDatumWriter; -import org.apache.avro.generic.GenericRecord; -import org.apache.avro.io.DatumWriter; -import org.apache.commons.text.StringEscapeUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.nifi.annotation.behavior.InputRequirement; -import org.apache.nifi.annotation.lifecycle.OnShutdown; -import org.apache.nifi.annotation.lifecycle.OnUnscheduled; -import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.components.ValidationResult; -import org.apache.nifi.expression.ExpressionLanguageScope; -import org.apache.nifi.flowfile.FlowFile; -import org.apache.nifi.flowfile.attributes.CoreAttributes; -import org.apache.nifi.annotation.behavior.WritesAttribute; -import org.apache.nifi.annotation.behavior.WritesAttributes; -import org.apache.nifi.annotation.lifecycle.OnScheduled; -import org.apache.nifi.annotation.documentation.CapabilityDescription; -import org.apache.nifi.annotation.documentation.Tags; -import org.apache.nifi.flowfile.attributes.FragmentAttributes; -import org.apache.nifi.logging.ComponentLog; -import org.apache.nifi.processor.ProcessContext; -import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.Relationship; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.processor.util.StandardValidators; -import org.apache.nifi.util.StopWatch; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.Charset; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicLong; - -@Tags({"cassandra", "cql", "select"}) -@InputRequirement(InputRequirement.Requirement.INPUT_ALLOWED) -@CapabilityDescription("Execute provided Cassandra Query Language (CQL) select query on a Cassandra 1.x, 2.x, or 3.0.x cluster. Query result " - + "may be converted to Avro or JSON format. Streaming is used so arbitrarily large result sets are supported. This processor can be " - + "scheduled to run on a timer, or cron expression, using the standard scheduling methods, or it can be triggered by an incoming FlowFile. " - + "If it is triggered by an incoming FlowFile, then attributes of that FlowFile will be available when evaluating the " - + "select query. FlowFile attribute 'executecql.row.count' indicates how many rows were selected.") -@WritesAttributes({ - @WritesAttribute(attribute = "executecql.row.count", description = "The number of rows returned by the CQL query"), - @WritesAttribute(attribute = "fragment.identifier", description = "If 'Max Rows Per Flow File' is set then all FlowFiles from the same query result set " - + "will have the same value for the fragment.identifier attribute. This can then be used to correlate the results."), - @WritesAttribute(attribute = "fragment.count", description = "If 'Max Rows Per Flow File' is set then this is the total number of " - + "FlowFiles produced by a single ResultSet. This can be used in conjunction with the " - + "fragment.identifier attribute in order to know how many FlowFiles belonged to the same incoming ResultSet. If Output Batch Size is set, then this " - + "attribute will not be populated."), - @WritesAttribute(attribute = "fragment.index", description = "If 'Max Rows Per Flow File' is set then the position of this FlowFile in the list of " - + "outgoing FlowFiles that were all derived from the same result set FlowFile. This can be " - + "used in conjunction with the fragment.identifier attribute to know which FlowFiles originated from the same query result set and in what order " - + "FlowFiles were produced") -}) -public class QueryCassandra extends AbstractCassandraProcessor { - - public static final String AVRO_FORMAT = "Avro"; - public static final String JSON_FORMAT = "JSON"; - - public static final String RESULT_ROW_COUNT = "executecql.row.count"; - - public static final String FRAGMENT_ID = FragmentAttributes.FRAGMENT_ID.key(); - public static final String FRAGMENT_INDEX = FragmentAttributes.FRAGMENT_INDEX.key(); - public static final String FRAGMENT_COUNT = FragmentAttributes.FRAGMENT_COUNT.key(); - - public static final PropertyDescriptor CQL_SELECT_QUERY = new PropertyDescriptor.Builder() - .name("CQL select query") - .description("CQL select query") - .required(true) - .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) - .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES) - .build(); - - public static final PropertyDescriptor QUERY_TIMEOUT = new PropertyDescriptor.Builder() - .name("Max Wait Time") - .description("The maximum amount of time allowed for a running CQL select query. Must be of format " - + " where is a non-negative integer and TimeUnit is a supported " - + "Time Unit, such as: nanos, millis, secs, mins, hrs, days. A value of zero means there is no limit. ") - .defaultValue("0 seconds") - .required(true) - .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES) - .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR) - .build(); - - public static final PropertyDescriptor FETCH_SIZE = new PropertyDescriptor.Builder() - .name("Fetch size") - .description("The number of result rows to be fetched from the result set at a time. Zero is the default " - + "and means there is no limit.") - .defaultValue("0") - .required(true) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .addValidator(StandardValidators.INTEGER_VALIDATOR) - .build(); - - public static final PropertyDescriptor MAX_ROWS_PER_FLOW_FILE = new PropertyDescriptor.Builder() - .name("Max Rows Per Flow File") - .description("The maximum number of result rows that will be included in a single FlowFile. This will allow you to break up very large " - + "result sets into multiple FlowFiles. If the value specified is zero, then all rows are returned in a single FlowFile.") - .defaultValue("0") - .required(true) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .addValidator(StandardValidators.INTEGER_VALIDATOR) - .build(); - - public static final PropertyDescriptor OUTPUT_BATCH_SIZE = new PropertyDescriptor.Builder() - .name("qdbt-output-batch-size") - .displayName("Output Batch Size") - .description("The number of output FlowFiles to queue before committing the process session. When set to zero, the session will be committed when all result set rows " - + "have been processed and the output FlowFiles are ready for transfer to the downstream relationship. For large result sets, this can cause a large burst of FlowFiles " - + "to be transferred at the end of processor execution. If this property is set, then when the specified number of FlowFiles are ready for transfer, then the session will " - + "be committed, thus releasing the FlowFiles to the downstream relationship. NOTE: The maxvalue.* and fragment.count attributes will not be set on FlowFiles when this " - + "property is set.") - .defaultValue("0") - .required(true) - .addValidator(StandardValidators.NON_NEGATIVE_INTEGER_VALIDATOR) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .build(); - - public static final PropertyDescriptor OUTPUT_FORMAT = new PropertyDescriptor.Builder() - .name("Output Format") - .description("The format to which the result rows will be converted. If JSON is selected, the output will " - + "contain an object with field 'results' containing an array of result rows. Each row in the array is a " - + "map of the named column to its value. For example: { \"results\": [{\"userid\":1, \"name\":\"Joe Smith\"}]}") - .required(true) - .allowableValues(AVRO_FORMAT, JSON_FORMAT) - .defaultValue(AVRO_FORMAT) - .build(); - - public static final PropertyDescriptor TIMESTAMP_FORMAT_PATTERN = new PropertyDescriptor.Builder() - .name("timestamp-format-pattern") - .displayName("Timestamp Format Pattern for JSON output") - .description("Pattern to use when converting timestamp fields to JSON. Note: the formatted timestamp will be in UTC timezone.") - .required(true) - .defaultValue("yyyy-MM-dd HH:mm:ssZ") - .addValidator((subject, input, context) -> { - final ValidationResult.Builder vrb = new ValidationResult.Builder().subject(subject).input(input); - try { - DateTimeFormatter.ofPattern(input); - vrb.valid(true).explanation("Valid date format pattern"); - } catch (Exception ex) { - vrb.valid(false).explanation("the pattern is invalid: " + ex.getMessage()); - } - return vrb.build(); - }) - .build(); - - private final static List propertyDescriptors; - - private final static Set relationships; - - /* - * Will ensure that the list of property descriptors is build only once. - * Will also create a Set of relationships - */ - static { - List _propertyDescriptors = new ArrayList<>(); - _propertyDescriptors.addAll(descriptors); - _propertyDescriptors.add(CQL_SELECT_QUERY); - _propertyDescriptors.add(QUERY_TIMEOUT); - _propertyDescriptors.add(FETCH_SIZE); - _propertyDescriptors.add(MAX_ROWS_PER_FLOW_FILE); - _propertyDescriptors.add(OUTPUT_BATCH_SIZE); - _propertyDescriptors.add(OUTPUT_FORMAT); - _propertyDescriptors.add(TIMESTAMP_FORMAT_PATTERN); - propertyDescriptors = Collections.unmodifiableList(_propertyDescriptors); - - Set _relationships = new HashSet<>(); - _relationships.add(REL_SUCCESS); - _relationships.add(REL_FAILURE); - _relationships.add(REL_RETRY); - relationships = Collections.unmodifiableSet(_relationships); - } - - @Override - public Set getRelationships() { - return relationships; - } - - @Override - public final List getSupportedPropertyDescriptors() { - return propertyDescriptors; - } - - @OnScheduled - public void onScheduled(final ProcessContext context) { - super.onScheduled(context); - - final int fetchSize = context.getProperty(FETCH_SIZE).evaluateAttributeExpressions().asInteger(); - if (fetchSize > 0) { - synchronized (cluster.get()) { - cluster.get().getConfiguration().getQueryOptions().setFetchSize(fetchSize); - } - } - - } - - @Override - public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException { - FlowFile fileToProcess = null; - - if (context.hasIncomingConnection()) { - fileToProcess = session.get(); - - // If we have no FlowFile, and all incoming connections are self-loops then we can continue on. - // However, if we have no FlowFile and we have connections coming from other Processors, then - // we know that we should run only if we have a FlowFile. - if (fileToProcess == null && context.hasNonLoopConnection()) { - return; - } - } - - final ComponentLog logger = getLogger(); - final String selectQuery = context.getProperty(CQL_SELECT_QUERY).evaluateAttributeExpressions(fileToProcess).getValue(); - final long queryTimeout = context.getProperty(QUERY_TIMEOUT).evaluateAttributeExpressions(fileToProcess).asTimePeriod(TimeUnit.MILLISECONDS); - final String outputFormat = context.getProperty(OUTPUT_FORMAT).getValue(); - final long maxRowsPerFlowFile = context.getProperty(MAX_ROWS_PER_FLOW_FILE).evaluateAttributeExpressions().asInteger(); - final long outputBatchSize = context.getProperty(OUTPUT_BATCH_SIZE).evaluateAttributeExpressions().asInteger(); - final Charset charset = Charset.forName(context.getProperty(CHARSET).evaluateAttributeExpressions(fileToProcess).getValue()); - final StopWatch stopWatch = new StopWatch(true); - - final List resultSetFlowFiles = new LinkedList<>(); - - try { - // The documentation for the driver recommends the session remain open the entire time the processor is running - // and states that it is thread-safe. This is why connectionSession is not in a try-with-resources. - final Session connectionSession = cassandraSession.get(); - final ResultSet resultSet; - - if (queryTimeout > 0) { - resultSet = connectionSession.execute(selectQuery, queryTimeout, TimeUnit.MILLISECONDS); - }else{ - resultSet = connectionSession.execute(selectQuery); - } - final AtomicLong nrOfRows = new AtomicLong(0L); - - if(fileToProcess == null) { - fileToProcess = session.create(); - } - - int fragmentIndex = 0; - final String fragmentId = UUID.randomUUID().toString(); - - while(true) { - - fileToProcess = session.write(fileToProcess, out -> { - try { - logger.debug("Executing CQL query {}", selectQuery); - if (queryTimeout > 0) { - if (AVRO_FORMAT.equals(outputFormat)) { - nrOfRows.set(convertToAvroStream(resultSet, maxRowsPerFlowFile, - out, queryTimeout, TimeUnit.MILLISECONDS)); - } else if (JSON_FORMAT.equals(outputFormat)) { - nrOfRows.set(convertToJsonStream(resultSet, maxRowsPerFlowFile, - out, charset, queryTimeout, TimeUnit.MILLISECONDS)); - } - } else { - if (AVRO_FORMAT.equals(outputFormat)) { - nrOfRows.set(convertToAvroStream(resultSet, maxRowsPerFlowFile, - out, 0, null)); - } else if (JSON_FORMAT.equals(outputFormat)) { - nrOfRows.set(convertToJsonStream(resultSet, maxRowsPerFlowFile, - out, charset, 0, null)); - } - } - } catch (final TimeoutException | InterruptedException | ExecutionException e) { - throw new ProcessException(e); - } - }); - - // set attribute how many rows were selected - fileToProcess = session.putAttribute(fileToProcess, RESULT_ROW_COUNT, String.valueOf(nrOfRows.get())); - - // set mime.type based on output format - fileToProcess = session.putAttribute(fileToProcess, CoreAttributes.MIME_TYPE.key(), - JSON_FORMAT.equals(outputFormat) ? "application/json" : "application/avro-binary"); - - if (logger.isDebugEnabled()) { - logger.info("{} contains {} records; transferring to 'success'", - fileToProcess, nrOfRows.get()); - } - if (maxRowsPerFlowFile > 0) { - fileToProcess = session.putAttribute(fileToProcess, FRAGMENT_ID, fragmentId); - fileToProcess = session.putAttribute(fileToProcess, FRAGMENT_INDEX, String.valueOf(fragmentIndex)); - } - session.getProvenanceReporter().modifyContent(fileToProcess, "Retrieved " + nrOfRows.get() + " rows", - stopWatch.getElapsed(TimeUnit.MILLISECONDS)); - resultSetFlowFiles.add(fileToProcess); - - if (outputBatchSize > 0) { - - if (resultSetFlowFiles.size() == outputBatchSize) { - session.transfer(resultSetFlowFiles, REL_SUCCESS); - session.commitAsync(); - resultSetFlowFiles.clear(); - } - } - fragmentIndex++; - resultSet.fetchMoreResults().get(); - if (resultSet.isExhausted()) { - // If we are splitting results but not outputting batches, set count on all FlowFiles - if (outputBatchSize == 0 && maxRowsPerFlowFile > 0) { - for (int i = 0; i < resultSetFlowFiles.size(); i++) { - resultSetFlowFiles.set(i, - session.putAttribute(resultSetFlowFiles.get(i), FRAGMENT_COUNT, Integer.toString(fragmentIndex))); - } - } - session.transfer(resultSetFlowFiles, REL_SUCCESS); - session.commitAsync(); - resultSetFlowFiles.clear(); - break; - } - fileToProcess = session.create(); - } - - } catch (final NoHostAvailableException nhae) { - getLogger().error("No host in the Cassandra cluster can be contacted successfully to execute this query", nhae); - // Log up to 10 error messages. Otherwise if a 1000-node cluster was specified but there was no connectivity, - // a thousand error messages would be logged. However we would like information from Cassandra itself, so - // cap the error limit at 10, format the messages, and don't include the stack trace (it is displayed by the - // logger message above). - getLogger().error(nhae.getCustomMessage(10, true, false)); - if (fileToProcess == null) { - fileToProcess = session.create(); - } - fileToProcess = session.penalize(fileToProcess); - session.transfer(fileToProcess, REL_RETRY); - } catch (final QueryExecutionException qee) { - logger.error("Cannot execute the query with the requested consistency level successfully", qee); - if (fileToProcess == null) { - fileToProcess = session.create(); - } - fileToProcess = session.penalize(fileToProcess); - session.transfer(fileToProcess, REL_RETRY); - - } catch (final QueryValidationException qve) { - if (context.hasIncomingConnection()) { - logger.error("The CQL query {} is invalid due to syntax error, authorization issue, or another " - + "validation problem; routing {} to failure", - selectQuery, fileToProcess, qve); - - if (fileToProcess == null) { - fileToProcess = session.create(); - } - fileToProcess = session.penalize(fileToProcess); - session.transfer(fileToProcess, REL_FAILURE); - } else { - // This can happen if any exceptions occur while setting up the connection, statement, etc. - logger.error("The CQL query {} is invalid due to syntax error, authorization issue, or another " - + "validation problem", selectQuery, qve); - if (fileToProcess != null) { - session.remove(fileToProcess); - } - context.yield(); - } - } catch (InterruptedException|ExecutionException ex) { - if (context.hasIncomingConnection()) { - logger.error("The CQL query {} has yielded an unknown error, routing {} to failure", - selectQuery, fileToProcess, ex); - - if (fileToProcess == null) { - fileToProcess = session.create(); - } - fileToProcess = session.penalize(fileToProcess); - session.transfer(fileToProcess, REL_FAILURE); - } else { - // This can happen if any exceptions occur while setting up the connection, statement, etc. - logger.error("The CQL query {} has run into an unknown error.", selectQuery, ex); - if (fileToProcess != null) { - session.remove(fileToProcess); - } - context.yield(); - } - } catch (final ProcessException e) { - if (context.hasIncomingConnection()) { - logger.error("Unable to execute CQL select query {} for {} due to {}; routing to failure", - selectQuery, fileToProcess, e); - if (fileToProcess == null) { - fileToProcess = session.create(); - } - fileToProcess = session.penalize(fileToProcess); - session.transfer(fileToProcess, REL_FAILURE); - - } else { - logger.error("Unable to execute CQL select query {} due to {}", - selectQuery, e); - if (fileToProcess != null) { - session.remove(fileToProcess); - } - context.yield(); - } - } - session.commitAsync(); - } - - @OnUnscheduled - public void stop(ProcessContext context) { - super.stop(context); - } - - @OnShutdown - public void shutdown(ProcessContext context) { - super.stop(context); - } - - /** - * Converts a result set into an Avro record and writes it to the given stream. - * - * @param rs The result set to convert - * @param outStream The stream to which the Avro record will be written - * @param timeout The max number of timeUnits to wait for a result set fetch to complete - * @param timeUnit The unit of time (SECONDS, e.g.) associated with the timeout amount - * @return The number of rows from the result set written to the stream - * @throws IOException If the Avro record cannot be written - * @throws InterruptedException If a result set fetch is interrupted - * @throws TimeoutException If a result set fetch has taken longer than the specified timeout - * @throws ExecutionException If any error occurs during the result set fetch - */ - public static long convertToAvroStream(final ResultSet rs, long maxRowsPerFlowFile, - final OutputStream outStream, - long timeout, TimeUnit timeUnit) - throws IOException, InterruptedException, TimeoutException, ExecutionException { - - final Schema schema = createSchema(rs); - final GenericRecord rec = new GenericData.Record(schema); - final DatumWriter datumWriter = new GenericDatumWriter<>(schema); - - try (final DataFileWriter dataFileWriter = new DataFileWriter<>(datumWriter)) { - dataFileWriter.create(schema, outStream); - - ColumnDefinitions columnDefinitions = rs.getColumnDefinitions(); - long nrOfRows = 0; - long rowsAvailableWithoutFetching = rs.getAvailableWithoutFetching(); - - if (columnDefinitions != null) { - - // Grab the ones we have - if (rowsAvailableWithoutFetching == 0 - || rowsAvailableWithoutFetching < maxRowsPerFlowFile) { - // Get more - if (timeout <= 0 || timeUnit == null) { - rs.fetchMoreResults().get(); - } else { - rs.fetchMoreResults().get(timeout, timeUnit); - } - } - - Row row; - while ((maxRowsPerFlowFile == 0) || nrOfRows < maxRowsPerFlowFile) { - try { - row = rs.iterator().next(); - } catch (NoSuchElementException nsee) { - break; - } - - // iterator().next() is like iterator().one() => return null on end - // https://docs.datastax.com/en/drivers/java/2.0/com/datastax/driver/core/ResultSet.html#one-- - if (row == null) { - break; - } - - for (int i = 0; i < columnDefinitions.size(); i++) { - final DataType dataType = columnDefinitions.getType(i); - - if (row.isNull(i)) { - rec.put(i, null); - } else { - rec.put(i, getCassandraObject(row, i, dataType)); - } - } - - dataFileWriter.append(rec); - nrOfRows += 1; - } - } - return nrOfRows; - } - } - - private static String getFormattedDate(final Optional context, Date value) { - final String dateFormatPattern = context - .map(_context -> _context.getProperty(TIMESTAMP_FORMAT_PATTERN).getValue()) - .orElse(TIMESTAMP_FORMAT_PATTERN.getDefaultValue()); - final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormatPattern); - final OffsetDateTime offsetDateTime = value.toInstant().atOffset(ZoneOffset.UTC); - return dateTimeFormatter.format(offsetDateTime); - } - - public static long convertToJsonStream(final ResultSet rs, long maxRowsPerFlowFile, - final OutputStream outStream, - Charset charset, long timeout, TimeUnit timeUnit) - throws IOException, InterruptedException, TimeoutException, ExecutionException { - return convertToJsonStream(Optional.empty(), rs, maxRowsPerFlowFile, outStream, charset, timeout, timeUnit); - } - - /** - * Converts a result set into an Json object and writes it to the given stream using the specified character set. - * - * @param rs The result set to convert - * @param outStream The stream to which the JSON object will be written - * @param timeout The max number of timeUnits to wait for a result set fetch to complete - * @param timeUnit The unit of time (SECONDS, e.g.) associated with the timeout amount - * @return The number of rows from the result set written to the stream - * @throws IOException If the JSON object cannot be written - * @throws InterruptedException If a result set fetch is interrupted - * @throws TimeoutException If a result set fetch has taken longer than the specified timeout - * @throws ExecutionException If any error occurs during the result set fetch - */ - @VisibleForTesting - public static long convertToJsonStream(final Optional context, - final ResultSet rs, long maxRowsPerFlowFile, - final OutputStream outStream, - Charset charset, long timeout, TimeUnit timeUnit) - throws IOException, InterruptedException, TimeoutException, ExecutionException { - - try { - // Write the initial object brace - outStream.write("{\"results\":[".getBytes(charset)); - ColumnDefinitions columnDefinitions = rs.getColumnDefinitions(); - long nrOfRows = 0; - long rowsAvailableWithoutFetching = rs.getAvailableWithoutFetching(); - - if (columnDefinitions != null) { - - // Grab the ones we have - if (rowsAvailableWithoutFetching == 0) { - // Get more - if (timeout <= 0 || timeUnit == null) { - rs.fetchMoreResults().get(); - } else { - rs.fetchMoreResults().get(timeout, timeUnit); - } - rowsAvailableWithoutFetching = rs.getAvailableWithoutFetching(); - } - - if(maxRowsPerFlowFile == 0){ - maxRowsPerFlowFile = rowsAvailableWithoutFetching; - } - - Row row; - while(nrOfRows < maxRowsPerFlowFile){ - try { - row = rs.iterator().next(); - }catch (NoSuchElementException nsee){ - nrOfRows -= 1; - break; - } - - // iterator().next() is like iterator().one() => return null on end - // https://docs.datastax.com/en/drivers/java/2.0/com/datastax/driver/core/ResultSet.html#one-- - if(row == null){ - break; - } - - if (nrOfRows != 0) { - outStream.write(",".getBytes(charset)); - } - - outStream.write("{".getBytes(charset)); - for (int i = 0; i < columnDefinitions.size(); i++) { - final DataType dataType = columnDefinitions.getType(i); - final String colName = columnDefinitions.getName(i); - if (i != 0) { - outStream.write(",".getBytes(charset)); - } - if (row.isNull(i)) { - outStream.write(("\"" + colName + "\"" + ":null").getBytes(charset)); - } else { - Object value = getCassandraObject(row, i, dataType); - String valueString; - if (value instanceof List || value instanceof Set) { - boolean first = true; - StringBuilder sb = new StringBuilder("["); - for (Object element : ((Collection) value)) { - if (!first) { - sb.append(","); - } - sb.append(getJsonElement(context, element)); - first = false; - } - sb.append("]"); - valueString = sb.toString(); - } else if (value instanceof Map) { - boolean first = true; - StringBuilder sb = new StringBuilder("{"); - for (Object element : ((Map) value).entrySet()) { - Map.Entry entry = (Map.Entry) element; - Object mapKey = entry.getKey(); - Object mapValue = entry.getValue(); - - if (!first) { - sb.append(","); - } - sb.append(getJsonElement(context, mapKey)); - sb.append(":"); - sb.append(getJsonElement(context, mapValue)); - first = false; - } - sb.append("}"); - valueString = sb.toString(); - } else { - valueString = getJsonElement(context, value); - } - outStream.write(("\"" + colName + "\":" - + valueString + "").getBytes(charset)); - } - } - nrOfRows += 1; - outStream.write("}".getBytes(charset)); - } - } - return nrOfRows; - } finally { - outStream.write("]}".getBytes()); - } - } - - protected static String getJsonElement(Object value) { - return getJsonElement(Optional.empty(), value); - } - - protected static String getJsonElement(final Optional context, Object value) { - if (value instanceof Number) { - return value.toString(); - } else if (value instanceof Date) { - return "\"" + getFormattedDate(context, (Date) value) + "\""; - } else if (value instanceof String) { - return "\"" + StringEscapeUtils.escapeJson((String) value) + "\""; - } else { - return "\"" + value.toString() + "\""; - } - } - - /** - * Creates an Avro schema from the given result set. The metadata (column definitions, data types, etc.) is used - * to determine a schema for Avro. - * - * @param rs The result set from which an Avro schema will be created - * @return An Avro schema corresponding to the given result set's metadata - */ - public static Schema createSchema(final ResultSet rs) { - final ColumnDefinitions columnDefinitions = rs.getColumnDefinitions(); - final int nrOfColumns = (columnDefinitions == null ? 0 : columnDefinitions.size()); - String tableName = "NiFi_Cassandra_Query_Record"; - if (nrOfColumns > 0) { - String tableNameFromMeta = columnDefinitions.getTable(0); - if (!StringUtils.isBlank(tableNameFromMeta)) { - tableName = tableNameFromMeta; - } - } - - final SchemaBuilder.FieldAssembler builder = SchemaBuilder.record(tableName).namespace("any.data").fields(); - if (columnDefinitions != null) { - for (int i = 0; i < nrOfColumns; i++) { - - DataType dataType = columnDefinitions.getType(i); - if (dataType == null) { - throw new IllegalArgumentException("No data type for column[" + i + "] with name " + columnDefinitions.getName(i)); - } - - // Map types from Cassandra to Avro where possible - if (dataType.isCollection()) { - List typeArguments = dataType.getTypeArguments(); - if (typeArguments == null || typeArguments.size() == 0) { - throw new IllegalArgumentException("Column[" + i + "] " + dataType.getName() - + " is a collection but no type arguments were specified!"); - } - // Get the first type argument, to be used for lists and sets - DataType firstArg = typeArguments.get(0); - if (dataType.equals(DataType.set(firstArg)) - || dataType.equals(DataType.list(firstArg))) { - builder.name(columnDefinitions.getName(i)).type().unionOf().nullBuilder().endNull().and().array() - .items(getUnionFieldType(getPrimitiveAvroTypeFromCassandraType(firstArg))).endUnion().noDefault(); - } else { - // Must be an n-arg collection like map - DataType secondArg = typeArguments.get(1); - if (dataType.equals(DataType.map(firstArg, secondArg))) { - builder.name(columnDefinitions.getName(i)).type().unionOf().nullBuilder().endNull().and().map().values( - getUnionFieldType(getPrimitiveAvroTypeFromCassandraType(secondArg))).endUnion().noDefault(); - } - } - } else { - builder.name(columnDefinitions.getName(i)) - .type(getUnionFieldType(getPrimitiveAvroTypeFromCassandraType(dataType))).noDefault(); - } - } - } - return builder.endRecord(); - } -} \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor deleted file mode 100644 index 88aa0d889d5f..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/main/resources/META-INF/services/org.apache.nifi.processor.Processor +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -org.apache.nifi.processors.cassandra.QueryCassandra -org.apache.nifi.processors.cassandra.PutCassandraQL -org.apache.nifi.processors.cassandra.PutCassandraRecord \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/AbstractCassandraProcessorTest.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/AbstractCassandraProcessorTest.java deleted file mode 100644 index ce21961316c1..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/AbstractCassandraProcessorTest.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.Configuration; -import com.datastax.driver.core.DataType; -import com.datastax.driver.core.Metadata; -import com.datastax.driver.core.Row; -import com.google.common.collect.Sets; -import org.apache.nifi.annotation.lifecycle.OnEnabled; -import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.controller.ConfigurationContext; -import org.apache.nifi.processor.ProcessContext; -import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.reporting.InitializationException; -import org.apache.nifi.service.CassandraSessionProvider; -import org.apache.nifi.ssl.SSLContextService; -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 javax.net.ssl.SSLContext; -import java.net.InetSocketAddress; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * Unit tests for the AbstractCassandraProcessor class - */ -public class AbstractCassandraProcessorTest { - - MockAbstractCassandraProcessor processor; - private TestRunner testRunner; - - @BeforeEach - public void setUp() throws Exception { - processor = new MockAbstractCassandraProcessor(); - testRunner = TestRunners.newTestRunner(processor); - } - - @Test - public void testCustomValidate() throws Exception { - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, ""); - testRunner.assertNotValid(); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost"); - testRunner.assertNotValid(); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost:9042"); - testRunner.assertValid(); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost:9042, node2: 4399"); - testRunner.assertValid(); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, " localhost : 9042, node2: 4399"); - testRunner.assertValid(); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost:9042, node2"); - testRunner.assertNotValid(); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost:65536"); - testRunner.assertNotValid(); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost:9042"); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "user"); - testRunner.assertNotValid(); // Needs a password set if user is set - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "password"); - testRunner.assertValid(); - } - - @Test - public void testCustomValidateEL() throws Exception { - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "${host}"); - testRunner.setProperty(AbstractCassandraProcessor.KEYSPACE, "${keyspace}"); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "${user}"); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "${password}"); - testRunner.setProperty(AbstractCassandraProcessor.CHARSET, "${charset}"); - testRunner.assertValid(); - } - - @SuppressWarnings("unchecked") - @Test - public void testGetCassandraObject() throws Exception { - Row row = CassandraQueryTestUtil.createRow("user1", "Joe", "Smith", - Sets.newHashSet("jsmith@notareal.com", "joes@fakedomain.com"), Arrays.asList("New York, NY", "Santa Clara, CA"), - new HashMap() {{ - put(Calendar.getInstance().getTime(), "Set my alarm for a month from now"); - }}, true, 1.0f, 2.0); - - assertEquals("user1", AbstractCassandraProcessor.getCassandraObject(row, 0, DataType.text())); - assertEquals("Joe", AbstractCassandraProcessor.getCassandraObject(row, 1, DataType.text())); - assertEquals("Smith", AbstractCassandraProcessor.getCassandraObject(row, 2, DataType.text())); - Set emails = (Set) AbstractCassandraProcessor.getCassandraObject(row, 3, DataType.set(DataType.text())); - assertNotNull(emails); - assertEquals(2, emails.size()); - List topPlaces = (List) AbstractCassandraProcessor.getCassandraObject(row, 4, DataType.list(DataType.text())); - assertNotNull(topPlaces); - Map todoMap = (Map) AbstractCassandraProcessor.getCassandraObject( - row, 5, DataType.map(DataType.timestamp(), DataType.text())); - assertNotNull(todoMap); - assertEquals(1, todoMap.values().size()); - Boolean registered = (Boolean) AbstractCassandraProcessor.getCassandraObject(row, 6, DataType.cboolean()); - assertNotNull(registered); - assertTrue(registered); - } - - @Test - public void testGetSchemaForType() throws Exception { - assertEquals(AbstractCassandraProcessor.getSchemaForType("string").getType().getName(), "string"); - assertEquals(AbstractCassandraProcessor.getSchemaForType("boolean").getType().getName(), "boolean"); - assertEquals(AbstractCassandraProcessor.getSchemaForType("int").getType().getName(), "int"); - assertEquals(AbstractCassandraProcessor.getSchemaForType("long").getType().getName(), "long"); - assertEquals(AbstractCassandraProcessor.getSchemaForType("float").getType().getName(), "float"); - assertEquals(AbstractCassandraProcessor.getSchemaForType("double").getType().getName(), "double"); - assertEquals(AbstractCassandraProcessor.getSchemaForType("bytes").getType().getName(), "bytes"); - } - - @Test - public void testGetSchemaForTypeBadType() throws Exception { - assertThrows(IllegalArgumentException.class, () -> AbstractCassandraProcessor.getSchemaForType("nothing")); - } - - @Test - public void testGetPrimitiveAvroTypeFromCassandraType() throws Exception { - assertEquals("string", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.ascii())); - assertEquals("string", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.text())); - assertEquals("string", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.varchar())); - assertEquals("string", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.timestamp())); - assertEquals("string", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.timeuuid())); - assertEquals("string", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.uuid())); - assertEquals("string", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.inet())); - assertEquals("string", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.varint())); - - assertEquals("boolean", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.cboolean())); - assertEquals("int", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.cint())); - - assertEquals("long", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.bigint())); - assertEquals("long", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.counter())); - - assertEquals("float", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.cfloat())); - assertEquals("double", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.cdouble())); - - assertEquals("bytes", AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(DataType.blob())); - } - - @Test - public void testGetPrimitiveAvroTypeFromCassandraTypeBadType() throws Exception { - DataType mockDataType = mock(DataType.class); - assertThrows(IllegalArgumentException.class, () -> AbstractCassandraProcessor.getPrimitiveAvroTypeFromCassandraType(mockDataType)); - } - - @Test - public void testGetPrimitiveDataTypeFromString() { - assertEquals(DataType.ascii(), AbstractCassandraProcessor.getPrimitiveDataTypeFromString("ascii")); - } - - @Test - public void testGetContactPoints() throws Exception { - List contactPoints = processor.getContactPoints(""); - assertNotNull(contactPoints); - assertEquals(1, contactPoints.size()); - assertEquals("localhost", contactPoints.get(0).getHostName()); - assertEquals(AbstractCassandraProcessor.DEFAULT_CASSANDRA_PORT, contactPoints.get(0).getPort()); - - contactPoints = processor.getContactPoints("192.168.99.100:9042"); - assertNotNull(contactPoints); - assertEquals(1, contactPoints.size()); - assertEquals("192.168.99.100", contactPoints.get(0).getAddress().getHostAddress()); - assertEquals(9042, contactPoints.get(0).getPort()); - - contactPoints = processor.getContactPoints("192.168.99.100:9042, mydomain.com : 4000"); - assertNotNull(contactPoints); - assertEquals(2, contactPoints.size()); - assertEquals("192.168.99.100", contactPoints.get(0).getAddress().getHostAddress()); - assertEquals(9042, contactPoints.get(0).getPort()); - assertEquals("mydomain.com", contactPoints.get(1).getHostName()); - assertEquals(4000, contactPoints.get(1).getPort()); - } - - @Test - public void testConnectToCassandra() throws Exception { - // Follow the non-null path - Cluster cluster = mock(Cluster.class); - processor.setCluster(cluster); - testRunner.setProperty(AbstractCassandraProcessor.CONSISTENCY_LEVEL, "ONE"); - processor.connectToCassandra(testRunner.getProcessContext()); - processor.stop(testRunner.getProcessContext()); - assertNull(processor.getCluster()); - - // Now do a connect where a cluster is "built" - processor.connectToCassandra(testRunner.getProcessContext()); - assertEquals("cluster1", processor.getCluster().getMetadata().getClusterName()); - } - - @Test - public void testConnectToCassandraWithSSL() throws Exception { - SSLContextService sslService = mock(SSLContextService.class); - when(sslService.getIdentifier()).thenReturn("ssl-context"); - testRunner.addControllerService("ssl-context", sslService); - testRunner.enableControllerService(sslService); - testRunner.setProperty(AbstractCassandraProcessor.PROP_SSL_CONTEXT_SERVICE, "ssl-context"); - testRunner.setProperty(AbstractCassandraProcessor.CONSISTENCY_LEVEL, "ONE"); - testRunner.assertValid(sslService); - processor.connectToCassandra(testRunner.getProcessContext()); - assertNotNull(processor.getCluster()); - processor.setCluster(null); - // Try with a ClientAuth value - testRunner.setProperty(AbstractCassandraProcessor.CLIENT_AUTH, "WANT"); - processor.connectToCassandra(testRunner.getProcessContext()); - assertNotNull(processor.getCluster()); - } - - @Test - public void testConnectToCassandraUsernamePassword() throws Exception { - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "user"); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "password"); - testRunner.setProperty(AbstractCassandraProcessor.CONSISTENCY_LEVEL, "ONE"); - // Now do a connect where a cluster is "built" - processor.connectToCassandra(testRunner.getProcessContext()); - assertNotNull(processor.getCluster()); - } - - @Test - public void testCustomValidateCassandraConnectionConfiguration() throws InitializationException { - MockCassandraSessionProvider sessionProviderService = new MockCassandraSessionProvider(); - - testRunner.addControllerService("cassandra-connection-provider", sessionProviderService); - testRunner.setProperty(sessionProviderService, CassandraSessionProvider.CONTACT_POINTS, "localhost:9042"); - testRunner.setProperty(sessionProviderService, CassandraSessionProvider.KEYSPACE, "somekyespace"); - - testRunner.setProperty(AbstractCassandraProcessor.CONNECTION_PROVIDER_SERVICE, "cassandra-connection-provider"); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost:9042"); - testRunner.setProperty(AbstractCassandraProcessor.KEYSPACE, "some-keyspace"); - testRunner.setProperty(AbstractCassandraProcessor.CONSISTENCY_LEVEL, "ONE"); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "user"); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "password"); - testRunner.enableControllerService(sessionProviderService); - - testRunner.assertNotValid(); - - testRunner.removeProperty(AbstractCassandraProcessor.CONTACT_POINTS); - testRunner.removeProperty(AbstractCassandraProcessor.KEYSPACE); - testRunner.removeProperty(AbstractCassandraProcessor.CONSISTENCY_LEVEL); - testRunner.removeProperty(AbstractCassandraProcessor.USERNAME); - testRunner.removeProperty(AbstractCassandraProcessor.PASSWORD); - - testRunner.assertValid(); - } - - /** - * Provides a stubbed processor instance for testing - */ - public static class MockAbstractCassandraProcessor extends AbstractCassandraProcessor { - - @Override - protected List getSupportedPropertyDescriptors() { - return Arrays.asList(CONNECTION_PROVIDER_SERVICE, CONTACT_POINTS, KEYSPACE, USERNAME, PASSWORD, CONSISTENCY_LEVEL, CHARSET); - } - - @Override - public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException { - - } - - @Override - protected Cluster createCluster(List contactPoints, SSLContext sslContext, - String username, String password, String compressionType) { - Cluster mockCluster = mock(Cluster.class); - Metadata mockMetadata = mock(Metadata.class); - when(mockMetadata.getClusterName()).thenReturn("cluster1"); - when(mockCluster.getMetadata()).thenReturn(mockMetadata); - Configuration config = Configuration.builder().build(); - when(mockCluster.getConfiguration()).thenReturn(config); - return mockCluster; - } - - public Cluster getCluster() { - return cluster.get(); - } - - public void setCluster(Cluster newCluster) { - this.cluster.set(newCluster); - } - } - - /** - * Mock CassandraSessionProvider implementation for testing purpose - */ - private class MockCassandraSessionProvider extends CassandraSessionProvider { - - @OnEnabled - public void onEnabled(final ConfigurationContext context) { - - } - - } -} \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/CassandraQueryTestUtil.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/CassandraQueryTestUtil.java deleted file mode 100644 index 6bdb400159b2..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/CassandraQueryTestUtil.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.ColumnDefinitions; -import com.datastax.driver.core.DataType; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.Row; -import com.google.common.collect.Sets; -import com.google.common.reflect.TypeToken; -import com.google.common.util.concurrent.ListenableFuture; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Set; -import java.util.Map; -import java.util.List; -import java.util.HashMap; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * Utility methods for Cassandra processors' unit tests - */ -public class CassandraQueryTestUtil { - - static final Date TEST_DATE; - static { - Calendar c = GregorianCalendar.getInstance(TimeZone.getTimeZone("PST")); - c.set(2020, Calendar.JANUARY, 1, 10, 10, 10); - c.set(Calendar.MILLISECOND, 10); - TEST_DATE = c.getTime(); - } - - public static ResultSet createMockResultSet(boolean falseThenTrue) throws Exception { - ResultSet resultSet = mock(ResultSet.class); - ColumnDefinitions columnDefinitions = mock(ColumnDefinitions.class); - when(columnDefinitions.size()).thenReturn(9); - when(columnDefinitions.getName(anyInt())).thenAnswer(new Answer() { - - List colNames = Arrays.asList( - "user_id", "first_name", "last_name", "emails", "top_places", "todo", "registered", "scale", "metric"); - - @Override - public String answer(InvocationOnMock invocationOnMock) { - return colNames.get((Integer) invocationOnMock.getArguments()[0]); - - } - }); - - when(columnDefinitions.getTable(0)).thenReturn("users"); - - when(columnDefinitions.getType(anyInt())).thenAnswer(new Answer() { - - List dataTypes = Arrays.asList( - DataType.text(), DataType.text(), DataType.text(), DataType.set(DataType.text()), - DataType.list(DataType.text()), DataType.map(DataType.timestamp(), DataType.text()), DataType.cboolean(), - DataType.cfloat(), DataType.cdouble() - ); - - @Override - public DataType answer(InvocationOnMock invocationOnMock) throws Throwable { - return dataTypes.get((Integer) invocationOnMock.getArguments()[0]); - - } - }); - - final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssZ"); - final Date aMonthPrior = Date.from(OffsetDateTime.parse("2016-01-03 05:00:00+0000", dateTimeFormatter).toInstant()); - final Date testDate = Date.from(OffsetDateTime.parse("2016-02-03 05:00:00+0000", dateTimeFormatter).toInstant()); - - List rows = Arrays.asList( - createRow("user1", "Joe", "Smith", Sets.newHashSet("jsmith@notareal.com"), - Arrays.asList("New York, NY", "Santa Clara, CA"), - new HashMap() {{ - put(aMonthPrior, "Set my alarm \"for\" a month from now"); - }}, false, 1.0f, 2.0), - createRow("user2", "Mary", "Jones", Sets.newHashSet("mjones@notareal.com"), - Collections.singletonList("Orlando, FL"), - new HashMap() {{ - put(testDate, "Get milk and bread"); - }}, true, 3.0f, 4.0) - ); - - ListenableFuture future = mock(ListenableFuture.class); - when(future.get()).thenReturn(rows); - when(resultSet.fetchMoreResults()).thenReturn(future); - - when(resultSet.iterator()).thenReturn(rows.iterator()); - when(resultSet.all()).thenReturn(rows); - when(resultSet.getAvailableWithoutFetching()).thenReturn(rows.size()); - when(resultSet.isFullyFetched()).thenReturn(false).thenReturn(true); - if(falseThenTrue) { - when(resultSet.isExhausted()).thenReturn(false, true); - }else{ - when(resultSet.isExhausted()).thenReturn(true); - } - when(resultSet.getColumnDefinitions()).thenReturn(columnDefinitions); - - return resultSet; - } - - public static ResultSet createMockResultSet() throws Exception { - return createMockResultSet(true); - } - - public static ResultSet createMockResultSetOneColumn() throws Exception { - ResultSet resultSet = mock(ResultSet.class); - ColumnDefinitions columnDefinitions = mock(ColumnDefinitions.class); - when(columnDefinitions.size()).thenReturn(1); - when(columnDefinitions.getName(anyInt())).thenAnswer(new Answer() { - List colNames = Arrays.asList("user_id"); - @Override - public String answer(InvocationOnMock invocationOnMock) throws Throwable { - return colNames.get((Integer) invocationOnMock.getArguments()[0]); - - } - }); - - when(columnDefinitions.getTable(0)).thenReturn("users"); - - when(columnDefinitions.getType(anyInt())).thenAnswer(new Answer() { - List dataTypes = Arrays.asList(DataType.text()); - @Override - public DataType answer(InvocationOnMock invocationOnMock) throws Throwable { - return dataTypes.get((Integer) invocationOnMock.getArguments()[0]); - - } - }); - - List rows = Arrays.asList( - createRow("user1"), - createRow("user2") - ); - - ListenableFuture future = mock(ListenableFuture.class); - when(future.get()).thenReturn(rows); - when(resultSet.fetchMoreResults()).thenReturn(future); - - when(resultSet.iterator()).thenReturn(rows.iterator()); - when(resultSet.all()).thenReturn(rows); - when(resultSet.getAvailableWithoutFetching()).thenReturn(rows.size()); - when(resultSet.isFullyFetched()).thenReturn(false).thenReturn(true); - when(resultSet.isExhausted()).thenReturn(false).thenReturn(true); - when(resultSet.getColumnDefinitions()).thenReturn(columnDefinitions); - return resultSet; - } - - public static ResultSet createMockDateResultSet() throws Exception { - ResultSet resultSet = mock(ResultSet.class); - ColumnDefinitions columnDefinitions = mock(ColumnDefinitions.class); - - when(columnDefinitions.size()).thenReturn(1); - when(columnDefinitions.getName(anyInt())).thenReturn("date"); - when(columnDefinitions.getTable(0)).thenReturn("users"); - when(columnDefinitions.getType(anyInt())).thenReturn(DataType.timestamp()); - - Row row = mock(Row.class); - when(row.getTimestamp(0)).thenReturn(TEST_DATE); - List rows = Collections.singletonList(row); - - when(resultSet.iterator()).thenReturn(rows.iterator()); - when(resultSet.all()).thenReturn(rows); - when(resultSet.getAvailableWithoutFetching()).thenReturn(rows.size()); - when(resultSet.isFullyFetched()).thenReturn(false).thenReturn(true); - when(resultSet.getColumnDefinitions()).thenReturn(columnDefinitions); - return resultSet; - } - - public static Row createRow(String user_id, String first_name, String last_name, Set emails, - List top_places, Map todo, boolean registered, - float scale, double metric) { - Row row = mock(Row.class); - when(row.getString(0)).thenReturn(user_id); - when(row.getString(1)).thenReturn(first_name); - when(row.getString(2)).thenReturn(last_name); - when(row.getSet(eq(3), any(TypeToken.class))).thenReturn(emails); - when(row.getList(eq(4), any(TypeToken.class))).thenReturn(top_places); - when(row.getMap(eq(5), any(TypeToken.class), any(TypeToken.class))).thenReturn(todo); - when(row.getBool(6)).thenReturn(registered); - when(row.getFloat(7)).thenReturn(scale); - when(row.getDouble(8)).thenReturn(metric); - - return row; - } - - public static Row createRow(String user_id) { - Row row = mock(Row.class); - when(row.getString(0)).thenReturn(user_id); - return row; - } -} - diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraQLTest.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraQLTest.java deleted file mode 100644 index 8b3534855783..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraQLTest.java +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.BoundStatement; -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.Configuration; -import com.datastax.driver.core.ConsistencyLevel; -import com.datastax.driver.core.Metadata; -import com.datastax.driver.core.PreparedStatement; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.ResultSetFuture; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.SniEndPoint; -import com.datastax.driver.core.Statement; -import com.datastax.driver.core.exceptions.InvalidQueryException; -import com.datastax.driver.core.exceptions.NoHostAvailableException; -import com.datastax.driver.core.exceptions.UnavailableException; -import org.apache.nifi.processor.exception.ProcessException; -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 javax.net.ssl.SSLContext; -import java.net.InetSocketAddress; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * Unit tests for the PutCassandraQL processor - */ -public class PutCassandraQLTest { - - private TestRunner testRunner; - private MockPutCassandraQL processor; - - @BeforeEach - public void setUp() { - processor = new MockPutCassandraQL(); - testRunner = TestRunners.newTestRunner(processor); - } - - @Test - public void testProcessorConfigValidity() { - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost:9042"); - testRunner.assertValid(); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "password"); - testRunner.assertNotValid(); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "username"); - testRunner.setProperty(AbstractCassandraProcessor.CONSISTENCY_LEVEL, "ONE"); - testRunner.assertValid(); - } - - @Test - public void testProcessorELConfigValidity() { - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "${hosts}"); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "${pass}"); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "${user}"); - testRunner.setProperty(AbstractCassandraProcessor.CHARSET, "${charset}"); - testRunner.setProperty(PutCassandraQL.STATEMENT_TIMEOUT, "${timeout}"); - - testRunner.assertValid(); - } - - @Test - public void testProcessorHappyPath() { - setUpStandardTestConfig(); - - testRunner.enqueue("INSERT INTO users (user_id, first_name, last_name, properties, bits, scaleset, largenum, scale, byteobject, ts) VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?, ?", - new HashMap() { - { - put("cql.args.1.type", "int"); - put("cql.args.1.value", "1"); - put("cql.args.2.type", "text"); - put("cql.args.2.value", "Joe"); - put("cql.args.3.type", "text"); - // No value for arg 3 to test setNull - put("cql.args.4.type", "map"); - put("cql.args.4.value", "{'a':'Hello', 'b':'World'}"); - put("cql.args.5.type", "list"); - put("cql.args.5.value", "[true,false,true]"); - put("cql.args.6.type", "set"); - put("cql.args.6.value", "{1.0, 2.0}"); - put("cql.args.7.type", "bigint"); - put("cql.args.7.value", "20000000"); - put("cql.args.8.type", "float"); - put("cql.args.8.value", "1.0"); - put("cql.args.9.type", "blob"); - put("cql.args.9.value", "0xDEADBEEF"); - put("cql.args.10.type", "timestamp"); - put("cql.args.10.value", "2016-07-01T15:21:05Z"); - - } - }); - - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(PutCassandraQL.REL_SUCCESS, 1); - testRunner.clearTransferState(); - } - - @Test - public void testProcessorHappyPathELConfig() { - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "${hosts}"); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "${pass}"); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "${user}"); - testRunner.setProperty(AbstractCassandraProcessor.CONSISTENCY_LEVEL, "ONE"); - testRunner.setProperty(AbstractCassandraProcessor.CHARSET, "${charset}"); - testRunner.setProperty(PutCassandraQL.STATEMENT_TIMEOUT, "${timeout}"); - testRunner.assertValid(); - - testRunner.setEnvironmentVariableValue("hosts", "localhost:9042"); - testRunner.setEnvironmentVariableValue("user", "username"); - testRunner.setEnvironmentVariableValue("pass", "password"); - testRunner.setEnvironmentVariableValue("charset", "UTF-8"); - testRunner.setEnvironmentVariableValue("timeout", "30 sec"); - - testRunner.enqueue("INSERT INTO users (user_id, first_name, last_name, properties, bits, scaleset, largenum, scale, byteobject, ts) VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?, ?", - new HashMap() { - { - put("cql.args.1.type", "int"); - put("cql.args.1.value", "1"); - put("cql.args.2.type", "text"); - put("cql.args.2.value", "Joe"); - put("cql.args.3.type", "text"); - // No value for arg 3 to test setNull - put("cql.args.4.type", "map"); - put("cql.args.4.value", "{'a':'Hello', 'b':'World'}"); - put("cql.args.5.type", "list"); - put("cql.args.5.value", "[true,false,true]"); - put("cql.args.6.type", "set"); - put("cql.args.6.value", "{1.0, 2.0}"); - put("cql.args.7.type", "bigint"); - put("cql.args.7.value", "20000000"); - put("cql.args.8.type", "float"); - put("cql.args.8.value", "1.0"); - put("cql.args.9.type", "blob"); - put("cql.args.9.value", "0xDEADBEEF"); - put("cql.args.10.type", "timestamp"); - put("cql.args.10.value", "2016-07-01T15:21:05Z"); - - } - }); - - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(PutCassandraQL.REL_SUCCESS, 1); - testRunner.clearTransferState(); - } - - @Test - public void testMultipleQuery() { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraQL.STATEMENT_CACHE_SIZE, "1"); - - HashMap testData = new HashMap<>(); - testData.put("cql.args.1.type", "int"); - testData.put("cql.args.1.value", "1"); - testData.put("cql.args.2.type", "text"); - testData.put("cql.args.2.value", "Joe"); - testData.put("cql.args.3.type", "text"); - // No value for arg 3 to test setNull - testData.put("cql.args.4.type", "map"); - testData.put("cql.args.4.value", "{'a':'Hello', 'b':'World'}"); - testData.put("cql.args.5.type", "list"); - testData.put("cql.args.5.value", "[true,false,true]"); - testData.put("cql.args.6.type", "set"); - testData.put("cql.args.6.value", "{1.0, 2.0}"); - testData.put("cql.args.7.type", "bigint"); - testData.put("cql.args.7.value", "20000000"); - testData.put("cql.args.8.type", "float"); - testData.put("cql.args.8.value", "1.0"); - testData.put("cql.args.9.type", "blob"); - testData.put("cql.args.9.value", "0xDEADBEEF"); - testData.put("cql.args.10.type", "timestamp"); - testData.put("cql.args.10.value", "2016-07-01T15:21:05Z"); - - testRunner.enqueue("INSERT INTO users (user_id, first_name, last_name, properties, bits, scaleset, largenum, scale, byteobject, ts) VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?, ?", - testData); - - testRunner.enqueue("INSERT INTO newusers (user_id, first_name, last_name, properties, bits, scaleset, largenum, scale, byteobject, ts) VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?, ?", - testData); - - // Change it up a bit, the same statement is executed with different data - testData.put("cql.args.1.value", "2"); - testRunner.enqueue("INSERT INTO users (user_id, first_name, last_name, properties, bits, scaleset, largenum, scale, byteobject, ts) VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?, ?", - testData); - - testRunner.enqueue("INSERT INTO users (user_id) VALUES ('user_id data');"); - - testRunner.run(4, true, true); - testRunner.assertAllFlowFilesTransferred(PutCassandraQL.REL_SUCCESS, 4); - } - - @Test - public void testProcessorBadTimestamp() { - setUpStandardTestConfig(); - processor.setExceptionToThrow( - new InvalidQueryException(new SniEndPoint(new InetSocketAddress("localhost", 9042), ""), "invalid timestamp")); - testRunner.enqueue("INSERT INTO users (user_id, first_name, last_name, properties, bits, scaleset, largenum, scale, byteobject, ts) VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?, ?", - new HashMap() { - { - put("cql.args.1.type", "int"); - put("cql.args.1.value", "1"); - put("cql.args.2.type", "text"); - put("cql.args.2.value", "Joe"); - put("cql.args.3.type", "text"); - // No value for arg 3 to test setNull - put("cql.args.4.type", "map"); - put("cql.args.4.value", "{'a':'Hello', 'b':'World'}"); - put("cql.args.5.type", "list"); - put("cql.args.5.value", "[true,false,true]"); - put("cql.args.6.type", "set"); - put("cql.args.6.value", "{1.0, 2.0}"); - put("cql.args.7.type", "bigint"); - put("cql.args.7.value", "20000000"); - put("cql.args.8.type", "float"); - put("cql.args.8.value", "1.0"); - put("cql.args.9.type", "blob"); - put("cql.args.9.value", "0xDEADBEEF"); - put("cql.args.10.type", "timestamp"); - put("cql.args.10.value", "not a timestamp"); - - } - }); - - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(PutCassandraQL.REL_FAILURE, 1); - testRunner.clearTransferState(); - } - - @Test - public void testProcessorUuid() { - setUpStandardTestConfig(); - - testRunner.enqueue("INSERT INTO users (user_id, first_name, last_name, properties, bits, scaleset, largenum, scale, byteobject, ts) VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?, ?", - new HashMap() { - { - put("cql.args.1.type", "int"); - put("cql.args.1.value", "1"); - put("cql.args.2.type", "text"); - put("cql.args.2.value", "Joe"); - put("cql.args.3.type", "text"); - // No value for arg 3 to test setNull - put("cql.args.4.type", "map"); - put("cql.args.4.value", "{'a':'Hello', 'b':'World'}"); - put("cql.args.5.type", "list"); - put("cql.args.5.value", "[true,false,true]"); - put("cql.args.6.type", "set"); - put("cql.args.6.value", "{1.0, 2.0}"); - put("cql.args.7.type", "bigint"); - put("cql.args.7.value", "20000000"); - put("cql.args.8.type", "float"); - put("cql.args.8.value", "1.0"); - put("cql.args.9.type", "blob"); - put("cql.args.9.value", "0xDEADBEEF"); - put("cql.args.10.type", "uuid"); - put("cql.args.10.value", "5442b1f6-4c16-11ea-87f5-45a32dbc5199"); - - } - }); - - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(PutCassandraQL.REL_SUCCESS, 1); - testRunner.clearTransferState(); - } - - @Test - public void testProcessorBadUuid() { - setUpStandardTestConfig(); - - testRunner.enqueue("INSERT INTO users (user_id, first_name, last_name, properties, bits, scaleset, largenum, scale, byteobject, ts) VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?, ?", - new HashMap() { - { - put("cql.args.1.type", "int"); - put("cql.args.1.value", "1"); - put("cql.args.2.type", "text"); - put("cql.args.2.value", "Joe"); - put("cql.args.3.type", "text"); - // No value for arg 3 to test setNull - put("cql.args.4.type", "map"); - put("cql.args.4.value", "{'a':'Hello', 'b':'World'}"); - put("cql.args.5.type", "list"); - put("cql.args.5.value", "[true,false,true]"); - put("cql.args.6.type", "set"); - put("cql.args.6.value", "{1.0, 2.0}"); - put("cql.args.7.type", "bigint"); - put("cql.args.7.value", "20000000"); - put("cql.args.8.type", "float"); - put("cql.args.8.value", "1.0"); - put("cql.args.9.type", "blob"); - put("cql.args.9.value", "0xDEADBEEF"); - put("cql.args.10.type", "uuid"); - put("cql.args.10.value", "bad-uuid"); - - } - }); - - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(PutCassandraQL.REL_FAILURE, 1); - testRunner.clearTransferState(); - } - - @Test - public void testProcessorInvalidQueryException() { - setUpStandardTestConfig(); - - // Test exceptions - processor.setExceptionToThrow( - new InvalidQueryException(new SniEndPoint(new InetSocketAddress("localhost", 9042), ""), "invalid query")); - testRunner.enqueue("UPDATE users SET cities = [ 'New York', 'Los Angeles' ] WHERE user_id = 'coast2coast';"); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(PutCassandraQL.REL_FAILURE, 1); - testRunner.clearTransferState(); - } - - @Test - public void testProcessorUnavailableException() { - setUpStandardTestConfig(); - - processor.setExceptionToThrow( - new UnavailableException(new SniEndPoint(new InetSocketAddress("localhost", 9042), ""), ConsistencyLevel.ALL, 5, 2)); - testRunner.enqueue("UPDATE users SET cities = [ 'New York', 'Los Angeles' ] WHERE user_id = 'coast2coast';"); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(PutCassandraQL.REL_RETRY, 1); - } - - @Test - public void testProcessorNoHostAvailableException() { - setUpStandardTestConfig(); - - processor.setExceptionToThrow(new NoHostAvailableException(new HashMap<>())); - testRunner.enqueue("UPDATE users SET cities = [ 'New York', 'Los Angeles' ] WHERE user_id = 'coast2coast';"); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(PutCassandraQL.REL_RETRY, 1); - } - - @Test - public void testProcessorProcessException() { - setUpStandardTestConfig(); - - processor.setExceptionToThrow(new ProcessException()); - testRunner.enqueue("UPDATE users SET cities = [ 'New York', 'Los Angeles' ] WHERE user_id = 'coast2coast';"); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(PutCassandraQL.REL_FAILURE, 1); - } - - private void setUpStandardTestConfig() { - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost:9042"); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "password"); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "username"); - testRunner.setProperty(AbstractCassandraProcessor.CONSISTENCY_LEVEL, "ONE"); - testRunner.assertValid(); - } - - /** - * Provides a stubbed processor instance for testing - */ - private static class MockPutCassandraQL extends PutCassandraQL { - - private Exception exceptionToThrow = null; - private Session mockSession = mock(Session.class); - - @Override - protected Cluster createCluster(List contactPoints, SSLContext sslContext, - String username, String password, String compressionType) { - Cluster mockCluster = mock(Cluster.class); - try { - Metadata mockMetadata = mock(Metadata.class); - when(mockMetadata.getClusterName()).thenReturn("cluster1"); - when(mockCluster.getMetadata()).thenReturn(mockMetadata); - when(mockCluster.connect()).thenReturn(mockSession); - when(mockCluster.connect(anyString())).thenReturn(mockSession); - Configuration config = Configuration.builder().build(); - when(mockCluster.getConfiguration()).thenReturn(config); - ResultSetFuture future = mock(ResultSetFuture.class); - ResultSet rs = CassandraQueryTestUtil.createMockResultSet(); - PreparedStatement ps = mock(PreparedStatement.class); - when(mockSession.prepare(anyString())).thenReturn(ps); - BoundStatement bs = mock(BoundStatement.class); - when(ps.bind()).thenReturn(bs); - when(future.getUninterruptibly()).thenReturn(rs); - try { - doReturn(rs).when(future).getUninterruptibly(anyLong(), any(TimeUnit.class)); - } catch (TimeoutException te) { - throw new IllegalArgumentException("Mocked cluster doesn't time out"); - } - if (exceptionToThrow != null) { - doThrow(exceptionToThrow).when(mockSession).executeAsync(anyString()); - doThrow(exceptionToThrow).when(mockSession).executeAsync(any(Statement.class)); - - } else { - when(mockSession.executeAsync(anyString())).thenReturn(future); - when(mockSession.executeAsync(any(Statement.class))).thenReturn(future); - } - when(mockSession.getCluster()).thenReturn(mockCluster); - } catch (Exception e) { - fail(e.getMessage()); - } - return mockCluster; - } - - void setExceptionToThrow(Exception e) { - exceptionToThrow = e; - doThrow(exceptionToThrow).when(mockSession).executeAsync(anyString()); - doThrow(exceptionToThrow).when(mockSession).executeAsync(any(Statement.class)); - } - - } -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordIT.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordIT.java deleted file mode 100644 index f0943c2afbcd..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordIT.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.querybuilder.QueryBuilder; -import com.datastax.driver.core.querybuilder.Select; -import com.datastax.driver.core.querybuilder.Truncate; -import org.apache.nifi.reporting.InitializationException; -import org.apache.nifi.serialization.record.MockRecordParser; -import org.apache.nifi.serialization.record.RecordFieldType; -import org.apache.nifi.util.TestRunner; -import org.apache.nifi.util.TestRunners; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.testcontainers.containers.CassandraContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; - -import java.net.InetSocketAddress; -import java.util.List; -import java.util.stream.Collectors; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@Testcontainers -public class PutCassandraRecordIT { - @Container - private static final CassandraContainer CASSANDRA_CONTAINER = new CassandraContainer(DockerImageName.parse("cassandra:4.1")); - - private static TestRunner testRunner; - private static MockRecordParser recordReader; - - private static Cluster cluster; - private static Session session; - - private static final String KEYSPACE = "sample_keyspace"; - private static final String TABLE = "sample_table"; - - @BeforeAll - public static void setup() throws InitializationException { - recordReader = new MockRecordParser(); - testRunner = TestRunners.newTestRunner(PutCassandraRecord.class); - - InetSocketAddress contactPoint = CASSANDRA_CONTAINER.getContactPoint(); - testRunner.setProperty(PutCassandraRecord.RECORD_READER_FACTORY, "reader"); - testRunner.setProperty(PutCassandraRecord.CONTACT_POINTS, contactPoint.getHostString() + ":" + contactPoint.getPort()); - testRunner.setProperty(PutCassandraRecord.KEYSPACE, KEYSPACE); - testRunner.setProperty(PutCassandraRecord.TABLE, TABLE); - testRunner.setProperty(PutCassandraRecord.CONSISTENCY_LEVEL, "SERIAL"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, "LOGGED"); - testRunner.addControllerService("reader", recordReader); - testRunner.enableControllerService(recordReader); - - cluster = Cluster.builder().addContactPoint(contactPoint.getHostName()) - .withPort(contactPoint.getPort()).build(); - session = cluster.connect(); - - String createKeyspace = "CREATE KEYSPACE IF NOT EXISTS " + KEYSPACE + " WITH replication = {'class':'SimpleStrategy','replication_factor':1};"; - String createTable = "CREATE TABLE IF NOT EXISTS " + KEYSPACE + "." + TABLE + "(id int PRIMARY KEY, name text, age int);"; - - session.execute(createKeyspace); - session.execute(createTable); - } - - @Test - public void testSimplePut() { - recordReader.addSchemaField("id", RecordFieldType.INT); - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - - recordReader.addRecord(1, "Ram", 42); - recordReader.addRecord(2, "Jeane", 47); - recordReader.addRecord(3, "Ilamaran", 27); - recordReader.addRecord(4, "Jian", 14); - recordReader.addRecord(5, "Sakura", 24); - - testRunner.enqueue(""); - testRunner.run(); - - testRunner.assertAllFlowFilesTransferred(PutCassandraRecord.REL_SUCCESS, 1); - assertEquals(5, getRecordsCount()); - } - - private int getRecordsCount() { - Select selectQuery = QueryBuilder.select().all().from(KEYSPACE, TABLE); - ResultSet result = session.execute(selectQuery); - - List resultsList = result.all() - .stream() - .map(r -> r.getInt(0)) - .collect(Collectors.toList()); - - dropRecords(); - return resultsList.size(); - } - - private void dropRecords() { - Truncate query = QueryBuilder.truncate(KEYSPACE, TABLE); - session.execute(query); - } - - @AfterAll - public static void shutdown() { - String dropKeyspace = "DROP KEYSPACE " + KEYSPACE; - String dropTable = "DROP TABLE IF EXISTS " + KEYSPACE + "." + TABLE; - - session.execute(dropTable); - session.execute(dropKeyspace); - - session.close(); - cluster.close(); - } -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordInsertTest.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordInsertTest.java deleted file mode 100644 index 2cb09db79057..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordInsertTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.querybuilder.Insert; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import org.apache.nifi.serialization.record.RecordFieldType; -import org.apache.nifi.serialization.record.RecordSchema; -import org.apache.nifi.util.Tuple; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; - -public class PutCassandraRecordInsertTest { - private PutCassandraRecord testSubject; - - @Mock - private RecordSchema schema; - private AutoCloseable mockCloseable; - - @BeforeEach - public void setUp() { - mockCloseable = MockitoAnnotations.openMocks(this); - testSubject = new PutCassandraRecord(); - } - - @AfterEach - public void closeMock() throws Exception { - if (mockCloseable != null) { - mockCloseable.close(); - } - } - - @Test - public void testGenerateInsert() { - testGenerateInsert( - "keyspace.table", - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("integerField", 15), - new Tuple<>("longField", 67L), - new Tuple<>("stringField", "abcd") - ), - Arrays.asList( - new Tuple<>("keyField", RecordFieldType.INT.getDataType()), - new Tuple<>("integerField", RecordFieldType.INT.getDataType()), - new Tuple<>("longField", RecordFieldType.LONG.getDataType()), - new Tuple<>("stringField", RecordFieldType.STRING.getDataType()) - ), - "INSERT INTO keyspace.table (keyField,integerField,longField,stringField) VALUES (1,15,67,'abcd');" - ); - } - - @Test - public void testGenerateInsertStringArray() { - testGenerateInsert( - "keyspace.table", - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("integerField", 15), - new Tuple<>("arrayField", new Object[]{"test1", "test2"}) - ), - Arrays.asList( - new Tuple<>("keyField", RecordFieldType.INT.getDataType()), - new Tuple<>("integerField", RecordFieldType.INT.getDataType()), - new Tuple<>("arrayField", RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.STRING.getDataType())) - ), - "INSERT INTO keyspace.table (keyField,integerField,arrayField) VALUES (1,15,['test1','test2']);" - ); - } - - private void testGenerateInsert(String table, List> records, List> recordSchema, String expected) { - Map recordContentMap = records.stream() - .collect(Collectors.toMap(Tuple::getKey, Tuple::getValue)); - - Map recordSchemaMap = recordSchema.stream() - .collect(Collectors.toMap(Tuple::getKey, Tuple::getValue)); - - List fieldNames = records.stream().map(Tuple::getKey).collect(Collectors.toList()); - - when(schema.getFieldNames()).thenReturn(fieldNames); - when(schema.getDataType(anyString())).thenAnswer(i -> Optional.of(recordSchemaMap.get(i.getArgument(0)))); - - Insert actual = (Insert)testSubject.generateInsert(table, schema, recordContentMap); - actual.setForceNoValues(true); - - // Codecs are normally registered in the onScheduled method - testSubject.registerAdditionalCodecs(); - - assertEquals(expected, actual.getQueryString()); - } -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordTest.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordTest.java deleted file mode 100644 index db1727bd0b6a..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordTest.java +++ /dev/null @@ -1,552 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.BoundStatement; -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.Configuration; -import com.datastax.driver.core.Metadata; -import com.datastax.driver.core.PreparedStatement; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.ResultSetFuture; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.Statement; -import org.apache.nifi.reporting.InitializationException; -import org.apache.nifi.serialization.record.MockRecordParser; -import org.apache.nifi.serialization.record.RecordField; -import org.apache.nifi.serialization.record.RecordFieldType; -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 javax.net.ssl.SSLContext; -import java.net.InetSocketAddress; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class PutCassandraRecordTest { - - private TestRunner testRunner; - private MockRecordParser recordReader; - - @BeforeEach - public void setUp() throws Exception { - MockPutCassandraRecord processor = new MockPutCassandraRecord(); - recordReader = new MockRecordParser(); - testRunner = TestRunners.newTestRunner(processor); - testRunner.setProperty(PutCassandraRecord.RECORD_READER_FACTORY, "reader"); - } - - @Test - public void testProcessorConfigValidity() throws InitializationException { - testRunner.setProperty(PutCassandraRecord.CONTACT_POINTS, "localhost:9042"); - testRunner.assertNotValid(); - - testRunner.setProperty(PutCassandraRecord.PASSWORD, "password"); - testRunner.assertNotValid(); - - testRunner.setProperty(PutCassandraRecord.USERNAME, "username"); - testRunner.assertNotValid(); - - testRunner.setProperty(PutCassandraRecord.CONSISTENCY_LEVEL, "SERIAL"); - testRunner.assertNotValid(); - - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, "LOGGED"); - testRunner.assertNotValid(); - - testRunner.setProperty(PutCassandraRecord.KEYSPACE, "sampleks"); - testRunner.assertNotValid(); - - testRunner.setProperty(PutCassandraRecord.TABLE, "sampletbl"); - testRunner.assertNotValid(); - - testRunner.addControllerService("reader", recordReader); - testRunner.enableControllerService(recordReader); - testRunner.assertValid(); - } - - private void setUpStandardTestConfig() throws InitializationException { - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost:9042"); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "password"); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "username"); - testRunner.setProperty(PutCassandraRecord.CONSISTENCY_LEVEL, "SERIAL"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, "LOGGED"); - testRunner.setProperty(PutCassandraRecord.TABLE, "sampleks.sampletbl"); - testRunner.addControllerService("reader", recordReader); - testRunner.enableControllerService(recordReader); - } - - @Test - public void testSimplePut() throws InitializationException { - setUpStandardTestConfig(); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("sport", RecordFieldType.STRING); - - recordReader.addRecord("John Doe", 48, "Soccer"); - recordReader.addRecord("Jane Doe", 47, "Tennis"); - recordReader.addRecord("Sally Doe", 47, "Curling"); - recordReader.addRecord("Jimmy Doe", 14, null); - recordReader.addRecord("Pizza Doe", 14, null); - - testRunner.enqueue(""); - testRunner.run(); - - testRunner.assertAllFlowFilesTransferred(PutCassandraRecord.REL_SUCCESS, 1); - } - - @Test - public void testStringArrayPut() throws InitializationException { - setUpStandardTestConfig(); - - recordReader.addSchemaField(new RecordField("names", RecordFieldType.ARRAY.getArrayDataType(RecordFieldType.STRING.getDataType()))); - recordReader.addSchemaField("age", RecordFieldType.INT); - - recordReader.addRecord(new Object[]{"John", "Doe"}, 1); - recordReader.addRecord(new Object[]{"John", "Doe"}, 2); - recordReader.addRecord(new Object[]{"John", "Doe"}, 3); - - testRunner.enqueue(""); - testRunner.run(); - - testRunner.assertAllFlowFilesTransferred(PutCassandraRecord.REL_SUCCESS, 1); - } - - @Test - public void testSimpleUpdate() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.UPDATE_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_METHOD, PutCassandraRecord.SET_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_KEYS, "name,age"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.COUNTER_TYPE); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("goals", RecordFieldType.INT); - - recordReader.addRecord("John Doe", 48, 1L); - recordReader.addRecord("Jane Doe", 47, 2L); - recordReader.addRecord("Sally Doe", 47, 0); - - testRunner.enqueue(""); - testRunner.run(); - - testRunner.assertAllFlowFilesTransferred(PutCassandraRecord.REL_SUCCESS, 1); - } - - @Test - public void testUpdateInvalidFieldType() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.UPDATE_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_METHOD, PutCassandraRecord.INCR_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_KEYS, "name,age"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.COUNTER_TYPE); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("goals", RecordFieldType.STRING); - - recordReader.addRecord("John Doe", 48,"1"); - recordReader.addRecord("Jane Doe", 47, "1"); - recordReader.addRecord("Sally Doe", 47, "1"); - - testRunner.enqueue(""); - testRunner.run(); - - testRunner.assertTransferCount(PutCassandraRecord.REL_FAILURE, 1); - testRunner.assertTransferCount(PutCassandraRecord.REL_SUCCESS, 0); - testRunner.assertTransferCount(PutCassandraRecord.REL_RETRY, 0); - } - - @Test - public void testUpdateEmptyUpdateKeys() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.UPDATE_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_METHOD, PutCassandraRecord.INCR_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_KEYS, ""); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.COUNTER_TYPE); - - testRunner.assertNotValid(); - } - - @Test - public void testUpdateNullUpdateKeys() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.UPDATE_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_METHOD, PutCassandraRecord.SET_TYPE); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.COUNTER_TYPE); - - testRunner.assertNotValid(); - } - - @Test - public void testUpdateSetLoggedBatch() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.UPDATE_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_METHOD, PutCassandraRecord.SET_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_KEYS, "name,age"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.LOGGED_TYPE); - - testRunner.assertValid(); - } - - @Test - public void testUpdateCounterWrongBatchStatementType() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.UPDATE_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_METHOD, PutCassandraRecord.INCR_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_KEYS, "name,age"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.LOGGED_TYPE); - - testRunner.assertNotValid(); - } - - @Test - public void testUpdateWithUpdateMethodAndKeyAttributes() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.UPDATE_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_METHOD, PutCassandraRecord.UPDATE_METHOD_USE_ATTR_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_KEYS, "${cql.update.keys}"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.COUNTER_TYPE); - - testRunner.assertValid(); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("goals", RecordFieldType.LONG); - - recordReader.addRecord("John Doe", 48, 1L); - recordReader.addRecord("Jane Doe", 47, 1L); - recordReader.addRecord("Sally Doe", 47, 1L); - - Map attributes = new HashMap<>(); - attributes.put("cql.update.method", "Increment"); - attributes.put("cql.update.keys", "name,age"); - testRunner.enqueue("", attributes); - testRunner.run(); - - testRunner.assertTransferCount(PutCassandraRecord.REL_FAILURE, 0); - testRunner.assertTransferCount(PutCassandraRecord.REL_SUCCESS, 1); - testRunner.assertTransferCount(PutCassandraRecord.REL_RETRY, 0); - } - - @Test - public void testInsertWithStatementAttribute() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.STATEMENT_TYPE_USE_ATTR_TYPE); - - testRunner.assertValid(); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("goals", RecordFieldType.LONG); - - recordReader.addRecord("John Doe", 48, 1L); - recordReader.addRecord("Jane Doe", 47, 1L); - recordReader.addRecord("Sally Doe", 47, 1L); - - Map attributes = new HashMap<>(); - attributes.put("cql.statement.type", "Insert"); - testRunner.enqueue("", attributes); - testRunner.run(); - - testRunner.assertTransferCount(PutCassandraRecord.REL_FAILURE, 0); - testRunner.assertTransferCount(PutCassandraRecord.REL_SUCCESS, 1); - testRunner.assertTransferCount(PutCassandraRecord.REL_RETRY, 0); - } - - @Test - public void testInsertWithStatementAttributeInvalid() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.STATEMENT_TYPE_USE_ATTR_TYPE); - - testRunner.assertValid(); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("goals", RecordFieldType.LONG); - - recordReader.addRecord("John Doe", 48, 1L); - recordReader.addRecord("Jane Doe", 47, 1L); - recordReader.addRecord("Sally Doe", 47, 1L); - - Map attributes = new HashMap<>(); - attributes.put("cql.statement.type", "invalid-type"); - testRunner.enqueue("", attributes); - testRunner.run(); - - testRunner.assertTransferCount(PutCassandraRecord.REL_FAILURE, 1); - testRunner.assertTransferCount(PutCassandraRecord.REL_SUCCESS, 0); - testRunner.assertTransferCount(PutCassandraRecord.REL_RETRY, 0); - } - - @Test - public void testInsertWithBatchStatementAttribute() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.INSERT_TYPE); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.BATCH_STATEMENT_TYPE_USE_ATTR_TYPE); - - testRunner.assertValid(); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("goals", RecordFieldType.LONG); - - recordReader.addRecord("John Doe", 48, 1L); - recordReader.addRecord("Jane Doe", 47, 1L); - recordReader.addRecord("Sally Doe", 47, 1L); - - Map attributes = new HashMap<>(); - attributes.put("cql.batch.statement.type", "counter"); - testRunner.enqueue("", attributes); - testRunner.run(); - - testRunner.assertTransferCount(PutCassandraRecord.REL_FAILURE, 0); - testRunner.assertTransferCount(PutCassandraRecord.REL_SUCCESS, 1); - testRunner.assertTransferCount(PutCassandraRecord.REL_RETRY, 0); - } - - @Test - public void testInsertWithBatchStatementAttributeInvalid() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.INSERT_TYPE); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.BATCH_STATEMENT_TYPE_USE_ATTR_TYPE); - - testRunner.assertValid(); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("goals", RecordFieldType.LONG); - - recordReader.addRecord("John Doe", 48, 1L); - recordReader.addRecord("Jane Doe", 47, 1L); - recordReader.addRecord("Sally Doe", 47, 1L); - - Map attributes = new HashMap<>(); - attributes.put("cql.batch.statement.type", "invalid-type"); - testRunner.enqueue("", attributes); - testRunner.run(); - - testRunner.assertTransferCount(PutCassandraRecord.REL_FAILURE, 1); - testRunner.assertTransferCount(PutCassandraRecord.REL_SUCCESS, 0); - testRunner.assertTransferCount(PutCassandraRecord.REL_RETRY, 0); - } - - @Test - public void testUpdateWithAttributesInvalidUpdateMethod() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.UPDATE_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_METHOD, PutCassandraRecord.UPDATE_METHOD_USE_ATTR_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_KEYS, "${cql.update.keys}"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.COUNTER_TYPE); - - testRunner.assertValid(); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("goals", RecordFieldType.INT); - - recordReader.addRecord("John Doe", 48, 1L); - recordReader.addRecord("Jane Doe", 47, 1L); - recordReader.addRecord("Sally Doe", 47, 1L); - - Map attributes = new HashMap<>(); - attributes.put("cql.update.method", "invalid-method"); - attributes.put("cql.update.keys", "name,age"); - testRunner.enqueue("", attributes); - testRunner.run(); - - testRunner.assertTransferCount(PutCassandraRecord.REL_FAILURE, 1); - testRunner.assertTransferCount(PutCassandraRecord.REL_SUCCESS, 0); - testRunner.assertTransferCount(PutCassandraRecord.REL_RETRY, 0); - } - - @Test - public void testUpdateWithAttributesIncompatibleBatchStatementType() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.UPDATE_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_METHOD, PutCassandraRecord.INCR_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_KEYS, "name,age"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.BATCH_STATEMENT_TYPE_USE_ATTR_TYPE); - - testRunner.assertValid(); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("goals", RecordFieldType.INT); - - recordReader.addRecord("John Doe", 48, 1L); - recordReader.addRecord("Jane Doe", 47, 1L); - recordReader.addRecord("Sally Doe", 47, 1L); - - Map attributes = new HashMap<>(); - attributes.put("cql.batch.statement.type", "LOGGED"); - testRunner.enqueue("", attributes); - testRunner.run(); - - testRunner.assertTransferCount(PutCassandraRecord.REL_FAILURE, 1); - testRunner.assertTransferCount(PutCassandraRecord.REL_SUCCESS, 0); - testRunner.assertTransferCount(PutCassandraRecord.REL_RETRY, 0); - } - - @Test - public void testUpdateWithAttributesEmptyUpdateKeysAttribute() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.UPDATE_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_METHOD, PutCassandraRecord.UPDATE_METHOD_USE_ATTR_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_KEYS, "${cql.update.keys}"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.COUNTER_TYPE); - - testRunner.assertValid(); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("goals", RecordFieldType.LONG); - - recordReader.addRecord("John Doe", 48, 1L); - recordReader.addRecord("Jane Doe", 47, 1L); - recordReader.addRecord("Sally Doe", 47, 1L); - - HashMap attributes = new HashMap<>(); - attributes.put("cql.update.method", "Increment"); - attributes.put("cql.update.keys", ""); - testRunner.enqueue("", attributes); - testRunner.run(); - - testRunner.assertTransferCount(PutCassandraRecord.REL_FAILURE, 1); - testRunner.assertTransferCount(PutCassandraRecord.REL_SUCCESS, 0); - testRunner.assertTransferCount(PutCassandraRecord.REL_RETRY, 0); - } - - @Test - public void testUpdateWithAttributesEmptyUpdateMethodAttribute() throws InitializationException { - setUpStandardTestConfig(); - testRunner.setProperty(PutCassandraRecord.STATEMENT_TYPE, PutCassandraRecord.UPDATE_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_METHOD, PutCassandraRecord.UPDATE_METHOD_USE_ATTR_TYPE); - testRunner.setProperty(PutCassandraRecord.UPDATE_KEYS, "name,age"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, PutCassandraRecord.COUNTER_TYPE); - - testRunner.assertValid(); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("goals", RecordFieldType.LONG); - - recordReader.addRecord("John Doe", 48, 1L); - recordReader.addRecord("Jane Doe", 47, 1L); - recordReader.addRecord("Sally Doe", 47, 1L); - - HashMap attributes = new HashMap<>(); - attributes.put("cql.update.method", ""); - testRunner.enqueue("", attributes); - testRunner.run(); - - testRunner.assertTransferCount(PutCassandraRecord.REL_FAILURE, 1); - testRunner.assertTransferCount(PutCassandraRecord.REL_SUCCESS, 0); - testRunner.assertTransferCount(PutCassandraRecord.REL_RETRY, 0); - } - - @Test - public void testEL() throws InitializationException { - testRunner.setProperty(PutCassandraRecord.CONTACT_POINTS, "${contact.points}"); - testRunner.setProperty(PutCassandraRecord.PASSWORD, "${pass}"); - testRunner.setProperty(PutCassandraRecord.USERNAME, "${user}"); - testRunner.setProperty(PutCassandraRecord.CONSISTENCY_LEVEL, "SERIAL"); - testRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, "LOGGED"); - testRunner.setProperty(PutCassandraRecord.TABLE, "sampleks.sampletbl"); - testRunner.addControllerService("reader", recordReader); - testRunner.enableControllerService(recordReader); - - testRunner.assertValid(); - - testRunner.setEnvironmentVariableValue("contact.points", "localhost:9042"); - testRunner.setEnvironmentVariableValue("user", "username"); - testRunner.setEnvironmentVariableValue("pass", "password"); - - recordReader.addSchemaField("name", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - recordReader.addSchemaField("sport", RecordFieldType.STRING); - - recordReader.addRecord("John Doe", 48, "Soccer"); - recordReader.addRecord("Jane Doe", 47, "Tennis"); - recordReader.addRecord("Sally Doe", 47, "Curling"); - recordReader.addRecord("Jimmy Doe", 14, null); - recordReader.addRecord("Pizza Doe", 14, null); - - testRunner.enqueue(""); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(PutCassandraRecord.REL_SUCCESS, 1); - } - - private static class MockPutCassandraRecord extends PutCassandraRecord { - private Exception exceptionToThrow = null; - private Session mockSession = mock(Session.class); - - @Override - protected Cluster createCluster(List contactPoints, SSLContext sslContext, - String username, String password, String compressionType) { - Cluster mockCluster = mock(Cluster.class); - try { - Metadata mockMetadata = mock(Metadata.class); - when(mockMetadata.getClusterName()).thenReturn("cluster1"); - when(mockCluster.getMetadata()).thenReturn(mockMetadata); - when(mockCluster.connect()).thenReturn(mockSession); - when(mockCluster.connect(anyString())).thenReturn(mockSession); - Configuration config = Configuration.builder().build(); - when(mockCluster.getConfiguration()).thenReturn(config); - ResultSetFuture future = mock(ResultSetFuture.class); - ResultSet rs = CassandraQueryTestUtil.createMockResultSet(); - PreparedStatement ps = mock(PreparedStatement.class); - when(mockSession.prepare(anyString())).thenReturn(ps); - BoundStatement bs = mock(BoundStatement.class); - when(ps.bind()).thenReturn(bs); - when(future.getUninterruptibly()).thenReturn(rs); - try { - doReturn(rs).when(future).getUninterruptibly(anyLong(), any(TimeUnit.class)); - } catch (TimeoutException te) { - throw new IllegalArgumentException("Mocked cluster doesn't time out"); - } - if (exceptionToThrow != null) { - doThrow(exceptionToThrow).when(mockSession).executeAsync(anyString()); - doThrow(exceptionToThrow).when(mockSession).executeAsync(any(Statement.class)); - - } else { - when(mockSession.executeAsync(anyString())).thenReturn(future); - when(mockSession.executeAsync(any(Statement.class))).thenReturn(future); - } - when(mockSession.getCluster()).thenReturn(mockCluster); - } catch (Exception e) { - fail(e.getMessage()); - } - return mockCluster; - } - } -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordUpdateTest.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordUpdateTest.java deleted file mode 100644 index 6afc14ac08c9..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/PutCassandraRecordUpdateTest.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.Statement; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.apache.nifi.serialization.record.RecordSchema; -import org.apache.nifi.util.Tuple; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class PutCassandraRecordUpdateTest { - private PutCassandraRecord testSubject; - - private final RecordSchema schema = mock(RecordSchema.class); - - @BeforeEach - public void setUp() { - testSubject = new PutCassandraRecord(); - } - - - @Test - public void testGenerateUpdateWithEmptyKeyList() { - Stream.of("", ",", ",,,").forEach(updateKeys -> testGenerateUpdate( - "keyspace.table", - updateKeys, - PutCassandraRecord.SET_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("stringField", "newStringValue") - ), - new IllegalArgumentException("No Update Keys were specified") - )); - } - - @Test - public void testGenerateUpdateWithMissingKey() { - testGenerateUpdate( - "keyspace.table", - "keyField,missingKeyField", - PutCassandraRecord.SET_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("stringField", "newStringValue") - ), - new IllegalArgumentException("Update key 'missingKeyField' is not present in the record schema") - ); - } - - @Test - public void testGenerateUpdateWithInvalidUpdateMethod() { - testGenerateUpdate( - "keyspace.table", - "keyField", - "invalidUpdateMethod", - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("longField", 15L) - ), - new IllegalArgumentException("Update Method 'invalidUpdateMethod' is not valid.") - ); - } - - @Test - public void testGenerateUpdateIncrementString() { - testGenerateUpdate( - "keyspace.table", - "keyField", - PutCassandraRecord.INCR_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("stringField", "15") - ), - new IllegalArgumentException("Field 'stringField' is not of type Number") - ); - } - - @Test - public void testGenerateUpdateSimpleTableName() { - testGenerateUpdate( - "table", - "keyField1", - PutCassandraRecord.SET_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField1", 1), - new Tuple<>("stringField", "newStringValue") - ), - "UPDATE table SET stringField='newStringValue' WHERE keyField1=1;" - ); - } - - @Test - public void testGenerateUpdateKeyspacedTableName() { - testGenerateUpdate( - "keyspace.table", - "keyField1", - PutCassandraRecord.SET_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField1", 1), - new Tuple<>("stringField", "newStringValue") - ), - "UPDATE keyspace.table SET stringField='newStringValue' WHERE keyField1=1;" - ); - } - - @Test - public void testGenerateUpdateMultipleKeys() { - testGenerateUpdate( - "keyspace.table", - "keyField1,keyField2,keyField3", - PutCassandraRecord.SET_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField1", 1), - new Tuple<>("keyField2", "key2"), - new Tuple<>("keyField3", 123L), - new Tuple<>("stringField", "newStringValue") - ), - "UPDATE keyspace.table SET stringField='newStringValue' WHERE keyField1=1 AND keyField2='key2' AND keyField3=123;" - ); - } - - @Test - public void testGenerateUpdateIncrementLong() { - testGenerateUpdate( - "keyspace.table", - "keyField", - PutCassandraRecord.INCR_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("longField", 15L) - ), - "UPDATE keyspace.table SET longField=longField+15 WHERE keyField=1;" - ); - } - - @Test - public void testGenerateUpdateDecrementLong() { - testGenerateUpdate( - "keyspace.table", - "keyField", - PutCassandraRecord.DECR_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("longField", 15L) - ), - "UPDATE keyspace.table SET longField=longField-15 WHERE keyField=1;" - ); - } - - @Test - public void testGenerateUpdateIncrementInteger() { - testGenerateUpdate( - "keyspace.table", - "keyField", - PutCassandraRecord.INCR_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("integerField", 15) - ), - "UPDATE keyspace.table SET integerField=integerField+15 WHERE keyField=1;" - ); - } - - @Test - public void testGenerateUpdateIncrementFloat() { - testGenerateUpdate( - "keyspace.table", - "keyField", - PutCassandraRecord.INCR_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("floatField", 15.05F) - ), - "UPDATE keyspace.table SET floatField=floatField+15 WHERE keyField=1;" - ); - } - - @Test - public void testGenerateUpdateIncrementDouble() { - testGenerateUpdate( - "keyspace.table", - "keyField", - PutCassandraRecord.INCR_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("doubleField", 15.05D) - ), - "UPDATE keyspace.table SET doubleField=doubleField+15 WHERE keyField=1;" - ); - } - - @Test - public void testGenerateUpdateSetMultipleValues() { - testGenerateUpdate( - "keyspace.table", - "keyField", - PutCassandraRecord.SET_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("stringField", "newStringValue"), - new Tuple<>("integerField", 15), - new Tuple<>("longField", 67L) - ), - "UPDATE keyspace.table SET stringField='newStringValue',integerField=15,longField=67 WHERE keyField=1;" - ); - } - - @Test - public void testGenerateUpdateIncrementMultipleValues() { - testGenerateUpdate( - "keyspace.table", - "keyField", - PutCassandraRecord.INCR_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("integerField", 15), - new Tuple<>("longField", 67L) - ), - "UPDATE keyspace.table SET integerField=integerField+15,longField=longField+67 WHERE keyField=1;" - ); - } - - @Test - public void testGenerateUpdateDecrementMultipleValues() { - testGenerateUpdate( - "keyspace.table", - "keyField", - PutCassandraRecord.DECR_TYPE.getValue(), - Arrays.asList( - new Tuple<>("keyField", 1), - new Tuple<>("integerField", 15), - new Tuple<>("longField", 67L) - ), - "UPDATE keyspace.table SET integerField=integerField-15,longField=longField-67 WHERE keyField=1;" - ); - } - - private void testGenerateUpdate(String table, String updateKeys, String updateMethod, List> records, String expected) { - Map recordContentMap = records.stream() - .collect(Collectors.toMap(Tuple::getKey, Tuple::getValue)); - - List fieldNames = records.stream().map(Tuple::getKey).collect(Collectors.toList()); - - when(schema.getFieldNames()).thenReturn(fieldNames); - Statement actual = testSubject.generateUpdate(table, schema, updateKeys, updateMethod, recordContentMap); - - assertEquals(expected, actual.toString()); - } - - private void testGenerateUpdate(String table, String updateKeys, String updateMethod, List> records, E expected) { - Map recordContentMap = records.stream() - .collect(Collectors.toMap(Tuple::getKey, Tuple::getValue)); - - List fieldNames = records.stream().map(Tuple::getKey).collect(Collectors.toList()); - - when(schema.getFieldNames()).thenReturn(fieldNames); - try { - testSubject.generateUpdate("keyspace.table", schema, updateKeys, updateMethod, recordContentMap); - fail(); - } catch (Exception e) { - assertEquals(expected.getClass(), e.getClass()); - assertEquals(expected.getMessage(), e.getMessage()); - } - } -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/QueryCassandraIT.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/QueryCassandraIT.java deleted file mode 100644 index ac1f089d7993..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/QueryCassandraIT.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.querybuilder.QueryBuilder; -import com.datastax.driver.core.querybuilder.Select; -import com.datastax.driver.core.querybuilder.Truncate; -import org.apache.nifi.reporting.InitializationException; -import org.apache.nifi.serialization.record.MockRecordParser; -import org.apache.nifi.serialization.record.RecordFieldType; -import org.apache.nifi.util.TestRunner; -import org.apache.nifi.util.TestRunners; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.testcontainers.containers.CassandraContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; -import org.testcontainers.utility.DockerImageName; - -import java.net.InetSocketAddress; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.ThreadLocalRandom; -import java.util.stream.Collectors; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@Testcontainers -public class QueryCassandraIT { - @Container - private static final CassandraContainer CASSANDRA_CONTAINER = new CassandraContainer(DockerImageName.parse("cassandra:4.1")); - - private static TestRunner putCassandraTestRunner; - private static TestRunner queryCassandraTestRunner; - private static MockRecordParser recordReader; - - private static Cluster cluster; - private static Session session; - - private static final int LOAD_FLOW_FILE_SIZE = 100; - private static final int LOAD_FLOW_FILE_BATCH_SIZE = 10; - - private static final String KEYSPACE = "sample_keyspace"; - private static final String TABLE = "sample_table"; - - @BeforeAll - public static void setup() throws InitializationException { - recordReader = new MockRecordParser(); - putCassandraTestRunner = TestRunners.newTestRunner(PutCassandraRecord.class); - queryCassandraTestRunner = TestRunners.newTestRunner(QueryCassandra.class); - - InetSocketAddress contactPoint = CASSANDRA_CONTAINER.getContactPoint(); - putCassandraTestRunner.setProperty(PutCassandraRecord.RECORD_READER_FACTORY, "reader"); - putCassandraTestRunner.setProperty(PutCassandraRecord.CONTACT_POINTS, contactPoint.getHostString() + ":" + contactPoint.getPort()); - putCassandraTestRunner.setProperty(PutCassandraRecord.KEYSPACE, KEYSPACE); - putCassandraTestRunner.setProperty(PutCassandraRecord.TABLE, TABLE); - putCassandraTestRunner.setProperty(PutCassandraRecord.CONSISTENCY_LEVEL, "SERIAL"); - putCassandraTestRunner.setProperty(PutCassandraRecord.BATCH_STATEMENT_TYPE, "LOGGED"); - putCassandraTestRunner.addControllerService("reader", recordReader); - putCassandraTestRunner.enableControllerService(recordReader); - - queryCassandraTestRunner.setProperty(QueryCassandra.CONTACT_POINTS, contactPoint.getHostName() + ":" + contactPoint.getPort()); - queryCassandraTestRunner.setProperty(QueryCassandra.FETCH_SIZE, "10"); - queryCassandraTestRunner.setProperty(QueryCassandra.OUTPUT_BATCH_SIZE, "10"); - queryCassandraTestRunner.setProperty(QueryCassandra.KEYSPACE, KEYSPACE); - queryCassandraTestRunner.setProperty(QueryCassandra.CQL_SELECT_QUERY, "select * from " + TABLE + ";"); - - cluster = Cluster.builder().addContactPoint(contactPoint.getHostName()) - .withPort(contactPoint.getPort()).build(); - session = cluster.connect(); - - String createKeyspace = "CREATE KEYSPACE IF NOT EXISTS " + KEYSPACE + " WITH replication = {'class':'SimpleStrategy','replication_factor':1};"; - String createTable = "CREATE TABLE IF NOT EXISTS " + KEYSPACE + "." + TABLE + "(id int PRIMARY KEY, uuid text, age int);"; - - session.execute(createKeyspace); - session.execute(createTable); - loadData(); - } - - private static void loadData() { - recordReader.addSchemaField("id", RecordFieldType.INT); - recordReader.addSchemaField("uuid", RecordFieldType.STRING); - recordReader.addSchemaField("age", RecordFieldType.INT); - int recordCount = 0; - - for (int i = 0; i resultsList = result.all() - .stream() - .map(r -> r.getInt(0)) - .collect(Collectors.toList()); - - return resultsList.size(); - } - - private void dropRecords() { - Truncate query = QueryBuilder.truncate(KEYSPACE, TABLE); - session.execute(query); - } - - @AfterAll - public static void shutdown() { - String dropKeyspace = "DROP KEYSPACE " + KEYSPACE; - String dropTable = "DROP TABLE IF EXISTS " + KEYSPACE + "." + TABLE; - - session.execute(dropTable); - session.execute(dropKeyspace); - - session.close(); - cluster.close(); - } -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/QueryCassandraTest.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/QueryCassandraTest.java deleted file mode 100644 index fa3505b83143..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-processors/src/test/java/org/apache/nifi/processors/cassandra/QueryCassandraTest.java +++ /dev/null @@ -1,594 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.processors.cassandra; - -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.Configuration; -import com.datastax.driver.core.ConsistencyLevel; -import com.datastax.driver.core.Metadata; -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.ResultSetFuture; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.SniEndPoint; -import com.datastax.driver.core.exceptions.InvalidQueryException; -import com.datastax.driver.core.exceptions.NoHostAvailableException; -import com.datastax.driver.core.exceptions.ReadTimeoutException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.avro.Schema; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.util.MockFlowFile; -import org.apache.nifi.util.MockProcessContext; -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 javax.net.ssl.SSLContext; -import java.io.ByteArrayOutputStream; -import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class QueryCassandraTest { - - private TestRunner testRunner; - private MockQueryCassandra processor; - - @BeforeEach - public void setUp() { - processor = new MockQueryCassandra(); - testRunner = TestRunners.newTestRunner(processor); - } - - @Test - public void testProcessorConfigValid() { - testRunner.setProperty(AbstractCassandraProcessor.CONSISTENCY_LEVEL, "ONE"); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost:9042"); - testRunner.assertNotValid(); - testRunner.setProperty(QueryCassandra.CQL_SELECT_QUERY, "select * from test"); - testRunner.assertValid(); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "password"); - testRunner.assertNotValid(); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "username"); - testRunner.assertValid(); - - testRunner.setProperty(QueryCassandra.TIMESTAMP_FORMAT_PATTERN, "invalid format"); - testRunner.assertNotValid(); - testRunner.setProperty(QueryCassandra.TIMESTAMP_FORMAT_PATTERN, "yyyy-MM-dd HH:mm:ss.SSSZ"); - testRunner.assertValid(); - } - - @Test - public void testProcessorELConfigValid() { - testRunner.setProperty(AbstractCassandraProcessor.CONSISTENCY_LEVEL, "ONE"); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "${hosts}"); - testRunner.setProperty(QueryCassandra.CQL_SELECT_QUERY, "${query}"); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "${pass}"); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "${user}"); - testRunner.assertValid(); - } - - @Test - public void testProcessorNoInputFlowFileAndExceptions() { - setUpStandardProcessorConfig(); - - // Test no input flowfile - testRunner.setIncomingConnection(false); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(QueryCassandra.REL_SUCCESS, 1); - testRunner.clearTransferState(); - - // Test exceptions - processor.setExceptionToThrow(new NoHostAvailableException(new HashMap<>())); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(QueryCassandra.REL_RETRY, 1); - testRunner.clearTransferState(); - - processor.setExceptionToThrow( - new ReadTimeoutException(new SniEndPoint(new InetSocketAddress("localhost", 9042), ""), ConsistencyLevel.ANY, 0, 1, false)); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(QueryCassandra.REL_RETRY, 1); - testRunner.clearTransferState(); - - processor.setExceptionToThrow( - new InvalidQueryException(new SniEndPoint(new InetSocketAddress("localhost", 9042), ""), "invalid query")); - testRunner.run(1, true, true); - // No files transferred to failure if there was no incoming connection - testRunner.assertAllFlowFilesTransferred(QueryCassandra.REL_FAILURE, 0); - testRunner.clearTransferState(); - - processor.setExceptionToThrow(new ProcessException()); - testRunner.run(1, true, true); - // No files transferred to failure if there was no incoming connection - testRunner.assertAllFlowFilesTransferred(QueryCassandra.REL_FAILURE, 0); - testRunner.clearTransferState(); - processor.setExceptionToThrow(null); - - } - - @Test - public void testProcessorJsonOutput() { - setUpStandardProcessorConfig(); - testRunner.setIncomingConnection(false); - - // Test JSON output - testRunner.setProperty(QueryCassandra.OUTPUT_FORMAT, QueryCassandra.JSON_FORMAT); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(QueryCassandra.REL_SUCCESS, 1); - List files = testRunner.getFlowFilesForRelationship(QueryCassandra.REL_SUCCESS); - assertNotNull(files); - assertEquals(1, files.size(), "One file should be transferred to success"); - assertEquals("{\"results\":[{\"user_id\":\"user1\",\"first_name\":\"Joe\",\"last_name\":\"Smith\"," - + "\"emails\":[\"jsmith@notareal.com\"],\"top_places\":[\"New York, NY\",\"Santa Clara, CA\"]," - + "\"todo\":{\"2016-01-03 05:00:00+0000\":\"Set my alarm \\\"for\\\" a month from now\"}," - + "\"registered\":\"false\",\"scale\":1.0,\"metric\":2.0}," - + "{\"user_id\":\"user2\",\"first_name\":\"Mary\",\"last_name\":\"Jones\"," - + "\"emails\":[\"mjones@notareal.com\"],\"top_places\":[\"Orlando, FL\"]," - + "\"todo\":{\"2016-02-03 05:00:00+0000\":\"Get milk and bread\"}," - + "\"registered\":\"true\",\"scale\":3.0,\"metric\":4.0}]}", - new String(files.get(0).toByteArray())); - } - - @Test - public void testProcessorJsonOutputFragmentAttributes() { - processor = new MockQueryCassandraTwoRounds(); - testRunner = TestRunners.newTestRunner(processor); - setUpStandardProcessorConfig(); - testRunner.setIncomingConnection(false); - testRunner.setProperty(QueryCassandra.MAX_ROWS_PER_FLOW_FILE, "1"); - - // Test JSON output - testRunner.setProperty(QueryCassandra.OUTPUT_FORMAT, QueryCassandra.JSON_FORMAT); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(QueryCassandra.REL_SUCCESS, 2); - List files = testRunner.getFlowFilesForRelationship(QueryCassandra.REL_SUCCESS); - assertNotNull(files); - assertEquals(2, files.size(), "Two files should be transferred to success"); - String indexIdentifier = null; - for (int i = 0; i < files.size(); i++) { - MockFlowFile flowFile = files.get(i); - flowFile.assertAttributeEquals(QueryCassandra.FRAGMENT_INDEX, String.valueOf(i)); - if (indexIdentifier == null) { - indexIdentifier = flowFile.getAttribute(QueryCassandra.FRAGMENT_ID); - } else { - flowFile.assertAttributeEquals(QueryCassandra.FRAGMENT_ID, indexIdentifier); - } - flowFile.assertAttributeEquals(QueryCassandra.FRAGMENT_COUNT, String.valueOf(files.size())); - } - } - - @Test - public void testProcessorELConfigJsonOutput() { - setUpStandardProcessorConfig(); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "${hosts}"); - testRunner.setProperty(QueryCassandra.CQL_SELECT_QUERY, "${query}"); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "${pass}"); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "${user}"); - testRunner.setProperty(AbstractCassandraProcessor.CHARSET, "${charset}"); - testRunner.setProperty(QueryCassandra.QUERY_TIMEOUT, "${timeout}"); - testRunner.setProperty(QueryCassandra.FETCH_SIZE, "${fetch}"); - testRunner.setProperty(QueryCassandra.MAX_ROWS_PER_FLOW_FILE, "${max-rows-per-flow}"); - testRunner.setIncomingConnection(false); - testRunner.assertValid(); - - testRunner.setEnvironmentVariableValue("hosts", "localhost:9042"); - testRunner.setEnvironmentVariableValue("user", "username"); - testRunner.setEnvironmentVariableValue("pass", "password"); - testRunner.setEnvironmentVariableValue("charset", "UTF-8"); - testRunner.setEnvironmentVariableValue("timeout", "30 sec"); - testRunner.setEnvironmentVariableValue("fetch", "0"); - testRunner.setEnvironmentVariableValue("max-rows-per-flow", "0"); - - // Test JSON output - testRunner.setProperty(QueryCassandra.OUTPUT_FORMAT, QueryCassandra.JSON_FORMAT); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(QueryCassandra.REL_SUCCESS, 1); - List files = testRunner.getFlowFilesForRelationship(QueryCassandra.REL_SUCCESS); - assertNotNull(files); - assertEquals(1, files.size(), "One file should be transferred to success"); - assertEquals("{\"results\":[{\"user_id\":\"user1\",\"first_name\":\"Joe\",\"last_name\":\"Smith\"," - + "\"emails\":[\"jsmith@notareal.com\"],\"top_places\":[\"New York, NY\",\"Santa Clara, CA\"]," - + "\"todo\":{\"2016-01-03 05:00:00+0000\":\"Set my alarm \\\"for\\\" a month from now\"}," - + "\"registered\":\"false\",\"scale\":1.0,\"metric\":2.0}," - + "{\"user_id\":\"user2\",\"first_name\":\"Mary\",\"last_name\":\"Jones\"," - + "\"emails\":[\"mjones@notareal.com\"],\"top_places\":[\"Orlando, FL\"]," - + "\"todo\":{\"2016-02-03 05:00:00+0000\":\"Get milk and bread\"}," - + "\"registered\":\"true\",\"scale\":3.0,\"metric\":4.0}]}", - new String(files.get(0).toByteArray())); - } - - @Test - public void testProcessorJsonOutputWithQueryTimeout() { - setUpStandardProcessorConfig(); - testRunner.setProperty(QueryCassandra.QUERY_TIMEOUT, "5 sec"); - testRunner.setIncomingConnection(false); - - // Test JSON output - testRunner.setProperty(QueryCassandra.OUTPUT_FORMAT, QueryCassandra.JSON_FORMAT); - testRunner.run(1, true, true); - testRunner.assertAllFlowFilesTransferred(QueryCassandra.REL_SUCCESS, 1); - List files = testRunner.getFlowFilesForRelationship(QueryCassandra.REL_SUCCESS); - assertNotNull(files); - assertEquals(1, files.size(), "One file should be transferred to success"); - } - - @Test - public void testProcessorEmptyFlowFile() { - setUpStandardProcessorConfig(); - - // Run with empty flowfile - testRunner.setIncomingConnection(true); - processor.setExceptionToThrow(null); - testRunner.enqueue("".getBytes()); - testRunner.run(1, true, true); - testRunner.assertTransferCount(QueryCassandra.REL_SUCCESS, 1); - testRunner.clearTransferState(); - } - - @Test - public void testProcessorEmptyFlowFileMaxRowsPerFlowFileEqOne() { - - processor = new MockQueryCassandraTwoRounds(); - testRunner = TestRunners.newTestRunner(processor); - - setUpStandardProcessorConfig(); - - testRunner.setIncomingConnection(true); - testRunner.setProperty(QueryCassandra.MAX_ROWS_PER_FLOW_FILE, "1"); - processor.setExceptionToThrow(null); - testRunner.enqueue("".getBytes()); - testRunner.run(1, true, true); - testRunner.assertTransferCount(QueryCassandra.REL_SUCCESS, 2); - testRunner.clearTransferState(); - } - - - @Test - public void testProcessorEmptyFlowFileAndNoHostAvailableException() { - setUpStandardProcessorConfig(); - - // Test exceptions - processor.setExceptionToThrow(new NoHostAvailableException(new HashMap<>())); - testRunner.enqueue("".getBytes()); - testRunner.run(1, true, true); - testRunner.assertTransferCount(QueryCassandra.REL_RETRY, 1); - testRunner.clearTransferState(); - } - - @Test - public void testProcessorEmptyFlowFileAndInetSocketAddressConsistencyLevelANY() { - setUpStandardProcessorConfig(); - - processor.setExceptionToThrow( - new ReadTimeoutException(new SniEndPoint(new InetSocketAddress("localhost", 9042), ""), ConsistencyLevel.ANY, 0, 1, false)); - testRunner.enqueue("".getBytes()); - testRunner.run(1, true, true); - testRunner.assertTransferCount(QueryCassandra.REL_RETRY, 1); - testRunner.clearTransferState(); - } - - @Test - public void testProcessorEmptyFlowFileAndInetSocketAddressDefault() { - setUpStandardProcessorConfig(); - - processor.setExceptionToThrow( - new InvalidQueryException(new SniEndPoint(new InetSocketAddress("localhost", 9042), ""), "invalid query")); - testRunner.enqueue("".getBytes()); - testRunner.run(1, true, true); - testRunner.assertTransferCount(QueryCassandra.REL_FAILURE, 1); - testRunner.clearTransferState(); - } - - @Test - public void testProcessorEmptyFlowFileAndExceptionsProcessException() { - setUpStandardProcessorConfig(); - - processor.setExceptionToThrow(new ProcessException()); - testRunner.enqueue("".getBytes()); - testRunner.run(1, true, true); - testRunner.assertTransferCount(QueryCassandra.REL_FAILURE, 1); - } - - @Test - public void testCreateSchemaOneColumn() throws Exception { - ResultSet rs = CassandraQueryTestUtil.createMockResultSetOneColumn(); - Schema schema = QueryCassandra.createSchema(rs); - assertNotNull(schema); - assertEquals(schema.getName(), "users"); - } - - @Test - public void testCreateSchema() throws Exception { - ResultSet rs = CassandraQueryTestUtil.createMockResultSet(true); - Schema schema = QueryCassandra.createSchema(rs); - assertNotNull(schema); - assertEquals(Schema.Type.RECORD, schema.getType()); - - // Check record fields, starting with user_id - Schema.Field field = schema.getField("user_id"); - assertNotNull(field); - Schema fieldSchema = field.schema(); - Schema.Type type = fieldSchema.getType(); - assertEquals(Schema.Type.UNION, type); - // Assert individual union types, first is null - assertEquals(Schema.Type.NULL, fieldSchema.getTypes().get(0).getType()); - assertEquals(Schema.Type.STRING, fieldSchema.getTypes().get(1).getType()); - - field = schema.getField("first_name"); - assertNotNull(field); - fieldSchema = field.schema(); - type = fieldSchema.getType(); - assertEquals(Schema.Type.UNION, type); - // Assert individual union types, first is null - assertEquals(Schema.Type.NULL, fieldSchema.getTypes().get(0).getType()); - assertEquals(Schema.Type.STRING, fieldSchema.getTypes().get(1).getType()); - - field = schema.getField("last_name"); - assertNotNull(field); - fieldSchema = field.schema(); - type = fieldSchema.getType(); - assertEquals(Schema.Type.UNION, type); - // Assert individual union types, first is null - assertEquals(Schema.Type.NULL, fieldSchema.getTypes().get(0).getType()); - assertEquals(Schema.Type.STRING, fieldSchema.getTypes().get(1).getType()); - - field = schema.getField("emails"); - assertNotNull(field); - fieldSchema = field.schema(); - type = fieldSchema.getType(); - // Should be a union of null and array - assertEquals(Schema.Type.UNION, type); - assertEquals(Schema.Type.NULL, fieldSchema.getTypes().get(0).getType()); - assertEquals(Schema.Type.ARRAY, fieldSchema.getTypes().get(1).getType()); - Schema arraySchema = fieldSchema.getTypes().get(1); - // Assert individual array element types are unions of null and String - Schema elementSchema = arraySchema.getElementType(); - assertEquals(Schema.Type.UNION, elementSchema.getType()); - assertEquals(Schema.Type.NULL, elementSchema.getTypes().get(0).getType()); - assertEquals(Schema.Type.STRING, elementSchema.getTypes().get(1).getType()); - - field = schema.getField("top_places"); - assertNotNull(field); - fieldSchema = field.schema(); - type = fieldSchema.getType(); - // Should be a union of null and array - assertEquals(Schema.Type.UNION, type); - assertEquals(Schema.Type.ARRAY, fieldSchema.getTypes().get(1).getType()); - arraySchema = fieldSchema.getTypes().get(1); - // Assert individual array element types are unions of null and String - elementSchema = arraySchema.getElementType(); - assertEquals(Schema.Type.UNION, elementSchema.getType()); - assertEquals(Schema.Type.NULL, elementSchema.getTypes().get(0).getType()); - assertEquals(Schema.Type.STRING, elementSchema.getTypes().get(1).getType()); - - field = schema.getField("todo"); - assertNotNull(field); - fieldSchema = field.schema(); - type = fieldSchema.getType(); - // Should be a union of null and map - assertEquals(Schema.Type.UNION, type); - assertEquals(Schema.Type.MAP, fieldSchema.getTypes().get(1).getType()); - Schema mapSchema = fieldSchema.getTypes().get(1); - // Assert individual map value types are unions of null and String - Schema valueSchema = mapSchema.getValueType(); - assertEquals(Schema.Type.NULL, valueSchema.getTypes().get(0).getType()); - assertEquals(Schema.Type.STRING, valueSchema.getTypes().get(1).getType()); - - field = schema.getField("registered"); - assertNotNull(field); - fieldSchema = field.schema(); - type = fieldSchema.getType(); - assertEquals(Schema.Type.UNION, type); - // Assert individual union types, first is null - assertEquals(Schema.Type.NULL, fieldSchema.getTypes().get(0).getType()); - assertEquals(Schema.Type.BOOLEAN, fieldSchema.getTypes().get(1).getType()); - - field = schema.getField("scale"); - assertNotNull(field); - fieldSchema = field.schema(); - type = fieldSchema.getType(); - assertEquals(Schema.Type.UNION, type); - // Assert individual union types, first is null - assertEquals(Schema.Type.NULL, fieldSchema.getTypes().get(0).getType()); - assertEquals(Schema.Type.FLOAT, fieldSchema.getTypes().get(1).getType()); - - field = schema.getField("metric"); - assertNotNull(field); - fieldSchema = field.schema(); - type = fieldSchema.getType(); - assertEquals(Schema.Type.UNION, type); - // Assert individual union types, first is null - assertEquals(Schema.Type.NULL, fieldSchema.getTypes().get(0).getType()); - assertEquals(Schema.Type.DOUBLE, fieldSchema.getTypes().get(1).getType()); - } - - @Test - public void testConvertToAvroStream() throws Exception { - processor = new MockQueryCassandraTwoRounds(); - testRunner = TestRunners.newTestRunner(processor); - setUpStandardProcessorConfig(); - ResultSet rs = CassandraQueryTestUtil.createMockResultSet(false); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - long numberOfRows = QueryCassandra.convertToAvroStream(rs, 0, baos, 0, null); - assertEquals(2, numberOfRows); - } - - @Test - public void testConvertToJSONStream() throws Exception { - setUpStandardProcessorConfig(); - ResultSet rs = CassandraQueryTestUtil.createMockResultSet(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - long numberOfRows = QueryCassandra.convertToJsonStream(rs, 0, baos, StandardCharsets.UTF_8, - 0, null); - assertEquals(2, numberOfRows); - } - - @Test - public void testDefaultDateFormatInConvertToJSONStream() throws Exception { - ResultSet rs = CassandraQueryTestUtil.createMockDateResultSet(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(QueryCassandra.TIMESTAMP_FORMAT_PATTERN.getDefaultValue()); - - long numberOfRows = QueryCassandra.convertToJsonStream(Optional.of(testRunner.getProcessContext()), rs, 0, baos, - StandardCharsets.UTF_8, 0, null); - assertEquals(1, numberOfRows); - - Map>> map = new ObjectMapper().readValue(baos.toByteArray(), HashMap.class); - String date = map.get("results").get(0).get("date"); - assertEquals(formatter.format(CassandraQueryTestUtil.TEST_DATE.toInstant().atOffset(ZoneOffset.UTC)), date); - } - - @Test - public void testCustomDateFormatInConvertToJSONStream() throws Exception { - MockProcessContext context = (MockProcessContext) testRunner.getProcessContext(); - ResultSet rs = CassandraQueryTestUtil.createMockDateResultSet(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - final String customDateFormat = "yyyy-MM-dd HH:mm:ss.SSSZ"; - context.setProperty(QueryCassandra.TIMESTAMP_FORMAT_PATTERN, customDateFormat); - final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(customDateFormat); - - long numberOfRows = QueryCassandra.convertToJsonStream(Optional.of(context), rs, 0, baos, StandardCharsets.UTF_8, 0, null); - assertEquals(1, numberOfRows); - - Map>> map = new ObjectMapper().readValue(baos.toByteArray(), HashMap.class); - String date = map.get("results").get(0).get("date"); - assertEquals(formatter.format(CassandraQueryTestUtil.TEST_DATE.toInstant().atOffset(ZoneOffset.UTC)), date); - } - - private void setUpStandardProcessorConfig() { - testRunner.setProperty(AbstractCassandraProcessor.CONSISTENCY_LEVEL, "ONE"); - testRunner.setProperty(AbstractCassandraProcessor.CONTACT_POINTS, "localhost:9042"); - testRunner.setProperty(QueryCassandra.CQL_SELECT_QUERY, "select * from test"); - testRunner.setProperty(AbstractCassandraProcessor.PASSWORD, "password"); - testRunner.setProperty(AbstractCassandraProcessor.USERNAME, "username"); - testRunner.setProperty(QueryCassandra.MAX_ROWS_PER_FLOW_FILE, "0"); - } - - /** - * Provides a stubbed processor instance for testing - */ - private static class MockQueryCassandra extends QueryCassandra { - - private Exception exceptionToThrow = null; - - @Override - protected Cluster createCluster(List contactPoints, SSLContext sslContext, - String username, String password, String compressionType) { - Cluster mockCluster = mock(Cluster.class); - try { - Metadata mockMetadata = mock(Metadata.class); - when(mockMetadata.getClusterName()).thenReturn("cluster1"); - when(mockCluster.getMetadata()).thenReturn(mockMetadata); - Session mockSession = mock(Session.class); - when(mockCluster.connect()).thenReturn(mockSession); - when(mockCluster.connect(anyString())).thenReturn(mockSession); - Configuration config = Configuration.builder().build(); - when(mockCluster.getConfiguration()).thenReturn(config); - ResultSetFuture future = mock(ResultSetFuture.class); - ResultSet rs = CassandraQueryTestUtil.createMockResultSet(false); - when(future.getUninterruptibly()).thenReturn(rs); - - try { - doReturn(rs).when(future).getUninterruptibly(anyLong(), any(TimeUnit.class)); - } catch (TimeoutException te) { - throw new IllegalArgumentException("Mocked cluster doesn't time out"); - } - - if (exceptionToThrow != null) { - when(mockSession.execute(anyString(), any(), any())).thenThrow(exceptionToThrow); - when(mockSession.execute(anyString())).thenThrow(exceptionToThrow); - } else { - when(mockSession.execute(anyString(),any(), any())).thenReturn(rs); - when(mockSession.execute(anyString())).thenReturn(rs); - } - } catch (Exception e) { - fail(e.getMessage()); - } - return mockCluster; - } - - public void setExceptionToThrow(Exception e) { - this.exceptionToThrow = e; - } - } - - private static class MockQueryCassandraTwoRounds extends MockQueryCassandra { - - private Exception exceptionToThrow = null; - - @Override - protected Cluster createCluster(List contactPoints, SSLContext sslContext, - String username, String password, String compressionType) { - Cluster mockCluster = mock(Cluster.class); - try { - Metadata mockMetadata = mock(Metadata.class); - when(mockMetadata.getClusterName()).thenReturn("cluster1"); - when(mockCluster.getMetadata()).thenReturn(mockMetadata); - Session mockSession = mock(Session.class); - when(mockCluster.connect()).thenReturn(mockSession); - when(mockCluster.connect(anyString())).thenReturn(mockSession); - Configuration config = Configuration.builder().build(); - when(mockCluster.getConfiguration()).thenReturn(config); - ResultSetFuture future = mock(ResultSetFuture.class); - ResultSet rs = CassandraQueryTestUtil.createMockResultSet(true); - when(future.getUninterruptibly()).thenReturn(rs); - - try { - doReturn(rs).when(future).getUninterruptibly(anyLong(), any(TimeUnit.class)); - } catch (TimeoutException te) { - throw new IllegalArgumentException("Mocked cluster doesn't time out"); - } - - if (exceptionToThrow != null) { - when(mockSession.execute(anyString(), any(), any())).thenThrow(exceptionToThrow); - when(mockSession.execute(anyString())).thenThrow(exceptionToThrow); - } else { - when(mockSession.execute(anyString(),any(), any())).thenReturn(rs); - when(mockSession.execute(anyString())).thenReturn(rs); - } - } catch (Exception e) { - fail(e.getMessage()); - } - return mockCluster; - } - - public void setExceptionToThrow(Exception e) { - this.exceptionToThrow = e; - } - } - -} - diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/pom.xml b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/pom.xml deleted file mode 100644 index 4195bfe32fd7..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/pom.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - nifi-cassandra-bundle - org.apache.nifi - 2.0.0-SNAPSHOT - - 4.0.0 - - nifi-cassandra-services-api-nar - nar - - - - org.apache.nifi - nifi-standard-shared-nar - nar - - - org.apache.nifi - nifi-cassandra-services-api - 2.0.0-SNAPSHOT - compile - - - \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/src/main/resources/META-INF/LICENSE b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/src/main/resources/META-INF/LICENSE deleted file mode 100644 index 106720e0f851..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/src/main/resources/META-INF/LICENSE +++ /dev/null @@ -1,266 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -APACHE NIFI SUBCOMPONENTS: - -The Apache NiFi project contains subcomponents with separate copyright -notices and license terms. Your use of the source code for the these -subcomponents is subject to the terms and conditions of the following -licenses. - -This product bundles 'asm' which is available under a 3-Clause BSD style license. -For details see http://asm.ow2.org/asmdex-license.html - - Copyright (c) 2012 France Télécom - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - THE POSSIBILITY OF SUCH DAMAGE. - -The binary distribution of this product bundles 'JNR x86asm' under an MIT -style license. - - Copyright (C) 2010 Wayne Meissner - Copyright (c) 2008-2009, Petr Kobalicek - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/src/main/resources/META-INF/NOTICE b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/src/main/resources/META-INF/NOTICE deleted file mode 100644 index 2d9d2aa14743..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api-nar/src/main/resources/META-INF/NOTICE +++ /dev/null @@ -1,227 +0,0 @@ -nifi-cassandra-services-api-nar -Copyright 2016-2020 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). - -****************** -Apache Software License v2 -****************** - - (ASLv2) The Netty Project - The following NOTICE information applies: - Copyright 2014 The Netty Project - ------------------------------------------------------------------------------- - This product contains the extensions to Java Collections Framework which has - been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene: - - * LICENSE: - * license/LICENSE.jsr166y.txt (Public Domain) - * HOMEPAGE: - * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/ - * http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/ - - This product contains a modified version of Robert Harder's Public Domain - Base64 Encoder and Decoder, which can be obtained at: - - * LICENSE: - * license/LICENSE.base64.txt (Public Domain) - * HOMEPAGE: - * http://iharder.sourceforge.net/current/java/base64/ - - This product contains a modified portion of 'Webbit', an event based - WebSocket and HTTP server, which can be obtained at: - - * LICENSE: - * license/LICENSE.webbit.txt (BSD License) - * HOMEPAGE: - * https://github.com/joewalnes/webbit - - This product contains a modified portion of 'SLF4J', a simple logging - facade for Java, which can be obtained at: - - * LICENSE: - * license/LICENSE.slf4j.txt (MIT License) - * HOMEPAGE: - * http://www.slf4j.org/ - - This product contains a modified portion of 'Apache Harmony', an open source - Java SE, which can be obtained at: - - * LICENSE: - * license/LICENSE.harmony.txt (Apache License 2.0) - * HOMEPAGE: - * http://archive.apache.org/dist/harmony/ - - This product contains a modified portion of 'jbzip2', a Java bzip2 compression - and decompression library written by Matthew J. Francis. It can be obtained at: - - * LICENSE: - * license/LICENSE.jbzip2.txt (MIT License) - * HOMEPAGE: - * https://code.google.com/p/jbzip2/ - - This product contains a modified portion of 'libdivsufsort', a C API library to construct - the suffix array and the Burrows-Wheeler transformed string for any input string of - a constant-size alphabet written by Yuta Mori. It can be obtained at: - - * LICENSE: - * license/LICENSE.libdivsufsort.txt (MIT License) - * HOMEPAGE: - * https://github.com/y-256/libdivsufsort - - This product contains a modified portion of Nitsan Wakart's 'JCTools', Java Concurrency Tools for the JVM, - which can be obtained at: - - * LICENSE: - * license/LICENSE.jctools.txt (ASL2 License) - * HOMEPAGE: - * https://github.com/JCTools/JCTools - - This product optionally depends on 'JZlib', a re-implementation of zlib in - pure Java, which can be obtained at: - - * LICENSE: - * license/LICENSE.jzlib.txt (BSD style License) - * HOMEPAGE: - * http://www.jcraft.com/jzlib/ - - This product optionally depends on 'Compress-LZF', a Java library for encoding and - decoding data in LZF format, written by Tatu Saloranta. It can be obtained at: - - * LICENSE: - * license/LICENSE.compress-lzf.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/ning/compress - - This product optionally depends on 'lz4', a LZ4 Java compression - and decompression library written by Adrien Grand. It can be obtained at: - - * LICENSE: - * license/LICENSE.lz4.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/jpountz/lz4-java - - This product optionally depends on 'lzma-java', a LZMA Java compression - and decompression library, which can be obtained at: - - * LICENSE: - * license/LICENSE.lzma-java.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/jponge/lzma-java - - This product contains a modified portion of 'jfastlz', a Java port of FastLZ compression - and decompression library written by William Kinney. It can be obtained at: - - * LICENSE: - * license/LICENSE.jfastlz.txt (MIT License) - * HOMEPAGE: - * https://code.google.com/p/jfastlz/ - - This product contains a modified portion of and optionally depends on 'Protocol Buffers', Google's data - interchange format, which can be obtained at: - - * LICENSE: - * license/LICENSE.protobuf.txt (New BSD License) - * HOMEPAGE: - * https://github.com/google/protobuf - - This product optionally depends on 'Bouncy Castle Crypto APIs' to generate - a temporary self-signed X.509 certificate when the JVM does not provide the - equivalent functionality. It can be obtained at: - - * LICENSE: - * license/LICENSE.bouncycastle.txt (MIT License) - * HOMEPAGE: - * http://www.bouncycastle.org/ - - This product optionally depends on 'Snappy', a compression library produced - by Google Inc, which can be obtained at: - - * LICENSE: - * license/LICENSE.snappy.txt (New BSD License) - * HOMEPAGE: - * https://github.com/google/snappy - - This product optionally depends on 'JBoss Marshalling', an alternative Java - serialization API, which can be obtained at: - - * LICENSE: - * license/LICENSE.jboss-marshalling.txt (GNU LGPL 2.1) - * HOMEPAGE: - * http://www.jboss.org/jbossmarshalling - - This product optionally depends on 'Caliper', Google's micro- - benchmarking framework, which can be obtained at: - - * LICENSE: - * license/LICENSE.caliper.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/google/caliper - - This product optionally depends on 'Apache Log4J', a logging framework, which - can be obtained at: - - * LICENSE: - * license/LICENSE.log4j.txt (Apache License 2.0) - * HOMEPAGE: - * http://logging.apache.org/log4j/ - - This product optionally depends on 'Aalto XML', an ultra-high performance - non-blocking XML processor, which can be obtained at: - - * LICENSE: - * license/LICENSE.aalto-xml.txt (Apache License 2.0) - * HOMEPAGE: - * http://wiki.fasterxml.com/AaltoHome - - This product contains a modified version of 'HPACK', a Java implementation of - the HTTP/2 HPACK algorithm written by Twitter. It can be obtained at: - - * LICENSE: - * license/LICENSE.hpack.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/twitter/hpack - - This product contains a modified portion of 'Apache Commons Lang', a Java library - provides utilities for the java.lang API, which can be obtained at: - - * LICENSE: - * license/LICENSE.commons-lang.txt (Apache License 2.0) - * HOMEPAGE: - * https://commons.apache.org/proper/commons-lang/ - - This product contains a forked and modified version of Tomcat Native - - * LICENSE: - * ASL2 - * HOMEPAGE: - * http://tomcat.apache.org/native-doc/ - * https://svn.apache.org/repos/asf/tomcat/native/ - - (ASLv2) Guava - The following NOTICE information applies: - Guava - Copyright 2015 The Guava Authors - - (ASLv2) Dropwizard Metrics - The following NOTICE information applies: - Copyright (c) 2010-2013 Coda Hale, Yammer.com - - This product includes software developed by Coda Hale and Yammer, Inc. - - This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64, - LongAdder), which was released with the following comments: - - Written by Doug Lea with assistance from members of JCP JSR-166 - Expert Group and released to the public domain, as explained at - http://creativecommons.org/publicdomain/zero/1.0/ - - ************************ - Eclipse Public License 1.0 - ************************ - - The following binary components are provided under the Eclipse Public License 1.0. See project link for details. - - (EPL 2.0)(GPL 2)(LGPL 2.1) JNR Posix ( jnr.posix ) https://github.com/jnr/jnr-posix/blob/master/LICENSE.txt - diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api/pom.xml b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api/pom.xml deleted file mode 100644 index 0ad093bbc757..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api/pom.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - nifi-cassandra-bundle - org.apache.nifi - 2.0.0-SNAPSHOT - - 4.0.0 - - nifi-cassandra-services-api - jar - - - - org.apache.nifi - nifi-api - provided - - - - com.datastax.cassandra - cassandra-driver-core - ${cassandra.sdk.version} - - - \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api/src/main/java/org/apache/nifi/cassandra/CassandraSessionProviderService.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api/src/main/java/org/apache/nifi/cassandra/CassandraSessionProviderService.java deleted file mode 100644 index fc2d22204af8..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-api/src/main/java/org/apache/nifi/cassandra/CassandraSessionProviderService.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.cassandra; - -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.Session; -import org.apache.nifi.controller.ControllerService; - -public interface CassandraSessionProviderService extends ControllerService { - /** - * Obtains a Cassandra session instance - * @return {@link Session} - */ - Session getCassandraSession(); - - /** - * Obtains a Cassandra cluster instance - * @return {@link Cluster} - */ - Cluster getCluster(); -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/pom.xml b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/pom.xml deleted file mode 100644 index 3957bb752cfa..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/pom.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - nifi-cassandra-bundle - org.apache.nifi - 2.0.0-SNAPSHOT - - 4.0.0 - - nifi-cassandra-services-nar - nar - - - - - - com.google.guava - guava - provided - - - - - - - org.apache.nifi - nifi-cassandra-services-api-nar - 2.0.0-SNAPSHOT - nar - - - org.apache.nifi - nifi-cassandra-services - 2.0.0-SNAPSHOT - - - org.apache.nifi - nifi-cassandra-distributedmapcache-service - 2.0.0-SNAPSHOT - - - \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/src/main/resources/META-INF/LICENSE b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/src/main/resources/META-INF/LICENSE deleted file mode 100644 index 04416de4c91f..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/src/main/resources/META-INF/LICENSE +++ /dev/null @@ -1,352 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -APACHE NIFI SUBCOMPONENTS: - -The Apache NiFi project contains subcomponents with separate copyright -notices and license terms. Your use of the source code for the these -subcomponents is subject to the terms and conditions of the following -licenses. - -This product bundles 'libffi' which is available under an MIT style license. - libffi - Copyright (c) 1996-2014 Anthony Green, Red Hat, Inc and others. - see https://github.com/java-native-access/jna/blob/master/native/libffi/LICENSE - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - ``Software''), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -This product bundles 'asm' which is available under a 3-Clause BSD style license. -For details see http://asm.ow2.org/asmdex-license.html - - Copyright (c) 2012 France Télécom - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the copyright holders nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - THE POSSIBILITY OF SUCH DAMAGE. - - The binary distribution of this product bundles 'Bouncy Castle JDK 1.5' - under an MIT style license. - - Copyright (c) 2000 - 2015 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - -The binary distribution of this product bundles 'JNR x86asm' under an MIT -style license. - - Copyright (C) 2010 Wayne Meissner - Copyright (c) 2008-2009, Petr Kobalicek - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - -This product bundles 'logback' which is dual-licensed under the EPL v1.0 -and the LGPL 2.1. - - Logback: the reliable, generic, fast and flexible logging framework. - - Copyright (C) 1999-2017, QOS.ch. All rights reserved. - - This program and the accompanying materials are dual-licensed under - either the terms of the Eclipse Public License v1.0 as published by - the Eclipse Foundation or (per the licensee's choosing) under the - terms of the GNU Lesser General Public License version 2.1 as - published by the Free Software Foundation. - -The binary distribution of this product bundles 'ANTLR 3' which is available -under a "3-clause BSD" license. For details see http://www.antlr.org/license.html - - Copyright (c) 2012 Terence Parr and Sam Harwell - All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of - conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials - provided with the distribution. - - Neither the name of the author nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/src/main/resources/META-INF/NOTICE b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/src/main/resources/META-INF/NOTICE deleted file mode 100644 index f2261add8685..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services-nar/src/main/resources/META-INF/NOTICE +++ /dev/null @@ -1,292 +0,0 @@ -nifi-cassandra-services-nar -Copyright 2016-2020 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). - -****************** -Apache Software License v2 -****************** - -The following binary components are provided under the Apache Software License v2 - - (ASLv2) DataStax Java Driver for Apache Cassandra - Core - The following NOTICE information applies: - DataStax Java Driver for Apache Cassandra - Core - Copyright (C) 2012-2017 DataStax Inc. - - (ASLv2) Jackson JSON processor - The following NOTICE information applies: - # Jackson JSON processor - - Jackson is a high-performance, Free/Open Source JSON processing library. - It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has - been in development since 2007. - It is currently developed by a community of developers, as well as supported - commercially by FasterXML.com. - - ## Licensing - - Jackson core and extension components may licensed under different licenses. - To find the details that apply to this artifact see the accompanying LICENSE file. - For more information, including possible other licensing options, contact - FasterXML.com (http://fasterxml.com). - - ## Credits - - A list of contributors may be found from CREDITS file, which is included - in some artifacts (usually source distributions); but is always available - from the source code management (SCM) system project uses. - - (ASLv2) Apache Commons Codec - The following NOTICE information applies: - Apache Commons Codec - Copyright 2002-2014 The Apache Software Foundation - - src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java - contains test data from http://aspell.net/test/orig/batch0.tab. - Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org) - - =============================================================================== - - The content of package org.apache.commons.codec.language.bm has been translated - from the original php source code available at http://stevemorse.org/phoneticinfo.htm - with permission from the original authors. - Original source copyright: - Copyright (c) 2008 Alexander Beider & Stephen P. Morse. - - (ASLv2) Apache Commons Lang - The following NOTICE information applies: - Apache Commons Lang - Copyright 2001-2017 The Apache Software Foundation - - This product includes software from the Spring Framework, - under the Apache License 2.0 (see: StringUtils.containsWhitespace()) - - (ASLv2) Guava - The following NOTICE information applies: - Guava - Copyright 2015 The Guava Authors - - (ASLv2) JSON-SMART - The following NOTICE information applies: - Copyright 2011 JSON-SMART authors - - (ASLv2) Dropwizard Metrics - The following NOTICE information applies: - Copyright (c) 2010-2013 Coda Hale, Yammer.com - - This product includes software developed by Coda Hale and Yammer, Inc. - - This product includes code derived from the JSR-166 project (ThreadLocalRandom, Striped64, - LongAdder), which was released with the following comments: - - Written by Doug Lea with assistance from members of JCP JSR-166 - Expert Group and released to the public domain, as explained at - http://creativecommons.org/publicdomain/zero/1.0/ - - (ASLv2) The Netty Project - The following NOTICE information applies: - Copyright 2014 The Netty Project - ------------------------------------------------------------------------------- - This product contains the extensions to Java Collections Framework which has - been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene: - - * LICENSE: - * license/LICENSE.jsr166y.txt (Public Domain) - * HOMEPAGE: - * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/ - * http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/ - - This product contains a modified version of Robert Harder's Public Domain - Base64 Encoder and Decoder, which can be obtained at: - - * LICENSE: - * license/LICENSE.base64.txt (Public Domain) - * HOMEPAGE: - * http://iharder.sourceforge.net/current/java/base64/ - - This product contains a modified portion of 'Webbit', an event based - WebSocket and HTTP server, which can be obtained at: - - * LICENSE: - * license/LICENSE.webbit.txt (BSD License) - * HOMEPAGE: - * https://github.com/joewalnes/webbit - - This product contains a modified portion of 'SLF4J', a simple logging - facade for Java, which can be obtained at: - - * LICENSE: - * license/LICENSE.slf4j.txt (MIT License) - * HOMEPAGE: - * http://www.slf4j.org/ - - This product contains a modified portion of 'Apache Harmony', an open source - Java SE, which can be obtained at: - - * LICENSE: - * license/LICENSE.harmony.txt (Apache License 2.0) - * HOMEPAGE: - * http://archive.apache.org/dist/harmony/ - - This product contains a modified portion of 'jbzip2', a Java bzip2 compression - and decompression library written by Matthew J. Francis. It can be obtained at: - - * LICENSE: - * license/LICENSE.jbzip2.txt (MIT License) - * HOMEPAGE: - * https://code.google.com/p/jbzip2/ - - This product contains a modified portion of 'libdivsufsort', a C API library to construct - the suffix array and the Burrows-Wheeler transformed string for any input string of - a constant-size alphabet written by Yuta Mori. It can be obtained at: - - * LICENSE: - * license/LICENSE.libdivsufsort.txt (MIT License) - * HOMEPAGE: - * https://github.com/y-256/libdivsufsort - - This product contains a modified portion of Nitsan Wakart's 'JCTools', Java Concurrency Tools for the JVM, - which can be obtained at: - - * LICENSE: - * license/LICENSE.jctools.txt (ASL2 License) - * HOMEPAGE: - * https://github.com/JCTools/JCTools - - This product optionally depends on 'JZlib', a re-implementation of zlib in - pure Java, which can be obtained at: - - * LICENSE: - * license/LICENSE.jzlib.txt (BSD style License) - * HOMEPAGE: - * http://www.jcraft.com/jzlib/ - - This product optionally depends on 'Compress-LZF', a Java library for encoding and - decoding data in LZF format, written by Tatu Saloranta. It can be obtained at: - - * LICENSE: - * license/LICENSE.compress-lzf.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/ning/compress - - This product optionally depends on 'lz4', a LZ4 Java compression - and decompression library written by Adrien Grand. It can be obtained at: - - * LICENSE: - * license/LICENSE.lz4.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/jpountz/lz4-java - - This product optionally depends on 'lzma-java', a LZMA Java compression - and decompression library, which can be obtained at: - - * LICENSE: - * license/LICENSE.lzma-java.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/jponge/lzma-java - - This product contains a modified portion of 'jfastlz', a Java port of FastLZ compression - and decompression library written by William Kinney. It can be obtained at: - - * LICENSE: - * license/LICENSE.jfastlz.txt (MIT License) - * HOMEPAGE: - * https://code.google.com/p/jfastlz/ - - This product contains a modified portion of and optionally depends on 'Protocol Buffers', Google's data - interchange format, which can be obtained at: - - * LICENSE: - * license/LICENSE.protobuf.txt (New BSD License) - * HOMEPAGE: - * https://github.com/google/protobuf - - This product optionally depends on 'Bouncy Castle Crypto APIs' to generate - a temporary self-signed X.509 certificate when the JVM does not provide the - equivalent functionality. It can be obtained at: - - * LICENSE: - * license/LICENSE.bouncycastle.txt (MIT License) - * HOMEPAGE: - * http://www.bouncycastle.org/ - - This product optionally depends on 'Snappy', a compression library produced - by Google Inc, which can be obtained at: - - * LICENSE: - * license/LICENSE.snappy.txt (New BSD License) - * HOMEPAGE: - * https://github.com/google/snappy - - This product optionally depends on 'JBoss Marshalling', an alternative Java - serialization API, which can be obtained at: - - * LICENSE: - * license/LICENSE.jboss-marshalling.txt (GNU LGPL 2.1) - * HOMEPAGE: - * http://www.jboss.org/jbossmarshalling - - This product optionally depends on 'Caliper', Google's micro- - benchmarking framework, which can be obtained at: - - * LICENSE: - * license/LICENSE.caliper.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/google/caliper - - This product optionally depends on 'Apache Log4J', a logging framework, which - can be obtained at: - - * LICENSE: - * license/LICENSE.log4j.txt (Apache License 2.0) - * HOMEPAGE: - * http://logging.apache.org/log4j/ - - This product optionally depends on 'Aalto XML', an ultra-high performance - non-blocking XML processor, which can be obtained at: - - * LICENSE: - * license/LICENSE.aalto-xml.txt (Apache License 2.0) - * HOMEPAGE: - * http://wiki.fasterxml.com/AaltoHome - - This product contains a modified version of 'HPACK', a Java implementation of - the HTTP/2 HPACK algorithm written by Twitter. It can be obtained at: - - * LICENSE: - * license/LICENSE.hpack.txt (Apache License 2.0) - * HOMEPAGE: - * https://github.com/twitter/hpack - - This product contains a modified portion of 'Apache Commons Lang', a Java library - provides utilities for the java.lang API, which can be obtained at: - - * LICENSE: - * license/LICENSE.commons-lang.txt (Apache License 2.0) - * HOMEPAGE: - * https://commons.apache.org/proper/commons-lang/ - - This product contains a forked and modified version of Tomcat Native - - * LICENSE: - * ASL2 - * HOMEPAGE: - * http://tomcat.apache.org/native-doc/ - * https://svn.apache.org/repos/asf/tomcat/native/ - - (ASLv2) Objenesis - The following NOTICE information applies: - Objenesis - Copyright 2006-2013 Joe Walnes, Henri Tremblay, Leonardo Mesquita - -************************ -Eclipse Public License 1.0 -************************ - -The following binary components are provided under the Eclipse Public License 1.0. See project link for details. - - (EPL 2.0)(GPL 2)(LGPL 2.1) JNR Posix ( jnr.posix ) https://github.com/jnr/jnr-posix/blob/master/LICENSE.txt - (EPL 1.0)(LGPL 2.1) Logback Classic (ch.qos.logback:logback-classic:jar:1.2.6 - http://logback.qos.ch/) - (EPL 1.0)(LGPL 2.1) Logback Core (ch.qos.logback:logback-core:jar:1.2.6 - http://logback.qos.ch/) diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/pom.xml b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/pom.xml deleted file mode 100644 index f2029682d188..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/pom.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - nifi-cassandra-bundle - org.apache.nifi - 2.0.0-SNAPSHOT - - 4.0.0 - - nifi-cassandra-services - jar - - - - org.apache.nifi - nifi-api - - - org.apache.nifi - nifi-utils - - - org.apache.nifi - nifi-cassandra-services-api - provided - - - - com.datastax.cassandra - cassandra-driver-core - ${cassandra.sdk.version} - provided - - - - org.apache.nifi - nifi-ssl-context-service-api - - - org.apache.nifi - nifi-framework-api - - - org.apache.nifi - nifi-mock - - - \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/CassandraSessionProvider.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/CassandraSessionProvider.java deleted file mode 100644 index 9a394539f570..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/java/org/apache/nifi/service/CassandraSessionProvider.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.service; - -import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.ConsistencyLevel; -import com.datastax.driver.core.Metadata; -import com.datastax.driver.core.ProtocolOptions; -import com.datastax.driver.core.RemoteEndpointAwareJdkSSLOptions; -import com.datastax.driver.core.SSLOptions; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.SocketOptions; -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.List; -import javax.net.ssl.SSLContext; -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.CassandraSessionProviderService; -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.controller.ControllerServiceInitializationContext; -import org.apache.nifi.expression.ExpressionLanguageScope; -import org.apache.nifi.logging.ComponentLog; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.processor.util.StandardValidators; -import org.apache.nifi.security.util.ClientAuth; -import org.apache.nifi.ssl.SSLContextService; - -@Tags({"cassandra", "dbcp", "database", "connection", "pooling"}) -@CapabilityDescription("Provides connection session for Cassandra processors to work with Apache Cassandra.") -public class CassandraSessionProvider extends AbstractControllerService implements CassandraSessionProviderService { - - public static final int DEFAULT_CASSANDRA_PORT = 9042; - - // Common descriptors - 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) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .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 the keyspace name before any table reference, in case of 'query' native processors or " + - "if the processor supports the 'Table' property, the keyspace name has to be provided with the " + - "table name in the form of .
") - .required(false) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .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 client certificate information for TLS/SSL " - + "connections.") - .required(false) - .identifiesControllerService(SSLContextService.class) - .build(); - - public static final PropertyDescriptor CLIENT_AUTH = new PropertyDescriptor.Builder() - .name("Client Auth") - .description("Client authentication policy when connecting to secure (TLS/SSL) cluster. " - + "Possible values are REQUIRED, WANT, NONE. This property is only used when an SSL Context " - + "has been defined and enabled.") - .required(false) - .allowableValues(ClientAuth.values()) - .defaultValue("REQUIRED") - .build(); - - public static final PropertyDescriptor USERNAME = new PropertyDescriptor.Builder() - .name("Username") - .description("Username to access the Cassandra cluster") - .required(false) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .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) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .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(ConsistencyLevel.values()) - .defaultValue("ONE") - .build(); - - static final PropertyDescriptor COMPRESSION_TYPE = new PropertyDescriptor.Builder() - .name("Compression Type") - .description("Enable compression at transport-level requests and responses") - .required(false) - .allowableValues(ProtocolOptions.Compression.values()) - .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) - .defaultValue("NONE") - .build(); - - static final PropertyDescriptor READ_TIMEOUT_MS = new PropertyDescriptor.Builder() - .name("read-timeout-ms") - .displayName("Read Timout (ms)") - .description("Read timeout (in milliseconds). 0 means no timeout. If no value is set, the underlying default will be used.") - .required(false) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .addValidator(StandardValidators.NON_NEGATIVE_INTEGER_VALIDATOR) - .build(); - - static final PropertyDescriptor CONNECT_TIMEOUT_MS = new PropertyDescriptor.Builder() - .name("connect-timeout-ms") - .displayName("Connect Timeout (ms)") - .description("Connection timeout (in milliseconds). 0 means no timeout. If no value is set, the underlying default will be used.") - .required(false) - .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT) - .addValidator(StandardValidators.NON_NEGATIVE_INTEGER_VALIDATOR) - .build(); - - private List properties; - private Cluster cluster; - private Session cassandraSession; - - @Override - public void init(final ControllerServiceInitializationContext context) { - List props = new ArrayList<>(); - - props.add(CONTACT_POINTS); - props.add(CLIENT_AUTH); - props.add(CONSISTENCY_LEVEL); - props.add(COMPRESSION_TYPE); - props.add(KEYSPACE); - props.add(USERNAME); - props.add(PASSWORD); - props.add(PROP_SSL_CONTEXT_SERVICE); - props.add(READ_TIMEOUT_MS); - props.add(CONNECT_TIMEOUT_MS); - - properties = props; - } - - @Override - public List getSupportedPropertyDescriptors() { - return properties; - } - - @OnEnabled - public void onEnabled(final ConfigurationContext context) { - connectToCassandra(context); - } - - @OnDisabled - public void onDisabled(){ - if (cassandraSession != null) { - cassandraSession.close(); - cassandraSession = null; - } - if (cluster != null) { - cluster.close(); - cluster = null; - } - } - - @Override - public Cluster getCluster() { - if (cluster != null) { - return cluster; - } else { - throw new ProcessException("Unable to get the Cassandra cluster detail."); - } - } - - @Override - public Session getCassandraSession() { - if (cassandraSession != null) { - return cassandraSession; - } else { - throw new ProcessException("Unable to get the Cassandra session."); - } - } - - private void connectToCassandra(ConfigurationContext context) { - if (cluster == null) { - ComponentLog log = getLogger(); - final String contactPointList = context.getProperty(CONTACT_POINTS).evaluateAttributeExpressions().getValue(); - final String consistencyLevel = context.getProperty(CONSISTENCY_LEVEL).getValue(); - final String compressionType = context.getProperty(COMPRESSION_TYPE).getValue(); - - List contactPoints = getContactPoints(contactPointList); - - // Set up the client for secure (SSL/TLS communications) if configured to do so - final SSLContextService sslService = - context.getProperty(PROP_SSL_CONTEXT_SERVICE).asControllerService(SSLContextService.class); - final SSLContext sslContext; - - if (sslService == null) { - sslContext = null; - } else { - sslContext = sslService.createContext();; - } - - final String username, password; - PropertyValue usernameProperty = context.getProperty(USERNAME).evaluateAttributeExpressions(); - PropertyValue passwordProperty = context.getProperty(PASSWORD).evaluateAttributeExpressions(); - - if (usernameProperty != null && passwordProperty != null) { - username = usernameProperty.getValue(); - password = passwordProperty.getValue(); - } else { - username = null; - password = null; - } - - final Integer readTimeoutMillis = context.getProperty(READ_TIMEOUT_MS).evaluateAttributeExpressions().asInteger(); - final Integer connectTimeoutMillis = context.getProperty(CONNECT_TIMEOUT_MS).evaluateAttributeExpressions().asInteger(); - - // Create the cluster and connect to it - Cluster newCluster = createCluster(contactPoints, sslContext, username, password, compressionType, readTimeoutMillis, connectTimeoutMillis); - PropertyValue keyspaceProperty = context.getProperty(KEYSPACE).evaluateAttributeExpressions(); - final Session newSession; - if (keyspaceProperty != null) { - newSession = newCluster.connect(keyspaceProperty.getValue()); - } else { - newSession = newCluster.connect(); - } - newCluster.getConfiguration().getQueryOptions().setConsistencyLevel(ConsistencyLevel.valueOf(consistencyLevel)); - Metadata metadata = newCluster.getMetadata(); - log.info("Connected to Cassandra cluster: {}", new Object[]{metadata.getClusterName()}); - - cluster = newCluster; - cassandraSession = newSession; - } - } - - private List getContactPoints(String contactPointList) { - - if (contactPointList == null) { - return null; - } - - final String[] contactPointStringList = contactPointList.split(","); - List contactPoints = new ArrayList<>(); - - for (String contactPointEntry : contactPointStringList) { - String[] addresses = contactPointEntry.split(":"); - final String hostName = addresses[0].trim(); - final int port = (addresses.length > 1) ? Integer.parseInt(addresses[1].trim()) : DEFAULT_CASSANDRA_PORT; - - contactPoints.add(new InetSocketAddress(hostName, port)); - } - - return contactPoints; - } - - private Cluster createCluster(final List contactPoints, final SSLContext sslContext, - final String username, final String password, final String compressionType, - final Integer readTimeoutMillis, final Integer connectTimeoutMillis) { - - Cluster.Builder builder = Cluster.builder().addContactPointsWithPorts(contactPoints); - if (sslContext != null) { - final SSLOptions sslOptions = RemoteEndpointAwareJdkSSLOptions.builder() - .withSSLContext(sslContext) - .build(); - builder = builder.withSSL(sslOptions); - } - - if (username != null && password != null) { - builder = builder.withCredentials(username, password); - } - - if (ProtocolOptions.Compression.SNAPPY.name().equals(compressionType)) { - builder = builder.withCompression(ProtocolOptions.Compression.SNAPPY); - } else if (ProtocolOptions.Compression.LZ4.name().equals(compressionType)) { - builder = builder.withCompression(ProtocolOptions.Compression.LZ4); - } - - SocketOptions socketOptions = new SocketOptions(); - if (readTimeoutMillis != null) { - socketOptions.setReadTimeoutMillis(readTimeoutMillis); - } - if (connectTimeoutMillis != null) { - socketOptions.setConnectTimeoutMillis(connectTimeoutMillis); - } - - builder.withSocketOptions(socketOptions); - - return builder.build(); - } -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService deleted file mode 100644 index 045f90625fc1..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/main/resources/META-INF/services/org.apache.nifi.controller.ControllerService +++ /dev/null @@ -1,16 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -org.apache.nifi.service.CassandraSessionProvider \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/test/java/org/apache/nifi/service/MockCassandraProcessor.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/test/java/org/apache/nifi/service/MockCassandraProcessor.java deleted file mode 100644 index 4891fe5a07a2..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/test/java/org/apache/nifi/service/MockCassandraProcessor.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.service; - -import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.processor.AbstractProcessor; -import org.apache.nifi.processor.ProcessContext; -import org.apache.nifi.processor.ProcessSession; -import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.processor.util.StandardValidators; - -import java.util.Collections; -import java.util.List; - -/** - * Mock Cassandra processor for testing CassandraSessionProvider - */ -public class MockCassandraProcessor extends AbstractProcessor{ - private static PropertyDescriptor CASSANDRA_SESSION_PROVIDER = new PropertyDescriptor.Builder() - .name("cassandra-session-provider") - .displayName("Cassandra Session Provider") - .required(true) - .description("Controller Service to obtain a Cassandra connection session") - .addValidator(StandardValidators.NON_EMPTY_VALIDATOR) - .identifiesControllerService(CassandraSessionProvider.class) - .build(); - - @Override - public List getSupportedPropertyDescriptors() { - return Collections.singletonList(CASSANDRA_SESSION_PROVIDER); - } - - @Override - public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException { - - } -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/test/java/org/apache/nifi/service/TestCassandraSessionProvider.java b/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/test/java/org/apache/nifi/service/TestCassandraSessionProvider.java deleted file mode 100644 index d6dbb5179992..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/nifi-cassandra-services/src/test/java/org/apache/nifi/service/TestCassandraSessionProvider.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.nifi.service; - -import org.apache.nifi.components.PropertyDescriptor; -import org.apache.nifi.reporting.InitializationException; -import org.apache.nifi.util.TestRunner; -import org.apache.nifi.util.TestRunners; -import org.junit.jupiter.api.BeforeAll; -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.assertTrue; - -public class TestCassandraSessionProvider { - - private static TestRunner runner; - private static CassandraSessionProvider sessionProvider; - - @BeforeAll - public static void setup() throws InitializationException { - MockCassandraProcessor mockCassandraProcessor = new MockCassandraProcessor(); - sessionProvider = new CassandraSessionProvider(); - - runner = TestRunners.newTestRunner(mockCassandraProcessor); - runner.addControllerService("cassandra-session-provider", sessionProvider); - } - - @Test - public void testGetPropertyDescriptors() { - List properties = sessionProvider.getPropertyDescriptors(); - - assertEquals(10, properties.size()); - assertTrue(properties.contains(CassandraSessionProvider.CLIENT_AUTH)); - 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)); - } - -} diff --git a/nifi-nar-bundles/nifi-cassandra-bundle/pom.xml b/nifi-nar-bundles/nifi-cassandra-bundle/pom.xml deleted file mode 100644 index 0107c8de154e..000000000000 --- a/nifi-nar-bundles/nifi-cassandra-bundle/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - 4.0.0 - - - nifi-standard-shared-bom - org.apache.nifi - 2.0.0-SNAPSHOT - ../nifi-standard-shared-bundle/nifi-standard-shared-bom - - - - 3.11.5 - 19.0 - - - nifi-cassandra-bundle - pom - - - nifi-cassandra-processors - nifi-cassandra-nar - nifi-cassandra-distributedmapcache-service - nifi-cassandra-services-api - nifi-cassandra-services-api-nar - nifi-cassandra-services - nifi-cassandra-services-nar - - - - - - org.apache.nifi - nifi-cassandra-processors - 2.0.0-SNAPSHOT - - - com.google.guava - guava - ${cassandra.guava.version} - - - - diff --git a/nifi-nar-bundles/pom.xml b/nifi-nar-bundles/pom.xml index 3149499600b0..29fce6292f89 100755 --- a/nifi-nar-bundles/pom.xml +++ b/nifi-nar-bundles/pom.xml @@ -61,7 +61,6 @@ nifi-splunk-bundle nifi-jms-bundle nifi-beats-bundle - nifi-cassandra-bundle nifi-hive-bundle nifi-site-to-site-reporting-bundle nifi-mqtt-bundle @@ -231,11 +230,6 @@ nifi-http-context-map 2.0.0-SNAPSHOT - - org.apache.nifi - nifi-cassandra-services-api - 2.0.0-SNAPSHOT - org.apache.nifi nifi-volatile-provenance-repository