diff --git a/.gitignore b/.gitignore index a7abbccd68..916ab0de99 100644 --- a/.gitignore +++ b/.gitignore @@ -84,3 +84,5 @@ venv/ # build-scripts will put cassandra-builds into the directory cassandra-builds/ cassandra-dtest/ + +conf/triggers/trigger-example.jar diff --git a/CHANGES.txt b/CHANGES.txt index decc10dd72..1ca7c2f89a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.1.1 + * Fix copying of JAR of a trigger to temporary file (CASSANDRA-18264) * Fix possible NoSuchFileException when removing a snapshot (CASSANDRA-18211) * PaxosPrepare may add instances to the Electorate that are not in gossip (CASSANDRA-18194) * Fix PAXOS2_COMMIT_AND_PREPARE_RSP serialisation AssertionError (CASSANDRA-18164) diff --git a/examples/ssl-factory/README.adoc b/examples/ssl-factory/README.adoc new file mode 100644 index 0000000000..eb89f6fb70 --- /dev/null +++ b/examples/ssl-factory/README.adoc @@ -0,0 +1,22 @@ +Cassandra Custom SslContextFactory Example +========================================== + +This example shows custom SslContextFactory implementation based on Kubernetes secrets +For the documentation please refer to the javadocs for `K8SecretsSslContextFactory.java.` + +Installation +------------- + +Step 1: Build the Cassandra classes locally + +---- +$ cd +$ ant build +---- + +Step 2: Run tests for the security examples + +---- +$ cd /examples/ssl-factory +$ ant test +---- diff --git a/examples/ssl-factory/README.txt b/examples/ssl-factory/README.txt deleted file mode 100644 index d0d2b30f24..0000000000 --- a/examples/ssl-factory/README.txt +++ /dev/null @@ -1,19 +0,0 @@ -Cassandra Custom SslContextFactory Example: -========================================== - -Example-1: Custom SslContextFactory implementation based on Kubernetes secrets ------------------------------------------------------------------------------- -For the documentation please refer to the javadocs for the K8SecretsSslContextFactory.java. - - -Installation: -============ -Step 1: Build the Cassandra classes locally - -change directory to -run "ant build" - -Step 2: Run tests for the security examples - -change directory to /examples/ssl-factory -run "ant test" diff --git a/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java b/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java index 8b22fff118..adaba108f3 100644 --- a/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java +++ b/examples/ssl-factory/test/unit/org/apache/cassandra/security/KubernetesSecretsSslContextFactoryTest.java @@ -38,7 +38,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.config.EncryptionOptions; -import org.apache.cassandra.io.util.PathUtils; +import org.apache.cassandra.io.util.File; import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.KEYSTORE_PASSWORD_ENV_VAR; import static org.apache.cassandra.security.KubernetesSecretsSslContextFactory.ConfigKeys.KEYSTORE_UPDATED_TIMESTAMP_PATH; @@ -64,7 +64,7 @@ public class KubernetesSecretsSslContextFactoryTest private static void deleteFileIfExists(String file) { Path filePath = Paths.get(file); - boolean deleted = PathUtils.tryDelete(filePath); + boolean deleted = new File(filePath).toJavaIOFile().delete(); if (!deleted) { logger.warn("File {} could not be deleted.", filePath); diff --git a/examples/triggers/README.adoc b/examples/triggers/README.adoc new file mode 100644 index 0000000000..9c2461d608 --- /dev/null +++ b/examples/triggers/README.adoc @@ -0,0 +1,63 @@ +Cassandra Trigger Example +========================== + +The `AuditTrigger` class will create a basic audit of +activity on a table. + +Installation +------------- + +---- +$ cd /examples/triggers +$ ant install +---- + +It will build the trigger and copy it to `conf/triggers`. `AuditTrigger.properties` +in `conf` directory of this example will be automatically bundled into built jar file. +It is not needed to copy it to `conf/triggers` directory. + +Usage +----- + +Create the keyspace and table configured in `AuditTrigger.properties`: + +---- +cqlsh> CREATE KEYSPACE test WITH REPLICATION = + { 'class' : 'SimpleStrategy', 'replication_factor' : '1' }; +cqlsh> CREATE TABLE test.audit (key timeuuid, keyspace_name text, + table_name text, primary_key text, PRIMARY KEY(key)); +---- + +Create a table to add the trigger to: + +Note: The example currently only handles non-composite partition keys +---- +cqlsh> CREATE TABLE test.test (key text, value text, PRIMARY KEY(key)); +---- + +Configure the trigger on the table: + +---- +cqlsh> CREATE TRIGGER test1 ON test.test USING 'org.apache.cassandra.triggers.AuditTrigger'; +---- + +Start inserting data to the table that has the trigger. For each +partition added to the table an entry should appear in the 'audit' table: + +---- +cqlsh> INSERT INTO test.test (key, value) VALUES ('1', '1'); +---- + +An entry will be automatically added to `test.audit` table: + +---- +cqlsh> SELECT * FROM test.audit ; + +@ Row 1 +---------------+-------------------------------------- + key | 885141d0-ad7c-11ed-b917-9958320828b8 + keyspace_name | test + primary_key | 1 + table_name | test +---- + diff --git a/examples/triggers/README.txt b/examples/triggers/README.txt deleted file mode 100644 index e5f1ecfd70..0000000000 --- a/examples/triggers/README.txt +++ /dev/null @@ -1,36 +0,0 @@ -Cassandra Trigger Example: -========================== - -The AuditTrigger class will create a basic audit of -activity on a table. - -Installation: -============ -change directory to /examples/triggers -run "ant jar" -Copy build/trigger-example.jar to /triggers/ -Copy conf/* to /conf/ - -Create the keyspace and table configured in AuditTrigger.properties: - CREATE KEYSPACE test WITH REPLICATION = - { 'class' : 'SimpleStrategy', 'replication_factor' : '1' }; - CREATE TABLE test.audit (key timeuuid, keyspace_name text, - table_name text, primary_key text, PRIMARY KEY(key)); - -Create a table to add the trigger to: - CREATE TABLE test.test (key text, value text, PRIMARY KEY(key)); - Note: The example currently only handles non-composite partition keys - -Configure the trigger on the table: - CREATE TRIGGER test1 ON test.test - USING 'org.apache.cassandra.triggers.AuditTrigger'; - -Start inserting data to the table that has the trigger. For each -partition added to the table an entry should appear in the 'audit' table: - INSERT INTO test.test (key, value) values ('1', '1'); - SELECT * FROM test.audit; - - key | keyspace_name | primary_key | table_name - --------------------------------------+---------------+-------------+------------ - 7dc75b60-770f-11e5-9019-033d8af33e6f | test | 1 | test - diff --git a/examples/triggers/build.xml b/examples/triggers/build.xml index 450def6773..b5a0f6f95d 100644 --- a/examples/triggers/build.xml +++ b/examples/triggers/build.xml @@ -57,7 +57,12 @@ + + + + + diff --git a/examples/triggers/src/org/apache/cassandra/triggers/AuditTrigger.java b/examples/triggers/src/org/apache/cassandra/triggers/AuditTrigger.java index b0172b0186..657394adfa 100644 --- a/examples/triggers/src/org/apache/cassandra/triggers/AuditTrigger.java +++ b/examples/triggers/src/org/apache/cassandra/triggers/AuditTrigger.java @@ -29,19 +29,27 @@ import org.apache.cassandra.db.partitions.Partition; import org.apache.cassandra.db.partitions.PartitionUpdate; import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.utils.FBUtilities; -import org.apache.cassandra.utils.UUIDGen; +import org.apache.cassandra.utils.TimeUUID; public class AuditTrigger implements ITrigger { - private Properties properties = loadProperties(); + private static final String AUDIT_PROPERTIES_FILE_NAME = "AuditTrigger.properties"; + + private final Properties properties; + private final String auditKeyspace; + private final String auditTable; + + public AuditTrigger() + { + properties = loadProperties(); + auditKeyspace = properties.getProperty("keyspace"); + auditTable = properties.getProperty("table"); + } public Collection augment(Partition update) { - String auditKeyspace = properties.getProperty("keyspace"); - String auditTable = properties.getProperty("table"); - TableMetadata metadata = Schema.instance.getTableMetadata(auditKeyspace, auditTable); - PartitionUpdate.SimpleBuilder audit = PartitionUpdate.simpleBuilder(metadata, UUIDGen.getTimeUUID()); + PartitionUpdate.SimpleBuilder audit = PartitionUpdate.simpleBuilder(metadata, TimeUUID.Generator.nextTimeUUID()); audit.row() .add("keyspace_name", update.metadata().keyspace) @@ -54,7 +62,7 @@ public class AuditTrigger implements ITrigger private static Properties loadProperties() { Properties properties = new Properties(); - InputStream stream = AuditTrigger.class.getClassLoader().getResourceAsStream("AuditTrigger.properties"); + InputStream stream = AuditTrigger.class.getClassLoader().getResourceAsStream(AUDIT_PROPERTIES_FILE_NAME); try { properties.load(stream); diff --git a/ide/idea/workspace.xml b/ide/idea/workspace.xml index f30a44ade3..8aea3156d4 100644 --- a/ide/idea/workspace.xml +++ b/ide/idea/workspace.xml @@ -227,6 +227,7 @@ -Dcassandra.logdir=$PROJECT_DIR$/data/logs -Dcassandra.reads.thresholds.coordinator.defensive_checks_enabled=true -Dcassandra.storagedir=$PROJECT_DIR$/data + -Dcassandra.triggers_dir=$PROJECT_DIR$/conf/triggers -Djava.library.path=$PROJECT_DIR$/lib/sigar-bin -Dlogback.configurationFile=file://$PROJECT_DIR$/conf/logback.xml -Xmx1G diff --git a/src/java/org/apache/cassandra/triggers/CustomClassLoader.java b/src/java/org/apache/cassandra/triggers/CustomClassLoader.java index 16b182ecf4..95be219fb9 100644 --- a/src/java/org/apache/cassandra/triggers/CustomClassLoader.java +++ b/src/java/org/apache/cassandra/triggers/CustomClassLoader.java @@ -19,11 +19,10 @@ package org.apache.cassandra.triggers; * under the License. * */ - - import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.StandardCopyOption; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiPredicate; @@ -39,15 +38,15 @@ import static java.nio.file.Files.*; /** * Custom class loader will load the classes from the class path, CCL will load - * the classes from the the URL first, if it cannot find the required class it - * will let the parent class loader do the its job. + * the classes from the URL first, if it cannot find the required class it + * will let the parent class loader do its job. * * Note: If the CCL is GC'ed then the associated classes will be unloaded. */ public class CustomClassLoader extends URLClassLoader { private static final Logger logger = LoggerFactory.getLogger(CustomClassLoader.class); - private final Map> cache = new ConcurrentHashMap>(); + private final Map> cache = new ConcurrentHashMap<>(); private final ClassLoader parent; public CustomClassLoader(ClassLoader parent) @@ -83,7 +82,7 @@ public class CustomClassLoader extends URLClassLoader logger.info("Loading new jar {}", inputJar.absolutePath()); try { - copy(inputJar.toPath(), out.toPath()); + copy(inputJar.toPath(), out.toPath(), StandardCopyOption.REPLACE_EXISTING); addURL(out.toPath().toUri().toURL()); } catch (IOException ex)