From dbbc40bac96e25ecc2687c87a779235171ad2396 Mon Sep 17 00:00:00 2001 From: Alexandre Dutra Date: Mon, 8 Feb 2016 14:39:54 +0100 Subject: [PATCH 1/2] JAVA-1069: Bootstrap driver-examples module. This commit renames the older 'driver-examples' submodule to 'driver-tests'. It contains substantial contributions by Olivier Michallat (@olim7t). --- README.md | 2 + changelog/README.md | 1 + driver-examples/README.md | 10 ++ driver-examples/pom.xml | 79 +++++---- .../basic/CreateAndPopulateKeyspace.java | 166 ++++++++++++++++++ .../examples/basic/ReadCassandraVersion.java | 72 ++++++++ .../basic/ReadTopologyAndSchemaMetadata.java | 64 +++++++ .../src/main/resources/logback.xml | 39 ++++ .../osgi/README.md | 5 +- .../osgi/pom.xml | 8 +- .../driver/osgi/api/MailboxException.java | 0 .../driver/osgi/api/MailboxMessage.java | 0 .../driver/osgi/api/MailboxService.java | 0 .../datastax/driver/osgi/impl/Activator.java | 0 .../driver/osgi/impl/MailboxImpl.java | 0 .../datastax/driver/osgi/BundleOptions.java | 0 .../driver/osgi/CCMBridgeListener.java | 0 .../driver/osgi/MailboxServiceDefaultIT.java | 0 .../driver/osgi/MailboxServiceGuava17IT.java | 0 .../driver/osgi/MailboxServiceGuava18IT.java | 0 .../driver/osgi/MailboxServiceGuava19IT.java | 0 .../driver/osgi/MailboxServiceShadedIT.java | 0 .../driver/osgi/MailboxServiceTests.java | 0 .../datastax/driver/osgi/VersionProvider.java | 0 .../osgi/src/test/resources/exam.properties | 0 .../osgi/src/test/resources/logback.xml | 0 driver-tests/pom.xml | 83 +++++++++ .../stress/README.md | 6 +- .../stress/bin/build | 0 .../stress/bin/stress | 2 +- .../stress/pom.xml | 6 +- .../driver/stress/AsynchronousConsumer.java | 0 .../driver/stress/BlockingConsumer.java | 0 .../stress/ConsistencyLevelConverter.java | 0 .../com/datastax/driver/stress/Consumer.java | 0 .../datastax/driver/stress/Generators.java | 0 .../driver/stress/QueryGenerator.java | 0 .../com/datastax/driver/stress/Reporter.java | 0 .../com/datastax/driver/stress/Stress.java | 0 pom.xml | 1 + 40 files changed, 500 insertions(+), 44 deletions(-) create mode 100644 driver-examples/README.md create mode 100644 driver-examples/src/main/java/com/datastax/driver/examples/basic/CreateAndPopulateKeyspace.java create mode 100644 driver-examples/src/main/java/com/datastax/driver/examples/basic/ReadCassandraVersion.java create mode 100644 driver-examples/src/main/java/com/datastax/driver/examples/basic/ReadTopologyAndSchemaMetadata.java create mode 100644 driver-examples/src/main/resources/logback.xml rename {driver-examples => driver-tests}/osgi/README.md (86%) rename {driver-examples => driver-tests}/osgi/pom.xml (97%) rename {driver-examples => driver-tests}/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxException.java (100%) rename {driver-examples => driver-tests}/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxMessage.java (100%) rename {driver-examples => driver-tests}/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxService.java (100%) rename {driver-examples => driver-tests}/osgi/src/main/java/com/datastax/driver/osgi/impl/Activator.java (100%) rename {driver-examples => driver-tests}/osgi/src/main/java/com/datastax/driver/osgi/impl/MailboxImpl.java (100%) rename {driver-examples => driver-tests}/osgi/src/test/java/com/datastax/driver/osgi/BundleOptions.java (100%) rename {driver-examples => driver-tests}/osgi/src/test/java/com/datastax/driver/osgi/CCMBridgeListener.java (100%) rename {driver-examples => driver-tests}/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceDefaultIT.java (100%) rename {driver-examples => driver-tests}/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava17IT.java (100%) rename {driver-examples => driver-tests}/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava18IT.java (100%) rename {driver-examples => driver-tests}/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava19IT.java (100%) rename {driver-examples => driver-tests}/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceShadedIT.java (100%) rename {driver-examples => driver-tests}/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceTests.java (100%) rename {driver-examples => driver-tests}/osgi/src/test/java/com/datastax/driver/osgi/VersionProvider.java (100%) rename {driver-examples => driver-tests}/osgi/src/test/resources/exam.properties (100%) rename {driver-examples => driver-tests}/osgi/src/test/resources/logback.xml (100%) create mode 100644 driver-tests/pom.xml rename {driver-examples => driver-tests}/stress/README.md (77%) rename {driver-examples => driver-tests}/stress/bin/build (100%) rename {driver-examples => driver-tests}/stress/bin/stress (87%) rename {driver-examples => driver-tests}/stress/pom.xml (94%) rename {driver-examples => driver-tests}/stress/src/main/java/com/datastax/driver/stress/AsynchronousConsumer.java (100%) rename {driver-examples => driver-tests}/stress/src/main/java/com/datastax/driver/stress/BlockingConsumer.java (100%) rename {driver-examples => driver-tests}/stress/src/main/java/com/datastax/driver/stress/ConsistencyLevelConverter.java (100%) rename {driver-examples => driver-tests}/stress/src/main/java/com/datastax/driver/stress/Consumer.java (100%) rename {driver-examples => driver-tests}/stress/src/main/java/com/datastax/driver/stress/Generators.java (100%) rename {driver-examples => driver-tests}/stress/src/main/java/com/datastax/driver/stress/QueryGenerator.java (100%) rename {driver-examples => driver-tests}/stress/src/main/java/com/datastax/driver/stress/Reporter.java (100%) rename {driver-examples => driver-tests}/stress/src/main/java/com/datastax/driver/stress/Stress.java (100%) diff --git a/README.md b/README.md index 3867a412d6d..51f1496aa21 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,10 @@ The driver contains the following modules: - driver-core: the core layer. - driver-mapping: the object mapper. +- driver-extras: optional features for the Java driver. - driver-examples: example applications using the other modules which are only meant for demonstration purposes. +- driver-tests: tests for the java-driver. **Useful links:** diff --git a/changelog/README.md b/changelog/README.md index 94f67b5e15f..a9e4b03fe64 100644 --- a/changelog/README.md +++ b/changelog/README.md @@ -20,6 +20,7 @@ - [bug] JAVA-1046: (Dynamic)CompositeTypes need to be parsed as string literal, not blob. - [improvement] JAVA-1164: Clarify documentation on Host.listenAddress and broadcastAddress. - [improvement] JAVA-1171: Add Host method to determine if DSE Graph is enabled. +- [improvement]: JAVA-1069: Bootstrap driver-examples module. Merged from 2.1 branch: diff --git a/driver-examples/README.md b/driver-examples/README.md new file mode 100644 index 00000000000..553cdbc5117 --- /dev/null +++ b/driver-examples/README.md @@ -0,0 +1,10 @@ +# DataStax Java Driver for Apache Cassandra - Examples + +This module contains examples of how to use the DataStax Java driver for +Apache Cassandra. + +## Usage + +Unless otherwise stated, all examples assume that you have a single-node Cassandra 3.0 cluster +listening on localhost:9042. + diff --git a/driver-examples/pom.xml b/driver-examples/pom.xml index b278ea3c165..be29915a546 100644 --- a/driver-examples/pom.xml +++ b/driver-examples/pom.xml @@ -15,16 +15,18 @@ limitations under the License. --> - - 4.0.0 + - com.datastax.cassandra cassandra-driver-parent + com.datastax.cassandra 3.0.1-SNAPSHOT - cassandra-driver-examples-parent - pom - DataStax Java Driver for Apache Cassandra Examples + 4.0.0 + + cassandra-driver-examples + + DataStax Java Driver for Apache Cassandra - Examples A collection of examples to demonstrate DataStax Java Driver for Apache Cassandra. https://github.com/datastax/java-driver @@ -32,36 +34,26 @@ ${project.parent.basedir} - - stress - osgi - + - - - Apache 2 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - Apache License Version 2.0 - - + + com.datastax.cassandra + cassandra-driver-core + ${project.parent.version} + - - scm:git:git@github.com:datastax/java-driver.git - scm:git:git@github.com:datastax/java-driver.git - https://github.com/datastax/java-driver - HEAD - + + ch.qos.logback + logback-classic + 1.1.3 + - - - Various - DataStax - - + + + org.apache.maven.plugins maven-install-plugin @@ -70,6 +62,7 @@ true + org.apache.maven.plugins maven-deploy-plugin @@ -78,6 +71,32 @@ true + + + + + + Apache 2 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + Apache License Version 2.0 + + + + + scm:git:git@github.com:datastax/java-driver.git + scm:git:git@github.com:datastax/java-driver.git + https://github.com/datastax/java-driver + HEAD + + + + + Various + DataStax + + + diff --git a/driver-examples/src/main/java/com/datastax/driver/examples/basic/CreateAndPopulateKeyspace.java b/driver-examples/src/main/java/com/datastax/driver/examples/basic/CreateAndPopulateKeyspace.java new file mode 100644 index 00000000000..14c843dd58c --- /dev/null +++ b/driver-examples/src/main/java/com/datastax/driver/examples/basic/CreateAndPopulateKeyspace.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2012-2015 DataStax Inc. + * + * 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. + */ +package com.datastax.driver.examples.basic; + +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; + +/** + * Creates a keyspace and tables, and loads some data into them. + *

+ * Preconditions: + * - a Cassandra cluster is running and accessible through the contacts points identified by CONTACT_POINTS and PORT. + *

+ * Side effects: + * - creates a new keyspace "simplex" in the cluster. It a keyspace with this name already exists, it will be reused; + * - creates two tables "simplex.songs" and "simplex.playlists". If they exist already, they will be reused; + * - inserts a row in each table. + * + * @see Java driver online manual + */ +public class CreateAndPopulateKeyspace { + + static String[] CONTACT_POINTS = {"127.0.0.1"}; + static int PORT = 9042; + + public static void main(String[] args) { + + CreateAndPopulateKeyspace client = new CreateAndPopulateKeyspace(); + + try { + + client.connect(CONTACT_POINTS, PORT); + client.createSchema(); + client.loadData(); + client.querySchema(); + + } finally { + client.close(); + } + } + + private Cluster cluster; + + private Session session; + + /** + * Initiates a connection to the cluster + * specified by the given contact point. + * + * @param contactPoints the contact points to use. + * @param port the port to use. + */ + public void connect(String[] contactPoints, int port) { + + cluster = Cluster.builder() + .addContactPoints(contactPoints).withPort(port) + .build(); + + System.out.printf("Connected to cluster: %s%n", cluster.getMetadata().getClusterName()); + + session = cluster.connect(); + } + + /** + * Creates the schema (keyspace) and tables + * for this example. + */ + public void createSchema() { + + session.execute("CREATE KEYSPACE IF NOT EXISTS simplex WITH replication " + + "= {'class':'SimpleStrategy', 'replication_factor':1};"); + + session.execute( + "CREATE TABLE IF NOT EXISTS simplex.songs (" + + "id uuid PRIMARY KEY," + + "title text," + + "album text," + + "artist text," + + "tags set," + + "data blob" + + ");"); + + session.execute( + "CREATE TABLE IF NOT EXISTS simplex.playlists (" + + "id uuid," + + "title text," + + "album text, " + + "artist text," + + "song_id uuid," + + "PRIMARY KEY (id, title, album, artist)" + + ");"); + } + + /** + * Inserts data into the tables. + */ + public void loadData() { + + session.execute( + "INSERT INTO simplex.songs (id, title, album, artist, tags) " + + "VALUES (" + + "756716f7-2e54-4715-9f00-91dcbea6cf50," + + "'La Petite Tonkinoise'," + + "'Bye Bye Blackbird'," + + "'Joséphine Baker'," + + "{'jazz', '2013'})" + + ";"); + + session.execute( + "INSERT INTO simplex.playlists (id, song_id, title, album, artist) " + + "VALUES (" + + "2cc9ccb7-6221-4ccb-8387-f22b6a1b354d," + + "756716f7-2e54-4715-9f00-91dcbea6cf50," + + "'La Petite Tonkinoise'," + + "'Bye Bye Blackbird'," + + "'Joséphine Baker'" + + ");"); + } + + /** + * Queries and displays data. + */ + public void querySchema() { + + ResultSet results = session.execute( + "SELECT * FROM simplex.playlists " + + "WHERE id = 2cc9ccb7-6221-4ccb-8387-f22b6a1b354d;"); + + System.out.printf("%-30s\t%-20s\t%-20s%n", "title", "album", "artist"); + System.out.println("-------------------------------+-----------------------+--------------------"); + + for (Row row : results) { + + System.out.printf("%-30s\t%-20s\t%-20s%n", + row.getString("title"), + row.getString("album"), + row.getString("artist")); + + } + + } + + /** + * Closes the session and the cluster. + */ + public void close() { + session.close(); + cluster.close(); + } + +} diff --git a/driver-examples/src/main/java/com/datastax/driver/examples/basic/ReadCassandraVersion.java b/driver-examples/src/main/java/com/datastax/driver/examples/basic/ReadCassandraVersion.java new file mode 100644 index 00000000000..69bac907119 --- /dev/null +++ b/driver-examples/src/main/java/com/datastax/driver/examples/basic/ReadCassandraVersion.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012-2015 DataStax Inc. + * + * 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. + */ +package com.datastax.driver.examples.basic; + +import com.datastax.driver.core.Cluster; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; + +/** + * Connects to a Cassandra cluster and extracts basic information from it. + *

+ * Preconditions: + * - a Cassandra cluster is running and accessible through the contacts points identified by CONTACT_POINTS and PORT. + *

+ * Side effects: none. + * + * @see Java driver online manual + */ +public class ReadCassandraVersion { + + static String[] CONTACT_POINTS = {"127.0.0.1"}; + static int PORT = 9042; + + public static void main(String[] args) { + + Cluster cluster = null; + try { + // The Cluster object is the main entry point of the driver. + // It holds the known state of the actual Cassandra cluster (notably the Metadata). + // This class is thread-safe, you should create a single instance (per target Cassandra cluster), and share + // it throughout your application. + cluster = Cluster.builder() + .addContactPoints(CONTACT_POINTS).withPort(PORT) + .build(); + + // The Session is what you use to execute queries. Likewise, it is thread-safe and should be reused. + Session session = cluster.connect(); + + // We use execute to send a query to Cassandra. This returns a ResultSet, which is essentially a collection + // of Row objects. + ResultSet rs = session.execute("select release_version from system.local"); + // Extract the first row (which is the only one in this case). + Row row = rs.one(); + + // Extract the value of the first (and only) column from the row. + String releaseVersion = row.getString("release_version"); + System.out.printf("Cassandra version is: %s%n", releaseVersion); + + } finally { + // Close the cluster after we’re done with it. This will also close any session that was created from this + // cluster. + // This step is important because it frees underlying resources (TCP connections, thread pools...). In a + // real application, you would typically do this at shutdown (for example, when undeploying your webapp). + if (cluster != null) + cluster.close(); + } + } +} diff --git a/driver-examples/src/main/java/com/datastax/driver/examples/basic/ReadTopologyAndSchemaMetadata.java b/driver-examples/src/main/java/com/datastax/driver/examples/basic/ReadTopologyAndSchemaMetadata.java new file mode 100644 index 00000000000..feebe7aa990 --- /dev/null +++ b/driver-examples/src/main/java/com/datastax/driver/examples/basic/ReadTopologyAndSchemaMetadata.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012-2015 DataStax Inc. + * + * 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. + */ +package com.datastax.driver.examples.basic; + +import com.datastax.driver.core.*; + +/** + * Gathers information about a Cassandra cluster's topology (which nodes belong to the cluster) and schema (what + * keyspaces, tables, etc. exist in this cluster). + *

+ * Preconditions: + * - a Cassandra cluster is running and accessible through the contacts points identified by CONTACT_POINTS and PORT. + *

+ * Side effects: none. + * + * @see Java driver online manual + */ +public class ReadTopologyAndSchemaMetadata { + + static String[] CONTACT_POINTS = {"127.0.0.1"}; + static int PORT = 9042; + + public static void main(String[] args) { + + Cluster cluster = null; + try { + cluster = Cluster.builder() + .addContactPoints(CONTACT_POINTS).withPort(PORT) + .build(); + + Metadata metadata = cluster.getMetadata(); + System.out.printf("Connected to cluster: %s%n", metadata.getClusterName()); + + for (Host host : metadata.getAllHosts()) { + System.out.printf("Datatacenter: %s; Host: %s; Rack: %s%n", + host.getDatacenter(), host.getAddress(), host.getRack()); + } + + for (KeyspaceMetadata keyspace : metadata.getKeyspaces()) { + for (TableMetadata table : keyspace.getTables()) { + System.out.printf("Keyspace: %s; Table: %s%n", + keyspace.getName(), table.getName()); + } + } + + } finally { + if (cluster != null) + cluster.close(); + } + } +} diff --git a/driver-examples/src/main/resources/logback.xml b/driver-examples/src/main/resources/logback.xml new file mode 100644 index 00000000000..3fb2dcf5269 --- /dev/null +++ b/driver-examples/src/main/resources/logback.xml @@ -0,0 +1,39 @@ + + + + + + + + %-5level - %msg%n + + + + + + + + + + + + diff --git a/driver-examples/osgi/README.md b/driver-tests/osgi/README.md similarity index 86% rename from driver-examples/osgi/README.md rename to driver-tests/osgi/README.md index ed9530d380e..0a4d698eb3d 100644 --- a/driver-examples/osgi/README.md +++ b/driver-tests/osgi/README.md @@ -1,7 +1,6 @@ # OSGi Example -A simple example application that demonstrates using the Java Driver in -an OSGi service. +A simple test for the Java Driver in an OSGi environment. `MailboxService` is an OSGi service that uses Cassandra to store messages that can be retrieved by email address. @@ -25,7 +24,7 @@ Once `mvn verify` completes, the bundle jar will be present in the `target/` dir The project includes integration tests that verify the service can be activated and used in an OSGi container. It also verifies that -driver-core can be used in an OSGi container in the following +the Java driver can be used in an OSGi container in the following configurations: 1. Default (default classifier with all dependencies) diff --git a/driver-examples/osgi/pom.xml b/driver-tests/osgi/pom.xml similarity index 97% rename from driver-examples/osgi/pom.xml rename to driver-tests/osgi/pom.xml index 707103caff4..d132e8a791a 100644 --- a/driver-examples/osgi/pom.xml +++ b/driver-tests/osgi/pom.xml @@ -20,14 +20,14 @@ 4.0.0 com.datastax.cassandra - cassandra-driver-examples-parent + cassandra-driver-tests-parent 3.0.1-SNAPSHOT - cassandra-driver-examples-osgi + cassandra-driver-tests-osgi bundle - DataStax Java Driver for Apache Cassandra Examples - OSGi - An example of using DataStax Java Driver in an OSGi container. + DataStax Java Driver for Apache Cassandra Tests - OSGi + A test for the DataStax Java Driver in an OSGi container. https://github.com/datastax/java-driver diff --git a/driver-examples/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxException.java b/driver-tests/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxException.java similarity index 100% rename from driver-examples/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxException.java rename to driver-tests/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxException.java diff --git a/driver-examples/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxMessage.java b/driver-tests/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxMessage.java similarity index 100% rename from driver-examples/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxMessage.java rename to driver-tests/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxMessage.java diff --git a/driver-examples/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxService.java b/driver-tests/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxService.java similarity index 100% rename from driver-examples/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxService.java rename to driver-tests/osgi/src/main/java/com/datastax/driver/osgi/api/MailboxService.java diff --git a/driver-examples/osgi/src/main/java/com/datastax/driver/osgi/impl/Activator.java b/driver-tests/osgi/src/main/java/com/datastax/driver/osgi/impl/Activator.java similarity index 100% rename from driver-examples/osgi/src/main/java/com/datastax/driver/osgi/impl/Activator.java rename to driver-tests/osgi/src/main/java/com/datastax/driver/osgi/impl/Activator.java diff --git a/driver-examples/osgi/src/main/java/com/datastax/driver/osgi/impl/MailboxImpl.java b/driver-tests/osgi/src/main/java/com/datastax/driver/osgi/impl/MailboxImpl.java similarity index 100% rename from driver-examples/osgi/src/main/java/com/datastax/driver/osgi/impl/MailboxImpl.java rename to driver-tests/osgi/src/main/java/com/datastax/driver/osgi/impl/MailboxImpl.java diff --git a/driver-examples/osgi/src/test/java/com/datastax/driver/osgi/BundleOptions.java b/driver-tests/osgi/src/test/java/com/datastax/driver/osgi/BundleOptions.java similarity index 100% rename from driver-examples/osgi/src/test/java/com/datastax/driver/osgi/BundleOptions.java rename to driver-tests/osgi/src/test/java/com/datastax/driver/osgi/BundleOptions.java diff --git a/driver-examples/osgi/src/test/java/com/datastax/driver/osgi/CCMBridgeListener.java b/driver-tests/osgi/src/test/java/com/datastax/driver/osgi/CCMBridgeListener.java similarity index 100% rename from driver-examples/osgi/src/test/java/com/datastax/driver/osgi/CCMBridgeListener.java rename to driver-tests/osgi/src/test/java/com/datastax/driver/osgi/CCMBridgeListener.java diff --git a/driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceDefaultIT.java b/driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceDefaultIT.java similarity index 100% rename from driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceDefaultIT.java rename to driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceDefaultIT.java diff --git a/driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava17IT.java b/driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava17IT.java similarity index 100% rename from driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava17IT.java rename to driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava17IT.java diff --git a/driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava18IT.java b/driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava18IT.java similarity index 100% rename from driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava18IT.java rename to driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava18IT.java diff --git a/driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava19IT.java b/driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava19IT.java similarity index 100% rename from driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava19IT.java rename to driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceGuava19IT.java diff --git a/driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceShadedIT.java b/driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceShadedIT.java similarity index 100% rename from driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceShadedIT.java rename to driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceShadedIT.java diff --git a/driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceTests.java b/driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceTests.java similarity index 100% rename from driver-examples/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceTests.java rename to driver-tests/osgi/src/test/java/com/datastax/driver/osgi/MailboxServiceTests.java diff --git a/driver-examples/osgi/src/test/java/com/datastax/driver/osgi/VersionProvider.java b/driver-tests/osgi/src/test/java/com/datastax/driver/osgi/VersionProvider.java similarity index 100% rename from driver-examples/osgi/src/test/java/com/datastax/driver/osgi/VersionProvider.java rename to driver-tests/osgi/src/test/java/com/datastax/driver/osgi/VersionProvider.java diff --git a/driver-examples/osgi/src/test/resources/exam.properties b/driver-tests/osgi/src/test/resources/exam.properties similarity index 100% rename from driver-examples/osgi/src/test/resources/exam.properties rename to driver-tests/osgi/src/test/resources/exam.properties diff --git a/driver-examples/osgi/src/test/resources/logback.xml b/driver-tests/osgi/src/test/resources/logback.xml similarity index 100% rename from driver-examples/osgi/src/test/resources/logback.xml rename to driver-tests/osgi/src/test/resources/logback.xml diff --git a/driver-tests/pom.xml b/driver-tests/pom.xml new file mode 100644 index 00000000000..a8c65650b5d --- /dev/null +++ b/driver-tests/pom.xml @@ -0,0 +1,83 @@ + + + 4.0.0 + + com.datastax.cassandra + cassandra-driver-parent + 3.0.1-SNAPSHOT + + cassandra-driver-tests-parent + pom + DataStax Java Driver for Apache Cassandra Tests + Tests for the DataStax Java Driver for Apache Cassandra. + https://github.com/datastax/java-driver + + + ${project.parent.basedir} + + + + stress + osgi + + + + + Apache 2 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + Apache License Version 2.0 + + + + + scm:git:git@github.com:datastax/java-driver.git + scm:git:git@github.com:datastax/java-driver.git + https://github.com/datastax/java-driver + HEAD + + + + + Various + DataStax + + + + + + + org.apache.maven.plugins + maven-install-plugin + 2.5.1 + + true + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.1 + + true + + + + + diff --git a/driver-examples/stress/README.md b/driver-tests/stress/README.md similarity index 77% rename from driver-examples/stress/README.md rename to driver-tests/stress/README.md index 6c946719ecc..ea0ea61bc88 100644 --- a/driver-examples/stress/README.md +++ b/driver-tests/stress/README.md @@ -1,11 +1,11 @@ # Stress application -A simple example application that uses the java driver to stress test -Cassandra. This also somewhat stress test the java driver as a result. +A simple example application that uses the Java driver to stress test +Cassandra. This also somewhat stress tests the Java driver as a result. Please note that this simple example is far from being a complete stress application. In particular it currently supports a very limited number of -stress scenario. +stress scenarios. ## Usage diff --git a/driver-examples/stress/bin/build b/driver-tests/stress/bin/build similarity index 100% rename from driver-examples/stress/bin/build rename to driver-tests/stress/bin/build diff --git a/driver-examples/stress/bin/stress b/driver-tests/stress/bin/stress similarity index 87% rename from driver-examples/stress/bin/stress rename to driver-tests/stress/bin/stress index 8a3165a47ce..e47ec0d7955 100755 --- a/driver-examples/stress/bin/stress +++ b/driver-tests/stress/bin/stress @@ -12,7 +12,7 @@ fi if [ "x$STRESS_JAR" = "x" ]; then - STRESS_JAR="$SCRIPT_DIR/../target/cassandra-driver-examples-stress-*-jar-with-dependencies.jar" + STRESS_JAR="$SCRIPT_DIR/../target/cassandra-driver-tests-stress-*-jar-with-dependencies.jar" if [ ! -f $STRESS_JAR ]; then # Trash the version file in case there was some crap in it diff --git a/driver-examples/stress/pom.xml b/driver-tests/stress/pom.xml similarity index 94% rename from driver-examples/stress/pom.xml rename to driver-tests/stress/pom.xml index edc9fe3e95f..97083ac6b6e 100644 --- a/driver-examples/stress/pom.xml +++ b/driver-tests/stress/pom.xml @@ -19,12 +19,12 @@ 4.0.0 com.datastax.cassandra - cassandra-driver-examples-parent + cassandra-driver-tests-parent 3.0.1-SNAPSHOT - cassandra-driver-examples-stress + cassandra-driver-tests-stress jar - DataStax Java Driver for Apache Cassandra Examples - Stress + DataStax Java Driver for Apache Cassandra Tests - Stress A stress test example for DataStax Java Driver for Apache Cassandra. https://github.com/datastax/java-driver diff --git a/driver-examples/stress/src/main/java/com/datastax/driver/stress/AsynchronousConsumer.java b/driver-tests/stress/src/main/java/com/datastax/driver/stress/AsynchronousConsumer.java similarity index 100% rename from driver-examples/stress/src/main/java/com/datastax/driver/stress/AsynchronousConsumer.java rename to driver-tests/stress/src/main/java/com/datastax/driver/stress/AsynchronousConsumer.java diff --git a/driver-examples/stress/src/main/java/com/datastax/driver/stress/BlockingConsumer.java b/driver-tests/stress/src/main/java/com/datastax/driver/stress/BlockingConsumer.java similarity index 100% rename from driver-examples/stress/src/main/java/com/datastax/driver/stress/BlockingConsumer.java rename to driver-tests/stress/src/main/java/com/datastax/driver/stress/BlockingConsumer.java diff --git a/driver-examples/stress/src/main/java/com/datastax/driver/stress/ConsistencyLevelConverter.java b/driver-tests/stress/src/main/java/com/datastax/driver/stress/ConsistencyLevelConverter.java similarity index 100% rename from driver-examples/stress/src/main/java/com/datastax/driver/stress/ConsistencyLevelConverter.java rename to driver-tests/stress/src/main/java/com/datastax/driver/stress/ConsistencyLevelConverter.java diff --git a/driver-examples/stress/src/main/java/com/datastax/driver/stress/Consumer.java b/driver-tests/stress/src/main/java/com/datastax/driver/stress/Consumer.java similarity index 100% rename from driver-examples/stress/src/main/java/com/datastax/driver/stress/Consumer.java rename to driver-tests/stress/src/main/java/com/datastax/driver/stress/Consumer.java diff --git a/driver-examples/stress/src/main/java/com/datastax/driver/stress/Generators.java b/driver-tests/stress/src/main/java/com/datastax/driver/stress/Generators.java similarity index 100% rename from driver-examples/stress/src/main/java/com/datastax/driver/stress/Generators.java rename to driver-tests/stress/src/main/java/com/datastax/driver/stress/Generators.java diff --git a/driver-examples/stress/src/main/java/com/datastax/driver/stress/QueryGenerator.java b/driver-tests/stress/src/main/java/com/datastax/driver/stress/QueryGenerator.java similarity index 100% rename from driver-examples/stress/src/main/java/com/datastax/driver/stress/QueryGenerator.java rename to driver-tests/stress/src/main/java/com/datastax/driver/stress/QueryGenerator.java diff --git a/driver-examples/stress/src/main/java/com/datastax/driver/stress/Reporter.java b/driver-tests/stress/src/main/java/com/datastax/driver/stress/Reporter.java similarity index 100% rename from driver-examples/stress/src/main/java/com/datastax/driver/stress/Reporter.java rename to driver-tests/stress/src/main/java/com/datastax/driver/stress/Reporter.java diff --git a/driver-examples/stress/src/main/java/com/datastax/driver/stress/Stress.java b/driver-tests/stress/src/main/java/com/datastax/driver/stress/Stress.java similarity index 100% rename from driver-examples/stress/src/main/java/com/datastax/driver/stress/Stress.java rename to driver-tests/stress/src/main/java/com/datastax/driver/stress/Stress.java diff --git a/pom.xml b/pom.xml index d60f5f61253..07d819ede40 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,7 @@ driver-mapping driver-extras driver-examples + driver-tests driver-dist From 16842dd17d5d79c0728604b502d47ba3fdaf030d Mon Sep 17 00:00:00 2001 From: olim7t Date: Wed, 6 Apr 2016 17:16:29 +0200 Subject: [PATCH 2/2] JAVA-1150: Add example and FAQ entry about ByteBuffer/BLOB. --- changelog/README.md | 1 + .../driver/examples/datatypes/Blobs.java | 253 ++++++++++++++++++ .../src/main/resources/cassandra_logo.png | Bin 0 -> 12623 bytes faq/README.md | 19 ++ 4 files changed, 273 insertions(+) create mode 100644 driver-examples/src/main/java/com/datastax/driver/examples/datatypes/Blobs.java create mode 100644 driver-examples/src/main/resources/cassandra_logo.png diff --git a/changelog/README.md b/changelog/README.md index a9e4b03fe64..4484eef0b97 100644 --- a/changelog/README.md +++ b/changelog/README.md @@ -21,6 +21,7 @@ - [improvement] JAVA-1164: Clarify documentation on Host.listenAddress and broadcastAddress. - [improvement] JAVA-1171: Add Host method to determine if DSE Graph is enabled. - [improvement]: JAVA-1069: Bootstrap driver-examples module. +- [documentation] JAVA-1150: Add example and FAQ entry about ByteBuffer/BLOB. Merged from 2.1 branch: diff --git a/driver-examples/src/main/java/com/datastax/driver/examples/datatypes/Blobs.java b/driver-examples/src/main/java/com/datastax/driver/examples/datatypes/Blobs.java new file mode 100644 index 00000000000..70aa1f8efb0 --- /dev/null +++ b/driver-examples/src/main/java/com/datastax/driver/examples/datatypes/Blobs.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2012-2015 DataStax Inc. + * + * 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. + */ +package com.datastax.driver.examples.datatypes; + +import com.datastax.driver.core.*; +import com.datastax.driver.core.utils.Bytes; +import com.google.common.collect.ImmutableMap; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.Map; + +/** + * Inserts and retrieves values in BLOB columns. + *

+ * By default, the Java driver maps this type to {@link java.nio.ByteBuffer}. The ByteBuffer API is a bit tricky to use + * at times, so we will show common pitfalls as well. We strongly recommend that you read the {@link java.nio.Buffer} + * and {@link ByteBuffer} API docs and become familiar with the capacity, limit and position properties. + * This tutorial might also help. + *

+ * Preconditions: + * - a Cassandra cluster is running and accessible through the contacts points identified by CONTACT_POINTS and PORT; + * - FILE references an existing file. + *

+ * Side effects: + * - creates a new keyspace "examples" in the cluster. It a keyspace with this name already exists, it will be reused; + * - creates a table "examples.blobs". If it already exists, it will be reused; + * - inserts data in the table. + */ +public class Blobs { + + static String[] CONTACT_POINTS = {"127.0.0.1"}; + static int PORT = 9042; + + static File FILE = new File(Blobs.class.getResource("/cassandra_logo.png").getFile()); + + public static void main(String[] args) throws IOException { + Cluster cluster = null; + try { + cluster = Cluster.builder() + .addContactPoints(CONTACT_POINTS).withPort(PORT) + .build(); + Session session = cluster.connect(); + + createSchema(session); + allocateAndInsert(session); + retrieveSimpleColumn(session); + retrieveMapColumn(session); + insertConcurrent(session); + insertFromAndRetrieveToFile(session); + } finally { + if (cluster != null) cluster.close(); + } + } + + private static void createSchema(Session session) { + session.execute("CREATE KEYSPACE IF NOT EXISTS examples " + + "WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1}"); + session.execute("CREATE TABLE IF NOT EXISTS examples.blobs(k int PRIMARY KEY, b blob, m map)"); + } + + private static void allocateAndInsert(Session session) { + // One way to get a byte buffer is to allocate it and fill it yourself: + ByteBuffer buffer = ByteBuffer.allocate(16); + while (buffer.hasRemaining()) + buffer.put((byte) 0xFF); + + // Don't forget to flip! The driver expects a buffer that is ready for reading. That is, it will consider all + // the data between buffer.position() and buffer.limit(). + // Right now we are positioned at the end because we just finished writing, so if we passed the buffer as-is it + // would appear to be empty: + assert buffer.limit() - buffer.position() == 0; + + buffer.flip(); + // Now position is back to the beginning, so the driver will see all 16 bytes. + assert buffer.limit() - buffer.position() == 16; + + session.execute("INSERT INTO examples.blobs (k, b, m) VALUES (1, ?, ?)", + buffer, ImmutableMap.of("test", buffer)); + } + + private static void retrieveSimpleColumn(Session session) { + Row row = session.execute("SELECT b, m FROM examples.blobs WHERE k = 1").one(); + + ByteBuffer buffer = row.getBytes("b"); + + // The driver always returns buffers that are ready for reading. + assert buffer.limit() - buffer.position() == 16; + + // One way to read from the buffer is to use absolute getters. Do NOT start reading at index 0, as the buffer + // might start at a different position (we'll see an example of that later). + for (int i = buffer.position(); i < buffer.limit(); i++) { + byte b = buffer.get(i); + assert b == (byte) 0xFF; + } + + // Another way is to use relative getters. + while (buffer.hasRemaining()) { + byte b = buffer.get(); + assert b == (byte) 0xFF; + } + // Note that relative getters change the position, so when we're done reading we're at the end again. + assert buffer.position() == buffer.limit(); + + // Reset the position for the next operation. + buffer.flip(); + + // Yet another way is to convert the buffer to a byte array. Do NOT use buffer.array(), because it returns the + // buffer's *backing array*, which is not the same thing as its contents: + // - not all byte buffers have backing arrays + // - even then, the backing array might be larger than the buffer's contents + // + // The driver provides a utility method that handles those details for you: + byte[] array = Bytes.getArray(buffer); + assert array.length == 16; + for (byte b : array) { + assert b == (byte) 0xFF; + } + } + + private static void retrieveMapColumn(Session session) { + Row row = session.execute("SELECT b, m FROM examples.blobs WHERE k = 1").one(); + + // The map columns illustrates the pitfalls with position() and array(). + Map m = row.getMap("m", String.class, ByteBuffer.class); + ByteBuffer buffer = m.get("test"); + + // We did get back a buffer that contains 16 bytes as expected. + assert buffer.limit() - buffer.position() == 16; + // However, it is not positioned at 0. And you can also see that its backing array contains more than 16 bytes. + // What happens is that the buffer is a "view" of the last 16 of a 32-byte array. + // This is an implementation detail and you shouldn't have to worry about it if you process the buffer correctly + // (don't iterate from 0, use Bytes.getArray()). + assert buffer.position() == 16; + assert buffer.array().length == 32; + } + + private static void insertConcurrent(Session session) { + PreparedStatement preparedStatement = session.prepare("INSERT INTO examples.blobs (k, b) VALUES (1, :b)"); + + // This is another convenient utility provided by the driver. It's useful for tests. + ByteBuffer buffer = Bytes.fromHexString("0xffffff"); + + // When you pass a byte buffer to a bound statement, it creates a shallow copy internally with the + // buffer.duplicate() method. + BoundStatement boundStatement = preparedStatement.bind(); + boundStatement.setBytes("b", buffer); + + // This means you can now move in the original buffer, without affecting the insertion if it happens later. + buffer.position(buffer.limit()); + + session.execute(boundStatement); + Row row = session.execute("SELECT b FROM examples.blobs WHERE k = 1").one(); + assert Bytes.toHexString(row.getBytes("b")).equals("0xffffff"); + + buffer.flip(); + + // HOWEVER duplicate() only performs a shallow copy. The two buffers still share the same contents. So if you + // modify the contents of the original buffer, this will affect another execution of the bound statement. + buffer.put(0, (byte) 0xaa); + session.execute(boundStatement); + row = session.execute("SELECT b FROM examples.blobs WHERE k = 1").one(); + assert Bytes.toHexString(row.getBytes("b")).equals("0xaaffff"); + + // This will also happen if you use the async API, e.g. create the bound statement, call executeAsync() on it + // and reuse the buffer immediately. + + // If you reuse buffers concurrently and want to avoid those issues, perform a deep copy of the buffer before + // passing it to the bound statement. + int startPosition = buffer.position(); + ByteBuffer buffer2 = ByteBuffer.allocate(buffer.limit() - startPosition); + buffer2.put(buffer); + buffer.position(startPosition); + buffer2.flip(); + boundStatement.setBytes("b", buffer2); + session.execute(boundStatement); + + // Note: unlike BoundStatement, SimpleStatement does not duplicate its arguments, so even the position will be + // affected if you change it before executing the statement. Again, resort to deep copies if required. + } + + private static void insertFromAndRetrieveToFile(Session session) throws IOException { + ByteBuffer buffer = readAll(FILE); + session.execute("INSERT INTO examples.blobs (k, b) VALUES (1, ?)", buffer); + + File tmpFile = File.createTempFile("blob", ".png"); + System.out.printf("Writing retrieved buffer to %s%n", tmpFile.getAbsoluteFile()); + + Row row = session.execute("SELECT b FROM examples.blobs WHERE k = 1").one(); + writeAll(row.getBytes("b"), tmpFile); + } + + // Note: + // - this is written with Java 6 APIs; if you're on a more recent version this can be improved (try-with-resources, + // new-new io...) + // - this reads the whole file in memory in one go. If your file does not fit in memory you should probably not + // insert it into Cassandra either ;) + private static ByteBuffer readAll(File file) throws IOException { + FileInputStream inputStream = null; + boolean threw = false; + try { + inputStream = new FileInputStream(file); + FileChannel channel = inputStream.getChannel(); + ByteBuffer buffer = ByteBuffer.allocate((int) channel.size()); + channel.read(buffer); + buffer.flip(); + return buffer; + } catch (IOException e) { + threw = true; + throw e; + } finally { + close(inputStream, threw); + } + } + + private static void writeAll(ByteBuffer buffer, File file) throws IOException { + FileOutputStream outputStream = null; + boolean threw = false; + try { + outputStream = new FileOutputStream(file); + FileChannel channel = outputStream.getChannel(); + channel.write(buffer); + } catch (IOException e) { + threw = true; + throw e; + } finally { + close(outputStream, threw); + } + } + + private static void close(Closeable inputStream, boolean threw) throws IOException { + if (inputStream != null) + try { + inputStream.close(); + } catch (IOException e) { + if (!threw) throw e; // else preserve original exception + } + } +} diff --git a/driver-examples/src/main/resources/cassandra_logo.png b/driver-examples/src/main/resources/cassandra_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1637e61c14e5b7b9d0b76fde6c825365596de96d GIT binary patch literal 12623 zcmY+r19WAZ?s;B(5j}2IfHy2Id#|-*~^HFEj%f*oi(E7$6l449hOFRe}2p3uh;;;Rpuy-QYjv z+dsZy3ox*68WzgxPUi=QW6BGRx#L0?>SY1Y*NXXW~gou@ng^q!k7oLcSh}*%~6rd<9`aj*jT0F$& zPEK|JdU{t^S2|Z_I$H-bdPYu8PI?9=dL|~?F9fZlyN#298?B8a$$ty^|KtdpI2t)v z*g09)+7SImu7RPgvl9<7@qZNk@AKc|bh0r0|25e-{*PH-2GalM3q2zp1O5NX{_4v8 zA1*-7!NTNA^MB;?GIIYH^8exej~;IN|0w^z2J_#R{ulSlR9<*)`v09aUU=zV3JowY zdVL9D0cE#u=iV@0Dk3W%=ra<2qy<9af*~N!1mf^ed}T34d!pixViq1=;cL0a(N#Mb7#bvY= zNpTkrEB;u1H{bB@->_F;+s*O1MGOItC8&)1r6R`LXRG_$Pfmw^?!}Iq{Y3iJ_qAjL zCeKc8Yz_!UJ5h8GcktSNDnVLGN(RDhr=e(klfV))cX#&#U)PWQv6TGkY7nA7>0@Vy zCsLjK%Xk1xjrB6qT%}H%b*K{`A0J@|GCuWKzNaBI_`52eHGW9iBokUf_w9a9H%_2G zb25nbo5W1`7NpIeY}Q+`r{`xJ@by{~1TSxIne-$YHN&ZF9uwj6j7^_cS_cOQ6ciNw znb3a`loM8S|NP7QLar~`F7uR%We?I){X*66BRDBzd}z)OCNgqcAy=Dh#VjpLB{CXq z2^AF;x6aRjGwG0TioD<580^UPw<3w1rh`I-`VSIU#vBjE8HF1?A8pRoTF`lUc~g14 zaTQ%I^6GgaS8Y3>91kZ4-g10;`Sjer$?<-05R;I|aK1iX4vo=w1Y&f({n734!14Oz z_T^Qr(v5;f!X=`iL2zBOhneJj)D8>|PL}W@%N+AKohyOx_4O4RFa4bT{J@$>XJw}T zkSnL><=sf+^nP(7B_~hce2{ua>AUiSfiMGyZs+**DPNO+LaUHS5&4b-1L|_6U*mir zAQ6BF_%mJVY^BX{Fp_<&N9M~TsOtL%0MaM!$_^;SR(QyKU=*r zn8vV`&Gz;g=Fb-hqP35}UX$l{_E_9_7hiA0ITpLQ9>CHwocN=m9@h?iUBfs$Jlr4m zrhNw}V}Gz-1%DYqo%f5Mqarte$@^Cg*3#0#k?0O95{*UXKr1XPjE&*D{gxBwa|K4= zwWn}*x{e1i#RD@HH!M<}<8_BBy6#4DGBjYY-s&t$<93?mit_WPwem-XXv|=@KZN~6 z!{QE1WOqspewu5;VD(^N{koFJ^>Qf}%?QNqXR(fU&S)Zj zl_y6&TSp?SGCTp-9j4<5A5R1}U+Pum%lZ+D>blbUJlViQXYvhmd|Y(i%CcClVY9#Q z)2DK~GhKAP+K?2U&X<{4zfdU^VK?YyyFDXA#^2xroVpzVZ!5Rd&_r-uF=)J`r2R{8 z zusP9kZUJsrTTGh)nZ8UAd)0Lxp|z3!TFRw+!F!_dm%#nY0-=^dQdC+nZ zouaUU1D;(jq=vTdI+nUTu^!Hsf;%!6yZL^@hx}Wif7!-!I$neF_y3kE7t^UanNx7G zku1y`j@;@==!If**)nt|BEW`#Mc0p4)fGL}^#oqk`9f{evY*U^WD1}1P|)dmIo&6; z*VPyJpsc_MEqON)o}=#5YzDAK)iV)A`Ud?dfhB)T+Hj zo10gwHOl6CEF&25L8G5uo58{bVsI?WAjwvkuY3vNu-g=Np#mlDu4(&sMn8_yS zDs^3<)wGizhECPh_XJ zdd53^qgV&3QTgF=KsW$;vP0az#JNquDPHe=LpfWm!>hRt8`RR9or2LdK>de~86Daz z{BvZSzhjVgy53vuY@-!UOO<{oS|wr&9Xs-`=C7rZqqgkzw*1&>J2hPoDrARaX;7S$ zHBhUex58*}tJI*c+G3Xcjm0)w!?Ekq9|ja6<(B--19tuI`0YK6{@<0-w$+2V+JxkO z(I{MtR)=#L(yNWbJRgLWhMH+uB*iPZSbIu5b{{4@EqHj;QymB@sDWm_Cos!FRsnqI z%E&X!7+pD;{?Vuw{$f#edmz16gE2E&Z@@Gq^^fcJm#3H9#;)(=Rcp}*@jECtJIay8 z)Ovi>kSSiBuoY;=5gu&Yg^o=btz!WOc*1l;D+J)wUh#IUrS(8=`A zl_?s^X&eGA{zXDoxWr(Xu+FH!`R|o|gtXLf-W3>ve=5<#uTnVz7p3?Ef}^JEq$qxC zGY~3uX8j>2-AES@FzwJC7mxzt93LI!7=LZC=EFJ(x{4ruFAFNmU)9>pKzG5ZqQ3MZ&H z#V?u2!ORLpcgvpvlmh`ix=)XT0qV`nFm(Z5c8h+E!$kqVQ-n8Kb=R5TO4!`$d;~IH zw%^W!iaRy_gk*^Av|C|cc<8gA#CXG)gCOIk6=3t&P%&_F=bPZI_v0H&%Jc7*5dNG5 z{xv-Zz=l)VvO;7xL9Ff7CIu5m-eCCTSOefrNy{1y7;YCT1mLgIf6w{`5S@ z&%lRhyo-;<8gbJ~f)E@C4xg%g_ zvXsmpINPqUi$k4AaWY7s@;zf=E~R(}@wCpwyI;zSB5UyEBSx7BB3yY^uloCR zE6Q*2m-6y{`>(cB zP;T4~e;xH05M-^F5~XvHjlz{C`7jqTRw4!R%ncg_Tlz zB&qrLD3=N$eJr0I~{JAwqGPFzh&jXtRB3O$78Mct{!sklHZrF z;G8e?Fvks6CY=Ijupo-{zD2s2tnnUkQw1Qk$7ZWi)rk1mP-@~f^XY;OdZYh*KDXq0 zC(BXJv)X|(jYmtrCMMtwtMei}@jcx9jL31xUAHC-A7EY=+cqExby{_$AgHZNNg4!Y$5ZB~`zXq2 zfSrxvALDu^#!Xf+EIS-&9Ej(dr+-&(>;MDP)C4;;!GlZOJCErKWto3dnl+|0Q{!-J z!&0UbS$A?^bg@O+UKjYf(6UX&*jibl#(4d_M`y?|WK}ha_4rWiUc>6nxJNJ#`<` zfoPHh(j+=~_R#n(?mF2%6gw zId*4~5B~|MhX}>mN4UE0892w=-US{j*CAZ3gJjJ!QEbCCcuUzU-z_;WP7?hBTN|n= zrVwaFl87yJrRYV&BT(wclf_s&toqEybyC7RcUuZn+fW|a*ZfWaO@Y2%2u7P$eu=y( zcfy#1UEu;Dx=#*7--`;j`F06}-P+j7Bgn&G(=-kkhz_KJ+gvWza59>#89n#!r89J7 zp~)gkgoPA6*{qlRj0bzIzQ5@b*cLi_$PnNM=DuGuq3@xCK5Yg)YdyH9%3{7n*iQF$ z>H6TUPq96m^%JDns}u%iOr&1AXJ(IP-m zwKNwRVZXzOkVETgiqH+T06jGd3;BfiHnd$+?mo`u?NcAto6I+Bm>Ur2P*L*S{01m( zwA~-l%&y4TTPSa2Ag%Z)cCIeqa---7+rPF=-G^Wne?c5{dgy$z5$i!vjPE(Cl3AT- z6hoM1BHLW^&>;7n*ZToB2nPnBR*jXSC^T%>wO=_PMc+1Vu6@DyK7pqg1@9wr+RTBo85^4Jiy98F{KfZXg741ee&RMNNHL{hzYgz&JO3C+M>*Zq`7{Ut z>AoiqM=U*#*tUn>E0J$Ct`2T7G&P;HH@_!dMNqk8?36-K+O|ELzGcBE9L=Snqc)$^ zpbI{|r?x%T8={KzUWkcq4^g)9aQoWeWO-tqFl`-_XY-({HqmvyyjbmaRZlsoxV!vb zdtC2g6?Mt{50z7np0KLA@Otqsb(c$=TClHeOu1}pi`pOK)6k$eiMYAgc?Q3231IZ^ zQ3QC(qi6}V_g}9lck(Q3(ED!w(7q^R0@9}x_WH*Ba#AT5J=&Wc!YS5`IPGUP<M) zV)f5%L$$O?Mw7Od39fZ`O#A7G0g9EN-WknjNrERF->(;h9uJ(m8>%;WXV|)zv%h`L zzq;q?Q)m;AS~(U;Wgvyy{ul`L1yaWo5(IreC>MQ?C9;;}3XFz}TN<%q8_TG+=4!lt zl!92@TCi2yI?ib#A}7YC*IPh$4uB9pT0@`wHnwfUR&w4&?odI^LCV4R^Ou6}`+}6) z>t)RCT-tQ=ZDm(nsJC5gh%$BXXNGNOPgje^{_GefNn?>EoGgqAEOl>OM{yZT zLDvmKgpc4c9m8^&#dpUv28MIP_Rk%p|=xF$w?QU+RAdyy4IrYp-#BOhjr6RD3y zMMa=Wg@`Za&J4@P3-bRYI&qg&zERVUcL}ejQ0pWxo^s7FF&f^0pJ{f&ujb$ciyanh zRdu8rB4iQS+R}74Bdp}_K50B{zg)Q4TzEvDZ=`N`Y!wz#Vb2g_l6=%5rZiQB;A>}4 zJY3%etsUSJ2(D&PbFC_MdobNBQS&+m6f~b%@iiJuJ^YY<8Om2@Jr*Lp6EyzaTi*QGqbO~-hF)ty49v^0;NRmZ$NY)}$51UiX63w)4@`4?SRfSHI*LY-N%8VXocvw}ur|645Q|nSf|GxxCH2fbV=P z9JMCqHn1tlcFvp8W}>3O%V!0(R!phojwy7a5Zw4~)f^nhNk{<2HmWoVO#*59W%yjoSC1$@Bn`$>LYOtDVQGp|nb{AcgJes^So z*R`I2PkVyY_BoBrKnLLB)J&%GHtK#_(ex8}hH<@kcYIs6q1KbzaOKx_(oXJKV;`LU$F_!3 zQTbPLnTw~@*%46p_5fT3WNMp)1ql_>$2c-!gb1rAM+ctUrOoE*%_f$xC4W+u`Yx%c zao!$I(T}Np48@a4Wop^B3`msgeX2dH=oTa6XKwcebIuu#nmL!IFFuq2fK4U^g@tJ@ zNI2X}Ef48dnNJ?~KeOSsVi^H)+#k(Ke1MnK52y3F5sh}Yn&SZ4K3!+Wm3oWGAS`V= zX@dLDb-U{P8iXpHRrjadkkP^Usg3#S0 z*by{|<>tnwxT6u}j(wfS{DjG;CY^X?DiuS~&H(wzVg5=Ls}b7NwTo;iPlN4~sI_EW z@%ikM@b>GQQGC%&*}Mec^d#bJA`Z8;As@+RY*Gvyx9&+}at-!U>#xOW5}8$%Y|<9# zpaA0!+2flW(q`Q9S&jJuZ!SfK+q&=9$8|Bp5uBehUZDZH3LjimO@B3?toW%2DHJ|%K<(us2#xi@$uE|-oHh{fX z;4Qc0sL5Sc`^zZJ!5uM^Vtv6}QlN^btD;d$*fk;ieDWx8HScB*1>e38$$SbU`WyGd zD_G&BV)h}u+tGH2y&vuxTe|iA(Qg%v*OsANh)0&Arh$-3Ko$QpOH;}f;Gl8!3VMp7 zIRqxLey4O~dF?7unsA(KZRfXdbn*$NJ;_o@WbR2NHMU?A{vrYzq$YWMy1ItEm`X73 zPq==lMSizf!bZCYHvp@}IbuFd%Nb`vB5GHt{E26EPVz{7^jQ!AFx}uggt;K`?$v#$ z&y&x&Y)%1GS1Lh72yg z_Ht@8KQJx{?w4FgbVoXe0eCt&*{?t6RZ|sxOgK)_Sy@DygLs~cI!B@yiZG@&MxMbs zjz}K0-X|HzO>?}#?!To#$-+ePA4(He_6k{zWr+S8;UuuR%(t*IDVWxq33pWIWO2H^ zsnDopgSBa)SqGtiT8loPNZ4ZI!axnBxm%@|?)ssj+96?->nu#*s7L7B+iHinon!m; z-;Y8hh@yk^w)M7L`j34Xe{7dysCfIrmtFjT1z5hRycpe$#tJj-@uv$~{6xrbeQgAa zAB6Q41Abx@6k} z+-T_NW7ts>rMqQhWIrxJDFoiA0pz(Wz_cb+lzvL+YuM9E?7MvS`N+NaTs-(@>c3vf zJQKA!-Y#EZp4itBs3k!Pj#_+yzr$@UJYh}@r);~XptreizaHb*ry^!)kt|?73D1Je z(qKneSP`n|$!t1%T)x{as(7QN)rFB|t9Et9_pH$W)1i5MA$nYBC8N7h40n^9DK6bO z-7?{(iZuVnENE@ojqjYaf*cMk|gkI>#GLXrN$x5f$pf6ZmqQMpr zBajRsprs%J(v(^gYoN*z*6F`fNPI(0bOYy1aq?MFEpil^O1A%pzX%ST?D!FPErXJ= z6zkb>nsDV6+Kv13#|#B$1MCx+T=2EYRg|*32#C>P9>om_t*ncFHUU-qA`OYjxRNCi zdvFpQ7Pt40;e|7Ok#Yz-8G8!It*-hX%Bzx6Y^@Jcyl-~Ya}{??$LJQ8UodbrAM-mf zlhcY^*B8hQJ$p=k@PHxb)e%(#p}6Rbmce>0sOl&J#%-{c=cOt8`wwIJ}XE<3%k>fF%`DQiSKsO($+*!|npn@ZmQCzyn6h=pi z8#Y{qn>*EVR-jCBPLyD#7B8JCqiZQc5Wp`>hxAw3v|~s*^G2*x0vC~oFIJ+cnh(M@ zx?}!l?NX_d=bY6v0aE-5Nv;S@_I9%0RxE>~VI(1^8B(Ko+wz{$z6M63U!*~CKlyrw z2_q^1Pe0mR0X|d4PXrk&JmZq!d(H=W|G1sl zEvDlcRt2glq>qC$F1kr3v*RF=&F*kVH-&1wCyUPhd19FM!?h~H$duIGSMuqjEM0cF zmpkc_B}QW;M~C^YfS{BCl`PJx2i02@{W%kQ+zj+rrS3Gf7Sxh7Yp}vEyynAsCMUPJ zrOCPi4i!V6u1IL!a`?R+`S|JSw`s5GgK0tOH~c8 zXA)?0gF*$)DvqCsTPauQ|IFgv~0M&&2Lo3L;G%I zrQN^h(#Dwkhx&)3154GGP!2IadoKvhh@y~U3LGctN#ifoxW04!yE$J@mmZR`vf;!J zk1(HdF5W$G=}g>~5zWBC)Y?u%5i2V@bDp0 zRvCAdV1HRW6V6F)qM&bB~5=c+j;e zfJu`8^`Eiv$U$Y=M8zH#LXnjRn3CogtSMNY%-f zwjvnf!8p7}wCON5VY=DOodrmLcJ>ewOE7PX8?GW|o+)84oBfrKMI-60rf1w4+8qSJ zS#qaEuUe1HqN?U@hg4q>tjs0x?VXoEea-6k8RRr6?#TU`1+(dvwyaIZgG6T{!th^X z_)vFoPd9(tug*d22X$jn*MTckD%V|OqdDzOlD_qj6r{5o#s3S(@>+U4# z#VfzszgC9K#Yv-%bJ@@3KE)Oc1xn(Ikw&rUCS!s0qM2}hA&YQHSjvZ7_CrGyI87qC z7t|sJ;k=)&_#EHwPU!pxnks8^4`)jjW852Z1oefwC-~lG+U4z(z|%sVvMZdbk#;j{ zjmjJ#i>(l838ZsXo=RguK%Z6d*U#(9lfY1IMLRSVOei;TG|fE;CZEsuM^mG25iA{J z@tpca)~OBm(Gr1K&rr}fQ+#TK|ygAz58G0Dusk1 z@e>eE+0#vso#Zg7>`mnRF)V3u*X>4Esy~5PSbu}uZKE=0H4Ek8ZBV##swmf0>kMlC zNd9B~$Xau_6rnfAKgF^j`FjcPSuw81y?==TlBQ7?D$snAB%%~QVOf?<6LQVbM)IrJ zN(c}dwY3_v%c8)u#3+dQa%R+BoCzg&&4!f=)3wy4C{`8I@g4dlfIldMH~x&OZ1xWe zpEYfGlqr0QV4vbJFlFsKzOR`(oc{|ONyeWHqj7E+bw57B8wC4h5wtloSs}Yh%PX7; zzCDh@V2`TRnl@a7T8Z(7CbWZop*Z|@xA4=-1-2NR5sEXV5MO+(R(~vw#|l$s2+c5A zbs-6S)MEmunI<-aVtG5!W{HmFH6coxl9fW;-5908i0$jka}bz zHbo&C1+|4dkw2Q}mj}nWo;#k%9_Dhq^(`e8Oco!?cfLEYU|q&QmutB-4yNAz#i}P0 zsFOLQArd+6pkh5YxG`0--T1NKRM%Xdo)l{C*SmyNK3il>iU$5Ef^`Yux+vp>klVE4 z9JUQ*q+tGGwKjqs=H(h^ixj!UyR}XH~**Jpr9IWyQO>;Yrg~o=(7x!>R9brY68@q>vZ&3VNgKEu021&dSj0 zC*(t<`v$|Lf<)&FouIrP?QSeID9(Fk)#R>dNsjORhtfzaS%7kOW=NxU(zz)hX~arZ ze2K83sTJ4+To~iG*4jfygILtEgKVs5v)Cv<&{qEGXGalx+4HA^BC>?gkbsR?)_cye zWEU``X9g}w6Dzus*@;mh-6Ej51J5MEh@jOaV`LU`Q6?Rw)P6`PQfKk*`0NyU6hAw9^#q*hNyy;G{l*hhR1G9bbAng| zG~f~e`A?^inzK|~x=71X;YpIJ1L7ZKE0I3;i!!c$;P}lkb;#_KGZCOhKp_>74{oNa z`jgsVd(5j0Y7~LNHE5jw_!slgv@u{xu|5)Ob9GY1<*@a#QjCqFho(a6~t2*V{4@c z{e`O+{mO}T<^}!8B$oGjG)F5VKcNwR$RZ8C6(3(XKsB)96#nb4m{psRlXsWAeD&je zGWh4;G-fkgn8*tnRSlC+B5S-omFyuM^Y}^XRi>g-i#wTPff~PF@sECGM~L3nX{4~O;_QMhpi&bsBMToiPj=LhXBZe}N=@(umaj)CN#CDK;yvHc?a^3?XVzsBYjdV)Q=r zxKboyHVYQQc>Y(V)#;9f#Lg=D{bXU($lYiFkV)+{zJilnEFatp5L+X00>6eEf-eMQ zLZh6ZpA&qUNihK0C6R^XVJBy%t{7F$dW8{1Zl6TxE>Ojq{u|~`D!E2c&q?snBBw)= z{_9@Wx|dNaIKsV^GSG3L?K~(CRO=`PFAf69Dce5>m&^uB#WURJLHsI=OyzLje@}LL zj%^Fj-CMTysLB0yfG*AqV&nx+B9s5f9ydcRPjy-rtUWxVqIH;5N8&$*x=|l1AroBW z;c{!Gch);EFTZ1hQoS;P(J-MoksLojmvn_PurBDQ{T@CSzt@~5Tl;aA(7hw3U|aQe zs@I&ATV(@kTPMtQ!b*6Fo25lis4$rNFG&U^TphQY|v71Pxf#AOTW6(G^+D0oZAj=led; zkU;XfCQ!f=;SdH%pY_NOR!o2=KY0d{a;*tv)a2t~Fg8iKG2Lj45mb(h+1Nq@z|Ag2 zX@1jWRkZ2B8M6I2%MJX{D2sE|5lwKy}ban3oJPcD1246!yY35P#GS)JYk zVTwvT-`=?&)f4}! z8xZH*a9k{7DUM&%5p`bCRrn+&TYsN_DvQEYZklPf-?E8WsNYXOMGmB)6_G?x#wnfB zX=F86Ga2iz|LdR=S*%bLxz)EjfM&>7fN2 zXHU3hMm0phTt>sPMkfE8TaL?ubG>7d?Bl(?qYLT{m-4lklF2O5N_(TM3@%c=XZ3lV zQirCfQ|+5&M7NIX^FzznKgpI0rgf8MU$ushlf4S-)pcj(M^};eI+JYJDYl6cT{-0k z+pv&LZD&4I!LN%LV*Zn~=JHTE{mauP8G5`indP*FA z^lb1Sqt@ekw~PhtyVf$yVC*CsgoX&7>&I_%#o|87mXxu|zgKIXCg zlaNFvs?T@9#NEY%{nX{>&n)VU#bm-frpk+>(2831b(}}dZ8SZrBpb%kAdH%YttTri zw`V&MAZCtWKM8eQ6hkmBlqkea